From a9253fa6fce74ef0008442c15944ff476b498c24 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Tue, 4 Mar 2025 13:00:32 +0000 Subject: [PATCH 01/13] [1 changes] chore!: remove deprecated hash functions from stdlib (https://github.com/noir-lang/noir/pull/7477) fix(frontend)!: Restrict capturing mutable variable in lambdas (https://github.com/noir-lang/noir/pull/7488) feat: perform constant sha256 compressions at compile-time (https://github.com/noir-lang/noir/pull/7566) chore: bump external pinned commits (https://github.com/noir-lang/noir/pull/7565) chore(ssa): Turn the Brillig constraints check back on by default (https://github.com/noir-lang/noir/pull/7404) chore: bump external pinned commits (https://github.com/noir-lang/noir/pull/7561) chore: address some frontend tests TODOs (https://github.com/noir-lang/noir/pull/7554) fix: shift right overflow in ACIR with unknown var now returns zero (https://github.com/noir-lang/noir/pull/7509) chore(cli): Forward `nargo execute` to `noir_artifact_cli` (https://github.com/noir-lang/noir/pull/7406) feat: Support `::method` in expressions (https://github.com/noir-lang/noir/pull/7551) chore: remove FileDiagnostic (https://github.com/noir-lang/noir/pull/7546) chore: add some extra tests (https://github.com/noir-lang/noir/pull/7544) fix: fix a few cases where safety comment wasn't correctly identified (https://github.com/noir-lang/noir/pull/7548) chore!: remove U128 struct from stdlib (https://github.com/noir-lang/noir/pull/7529) feat: simplify simple conditionals for brillig (https://github.com/noir-lang/noir/pull/7205) chore: put RcTracker as part of the DIE context (https://github.com/noir-lang/noir/pull/7309) --- .noir-sync-commit | 2 +- noir/noir-repo/.github/benchmark_projects.yml | 28 +- .../.github/scripts/wasm-bindgen-install.sh | 4 +- .../workflows/bump-aztec-packages-commit.yml | 50 + noir/noir-repo/.github/workflows/docs-pr.yml | 10 +- .../.github/workflows/formatting.yml | 8 +- .../.github/workflows/publish-acvm.yml | 2 +- .../.github/workflows/publish-docs.yml | 4 +- .../.github/workflows/publish-es-packages.yml | 16 +- .../.github/workflows/publish-nargo.yml | 101 +- noir/noir-repo/.github/workflows/release.yml | 16 +- noir/noir-repo/.github/workflows/reports.yml | 5 +- .../.github/workflows/test-js-packages.yml | 8 +- .../workflows/test-rust-workspace-msrv.yml | 20 +- .../.github/workflows/test-rust-workspace.yml | 14 +- noir/noir-repo/.gitignore | 8 +- noir/noir-repo/.release-please-manifest.json | 2 +- noir/noir-repo/.vscode/settings.json | 6 + noir/noir-repo/CHANGELOG.md | 57 + noir/noir-repo/Cargo.lock | 270 ++- noir/noir-repo/Cargo.toml | 26 +- noir/noir-repo/EXTERNAL_NOIR_LIBRARIES.yml | 8 +- noir/noir-repo/README.md | 2 +- noir/noir-repo/acvm-repo/acir/Cargo.toml | 2 +- .../acvm-repo/acir/benches/serialization.rs | 4 +- .../acvm-repo/acir/src/circuit/mod.rs | 8 +- noir/noir-repo/acvm-repo/acir/src/lib.rs | 4 +- .../src/native_types/expression/ordering.rs | 6 +- .../acir/src/native_types/witness_map.rs | 4 +- .../acir/src/native_types/witness_stack.rs | 22 +- .../acir/tests/test_program_serialization.rs | 2 +- .../noir-repo/acvm-repo/acir_field/Cargo.toml | 8 +- .../acir_field/benches/field_element.rs | 11 + .../acvm-repo/acir_field/src/field_element.rs | 82 +- noir/noir-repo/acvm-repo/acvm/Cargo.toml | 2 +- .../acvm-repo/acvm/src/compiler/mod.rs | 4 +- .../acvm/src/compiler/optimizers/general.rs | 2 +- .../compiler/optimizers/merge_expressions.rs | 10 +- .../acvm/src/compiler/optimizers/mod.rs | 4 +- .../compiler/optimizers/redundant_range.rs | 8 +- .../src/compiler/optimizers/unused_memory.rs | 2 +- .../acvm-repo/acvm/src/compiler/simulator.rs | 6 +- .../acvm/src/compiler/transformers/csat.rs | 4 +- .../acvm/src/compiler/transformers/mod.rs | 8 +- .../acvm-repo/acvm/src/pwg/arithmetic.rs | 51 +- .../acvm-repo/acvm/src/pwg/blackbox/aes128.rs | 4 +- .../acvm-repo/acvm/src/pwg/blackbox/bigint.rs | 2 +- .../src/pwg/blackbox/embedded_curve_ops.rs | 4 +- .../acvm-repo/acvm/src/pwg/blackbox/hash.rs | 25 +- .../acvm-repo/acvm/src/pwg/blackbox/logic.rs | 4 +- .../acvm-repo/acvm/src/pwg/blackbox/mod.rs | 14 +- .../acvm-repo/acvm/src/pwg/blackbox/range.rs | 37 +- .../acvm/src/pwg/blackbox/signature/ecdsa.rs | 4 +- .../acvm-repo/acvm/src/pwg/blackbox/utils.rs | 4 +- .../acvm-repo/acvm/src/pwg/brillig.rs | 12 +- .../acvm-repo/acvm/src/pwg/memory_op.rs | 6 +- noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs | 17 +- noir/noir-repo/acvm-repo/acvm/tests/solver.rs | 8 +- noir/noir-repo/acvm-repo/acvm_js/Cargo.toml | 4 +- noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- noir/noir-repo/acvm-repo/acvm_js/package.json | 2 +- .../acvm_js/src/black_box_solvers.rs | 2 +- .../acvm-repo/acvm_js/src/execute.rs | 10 +- .../acvm_js/src/foreign_call/inputs.rs | 2 +- .../acvm-repo/acvm_js/src/foreign_call/mod.rs | 4 +- .../acvm_js/src/foreign_call/outputs.rs | 2 +- .../acvm_js/src/js_execution_error.rs | 4 +- .../acvm-repo/acvm_js/src/js_witness_map.rs | 14 +- .../acvm-repo/acvm_js/src/js_witness_stack.rs | 4 +- .../acvm-repo/acvm_js/src/logging.rs | 2 +- .../acvm-repo/acvm_js/src/public_witness.rs | 20 +- .../acvm-repo/blackbox_solver/Cargo.toml | 2 +- .../acvm-repo/blackbox_solver/src/bigint.rs | 7 +- .../src/curve_specific_solver.rs | 4 +- .../blackbox_solver/src/ecdsa/secp256k1.rs | 8 +- .../blackbox_solver/src/ecdsa/secp256r1.rs | 8 +- .../bn254_blackbox_solver/Cargo.toml | 2 +- .../benches/criterion.rs | 2 +- .../src/generator/hash_to_curve.rs | 6 +- .../bn254_blackbox_solver/src/lib.rs | 4 +- .../bn254_blackbox_solver/src/poseidon2.rs | 4 +- noir/noir-repo/acvm-repo/brillig/Cargo.toml | 2 +- .../acvm-repo/brillig/src/black_box.rs | 2 +- .../noir-repo/acvm-repo/brillig_vm/Cargo.toml | 2 +- .../acvm-repo/brillig_vm/src/arithmetic.rs | 16 +- .../acvm-repo/brillig_vm/src/black_box.rs | 6 +- .../noir-repo/acvm-repo/brillig_vm/src/lib.rs | 144 +- .../acvm-repo/brillig_vm/src/memory.rs | 6 +- .../compiler/integration-tests/package.json | 2 +- .../compiler/noirc_driver/src/abi_gen.rs | 22 +- .../compiler/noirc_driver/src/contract.rs | 4 +- .../compiler/noirc_driver/src/lib.rs | 97 +- .../compiler/noirc_driver/src/program.rs | 2 +- .../compiler/noirc_driver/tests/contracts.rs | 10 +- .../noirc_driver/tests/stdlib_warnings.rs | 4 +- .../compiler/noirc_errors/src/debug_info.rs | 8 +- .../compiler/noirc_errors/src/lib.rs | 20 +- .../compiler/noirc_errors/src/position.rs | 85 +- .../compiler/noirc_errors/src/reporter.rs | 86 +- .../compiler/noirc_evaluator/Cargo.toml | 1 + .../noirc_evaluator/src/acir/acir_variable.rs | 66 +- .../noirc_evaluator/src/acir/black_box.rs | 14 +- .../noirc_evaluator/src/acir/brillig_call.rs | 6 +- .../src/acir/brillig_directive.rs | 2 +- .../src/acir/generated_acir.rs | 16 +- .../compiler/noirc_evaluator/src/acir/mod.rs | 83 +- .../src/brillig/brillig_gen.rs | 6 +- .../brillig/brillig_gen/brillig_black_box.rs | 68 +- .../src/brillig/brillig_gen/brillig_block.rs | 120 +- .../brillig_gen/brillig_block_variables.rs | 6 +- .../src/brillig/brillig_gen/brillig_fn.rs | 2 +- .../brillig/brillig_gen/brillig_globals.rs | 14 +- .../brillig/brillig_gen/brillig_slice_ops.rs | 8 +- .../noirc_evaluator/src/brillig/brillig_ir.rs | 6 +- .../src/brillig/brillig_ir/artifact.rs | 22 +- .../brillig/brillig_ir/brillig_variable.rs | 4 +- .../src/brillig/brillig_ir/codegen_binary.rs | 6 +- .../src/brillig/brillig_ir/codegen_calls.rs | 4 +- .../brillig_ir/codegen_control_flow.rs | 4 +- .../brillig/brillig_ir/codegen_intrinsic.rs | 4 +- .../src/brillig/brillig_ir/codegen_memory.rs | 4 +- .../src/brillig/brillig_ir/codegen_stack.rs | 8 +- .../src/brillig/brillig_ir/debug_show.rs | 20 +- .../src/brillig/brillig_ir/entry_point.rs | 4 +- .../src/brillig/brillig_ir/instructions.rs | 28 +- .../brillig_ir/procedures/array_copy.rs | 4 +- .../brillig_ir/procedures/array_reverse.rs | 4 +- .../procedures/check_max_stack_depth.rs | 2 +- .../brillig/brillig_ir/procedures/mem_copy.rs | 4 +- .../src/brillig/brillig_ir/procedures/mod.rs | 4 +- .../procedures/prepare_vector_insert.rs | 6 +- .../procedures/prepare_vector_push.rs | 4 +- .../procedures/revert_with_string.rs | 2 +- .../brillig_ir/procedures/vector_copy.rs | 4 +- .../brillig_ir/procedures/vector_pop_back.rs | 4 +- .../brillig_ir/procedures/vector_pop_front.rs | 4 +- .../brillig_ir/procedures/vector_remove.rs | 4 +- .../src/brillig/brillig_ir/registers.rs | 2 +- .../compiler/noirc_evaluator/src/errors.rs | 48 +- .../compiler/noirc_evaluator/src/ssa.rs | 25 +- .../check_for_underconstrained_values.rs | 68 +- .../src/ssa/function_builder/mod.rs | 10 +- .../noirc_evaluator/src/ssa/ir/dfg.rs | 47 +- .../noirc_evaluator/src/ssa/ir/dom.rs | 9 +- .../src/ssa/ir/function_inserter.rs | 25 + .../noirc_evaluator/src/ssa/ir/instruction.rs | 53 +- .../src/ssa/ir/instruction/binary.rs | 54 +- .../src/ssa/ir/instruction/call.rs | 20 +- .../src/ssa/ir/instruction/call/blackbox.rs | 85 +- .../src/ssa/ir/instruction/cast.rs | 2 +- .../src/ssa/ir/instruction/constrain.rs | 24 +- .../noirc_evaluator/src/ssa/ir/printer.rs | 8 +- .../noirc_evaluator/src/ssa/ir/types.rs | 41 +- .../noirc_evaluator/src/ssa/opt/array_set.rs | 8 +- .../src/ssa/opt/basic_conditional.rs | 526 +++++ .../src/ssa/opt/brillig_array_gets.rs | 168 ++ .../src/ssa/opt/brillig_entry_points.rs | 2 +- .../src/ssa/opt/check_u128_mul_overflow.rs | 285 +++ .../src/ssa/opt/constant_folding.rs | 15 +- .../noirc_evaluator/src/ssa/opt/die.rs | 113 +- .../src/ssa/opt/flatten_cfg.rs | 81 +- .../ssa/opt/flatten_cfg/branch_analysis.rs | 4 +- .../src/ssa/opt/flatten_cfg/value_merger.rs | 2 +- .../noirc_evaluator/src/ssa/opt/hint.rs | 6 +- .../noirc_evaluator/src/ssa/opt/inlining.rs | 18 +- .../src/ssa/opt/inlining/inline_info.rs | 6 +- .../src/ssa/opt/loop_invariant.rs | 8 +- .../src/ssa/opt/make_constrain_not_equal.rs | 2 +- .../noirc_evaluator/src/ssa/opt/mem2reg.rs | 6 +- .../src/ssa/opt/mem2reg/block.rs | 6 +- .../noirc_evaluator/src/ssa/opt/mod.rs | 3 + .../src/ssa/opt/preprocess_fns.rs | 4 +- .../noirc_evaluator/src/ssa/opt/pure.rs | 2 +- .../src/ssa/opt/remove_bit_shifts.rs | 50 +- .../src/ssa/opt/remove_enable_side_effects.rs | 4 +- .../src/ssa/opt/remove_if_else.rs | 4 +- .../src/ssa/opt/simplify_cfg.rs | 6 +- .../noirc_evaluator/src/ssa/opt/unrolling.rs | 24 +- .../src/ssa/parser/into_ssa.rs | 4 +- .../noirc_evaluator/src/ssa/parser/lexer.rs | 2 +- .../noirc_evaluator/src/ssa/parser/mod.rs | 51 +- .../noirc_evaluator/src/ssa/parser/tests.rs | 2 +- .../noirc_evaluator/src/ssa/parser/token.rs | 2 +- .../src/ssa/ssa_gen/context.rs | 32 +- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 12 +- .../compiler/noirc_frontend/Cargo.toml | 1 - .../noirc_frontend/src/ast/enumeration.rs | 4 +- .../noirc_frontend/src/ast/expression.rs | 118 +- .../noirc_frontend/src/ast/function.rs | 11 +- .../compiler/noirc_frontend/src/ast/mod.rs | 63 +- .../noirc_frontend/src/ast/statement.rs | 285 +-- .../noirc_frontend/src/ast/structure.rs | 4 +- .../compiler/noirc_frontend/src/ast/traits.rs | 16 +- .../noirc_frontend/src/ast/type_alias.rs | 4 +- .../noirc_frontend/src/ast/visitor.rs | 192 +- .../compiler/noirc_frontend/src/debug/mod.rs | 243 +- .../noirc_frontend/src/elaborator/comptime.rs | 157 +- .../noirc_frontend/src/elaborator/enums.rs | 410 +++- .../src/elaborator/expressions.rs | 507 ++-- .../noirc_frontend/src/elaborator/lints.rs | 68 +- .../noirc_frontend/src/elaborator/mod.rs | 424 ++-- .../noirc_frontend/src/elaborator/options.rs | 62 + .../src/elaborator/path_resolution.rs | 18 +- .../noirc_frontend/src/elaborator/patterns.rs | 158 +- .../noirc_frontend/src/elaborator/scope.rs | 28 +- .../src/elaborator/statements.rs | 175 +- .../src/elaborator/trait_impls.rs | 39 +- .../noirc_frontend/src/elaborator/traits.rs | 52 +- .../noirc_frontend/src/elaborator/types.rs | 526 +++-- .../noirc_frontend/src/elaborator/unquote.rs | 13 +- .../compiler/noirc_frontend/src/graph/mod.rs | 4 +- .../src/hir/comptime/display.rs | 75 +- .../noirc_frontend/src/hir/comptime/errors.rs | 208 +- .../src/hir/comptime/hir_to_display_ast.rs | 159 +- .../src/hir/comptime/interpreter.rs | 151 +- .../src/hir/comptime/interpreter/builtin.rs | 225 +- .../interpreter/builtin/builtin_helpers.rs | 46 +- .../src/hir/comptime/interpreter/foreign.rs | 22 +- .../src/hir/comptime/interpreter/unquote.rs | 12 +- .../noirc_frontend/src/hir/comptime/mod.rs | 2 +- .../noirc_frontend/src/hir/comptime/tests.rs | 12 +- .../noirc_frontend/src/hir/comptime/value.rs | 224 +- .../src/hir/def_collector/dc_crate.rs | 123 +- .../src/hir/def_collector/dc_mod.rs | 184 +- .../src/hir/def_collector/errors.rs | 191 +- .../src/hir/def_map/item_scope.rs | 10 +- .../noirc_frontend/src/hir/def_map/mod.rs | 19 +- .../compiler/noirc_frontend/src/hir/mod.rs | 17 +- .../src/hir/resolution/errors.rs | 529 +++-- .../src/hir/resolution/import.rs | 81 +- .../src/hir/resolution/visibility.rs | 4 +- .../src/hir/type_check/errors.rs | 552 +++-- .../src/hir/type_check/generics.rs | 2 +- .../noirc_frontend/src/hir_def/expr.rs | 58 +- .../noirc_frontend/src/hir_def/function.rs | 2 +- .../noirc_frontend/src/hir_def/stmt.rs | 2 +- .../noirc_frontend/src/hir_def/traits.rs | 16 +- .../noirc_frontend/src/hir_def/types.rs | 156 +- .../src/hir_def/types/arithmetic.rs | 46 +- .../noirc_frontend/src/lexer/errors.rs | 136 +- .../noirc_frontend/src/lexer/lexer.rs | 220 +- .../noirc_frontend/src/lexer/token.rs | 117 +- .../compiler/noirc_frontend/src/lib.rs | 5 +- .../compiler/noirc_frontend/src/locations.rs | 10 +- .../src/monomorphization/ast.rs | 6 +- .../src/monomorphization/debug.rs | 39 +- .../src/monomorphization/errors.rs | 31 +- .../src/monomorphization/mod.rs | 87 +- .../src/monomorphization/printer.rs | 2 +- .../noirc_frontend/src/node_interner.rs | 133 +- .../noirc_frontend/src/parser/errors.rs | 82 +- .../compiler/noirc_frontend/src/parser/mod.rs | 8 +- .../noirc_frontend/src/parser/parser.rs | 163 +- .../src/parser/parser/arguments.rs | 4 +- .../src/parser/parser/attributes.rs | 144 +- .../src/parser/parser/doc_comments.rs | 27 +- .../noirc_frontend/src/parser/parser/enums.rs | 47 +- .../src/parser/parser/expression.rs | 287 +-- .../src/parser/parser/function.rs | 157 +- .../src/parser/parser/generics.rs | 22 +- .../src/parser/parser/global.rs | 44 +- .../noirc_frontend/src/parser/parser/impls.rs | 59 +- .../noirc_frontend/src/parser/parser/infix.rs | 18 +- .../noirc_frontend/src/parser/parser/item.rs | 30 +- .../src/parser/parser/item_visibility.rs | 16 +- .../src/parser/parser/lambda.rs | 4 +- .../src/parser/parser/modifiers.rs | 31 +- .../src/parser/parser/module.rs | 18 +- .../src/parser/parser/parse_many.rs | 10 +- .../noirc_frontend/src/parser/parser/path.rs | 65 +- .../src/parser/parser/pattern.rs | 60 +- .../src/parser/parser/statement.rs | 205 +- .../statement_or_expression_or_lvalue.rs | 8 +- .../src/parser/parser/structs.rs | 48 +- .../src/parser/parser/traits.rs | 58 +- .../src/parser/parser/type_alias.rs | 28 +- .../src/parser/parser/type_expression.rs | 111 +- .../noirc_frontend/src/parser/parser/types.rs | 109 +- .../src/parser/parser/use_tree.rs | 59 +- .../src/parser/parser/where_clause.rs | 20 +- .../noirc_frontend/src/signed_field.rs | 175 ++ .../compiler/noirc_frontend/src/tests.rs | 2048 +++++++---------- .../src/tests/arithmetic_generics.rs | 24 +- .../noirc_frontend/src/tests/bound_checks.rs | 68 +- .../noirc_frontend/src/tests/enums.rs | 201 ++ .../noirc_frontend/src/tests/imports.rs | 66 +- .../src/tests/metaprogramming.rs | 78 +- .../noirc_frontend/src/tests/references.rs | 58 +- .../noirc_frontend/src/tests/traits.rs | 345 +-- .../noirc_frontend/src/tests/turbofish.rs | 155 +- .../noirc_frontend/src/tests/unused_items.rs | 109 +- .../noirc_frontend/src/tests/visibility.rs | 170 +- noir/noir-repo/compiler/wasm/package.json | 2 +- noir/noir-repo/compiler/wasm/src/compile.rs | 49 +- .../compiler/wasm/src/compile_new.rs | 14 +- noir/noir-repo/compiler/wasm/src/errors.rs | 17 +- noir/noir-repo/compiler/wasm/src/lib.rs | 6 +- .../compiler/wasm/src/types/noir_artifact.ts | 4 +- .../wasm/test/compiler/shared/compile.test.ts | 18 +- noir/noir-repo/cspell.json | 5 +- .../docs/how_to/how-to-solidity-verifier.mdx | 17 +- .../docs/noir/concepts/data_types/integers.md | 48 - .../docs/docs/noir/concepts/traits.md | 31 + .../modules_packages_crates/dependencies.md | 4 +- .../noir/standard_library/black_box_fns.md | 2 +- .../cryptographic_primitives/hashes.mdx | 33 +- .../docs/docs/noir/standard_library/traits.md | 2 +- noir/noir-repo/docs/docs/tooling/profiler.md | 135 ++ noir/noir-repo/docs/docs/tooling/security.md | 2 +- .../profiler/acir-flamegraph-optimized.png | Bin 0 -> 90387 bytes .../profiler/acir-flamegraph-unoptimized.png | Bin 0 -> 90830 bytes .../profiler/brillig-trace-initial-32.png | Bin 0 -> 98463 bytes .../tooling/profiler/brillig-trace-opt-32.png | Bin 0 -> 114318 bytes .../gates-flamegraph-optimized-2048.png | Bin 0 -> 85893 bytes .../profiler/gates-flamegraph-optimized.png | Bin 0 -> 34504 bytes .../gates-flamegraph-unoptimized-2048.png | Bin 0 -> 75614 bytes .../profiler/gates-flamegraph-unoptimized.png | Bin 0 -> 67875 bytes .../how_to/how-to-solidity-verifier.mdx | 15 +- .../how_to/using-devcontainers.mdx | 10 +- .../noir/concepts/traits.md | 56 +- .../explainers}/cspell.json | 2 +- .../explainers/explainer-oracle.md | 57 + .../explainers/explainer-recursion.md | 176 ++ .../explainers/explainer-writing-noir.md | 208 ++ .../getting_started/noir_installation.md | 106 + .../getting_started/project_breakdown.md | 159 ++ .../getting_started/quick_start.md | 127 + .../setting_up_shell_completions.md | 87 + .../how_to/_category_.json | 5 + .../how_to/debugger/_category_.json | 6 + .../debugger/debugging_with_the_repl.md | 164 ++ .../how_to/debugger/debugging_with_vs_code.md | 68 + .../how_to/how-to-oracles.md | 275 +++ .../how_to/how-to-recursion.md | 172 ++ .../how_to/how-to-solidity-verifier.mdx | 305 +++ .../how_to/merkle-proof.mdx | 48 + .../how_to/using-devcontainers.mdx | 91 + .../version-v1.0.0-beta.3/index.mdx | 76 + .../version-v1.0.0-beta.3/migration_notes.md | 105 + .../noir/concepts/_category_.json | 6 + .../noir/concepts/assert.md | 79 + .../noir/concepts/comments.md | 33 + .../noir/concepts/comptime.md | 565 +++++ .../noir/concepts/control_flow.md | 111 + .../noir/concepts/data_bus.mdx | 23 + .../noir/concepts/data_types/_category_.json | 5 + .../noir/concepts/data_types/arrays.md | 276 +++ .../noir/concepts/data_types/booleans.md | 28 + .../noir/concepts/data_types/fields.md | 260 +++ .../concepts/data_types/function_types.md | 26 + .../noir/concepts/data_types/index.md | 126 + .../noir/concepts/data_types/integers.md | 178 ++ .../noir/concepts/data_types/references.md | 23 + .../noir/concepts/data_types/slices.mdx | 358 +++ .../noir/concepts/data_types/strings.md | 114 + .../noir/concepts/data_types/structs.md | 96 + .../noir/concepts/data_types/tuples.md | 48 + .../noir/concepts/functions.md | 226 ++ .../noir/concepts/generics.md | 262 +++ .../noir/concepts/globals.md | 82 + .../noir/concepts/lambdas.md | 81 + .../noir/concepts/mutability.md | 121 + .../noir/concepts/ops.md | 98 + .../noir/concepts/oracles.mdx | 29 + .../noir/concepts/shadowing.md | 44 + .../noir/concepts/traits.md | 611 +++++ .../noir/concepts/unconstrained.md | 104 + .../modules_packages_crates/_category_.json | 6 + .../crates_and_packages.md | 43 + .../modules_packages_crates/dependencies.md | 122 + .../noir/modules_packages_crates/modules.md | 221 ++ .../modules_packages_crates/workspaces.md | 46 + .../noir/standard_library/_category_.json | 6 + .../noir/standard_library/black_box_fns.md | 31 + .../noir/standard_library/bn254.md | 46 + .../standard_library/containers/boundedvec.md | 479 ++++ .../standard_library/containers/hashmap.md | 587 +++++ .../noir/standard_library/containers/index.md | 5 + .../noir/standard_library/containers/vec.mdx | 170 ++ .../cryptographic_primitives/_category_.json | 5 + .../cryptographic_primitives/ciphers.mdx | 36 + .../ecdsa_sig_verification.mdx | 98 + .../embedded_curve_ops.mdx | 95 + .../cryptographic_primitives/hashes.mdx | 228 ++ .../cryptographic_primitives/index.md | 14 + .../noir/standard_library/fmtstr.md | 17 + .../noir/standard_library/is_unconstrained.md | 69 + .../noir/standard_library/logging.md | 78 + .../noir/standard_library/mem.md | 82 + .../noir/standard_library/merkle_trees.md | 58 + .../noir/standard_library/meta/ctstring.md | 100 + .../noir/standard_library/meta/expr.md | 380 +++ .../standard_library/meta/function_def.md | 181 ++ .../noir/standard_library/meta/index.md | 224 ++ .../noir/standard_library/meta/module.md | 82 + .../noir/standard_library/meta/op.md | 244 ++ .../noir/standard_library/meta/quoted.md | 140 ++ .../noir/standard_library/meta/struct_def.md | 199 ++ .../standard_library/meta/trait_constraint.md | 17 + .../noir/standard_library/meta/trait_def.md | 26 + .../noir/standard_library/meta/trait_impl.md | 62 + .../noir/standard_library/meta/typ.md | 264 +++ .../noir/standard_library/meta/typed_expr.md | 27 + .../standard_library/meta/unresolved_type.md | 57 + .../noir/standard_library/options.md | 101 + .../noir/standard_library/recursion.mdx | 67 + .../noir/standard_library/traits.md | 655 ++++++ .../reference/NoirJS/noir_js/.nojekyll | 1 + .../reference/NoirJS/noir_js/classes/Noir.md | 52 + .../reference/NoirJS/noir_js/functions/and.md | 22 + .../NoirJS/noir_js/functions/blake2s256.md | 21 + .../functions/ecdsa_secp256k1_verify.md | 28 + .../functions/ecdsa_secp256r1_verify.md | 28 + .../reference/NoirJS/noir_js/functions/xor.md | 22 + .../reference/NoirJS/noir_js/index.md | 47 + .../noir_js/type-aliases/ErrorWithPayload.md | 15 + .../type-aliases/ForeignCallHandler.md | 24 + .../noir_js/type-aliases/ForeignCallInput.md | 9 + .../noir_js/type-aliases/ForeignCallOutput.md | 9 + .../NoirJS/noir_js/type-aliases/WitnessMap.md | 9 + .../NoirJS/noir_js/typedoc-sidebar.cjs | 4 + .../reference/NoirJS/noir_wasm/.nojekyll | 1 + .../NoirJS/noir_wasm/functions/compile.md | 51 + .../noir_wasm/functions/compile_contract.md | 51 + .../noir_wasm/functions/createFileManager.md | 21 + .../functions/inflateDebugSymbols.md | 21 + .../reference/NoirJS/noir_wasm/index.md | 49 + .../NoirJS/noir_wasm/typedoc-sidebar.cjs | 4 + .../reference/_category_.json | 5 + .../reference/debugger/_category_.json | 6 + .../debugger/debugger_known_limitations.md | 59 + .../reference/debugger/debugger_repl.md | 360 +++ .../reference/debugger/debugger_vscode.md | 82 + .../reference/nargo_commands.md | 580 +++++ .../reference/noir_codegen.md | 116 + .../version-v1.0.0-beta.3/tooling/cspell.json | 8 + .../version-v1.0.0-beta.3/tooling/debugger.md | 26 + .../tooling/language_server.md | 43 + .../version-v1.0.0-beta.3/tooling/profiler.md | 115 + .../version-v1.0.0-beta.3/tooling/security.md | 61 + .../version-v1.0.0-beta.3/tooling/testing.md | 79 + .../tutorials/noirjs_app.md | 304 +++ .../version-v1.0.0-beta.3-sidebars.json | 93 + .../codegen_verifier/codegen_verifier.sh | 8 +- .../prove_and_verify/prove_and_verify.sh | 6 +- .../recursion/generate_recursive_proof.sh | 24 +- noir/noir-repo/noir_stdlib/src/cmp.nr | 16 + noir/noir-repo/noir_stdlib/src/convert.nr | 23 + noir/noir-repo/noir_stdlib/src/default.nr | 6 + .../noir_stdlib/src/ecdsa_secp256k1.nr | 1 - noir/noir-repo/noir_stdlib/src/hash/keccak.nr | 155 -- noir/noir-repo/noir_stdlib/src/hash/mod.nr | 48 +- noir/noir-repo/noir_stdlib/src/hash/sha256.nr | 845 ------- noir/noir-repo/noir_stdlib/src/hash/sha512.nr | 165 -- noir/noir-repo/noir_stdlib/src/lib.nr | 41 +- noir/noir-repo/noir_stdlib/src/meta/op.nr | 1 - noir/noir-repo/noir_stdlib/src/ops/arith.nr | 26 +- noir/noir-repo/noir_stdlib/src/ops/bit.nr | 35 +- noir/noir-repo/noir_stdlib/src/prelude.nr | 1 - noir/noir-repo/noir_stdlib/src/sha256.nr | 10 - noir/noir-repo/noir_stdlib/src/sha512.nr | 5 - noir/noir-repo/noir_stdlib/src/uint128.nr | 572 ----- noir/noir-repo/rust-toolchain.toml | 4 +- ...ommit.sh => bump-aztec-packages-commit.sh} | 0 .../benchmarks/bench_sha256/Prover.toml | 1 - .../benchmarks/bench_sha256/src/main.nr | 4 - .../benchmarks/bench_sha256_100/Prover.toml | 102 - .../benchmarks/bench_sha256_100/src/main.nr | 10 - .../benchmarks/bench_sha256_30/Nargo.toml | 7 - .../benchmarks/bench_sha256_30/Prover.toml | 32 - .../benchmarks/bench_sha256_30/src/main.nr | 10 - .../benchmarks/bench_sha256_long/Prover.toml | 191 -- .../benchmarks/bench_sha256_long/src/main.nr | 7 - .../test_programs/compilation_report.sh | 2 +- .../broken_impl}/Nargo.toml | 4 +- .../compile_failure/broken_impl/src/main.nr | 2 + .../src/main.nr | 1 - .../comptime_as_field/src/main.nr | 6 - .../Nargo.toml | 0 .../comptime_as_primitive/src/main.nr | 7 + .../comptime_fmt_strings/src/main.nr | 1 - .../comptime_from_field/Nargo.toml | 6 - .../comptime_from_field/src/main.nr | 6 - .../comptime_function_definition/src/main.nr | 6 +- .../comptime_keccak/Nargo.toml | 7 - .../comptime_keccak/src/main.nr | 32 - .../comptime_traits/src/main.nr | 1 - .../src/main.nr | 1 - .../compile_success_empty/enums/src/main.nr | 39 +- .../src/main.nr | 1 - .../trait_override_implementation/src/main.nr | 1 - .../unquote_struct/src/main.nr | 15 + .../src/main.nr | 1 - .../u128_multiplication_overflow/Nargo.toml | 7 + .../u128_multiplication_overflow/Prover.toml | 2 + .../u128_multiplication_overflow/src/main.nr | 7 + .../test_programs/execution_report.sh | 2 +- .../execution_success/6/Prover.toml | 35 +- .../execution_success/6/src/main.nr | 8 +- .../array_dynamic_blackbox_input/Prover.toml | 2 +- .../array_dynamic_blackbox_input/src/main.nr | 2 +- .../Prover.toml | 4 +- .../src/main.nr | 2 +- .../execution_success/bit_and/src/main.nr | 1 - .../execution_success/bool_not/src/main.nr | 1 - .../execution_success/bool_or/src/main.nr | 1 - .../brillig_calls_conditionals/src/main.nr | 1 - .../brillig_cow_regression/src/main.nr | 4 +- .../brillig_nested_arrays/src/main.nr | 1 - .../brillig_pedersen/src/main.nr | 1 - .../execution_success/cast_bool/src/main.nr | 1 - .../conditional_1/Prover.toml | 2 +- .../conditional_1/src/main.nr | 2 +- .../conditional_regression_661/src/main.nr | 1 - .../Prover.toml | 35 +- .../src/main.nr | 4 +- .../execution_success/databus/src/main.nr | 1 - .../ecdsa_secp256k1/Prover.toml | 40 - .../ecdsa_secp256k1/src/main.nr | 12 +- .../fold_complex_outputs/src/main.nr | 1 - .../src/main.nr | 1 - .../if_else_chain/src/main.nr | 1 - .../execution_success/keccak256/Prover.toml | 35 - .../execution_success/keccak256/src/main.nr | 20 - .../nested_array_dynamic/src/main.nr | 1 - .../nested_arrays_from_brillig/src/main.nr | 1 - .../pedersen_check/src/main.nr | 1 - .../pedersen_commitment/src/main.nr | 1 - .../pedersen_hash/src/main.nr | 1 - .../ram_blowup_regression/src/main.nr | 10 +- .../reference_only_used_as_alias/src/main.nr | 1 - .../regression_4449/Prover.toml | 2 +- .../regression_4449/src/main.nr | 2 +- .../{sha256 => regression_7195}/Nargo.toml | 4 +- .../regression_7195/Prover.toml | 2 + .../regression_7195/src/main.nr | 21 + .../{sha2_byte => regression_7451}/Nargo.toml | 2 +- .../regression_7451/Prover.toml | 1 + .../regression_7451/src/main.nr | 12 + .../execution_success/sha256/Prover.toml | 38 - .../execution_success/sha256/src/main.nr | 27 - .../Nargo.toml | 7 - .../Prover.toml | 16 - .../src/main.nr | 104 - .../sha256_regression/Nargo.toml | 7 - .../sha256_regression/Prover.toml | 14 - .../sha256_regression/src/main.nr | 39 - .../sha256_var_padding_regression/Nargo.toml | 7 - .../sha256_var_padding_regression/Prover.toml | 2 - .../sha256_var_padding_regression/src/main.nr | 29 - .../sha256_var_size_regression/Nargo.toml | 7 - .../sha256_var_size_regression/Prover.toml | 3 - .../sha256_var_size_regression/src/main.nr | 17 - .../Nargo.toml | 7 - .../Prover.toml | 2 - .../src/main.nr | 9 - .../execution_success/sha2_byte/Prover.toml | 5 - .../execution_success/sha2_byte/src/main.nr | 8 - .../Nargo.toml | 3 +- .../shift_right_overflow/Prover.toml | 1 + .../shift_right_overflow/src/main.nr | 5 + .../slice_dynamic_index/src/main.nr | 1 - .../execution_success/slice_regex/src/main.nr | 1 - .../struct_fields_ordering/src/main.nr | 1 - .../execution_success/submodules/src/main.nr | 1 - .../execution_success/u128/Nargo.toml | 6 - .../execution_success/u128/Prover.toml | 7 - .../execution_success/u128/src/main.nr | 42 - .../u128_type}/Nargo.toml | 4 +- .../execution_success/u128_type/Prover.toml | 3 + .../execution_success/u128_type/src/main.nr | 22 + .../wrapping_operations/src/main.nr | 1 - noir/noir-repo/test_programs/memory_report.sh | 2 +- .../noir_test_success/bounded_vec/src/main.nr | 1 - .../brillig_overflow_checks/src/main.nr | 1 - .../comptime_blackbox/src/main.nr | 13 +- .../noir_test_success/global_eval/src/main.nr | 11 - .../noir_test_success/mock_oracle/src/main.nr | 1 - .../u128_type}/Nargo.toml | 4 +- .../noir_test_success/u128_type/src/main.nr | 49 + .../tooling/acvm_cli/src/cli/execute_cmd.rs | 9 +- .../tooling/acvm_cli/src/fs/witness.rs | 4 +- noir/noir-repo/tooling/acvm_cli/src/main.rs | 2 +- .../tooling/artifact_cli/src/bin/execute.rs | 4 +- .../artifact_cli/src/commands/execute_cmd.rs | 178 +- .../tooling/artifact_cli/src/commands/mod.rs | 1 + .../tooling/artifact_cli/src/errors.rs | 18 +- .../tooling/artifact_cli/src/execution.rs | 135 ++ .../tooling/artifact_cli/src/fs/artifact.rs | 60 +- .../tooling/artifact_cli/src/fs/inputs.rs | 2 +- .../tooling/artifact_cli/src/fs/witness.rs | 8 +- .../noir-repo/tooling/artifact_cli/src/lib.rs | 1 + .../noir-repo/tooling/debugger/src/context.rs | 20 +- noir/noir-repo/tooling/debugger/src/dap.rs | 6 +- .../tooling/debugger/src/foreign_calls.rs | 8 +- noir/noir-repo/tooling/debugger/src/lib.rs | 3 +- noir/noir-repo/tooling/debugger/src/repl.rs | 50 +- .../debugger/src/source_code_printer.rs | 40 +- .../noir-repo/tooling/debugger/tests/debug.rs | 111 + .../tooling/fuzzer/src/dictionary/mod.rs | 4 +- noir/noir-repo/tooling/fuzzer/src/lib.rs | 2 +- .../tooling/fuzzer/src/strategies/int.rs | 12 +- .../tooling/fuzzer/src/strategies/mod.rs | 2 +- .../tooling/fuzzer/src/strategies/uint.rs | 6 +- noir/noir-repo/tooling/inspector/Cargo.toml | 1 + .../tooling/inspector/src/cli/info_cmd.rs | 50 +- .../tooling/inspector/src/cli/mod.rs | 2 +- .../inspector/src/cli/print_acir_cmd.rs | 28 +- noir/noir-repo/tooling/lsp/Cargo.toml | 1 + .../lsp/src/attribute_reference_finder.rs | 6 +- noir/noir-repo/tooling/lsp/src/lib.rs | 41 +- noir/noir-repo/tooling/lsp/src/modules.rs | 7 +- .../tooling/lsp/src/notifications/mod.rs | 44 +- .../tooling/lsp/src/requests/code_action.rs | 24 +- .../code_action/fill_struct_fields.rs | 6 +- .../code_action/implement_missing_members.rs | 2 +- .../requests/code_action/import_or_qualify.rs | 4 +- .../src/requests/code_action/import_trait.rs | 6 +- .../code_action/remove_bang_from_call.rs | 4 +- .../code_action/remove_unused_import.rs | 19 +- .../lsp/src/requests/code_action/tests.rs | 6 +- .../lsp/src/requests/code_lens_request.rs | 5 +- .../tooling/lsp/src/requests/completion.rs | 81 +- .../src/requests/completion/auto_import.rs | 6 +- .../lsp/src/requests/completion/builtins.rs | 9 +- .../requests/completion/completion_items.rs | 10 +- .../lsp/src/requests/completion/kinds.rs | 2 +- .../lsp/src/requests/completion/tests.rs | 18 +- .../lsp/src/requests/document_symbol.rs | 40 +- .../lsp/src/requests/goto_declaration.rs | 4 +- .../lsp/src/requests/goto_definition.rs | 8 +- .../tooling/lsp/src/requests/hover.rs | 2 +- .../lsp/src/requests/hover/from_reference.rs | 30 +- .../lsp/src/requests/hover/from_visitor.rs | 43 +- .../tooling/lsp/src/requests/inlay_hint.rs | 52 +- .../noir-repo/tooling/lsp/src/requests/mod.rs | 22 +- .../tooling/lsp/src/requests/references.rs | 2 +- .../tooling/lsp/src/requests/rename.rs | 9 +- .../lsp/src/requests/signature_help.rs | 27 +- .../tooling/lsp/src/requests/test_run.rs | 15 +- .../tooling/lsp/src/requests/tests.rs | 15 +- .../src/trait_impl_method_stub_generator.rs | 4 +- noir/noir-repo/tooling/lsp/src/types.rs | 2 +- .../tooling/lsp/src/use_segment_positions.rs | 8 +- noir/noir-repo/tooling/lsp/src/utils.rs | 6 +- noir/noir-repo/tooling/lsp/src/with_file.rs | 1022 ++++++++ noir/noir-repo/tooling/nargo/src/errors.rs | 18 +- .../nargo/src/foreign_calls/default.rs | 8 +- .../tooling/nargo/src/foreign_calls/layers.rs | 2 +- .../tooling/nargo/src/foreign_calls/mocker.rs | 2 +- .../tooling/nargo/src/foreign_calls/print.rs | 2 +- .../tooling/nargo/src/foreign_calls/rpc.rs | 6 +- noir/noir-repo/tooling/nargo/src/lib.rs | 6 +- noir/noir-repo/tooling/nargo/src/ops/check.rs | 13 +- .../tooling/nargo/src/ops/compile.rs | 8 +- .../tooling/nargo/src/ops/execute.rs | 6 +- noir/noir-repo/tooling/nargo/src/ops/mod.rs | 2 +- .../tooling/nargo/src/ops/optimize.rs | 2 +- noir/noir-repo/tooling/nargo/src/ops/test.rs | 22 +- .../tooling/nargo/src/ops/transform.rs | 2 +- noir/noir-repo/tooling/nargo/src/workspace.rs | 2 +- noir/noir-repo/tooling/nargo_cli/Cargo.toml | 1 + .../tooling/nargo_cli/benches/criterion.rs | 56 +- .../tooling/nargo_cli/benches/iai.rs | 2 +- noir/noir-repo/tooling/nargo_cli/build.rs | 5 +- .../tooling/nargo_cli/src/cli/check_cmd.rs | 9 +- .../tooling/nargo_cli/src/cli/compile_cmd.rs | 19 +- .../tooling/nargo_cli/src/cli/dap_cmd.rs | 28 +- .../tooling/nargo_cli/src/cli/debug_cmd.rs | 45 +- .../tooling/nargo_cli/src/cli/execute_cmd.rs | 145 +- .../tooling/nargo_cli/src/cli/export_cmd.rs | 10 +- .../tooling/nargo_cli/src/cli/fmt_cmd.rs | 9 +- .../tooling/nargo_cli/src/cli/fs/inputs.rs | 42 - .../tooling/nargo_cli/src/cli/fs/mod.rs | 30 - .../tooling/nargo_cli/src/cli/fs/program.rs | 51 - .../tooling/nargo_cli/src/cli/fs/witness.rs | 22 - .../src/cli/generate_completion_script_cmd.rs | 2 +- .../tooling/nargo_cli/src/cli/info_cmd.rs | 15 +- .../tooling/nargo_cli/src/cli/init_cmd.rs | 15 +- .../tooling/nargo_cli/src/cli/mod.rs | 29 +- .../tooling/nargo_cli/src/cli/new_cmd.rs | 2 +- .../tooling/nargo_cli/src/cli/test_cmd.rs | 134 +- .../nargo_cli/src/cli/test_cmd/formatters.rs | 13 +- .../noir-repo/tooling/nargo_cli/src/errors.rs | 46 +- noir/noir-repo/tooling/nargo_cli/src/main.rs | 2 +- .../tooling/nargo_cli/tests/stdlib-props.rs | 137 +- .../tooling/nargo_cli/tests/stdlib-tests.rs | 6 +- noir/noir-repo/tooling/nargo_fmt/Cargo.toml | 1 + noir/noir-repo/tooling/nargo_fmt/build.rs | 4 +- .../noir-repo/tooling/nargo_fmt/src/chunks.rs | 8 +- .../noir-repo/tooling/nargo_fmt/src/config.rs | 2 +- .../tooling/nargo_fmt/src/formatter.rs | 9 +- .../tooling/nargo_fmt/src/formatter/alias.rs | 2 +- .../nargo_fmt/src/formatter/attribute.rs | 2 +- .../tooling/nargo_fmt/src/formatter/buffer.rs | 7 + .../src/formatter/comments_and_whitespace.rs | 9 +- .../nargo_fmt/src/formatter/doc_comments.rs | 2 +- .../tooling/nargo_fmt/src/formatter/enums.rs | 2 +- .../nargo_fmt/src/formatter/expression.rs | 33 +- .../nargo_fmt/src/formatter/function.rs | 3 +- .../nargo_fmt/src/formatter/generics.rs | 2 +- .../tooling/nargo_fmt/src/formatter/global.rs | 4 +- .../tooling/nargo_fmt/src/formatter/impls.rs | 2 +- .../tooling/nargo_fmt/src/formatter/item.rs | 20 +- .../tooling/nargo_fmt/src/formatter/lvalue.rs | 6 +- .../tooling/nargo_fmt/src/formatter/module.rs | 6 +- .../tooling/nargo_fmt/src/formatter/path.rs | 2 +- .../nargo_fmt/src/formatter/pattern.rs | 8 +- .../nargo_fmt/src/formatter/statement.rs | 13 +- .../nargo_fmt/src/formatter/structs.rs | 2 +- .../nargo_fmt/src/formatter/trait_impl.rs | 2 +- .../tooling/nargo_fmt/src/formatter/traits.rs | 24 +- .../src/formatter/type_expression.rs | 2 +- .../tooling/nargo_fmt/src/formatter/types.rs | 6 +- .../nargo_fmt/src/formatter/use_tree.rs | 6 +- .../nargo_fmt/src/formatter/use_tree_merge.rs | 10 +- .../nargo_fmt/src/formatter/visibility.rs | 2 +- .../nargo_fmt/src/formatter/where_clause.rs | 9 +- noir/noir-repo/tooling/nargo_fmt/src/lib.rs | 4 +- .../nargo_fmt/tests/expected/databus.nr | 1 - .../tooling/nargo_fmt/tests/expected/fn.nr | 1 - .../nargo_fmt/tests/expected/ignore.nr | 1 - .../tooling/nargo_fmt/tests/expected/trait.nr | 1 - .../nargo_fmt/tests/expected/trait_alias.nr | 1 - .../nargo_fmt/tests/expected/unsafe.nr | 1 - .../tooling/nargo_toml/src/errors.rs | 20 +- .../noir-repo/tooling/nargo_toml/src/flock.rs | 7 +- noir/noir-repo/tooling/nargo_toml/src/lib.rs | 17 +- .../tooling/nargo_toml/src/semver.rs | 30 +- .../tooling/noir_codegen/package.json | 2 +- noir/noir-repo/tooling/noir_js/package.json | 2 +- .../tooling/noir_js_types/package.json | 2 +- .../input_parser/json.txt | 1 + .../tooling/noirc_abi/src/arbitrary.rs | 10 +- .../noir-repo/tooling/noirc_abi/src/errors.rs | 20 +- .../noirc_abi/src/input_parser/json.rs | 10 +- .../tooling/noirc_abi/src/input_parser/mod.rs | 41 +- .../noirc_abi/src/input_parser/toml.rs | 10 +- noir/noir-repo/tooling/noirc_abi/src/lib.rs | 6 +- .../tooling/noirc_abi_wasm/Cargo.toml | 2 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- .../tooling/noirc_abi_wasm/package.json | 2 +- .../noirc_abi_wasm/src/js_witness_map.rs | 12 +- .../tooling/noirc_abi_wasm/src/lib.rs | 9 +- .../tooling/noirc_artifacts/src/contract.rs | 45 +- .../tooling/noirc_artifacts/src/debug.rs | 6 +- .../tooling/noirc_artifacts/src/lib.rs | 41 + .../tooling/noirc_artifacts/src/program.rs | 5 +- .../tooling/noirc_artifacts_info/src/lib.rs | 2 +- noir/noir-repo/tooling/profiler/Cargo.toml | 6 +- .../src/cli/execution_flamegraph_cmd.rs | 64 +- .../profiler/src/cli/gates_flamegraph_cmd.rs | 49 +- .../noir-repo/tooling/profiler/src/cli/mod.rs | 4 +- .../src/cli/opcodes_flamegraph_cmd.rs | 170 +- noir/noir-repo/tooling/profiler/src/errors.rs | 16 + .../tooling/profiler/src/flamegraph.rs | 16 +- noir/noir-repo/tooling/profiler/src/fs.rs | 42 - .../tooling/profiler/src/gates_provider.rs | 2 +- noir/noir-repo/tooling/profiler/src/main.rs | 6 +- .../tooling/profiler/src/opcode_formatter.rs | 4 +- noir/noir-repo/yarn.lock | 11 +- 761 files changed, 27300 insertions(+), 11990 deletions(-) create mode 100644 noir/noir-repo/.github/workflows/bump-aztec-packages-commit.yml create mode 100644 noir/noir-repo/acvm-repo/acir_field/benches/field_element.rs create mode 100644 noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/basic_conditional.rs create mode 100644 noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_array_gets.rs create mode 100644 noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/check_u128_mul_overflow.rs create mode 100644 noir/noir-repo/compiler/noirc_frontend/src/elaborator/options.rs create mode 100644 noir/noir-repo/compiler/noirc_frontend/src/signed_field.rs create mode 100644 noir/noir-repo/compiler/noirc_frontend/src/tests/enums.rs create mode 100644 noir/noir-repo/docs/docs/tooling/profiler.md create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/acir-flamegraph-optimized.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/acir-flamegraph-unoptimized.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/brillig-trace-initial-32.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/brillig-trace-opt-32.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/gates-flamegraph-optimized-2048.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/gates-flamegraph-optimized.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/gates-flamegraph-unoptimized-2048.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/gates-flamegraph-unoptimized.png rename noir/noir-repo/docs/{docs/tooling => versioned_docs/version-v1.0.0-beta.3/explainers}/cspell.json (55%) create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-oracle.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-recursion.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-writing-noir.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/noir_installation.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/project_breakdown.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/quick_start.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/setting_up_shell_completions.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_the_repl.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_vs_code.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-oracles.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-recursion.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-solidity-verifier.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/merkle-proof.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/using-devcontainers.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/index.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/migration_notes.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/assert.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comments.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comptime.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/control_flow.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_bus.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/arrays.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/booleans.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/fields.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/function_types.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/index.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/integers.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/references.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/slices.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/strings.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/structs.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/tuples.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/functions.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/generics.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/globals.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/lambdas.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/mutability.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/ops.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/oracles.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/shadowing.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/traits.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/unconstrained.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/crates_and_packages.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/dependencies.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/modules.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/workspaces.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/black_box_fns.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/bn254.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/boundedvec.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/hashmap.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/index.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/vec.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ciphers.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/hashes.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/index.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/fmtstr.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/is_unconstrained.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/logging.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/mem.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/merkle_trees.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/ctstring.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/expr.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/function_def.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/index.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/module.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/op.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/quoted.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/struct_def.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_constraint.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_def.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_impl.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typ.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typed_expr.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/unresolved_type.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/options.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/recursion.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/traits.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/.nojekyll create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/classes/Noir.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/and.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/blake2s256.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/xor.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/index.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/WitnessMap.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/typedoc-sidebar.cjs create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/.nojekyll create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile_contract.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/createFileManager.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/index.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_known_limitations.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_repl.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_vscode.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/nargo_commands.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/noir_codegen.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/cspell.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/debugger.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/language_server.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/profiler.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/security.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/testing.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tutorials/noirjs_app.md create mode 100644 noir/noir-repo/docs/versioned_sidebars/version-v1.0.0-beta.3-sidebars.json delete mode 100644 noir/noir-repo/noir_stdlib/src/hash/keccak.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/hash/sha256.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/hash/sha512.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/sha256.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/sha512.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/uint128.nr rename noir/noir-repo/scripts/{bump-aztec-packges-commit.sh => bump-aztec-packages-commit.sh} (100%) delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256/Prover.toml delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256/src/main.nr delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Prover.toml delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_100/src/main.nr delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Prover.toml delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_30/src/main.nr delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Prover.toml delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_long/src/main.nr rename noir/noir-repo/test_programs/{benchmarks/bench_sha256_100 => compile_failure/broken_impl}/Nargo.toml (51%) create mode 100644 noir/noir-repo/test_programs/compile_failure/broken_impl/src/main.nr delete mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_as_field/src/main.nr rename noir/noir-repo/test_programs/compile_success_empty/{comptime_as_field => comptime_as_primitive}/Nargo.toml (100%) create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_as_primitive/src/main.nr delete mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/src/main.nr delete mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/keccak256/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/keccak256/src/main.nr rename noir/noir-repo/test_programs/execution_success/{sha256 => regression_7195}/Nargo.toml (50%) create mode 100644 noir/noir-repo/test_programs/execution_success/regression_7195/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/regression_7195/src/main.nr rename noir/noir-repo/test_programs/execution_success/{sha2_byte => regression_7451}/Nargo.toml (68%) create mode 100644 noir/noir-repo/test_programs/execution_success/regression_7451/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/regression_7451/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_regression/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_regression/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_regression/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha2_byte/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha2_byte/src/main.nr rename noir/noir-repo/test_programs/execution_success/{keccak256 => shift_right_overflow}/Nargo.toml (63%) create mode 100644 noir/noir-repo/test_programs/execution_success/shift_right_overflow/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/shift_right_overflow/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/u128/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/u128/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/u128/src/main.nr rename noir/noir-repo/test_programs/{benchmarks/bench_sha256 => execution_success/u128_type}/Nargo.toml (52%) create mode 100644 noir/noir-repo/test_programs/execution_success/u128_type/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/u128_type/src/main.nr rename noir/noir-repo/test_programs/{benchmarks/bench_sha256_long => noir_test_success/u128_type}/Nargo.toml (52%) create mode 100644 noir/noir-repo/test_programs/noir_test_success/u128_type/src/main.nr create mode 100644 noir/noir-repo/tooling/artifact_cli/src/execution.rs create mode 100644 noir/noir-repo/tooling/lsp/src/with_file.rs delete mode 100644 noir/noir-repo/tooling/nargo_cli/src/cli/fs/inputs.rs delete mode 100644 noir/noir-repo/tooling/nargo_cli/src/cli/fs/mod.rs delete mode 100644 noir/noir-repo/tooling/nargo_cli/src/cli/fs/program.rs delete mode 100644 noir/noir-repo/tooling/nargo_cli/src/cli/fs/witness.rs create mode 100644 noir/noir-repo/tooling/profiler/src/errors.rs delete mode 100644 noir/noir-repo/tooling/profiler/src/fs.rs diff --git a/.noir-sync-commit b/.noir-sync-commit index 58cd9057d55a..1c31185be8c2 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -fdfe2bf752771b9611dc71953d50423b4ae7ec44 +a5b47df4d7c1da746f5377f9c1d35b5a06ea5648 diff --git a/noir/noir-repo/.github/benchmark_projects.yml b/noir/noir-repo/.github/benchmark_projects.yml index e1bcb080829b..7176bd7448e8 100644 --- a/noir/noir-repo/.github/benchmark_projects.yml +++ b/noir/noir-repo/.github/benchmark_projects.yml @@ -1,4 +1,4 @@ -define: &AZ_COMMIT 1350f93c3e9af8f601ca67ca3e67d0127c9767b6 +define: &AZ_COMMIT cca90e5e655ed9a2d2bb969f034e42ac15f87439 projects: private-kernel-inner: repo: AztecProtocol/aztec-packages @@ -7,7 +7,7 @@ projects: num_runs: 5 compilation-timeout: 2.5 execution-timeout: 0.08 - compilation-memory-limit: 300 + compilation-memory-limit: 350 execution-memory-limit: 250 private-kernel-tail: repo: AztecProtocol/aztec-packages @@ -18,7 +18,7 @@ projects: compilation-timeout: 1.2 execution-timeout: 0.02 compilation-memory-limit: 250 - execution-memory-limit: 200 + execution-memory-limit: 230 private-kernel-reset: repo: AztecProtocol/aztec-packages ref: *AZ_COMMIT @@ -35,19 +35,19 @@ projects: path: noir-projects/noir-protocol-circuits/crates/rollup-base-private num_runs: 5 timeout: 15 - compilation-timeout: 10 - execution-timeout: 0.5 - compilation-memory-limit: 1100 - execution-memory-limit: 500 + compilation-timeout: 20 + execution-timeout: 1 + compilation-memory-limit: 1500 + execution-memory-limit: 650 rollup-base-public: repo: AztecProtocol/aztec-packages ref: *AZ_COMMIT path: noir-projects/noir-protocol-circuits/crates/rollup-base-public num_runs: 5 timeout: 15 - compilation-timeout: 8 - execution-timeout: 0.4 - compilation-memory-limit: 1000 + compilation-timeout: 15 + execution-timeout: 0.75 + compilation-memory-limit: 1300 execution-memory-limit: 500 rollup-block-root-empty: repo: AztecProtocol/aztec-packages @@ -65,17 +65,17 @@ projects: cannot_execute: true num_runs: 1 timeout: 60 - compilation-timeout: 100 - compilation-memory-limit: 7000 + compilation-timeout: 110 + compilation-memory-limit: 8000 rollup-block-root: repo: AztecProtocol/aztec-packages ref: *AZ_COMMIT path: noir-projects/noir-protocol-circuits/crates/rollup-block-root num_runs: 1 timeout: 60 - compilation-timeout: 110 + compilation-timeout: 120 execution-timeout: 40 - compilation-memory-limit: 7000 + compilation-memory-limit: 8000 execution-memory-limit: 1500 rollup-merge: repo: AztecProtocol/aztec-packages diff --git a/noir/noir-repo/.github/scripts/wasm-bindgen-install.sh b/noir/noir-repo/.github/scripts/wasm-bindgen-install.sh index 209080036934..bb48062cb43f 100755 --- a/noir/noir-repo/.github/scripts/wasm-bindgen-install.sh +++ b/noir/noir-repo/.github/scripts/wasm-bindgen-install.sh @@ -6,8 +6,8 @@ cd $(dirname "$0") ./cargo-binstall-install.sh # Install wasm-bindgen-cli. -if [ "$(wasm-bindgen --version &> /dev/null | cut -d' ' -f2)" != "0.2.86" ]; then +if [ "$(wasm-bindgen --version &> /dev/null | cut -d' ' -f2)" != "0.2.100" ]; then echo "Building wasm-bindgen..." - cargo binstall wasm-bindgen-cli@0.2.86 --force --no-confirm + cargo binstall wasm-bindgen-cli@0.2.100 --force --no-confirm fi diff --git a/noir/noir-repo/.github/workflows/bump-aztec-packages-commit.yml b/noir/noir-repo/.github/workflows/bump-aztec-packages-commit.yml new file mode 100644 index 000000000000..6b7c1a80a0dd --- /dev/null +++ b/noir/noir-repo/.github/workflows/bump-aztec-packages-commit.yml @@ -0,0 +1,50 @@ +name: Bump external repos pinned commits + +on: + workflow_dispatch: + schedule: + # Trigger at 8am on Mondays + - cron: '0 8 * * 1' + + +jobs: + bump-commit: + name: Update external repo pinned commits + runs-on: ubuntu-22.04 + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + ref: master + + - name: Check for existing PR + id: pr-check + run: | + set -xue # print commands + PR_URL=$(gh pr list --repo noir-lang/noir --head bump-aztec-packages --json url --jq ".[0].url") + echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT + env: + GH_TOKEN: ${{ github.token }} + + - name: Configure git + run: | + git config user.name noirwhal + git config user.email tomfrench@aztecprotocol.com + + - name: Update commit + run: | + git checkout bump-aztec-packages || git checkout -b bump-aztec-packages + ./scripts/bump-aztec-packages-commit.sh + git add . + git commit -m 'chore: Update pinned commit of aztec-packages' + git push --set-upstream origin bump-aztec-packages --force + + - name: Create PR + if: ${{ steps.pr-check.outputs.pr_url == '' }} + run: | + PR_BODY=""" + Automated update of the pinned commit of [aztec-packages](https://github.com/AztecProtocol/aztec-packages) repository against which we run benchmarks. + """ + gh pr create --repo noir-lang/noir --title "chore: bump external pinned commits" --body "$PR_BODY" --base master --head bump-aztec-packages + env: + GH_TOKEN: ${{ secrets.NOIR_REPO_TOKEN }} diff --git a/noir/noir-repo/.github/workflows/docs-pr.yml b/noir/noir-repo/.github/workflows/docs-pr.yml index c123def6ba39..d60bc8b841eb 100644 --- a/noir/noir-repo/.github/workflows/docs-pr.yml +++ b/noir/noir-repo/.github/workflows/docs-pr.yml @@ -33,7 +33,7 @@ jobs: // Check if any file is within the 'docs' folder const docsChanged = files.some(file => file.filename.startsWith('docs/')); return docsChanged; - + - name: Add label if not present if: steps.check-labels.outputs.result == 'true' uses: actions/github-script@v7.0.1 @@ -57,7 +57,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -71,7 +71,7 @@ jobs: - name: Install wasm-bindgen-cli uses: taiki-e/install-action@v2 with: - tool: wasm-bindgen-cli@0.2.86 + tool: wasm-bindgen-cli@0.2.100 - name: Install wasm-opt run: | @@ -102,13 +102,13 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - + - name: Download built docs uses: actions/download-artifact@v4 with: name: docs path: ./docs/build - + - name: Deploy to Netlify uses: nwtgck/actions-netlify@v2.1 with: diff --git a/noir/noir-repo/.github/workflows/formatting.yml b/noir/noir-repo/.github/workflows/formatting.yml index 34216c22e01a..007fd89b0aeb 100644 --- a/noir/noir-repo/.github/workflows/formatting.yml +++ b/noir/noir-repo/.github/workflows/formatting.yml @@ -25,7 +25,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: x86_64-unknown-linux-gnu components: clippy, rustfmt @@ -51,7 +51,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: x86_64-unknown-linux-gnu components: clippy, rustfmt @@ -89,7 +89,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -121,7 +121,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - + - name: Download nargo binary uses: ./.github/actions/download-nargo diff --git a/noir/noir-repo/.github/workflows/publish-acvm.yml b/noir/noir-repo/.github/workflows/publish-acvm.yml index 27d927a67d13..60e523777e80 100644 --- a/noir/noir-repo/.github/workflows/publish-acvm.yml +++ b/noir/noir-repo/.github/workflows/publish-acvm.yml @@ -18,7 +18,7 @@ jobs: ref: ${{ inputs.noir-ref }} - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 # These steps are in a specific order so crate dependencies are updated first - name: Publish acir_field diff --git a/noir/noir-repo/.github/workflows/publish-docs.yml b/noir/noir-repo/.github/workflows/publish-docs.yml index 16959256d2aa..f949576900f2 100644 --- a/noir/noir-repo/.github/workflows/publish-docs.yml +++ b/noir/noir-repo/.github/workflows/publish-docs.yml @@ -22,12 +22,12 @@ jobs: - name: Install wasm-bindgen-cli uses: taiki-e/install-action@v2 with: - tool: wasm-bindgen-cli@0.2.86 + tool: wasm-bindgen-cli@0.2.100 - name: Install wasm-opt run: | npm i wasm-opt -g - + - name: Query active docs versions run: yarn workspace docs version::stables diff --git a/noir/noir-repo/.github/workflows/publish-es-packages.yml b/noir/noir-repo/.github/workflows/publish-es-packages.yml index 76c6fce6d5e9..8186213effda 100644 --- a/noir/noir-repo/.github/workflows/publish-es-packages.yml +++ b/noir/noir-repo/.github/workflows/publish-es-packages.yml @@ -22,9 +22,9 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ inputs.noir-ref }} - + - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -44,7 +44,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: noirc_abi_wasm - path: | + path: | ./tooling/noirc_abi_wasm/nodejs ./tooling/noirc_abi_wasm/web retention-days: 10 @@ -58,7 +58,7 @@ jobs: ref: ${{ inputs.noir-ref }} - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -95,7 +95,7 @@ jobs: ref: ${{ inputs.noir-ref }} - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -119,7 +119,7 @@ jobs: ./acvm-repo/acvm_js/nodejs ./acvm-repo/acvm_js/web retention-days: 3 - + publish-es-packages: runs-on: ubuntu-22.04 needs: [build-acvm_js, build-noirc_abi_wasm, build-noir_wasm] @@ -133,12 +133,12 @@ jobs: with: name: acvm-js path: acvm-repo/acvm_js - + - uses: actions/download-artifact@v4 with: name: noir_wasm path: compiler/wasm - + - uses: actions/download-artifact@v4 with: name: noirc_abi_wasm diff --git a/noir/noir-repo/.github/workflows/publish-nargo.yml b/noir/noir-repo/.github/workflows/publish-nargo.yml index d7d9c1ea03eb..e18dac52ca48 100644 --- a/noir/noir-repo/.github/workflows/publish-nargo.yml +++ b/noir/noir-repo/.github/workflows/publish-nargo.yml @@ -38,15 +38,8 @@ jobs: with: ref: ${{ inputs.tag || env.GITHUB_REF }} - - name: Setup for Apple Silicon - if: matrix.target == 'aarch64-apple-darwin' - run: | - sudo xcode-select -s /Applications/Xcode_15.4.0.app/Contents/Developer/ - echo "SDKROOT=$(xcrun -sdk macosx$(sw_vers -productVersion) --show-sdk-path)" >> $GITHUB_ENV - echo "MACOSX_DEPLOYMENT_TARGET=$(xcrun -sdk macosx$(sw_vers -productVersion) --show-sdk-platform-version)" >> $GITHUB_ENV - - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: ${{ matrix.target }} @@ -59,21 +52,28 @@ jobs: - name: Build environment and Compile run: | cargo build --package nargo_cli --release --target ${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" + cargo build --package noir_profiler --release --target ${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" + cargo build --package noir_inspector --release --target ${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" - name: Package artifacts run: | mkdir dist cp ./target/${{ matrix.target }}/release/nargo ./dist/nargo - 7z a -ttar -so -an ./dist/* | 7z a -si ./nargo-${{ matrix.target }}.tar.gz + cp ./target/${{ matrix.target }}/release/noir-profiler ./dist/noir-profiler + cp ./target/${{ matrix.target }}/release/noir-inspector ./dist/noir-inspector + + # TODO(https://github.com/noir-lang/noir/issues/7445): Remove the separate nargo binary + 7z a -ttar -so -an ./dist/nargo | 7z a -si ./nargo-${{ matrix.target }}.tar.gz + 7z a -ttar -so -an ./dist/* | 7z a -si ./noir-${{ matrix.target }}.tar.gz - - name: Upload artifact + - name: Upload Noir binaries artifact uses: actions/upload-artifact@v4 with: - name: nargo-${{ matrix.target }} + name: noir-${{ matrix.target }} path: ./dist/* retention-days: 3 - - name: Upload binaries to release tag + - name: Upload nargo binary to release tag uses: svenstaro/upload-release-action@v2 if: ${{ inputs.publish || github.event_name == 'schedule' }} with: @@ -84,12 +84,23 @@ jobs: overwrite: true tag: ${{ inputs.tag || 'nightly' }} # This will fail if `inputs.tag` is not a tag (e.g. testing a branch) + - name: Upload Noir binaries to release tag + uses: svenstaro/upload-release-action@v2 + if: ${{ inputs.publish || github.event_name == 'schedule' }} + with: + repo_name: noir-lang/noir + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ./noir-${{ matrix.target }}.tar.gz + asset_name: noir-${{ matrix.target }}.tar.gz + overwrite: true + tag: ${{ inputs.tag || 'nightly' }} # This will fail if `inputs.tag` is not a tag (e.g. testing a branch) + - name: Get formatted date id: date if: ${{ inputs.tag == '' && inputs.publish || github.event_name == 'schedule' }} run: echo "date=$(date '+%Y-%m-%d')" >> $GITHUB_OUTPUT - - name: Upload binaries to release with date tag + - name: Upload nargo binary to release with date tag uses: svenstaro/upload-release-action@v2 if: ${{ inputs.tag == '' && inputs.publish || github.event_name == 'schedule' }} with: @@ -102,6 +113,19 @@ jobs: overwrite: true tag: ${{ format('{0}-{1}', 'nightly', steps.date.outputs.date) }} + - name: Upload Noir binaries to release with date tag + uses: svenstaro/upload-release-action@v2 + if: ${{ inputs.tag == '' && inputs.publish || github.event_name == 'schedule' }} + with: + repo_name: noir-lang/noir + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ./noir-${{ matrix.target }}.tar.gz + asset_name: noir-${{ matrix.target }}.tar.gz + prerelease: true + make_latest: false + overwrite: true + tag: ${{ format('{0}-{1}', 'nightly', steps.date.outputs.date) }} + build-linux: runs-on: ubuntu-22.04 env: @@ -120,7 +144,7 @@ jobs: ref: ${{ inputs.tag || env.GITHUB_REF }} - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: ${{ matrix.target }} @@ -135,23 +159,31 @@ jobs: with: tool: cross@0.2.5 - - name: Build Nargo - run: cross build --package nargo_cli --release --target=${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" + - name: Build binaries + run: | + cross build --package nargo_cli --release --target=${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" + cross build --package noir_profiler --release --target=${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" + cross build --package noir_inspector --release --target ${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" - name: Package artifacts run: | mkdir dist cp ./target/${{ matrix.target }}/release/nargo ./dist/nargo - 7z a -ttar -so -an ./dist/* | 7z a -si ./nargo-${{ matrix.target }}.tar.gz + cp ./target/${{ matrix.target }}/release/noir-profiler ./dist/noir-profiler + cp ./target/${{ matrix.target }}/release/noir-inspector ./dist/noir-inspector - - name: Upload artifact + # TODO(https://github.com/noir-lang/noir/issues/7445): Remove the separate nargo binary + tar -czf nargo-${{ matrix.target }}.tar.gz -C dist nargo + tar -czf noir-${{ matrix.target }}.tar.gz -C dist . + + - name: Upload Noir binaries artifact uses: actions/upload-artifact@v4 with: - name: nargo-${{ matrix.target }} + name: noir-${{ matrix.target }} path: ./dist/* retention-days: 3 - - name: Upload binaries to release tag + - name: Upload nargo binary to release tag uses: svenstaro/upload-release-action@v2 if: ${{ inputs.publish }} with: @@ -163,12 +195,24 @@ jobs: overwrite: true tag: ${{ inputs.tag || 'nightly' }} # This will fail if `inputs.tag` is not a tag (e.g. testing a branch) + - name: Upload Noir binaries to release tag + uses: svenstaro/upload-release-action@v2 + if: ${{ inputs.publish }} + with: + repo_name: noir-lang/noir + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ./noir-${{ matrix.target }}.tar.gz + asset_name: noir-${{ matrix.target }}.tar.gz + prerelease: true + overwrite: true + tag: ${{ inputs.tag || 'nightly' }} # This will fail if `inputs.tag` is not a tag (e.g. testing a branch) + - name: Get formatted date id: date if: ${{ env.NIGHTLY_RELEASE && inputs.publish }} run: echo "date=$(date '+%Y-%m-%d')" >> $GITHUB_OUTPUT - - name: Upload binaries to release with date tag + - name: Upload nargo binary to release with date tag uses: svenstaro/upload-release-action@v2 if: ${{ env.NIGHTLY_RELEASE && inputs.publish }} with: @@ -181,4 +225,17 @@ jobs: overwrite: true tag: ${{ format('{0}-{1}', 'nightly', steps.date.outputs.date) }} - + - name: Upload Noir binaries to release with date tag + uses: svenstaro/upload-release-action@v2 + if: ${{ env.NIGHTLY_RELEASE && inputs.publish }} + with: + repo_name: noir-lang/noir + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ./noir-${{ matrix.target }}.tar.gz + asset_name: noir-${{ matrix.target }}.tar.gz + prerelease: true + make_latest: false + overwrite: true + tag: ${{ format('{0}-{1}', 'nightly', steps.date.outputs.date) }} + + diff --git a/noir/noir-repo/.github/workflows/release.yml b/noir/noir-repo/.github/workflows/release.yml index bbe9a7fff620..ea1f1eba6197 100644 --- a/noir/noir-repo/.github/workflows/release.yml +++ b/noir/noir-repo/.github/workflows/release.yml @@ -47,10 +47,10 @@ jobs: node-version: 18.19.0 cache: 'yarn' cache-dependency-path: 'yarn.lock' - + - name: Update yarn.lock run: yarn - + - name: Configure git run: | git config user.name noirwhal @@ -61,13 +61,13 @@ jobs: git add . git commit -m 'chore: Update root workspace acvm versions and lockfile' git push - + update-docs: name: Update docs needs: [release-please, update-acvm-workspace-package-versions] if: ${{ needs.release-please.outputs.release-pr }} runs-on: ubuntu-22.04 - + steps: - name: Checkout release branch uses: actions/checkout@v4 @@ -81,7 +81,7 @@ jobs: - name: Install wasm-bindgen-cli uses: taiki-e/install-action@v2 with: - tool: wasm-bindgen-cli@0.2.86 + tool: wasm-bindgen-cli@0.2.100 - name: Install wasm-opt run: | @@ -113,7 +113,7 @@ jobs: runs-on: ubuntu-22.04 # We want this job to always run (even if the dependant jobs fail) as we need apply changes to the sticky comment. if: ${{ always() }} - + needs: - release-please - update-acvm-workspace-package-versions @@ -123,7 +123,7 @@ jobs: # We treat any skipped or failing jobs as a failure for the workflow as a whole. FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }} - steps: + steps: - name: Add warning to sticky comment uses: marocchino/sticky-pull-request-comment@v2 with: @@ -175,7 +175,7 @@ jobs: needs: [release-please] if: ${{ needs.release-please.outputs.tag-name }} runs-on: ubuntu-22.04 - + steps: - name: Dispatch to publish-acvm uses: benc-uk/workflow-dispatch@v1 diff --git a/noir/noir-repo/.github/workflows/reports.yml b/noir/noir-repo/.github/workflows/reports.yml index 37f313259ad3..d458f8998f00 100644 --- a/noir/noir-repo/.github/workflows/reports.yml +++ b/noir/noir-repo/.github/workflows/reports.yml @@ -32,7 +32,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -417,9 +417,6 @@ jobs: - name: Download nargo binary uses: ./scripts/.github/actions/download-nargo - - name: Download nargo binary - uses: ./scripts/.github/actions/download-nargo - - name: Checkout uses: actions/checkout@v4 with: diff --git a/noir/noir-repo/.github/workflows/test-js-packages.yml b/noir/noir-repo/.github/workflows/test-js-packages.yml index 0af0e41d46ae..f88e64fa1a5a 100644 --- a/noir/noir-repo/.github/workflows/test-js-packages.yml +++ b/noir/noir-repo/.github/workflows/test-js-packages.yml @@ -54,7 +54,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -87,7 +87,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -119,7 +119,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -154,7 +154,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: diff --git a/noir/noir-repo/.github/workflows/test-rust-workspace-msrv.yml b/noir/noir-repo/.github/workflows/test-rust-workspace-msrv.yml index 38bc3cba153e..8f061bcad64e 100644 --- a/noir/noir-repo/.github/workflows/test-rust-workspace-msrv.yml +++ b/noir/noir-repo/.github/workflows/test-rust-workspace-msrv.yml @@ -3,7 +3,7 @@ name: Test (MSRV check) # TL;DR https://github.com/noir-lang/noir/issues/4384 # # This workflow acts to ensure that we can publish to crates.io, we need this extra check as libraries don't respect the Cargo.lock file committed in this repository. -# We must then always be able to build the workspace using the latest versions of all of our dependencies, so we explicitly update them and build in this workflow. +# We must then always be able to build the workspace using the latest versions of all of our dependencies, so we explicitly update them and build in this workflow. on: schedule: @@ -29,12 +29,12 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: x86_64-unknown-linux-gnu # We force the ACVM crate and all of its dependencies to update their dependencies - # This ensures that we'll be able to build the crates when they're being published. + # This ensures that we'll be able to build the crates when they're being published. - name: Update Cargo.lock run: | cargo update --package acvm --aggressive @@ -53,7 +53,7 @@ jobs: - name: Build and archive tests run: cargo nextest archive --workspace --archive-file nextest-archive.tar.zst - + - name: Upload archive to workflow uses: actions/upload-artifact@v4 with: @@ -70,9 +70,9 @@ jobs: partition: [1, 2, 3, 4] steps: - uses: actions/checkout@v4 - + - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: x86_64-unknown-linux-gnu @@ -80,7 +80,7 @@ jobs: uses: taiki-e/install-action@v2 with: tool: nextest@0.9.67 - + - name: Download archive uses: actions/download-artifact@v4 with: @@ -99,9 +99,9 @@ jobs: runs-on: ubuntu-22.04 # We want this job to always run (even if the dependant jobs fail) as we want this job to fail rather than skipping. if: ${{ always() }} - needs: + needs: - run-tests - + steps: - name: Report overall success run: | @@ -113,7 +113,7 @@ jobs: env: # We treat any cancelled, skipped or failing jobs as a failure for the workflow as a whole. FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'skipped') }} - + - name: Checkout if: ${{ failure() }} uses: actions/checkout@v4 diff --git a/noir/noir-repo/.github/workflows/test-rust-workspace.yml b/noir/noir-repo/.github/workflows/test-rust-workspace.yml index fe4213610723..91809a98a26a 100644 --- a/noir/noir-repo/.github/workflows/test-rust-workspace.yml +++ b/noir/noir-repo/.github/workflows/test-rust-workspace.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: x86_64-unknown-linux-gnu @@ -40,7 +40,7 @@ jobs: - name: Build and archive tests run: cargo nextest archive --workspace --archive-file nextest-archive.tar.zst - + - name: Upload archive to workflow uses: actions/upload-artifact@v4 with: @@ -57,9 +57,9 @@ jobs: partition: [1, 2, 3, 4] steps: - uses: actions/checkout@v4 - + - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: x86_64-unknown-linux-gnu @@ -67,7 +67,7 @@ jobs: uses: taiki-e/install-action@v2 with: tool: nextest@0.9.67 - + - name: Download archive uses: actions/download-artifact@v4 with: @@ -86,9 +86,9 @@ jobs: runs-on: ubuntu-22.04 # We want this job to always run (even if the dependant jobs fail) as we want this job to fail rather than skipping. if: ${{ always() }} - needs: + needs: - run-tests - + steps: - name: Report overall success run: | diff --git a/noir/noir-repo/.gitignore b/noir/noir-repo/.gitignore index 49d9e2dbb2ff..334901860d91 100644 --- a/noir/noir-repo/.gitignore +++ b/noir/noir-repo/.gitignore @@ -52,4 +52,10 @@ tooling/noir_js/lib # docs autogen build /docs/docs/noir_js/reference/ -codegen \ No newline at end of file +codegen + +**/cspell.json +!./cspell.json + +mutants.out +mutants.out.old diff --git a/noir/noir-repo/.release-please-manifest.json b/noir/noir-repo/.release-please-manifest.json index e3112f8e85e1..52d52e73e5a8 100644 --- a/noir/noir-repo/.release-please-manifest.json +++ b/noir/noir-repo/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.0.0-beta.2" + ".": "1.0.0-beta.3" } diff --git a/noir/noir-repo/.vscode/settings.json b/noir/noir-repo/.vscode/settings.json index cd1c5f886dfa..7568da6a1af5 100644 --- a/noir/noir-repo/.vscode/settings.json +++ b/noir/noir-repo/.vscode/settings.json @@ -8,5 +8,11 @@ "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, + "[markdown]": { + "files.trimTrailingWhitespace": true + }, + "[yaml]": { + "files.trimTrailingWhitespace": true + }, "rust-analyzer.server.extraEnv": { "RUSTUP_TOOLCHAIN": "stable" } } diff --git a/noir/noir-repo/CHANGELOG.md b/noir/noir-repo/CHANGELOG.md index 31d87763a150..0c6662010c2a 100644 --- a/noir/noir-repo/CHANGELOG.md +++ b/noir/noir-repo/CHANGELOG.md @@ -1,5 +1,62 @@ # Changelog +## [1.0.0-beta.3](https://github.com/noir-lang/noir/compare/v1.0.0-beta.2...v1.0.0-beta.3) (2025-02-20) + + +### ⚠ BREAKING CHANGES + +* make `ResolverError::OracleMarkedAsConstrained` into a full error ([#7426](https://github.com/noir-lang/noir/issues/7426)) +* remove bigint from stdlib ([#7411](https://github.com/noir-lang/noir/issues/7411)) +* Only decrement the counter of an array if its address has not changed ([#7297](https://github.com/noir-lang/noir/issues/7297)) + +### Features + +* `FunctionDefinition::as_typed_expr` ([#7358](https://github.com/noir-lang/noir/issues/7358)) ([97afa52](https://github.com/noir-lang/noir/commit/97afa52f5212be2d05af26b9e8dde9c3ea7a1d2e)) +* **acir_field:** Add little-endian byte serialization for FieldElement ([#7258](https://github.com/noir-lang/noir/issues/7258)) ([f37eedc](https://github.com/noir-lang/noir/commit/f37eedca7f286187c71587797e08da189c4eee70)) +* Add native `u128` type ([#7301](https://github.com/noir-lang/noir/issues/7301)) ([8783e48](https://github.com/noir-lang/noir/commit/8783e480a39efb47c7783c68b0e09c1c65b1f33a)) +* Allow unquoting TraitConstraint in trait impl position ([#7395](https://github.com/noir-lang/noir/issues/7395)) ([40d2763](https://github.com/noir-lang/noir/commit/40d276328e568712fcb742c1c2ecdd975e5123b6)) +* **brillig:** Hoist shared constants across functions to the global space ([#7216](https://github.com/noir-lang/noir/issues/7216)) ([8652072](https://github.com/noir-lang/noir/commit/8652072920b3abc5990be5d57db27a7e66221252)) +* **ci:** Publish binaries for noir-profiler ([#7443](https://github.com/noir-lang/noir/issues/7443)) ([af5e4cd](https://github.com/noir-lang/noir/commit/af5e4cd1f200da6f21c543616dea9c188190d833)) +* **ci:** Release noir-inspector in binaries ([#7464](https://github.com/noir-lang/noir/issues/7464)) ([7a9c8c1](https://github.com/noir-lang/noir/commit/7a9c8c13f626575f23d25f9cb8689e44dc1c6116)) +* **cli:** Add `--target-dir` option ([#7350](https://github.com/noir-lang/noir/issues/7350)) ([1b6ba5d](https://github.com/noir-lang/noir/commit/1b6ba5d960239f8fa934d9543699eb86edd3c43b)) +* **cli:** Add noir-execute binary ([#7384](https://github.com/noir-lang/noir/issues/7384)) ([fdfe2bf](https://github.com/noir-lang/noir/commit/fdfe2bf752771b9611dc71953d50423b4ae7ec44)) +* **experimental:** Compile match expressions ([#7312](https://github.com/noir-lang/noir/issues/7312)) ([4c3dee1](https://github.com/noir-lang/noir/commit/4c3dee165f400124ba727a884455d83948f2006e)) +* **experimental:** Show macro errors where they happen ([#7333](https://github.com/noir-lang/noir/issues/7333)) ([04dd6d9](https://github.com/noir-lang/noir/commit/04dd6d9cdc906210e77cf755c711d45d04cb29aa)) +* LSP hover for integer literals ([#7368](https://github.com/noir-lang/noir/issues/7368)) ([967ab5f](https://github.com/noir-lang/noir/commit/967ab5f6e23b7e9f4503fe01d05b8a7a94ca70f6)) +* **LSP:** Auto-import via visible reexport ([#7409](https://github.com/noir-lang/noir/issues/7409)) ([9b03217](https://github.com/noir-lang/noir/commit/9b03217ff4b7f8eb13a43466bf9b2d761944c6f7)) +* Optimize FieldElement::num_bits ([#7147](https://github.com/noir-lang/noir/issues/7147)) ([44c35dc](https://github.com/noir-lang/noir/commit/44c35dc3ed5cceb34131bcb90622b0e3e0156b9c)) +* **performance:** Check sub operations against induction variables ([#7356](https://github.com/noir-lang/noir/issues/7356)) ([7cdce1f](https://github.com/noir-lang/noir/commit/7cdce1fef7e0fd63355fe6dc0993415bbb210ebf)) +* **performance:** Use unchecked ops based upon known induction variables ([#7344](https://github.com/noir-lang/noir/issues/7344)) ([10b377f](https://github.com/noir-lang/noir/commit/10b377fb4eb9284df66f5c0bd830f6d20ab2c003)) +* Remove bigint from stdlib ([#7411](https://github.com/noir-lang/noir/issues/7411)) ([31cc6a1](https://github.com/noir-lang/noir/commit/31cc6a1cf9ea0a02931ef60c71d4d41524f8b84c)) +* Require safety comments instead of safety doc comments ([#7295](https://github.com/noir-lang/noir/issues/7295)) ([e895feb](https://github.com/noir-lang/noir/commit/e895feb4e7b25530a22668bca597dfc78be92584)) +* Simplify assertions that squared values are equal to zero ([#7432](https://github.com/noir-lang/noir/issues/7432)) ([5d19109](https://github.com/noir-lang/noir/commit/5d19109b6a5738a97b745c6117cf0a1e9f1552bb)) +* While statement ([#7280](https://github.com/noir-lang/noir/issues/7280)) ([582f56e](https://github.com/noir-lang/noir/commit/582f56e6b6ea43ab79b08aacfe7f1ba67a097f26)) + + +### Bug Fixes + +* **brillig:** Brillig entry point analysis and function specialization through duplication ([#7277](https://github.com/noir-lang/noir/issues/7277)) ([119bf62](https://github.com/noir-lang/noir/commit/119bf620005b52362e3aca9321b69e96e8a42fc0)) +* **cli:** Only lock the packages selected in the workspace ([#7345](https://github.com/noir-lang/noir/issues/7345)) ([f0ce5c5](https://github.com/noir-lang/noir/commit/f0ce5c5a57bc4cd8b3b482a3b682e8d5c2605d5c)) +* Do not discard negative sign from field literals in comptime interpreter ([#7439](https://github.com/noir-lang/noir/issues/7439)) ([1d04f8b](https://github.com/noir-lang/noir/commit/1d04f8ba0292e493e4e64cf8caf6df15c51b3346)) +* Don't let nargo fmt produce multiple trailing newlines ([#7444](https://github.com/noir-lang/noir/issues/7444)) ([093a8ec](https://github.com/noir-lang/noir/commit/093a8ec60eaacd1b307447545e166a40e037436d)) +* Field zero division in brillig ([#7386](https://github.com/noir-lang/noir/issues/7386)) ([e73f8cd](https://github.com/noir-lang/noir/commit/e73f8cd669c13cdb792313b46dd4aa012c40a0ad)) +* Format global attributes ([#7401](https://github.com/noir-lang/noir/issues/7401)) ([b7ace68](https://github.com/noir-lang/noir/commit/b7ace682af1ab8a43308457302f08b151af342db)) +* Give "correct" error when trying to use AsTraitPath ([#7360](https://github.com/noir-lang/noir/issues/7360)) ([8f20392](https://github.com/noir-lang/noir/commit/8f20392cab7cca4abf0f1811204ce1a4229f827a)) +* Incorrect secondary file in LSP errors ([#7347](https://github.com/noir-lang/noir/issues/7347)) ([5d782f0](https://github.com/noir-lang/noir/commit/5d782f020f6aec6aaa8a445c3a6a5fb9b275e3c6)) +* Let LSP read `noirfmt.toml` for formatting files ([#7355](https://github.com/noir-lang/noir/issues/7355)) ([81b86e2](https://github.com/noir-lang/noir/commit/81b86e2a9bfe991bc0385118094656648a125587)) +* Only decrement the counter of an array if its address has not changed ([#7297](https://github.com/noir-lang/noir/issues/7297)) ([93d1740](https://github.com/noir-lang/noir/commit/93d17407f7170abbab7a6e9c8df6b39fb478ec18)) +* **performance:** Remove redundant slice access check from brillig ([#7434](https://github.com/noir-lang/noir/issues/7434)) ([49a095d](https://github.com/noir-lang/noir/commit/49a095ded5cd33795bcdac60cbd98ce7c5ab9198)) +* Prevent incorrect ACIRgen caused by noop truncations ([#7456](https://github.com/noir-lang/noir/issues/7456)) ([1fa9b33](https://github.com/noir-lang/noir/commit/1fa9b33aab796dbc2a61f3062bf80e120831b462)) +* Require loop/for/while body to be unit ([#7437](https://github.com/noir-lang/noir/issues/7437)) ([13a7309](https://github.com/noir-lang/noir/commit/13a7309e6e2aec58cce3e12d4dc5f9ce8eb08a67)) +* **ssa:** Accurately mark binary ops for hoisting and check Div/Mod against induction variable lower bound ([#7396](https://github.com/noir-lang/noir/issues/7396)) ([64890c0](https://github.com/noir-lang/noir/commit/64890c0d7420adb32d3867e51dd194e48b87bb32)) +* **ssa:** Do not deduplicate division by a zero constant ([#7393](https://github.com/noir-lang/noir/issues/7393)) ([38eeee3](https://github.com/noir-lang/noir/commit/38eeee39a98a62747dcca3b31b409151761d4ef1)) +* **ssa:** Make the lookback feature opt-in ([#7190](https://github.com/noir-lang/noir/issues/7190)) ([31becc6](https://github.com/noir-lang/noir/commit/31becc6863688dc9cadf15d2e9726aab9f2a0150)) + + +### Miscellaneous Chores + +* Make `ResolverError::OracleMarkedAsConstrained` into a full error ([#7426](https://github.com/noir-lang/noir/issues/7426)) ([40184eb](https://github.com/noir-lang/noir/commit/40184eb75d69153fb7849700ad10c53bf19cacf3)) + ## [1.0.0-beta.2](https://github.com/noir-lang/noir/compare/v1.0.0-beta.1...v1.0.0-beta.2) (2025-02-10) diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index 8b6912452f04..48474cc79b1a 100644 --- a/noir/noir-repo/Cargo.lock +++ b/noir/noir-repo/Cargo.lock @@ -1,10 +1,10 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "acir" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir_field", "base64 0.21.7", @@ -26,12 +26,14 @@ dependencies = [ [[package]] name = "acir_field" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "ark-bls12-381", "ark-bn254 0.5.0", "ark-ff 0.5.0", + "ark-std 0.5.0", "cfg-if", + "criterion", "hex", "num-bigint", "proptest", @@ -40,7 +42,7 @@ dependencies = [ [[package]] name = "acvm" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "acvm_blackbox_solver", @@ -63,7 +65,7 @@ dependencies = [ [[package]] name = "acvm_blackbox_solver" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "blake2", @@ -101,7 +103,7 @@ dependencies = [ [[package]] name = "acvm_js" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "bn254_blackbox_solver", @@ -251,9 +253,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" [[package]] name = "ark-bls12-381" @@ -730,15 +732,16 @@ dependencies = [ [[package]] name = "blake3" -version = "1.5.5" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e" +checksum = "1230237285e3e10cde447185e8975408ae24deaa67205ce684805c25bc0c7937" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", + "memmap2", ] [[package]] @@ -765,7 +768,7 @@ dependencies = [ [[package]] name = "bn254_blackbox_solver" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "acvm_blackbox_solver", @@ -783,7 +786,7 @@ dependencies = [ [[package]] name = "brillig" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir_field", "serde", @@ -791,7 +794,7 @@ dependencies = [ [[package]] name = "brillig_vm" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "acvm_blackbox_solver", @@ -854,9 +857,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.13" +version = "1.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" +checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" dependencies = [ "shlex", ] @@ -875,9 +878,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.39" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" dependencies = [ "android-tzdata", "iana-time-zone", @@ -885,7 +888,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -917,9 +920,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.28" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff" +checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" dependencies = [ "clap_builder", "clap_derive", @@ -935,9 +938,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.27" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" +checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" dependencies = [ "anstream", "anstyle", @@ -947,9 +950,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.44" +version = "4.5.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375f9d8255adeeedd51053574fd8d4ba875ea5fa558e86617b07f09f1680c8b6" +checksum = "f5c5508ea23c5366f77e53f5a0070e5a84e51687ec3ef9e0464c86dc8d13ce98" dependencies = [ "clap", ] @@ -1288,9 +1291,9 @@ dependencies = [ [[package]] name = "csv-core" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" dependencies = [ "memchr", ] @@ -1511,9 +1514,9 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" [[package]] name = "elliptic-curve" @@ -1608,9 +1611,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" @@ -1731,12 +1734,12 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.35" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" dependencies = [ "crc32fast", - "miniz_oxide 0.8.3", + "miniz_oxide 0.8.5", ] [[package]] @@ -1750,7 +1753,7 @@ dependencies = [ [[package]] name = "fm" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "codespan-reporting", "iter-extended", @@ -1998,9 +2001,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" dependencies = [ "atomic-waker", "bytes", @@ -2554,7 +2557,7 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "iter-extended" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" [[package]] name = "itertools" @@ -2602,10 +2605,11 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.63" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -2796,9 +2800,9 @@ checksum = "82903360c009b816f5ab72a9b68158c27c301ee2c3f20655b55c5e589e7d3bb7" [[package]] name = "libc" -version = "0.2.169" +version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" [[package]] name = "libredox" @@ -2819,7 +2823,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.8.0", "libc", - "redox_syscall 0.5.8", + "redox_syscall 0.5.9", ] [[package]] @@ -2858,9 +2862,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.25" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "louds-rs" @@ -2945,6 +2949,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d97bbf43eb4f088f8ca469930cde17fa036207c9a5e02ccc5107c4e8b17c964" +[[package]] +name = "minicov" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" +dependencies = [ + "cc", + "walkdir", +] + [[package]] name = "miniz_oxide" version = "0.7.4" @@ -2956,9 +2970,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", ] @@ -2988,7 +3002,7 @@ dependencies = [ [[package]] name = "nargo" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "fm", @@ -3013,7 +3027,7 @@ dependencies = [ [[package]] name = "nargo_cli" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "ark-bn254 0.4.0", @@ -3042,6 +3056,7 @@ dependencies = [ "nargo", "nargo_fmt", "nargo_toml", + "noir_artifact_cli", "noir_debugger", "noir_lsp", "noirc_abi", @@ -3079,8 +3094,9 @@ dependencies = [ [[package]] name = "nargo_fmt" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ + "noirc_errors", "noirc_frontend", "serde", "similar-asserts", @@ -3090,7 +3106,7 @@ dependencies = [ [[package]] name = "nargo_toml" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "dirs", "fm", @@ -3157,7 +3173,7 @@ dependencies = [ [[package]] name = "noir_artifact_cli" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "acvm", @@ -3181,7 +3197,7 @@ dependencies = [ [[package]] name = "noir_debugger" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "assert_cmd", @@ -3205,7 +3221,7 @@ dependencies = [ [[package]] name = "noir_fuzzer" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "noirc_abi", @@ -3216,12 +3232,13 @@ dependencies = [ [[package]] name = "noir_inspector" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "clap", "color-eyre", "const_format", + "noir_artifact_cli", "noirc_artifacts", "noirc_artifacts_info", "serde", @@ -3230,7 +3247,7 @@ dependencies = [ [[package]] name = "noir_lsp" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "async-lsp", @@ -3238,6 +3255,7 @@ dependencies = [ "convert_case", "fm", "fxhash", + "iter-extended", "lsp-types 0.94.1", "nargo", "nargo_fmt", @@ -3258,7 +3276,7 @@ dependencies = [ [[package]] name = "noir_profiler" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "bn254_blackbox_solver", @@ -3270,6 +3288,7 @@ dependencies = [ "im", "inferno", "nargo", + "noir_artifact_cli", "noirc_abi", "noirc_artifacts", "noirc_driver", @@ -3278,13 +3297,14 @@ dependencies = [ "serde", "serde_json", "tempfile", + "thiserror", "tracing-appender", "tracing-subscriber", ] [[package]] name = "noir_wasm" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "build-data", @@ -3308,7 +3328,7 @@ dependencies = [ [[package]] name = "noirc_abi" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "iter-extended", @@ -3327,7 +3347,7 @@ dependencies = [ [[package]] name = "noirc_abi_wasm" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "build-data", @@ -3344,11 +3364,11 @@ dependencies = [ [[package]] name = "noirc_arena" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" [[package]] name = "noirc_artifacts" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "codespan-reporting", @@ -3363,7 +3383,7 @@ dependencies = [ [[package]] name = "noirc_artifacts_info" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "acvm", @@ -3378,7 +3398,7 @@ dependencies = [ [[package]] name = "noirc_driver" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "build-data", @@ -3397,7 +3417,7 @@ dependencies = [ [[package]] name = "noirc_errors" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "base64 0.21.7", @@ -3414,7 +3434,7 @@ dependencies = [ [[package]] name = "noirc_evaluator" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "bn254_blackbox_solver", @@ -3444,7 +3464,7 @@ dependencies = [ [[package]] name = "noirc_frontend" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "base64 0.21.7", @@ -3476,7 +3496,7 @@ dependencies = [ [[package]] name = "noirc_printable_type" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "proptest", @@ -3687,7 +3707,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.8", + "redox_syscall 0.5.9", "smallvec", "windows-targets 0.52.6", ] @@ -4137,9 +4157,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" dependencies = [ "bitflags 2.8.0", ] @@ -4240,15 +4260,14 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.8" +version = "0.17.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "da5349ae27d3887ca812fb375b45a4fbb36d8d12d2df394968cd86e35683fe73" dependencies = [ "cc", "cfg-if", "getrandom 0.2.15", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] @@ -4335,9 +4354,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.22" +version = "0.23.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb9263ab4eb695e42321db096e3b8fbd715a59b154d5c88d82db2175b681ba7" +checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" dependencies = [ "log", "once_cell", @@ -4543,12 +4562,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -4601,9 +4614,9 @@ checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" [[package]] name = "serde" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] @@ -4644,9 +4657,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", @@ -4655,9 +4668,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.138" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" dependencies = [ "itoa", "memchr", @@ -4790,9 +4803,9 @@ dependencies = [ [[package]] name = "similar-asserts" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f08357795f0d604ea7d7130f22c74b03838c959bdb14adde3142aab4d18a293" +checksum = "b5b441962c817e33508847a22bd82f03a30cff43642dc2fae8b050566121eb9a" dependencies = [ "console", "similar", @@ -4834,9 +4847,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" dependencies = [ "serde", ] @@ -5023,9 +5036,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.16.0" +version = "3.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" +checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" dependencies = [ "cfg-if", "fastrand", @@ -5313,7 +5326,7 @@ checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap 2.7.1", "toml_datetime", - "winnow 0.7.2", + "winnow 0.7.3", ] [[package]] @@ -5491,9 +5504,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unarray" @@ -5503,9 +5516,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" [[package]] name = "unicode-linebreak" @@ -5569,9 +5582,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" +checksum = "bd8dcafa1ca14750d8d7a05aa05988c17aab20886e1f3ae33a40223c58d92ef7" [[package]] name = "valuable" @@ -5655,11 +5668,13 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.86" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "serde", "serde_json", "wasm-bindgen-macro", @@ -5667,13 +5682,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.86" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn 2.0.98", @@ -5682,21 +5696,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.36" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.86" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5704,9 +5719,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.86" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -5717,19 +5732,21 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.86" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-bindgen-test" -version = "0.3.36" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e636f3a428ff62b3742ebc3c70e254dfe12b8c2b469d688ea59cdd4abcf502" +checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3" dependencies = [ - "console_error_panic_hook", "js-sys", - "scoped-tls", + "minicov", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", @@ -5737,19 +5754,20 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.36" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f18c1fad2f7c4958e7bcce014fa212f59a65d5e3721d0f77e6c0b27ede936ba3" +checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", + "syn 2.0.98", ] [[package]] name = "web-sys" -version = "0.3.63" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -5804,6 +5822,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-link" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" + [[package]] name = "windows-sys" version = "0.48.0" @@ -5963,9 +5987,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" +checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" dependencies = [ "memchr", ] diff --git a/noir/noir-repo/Cargo.toml b/noir/noir-repo/Cargo.toml index c91eb816083f..9c5bf1351d1c 100644 --- a/noir/noir-repo/Cargo.toml +++ b/noir/noir-repo/Cargo.toml @@ -49,11 +49,11 @@ resolver = "2" [workspace.package] # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors = ["The Noir Team "] -edition = "2021" -rust-version = "1.75.0" +edition = "2024" +rust-version = "1.85.0" license = "MIT OR Apache-2.0" repository = "https://github.com/noir-lang/noir/" @@ -66,13 +66,13 @@ unused_qualifications = "warn" [workspace.dependencies] # ACVM workspace dependencies -acir_field = { version = "1.0.0-beta.2", path = "acvm-repo/acir_field", default-features = false } -acir = { version = "1.0.0-beta.2", path = "acvm-repo/acir", default-features = false } -acvm = { version = "1.0.0-beta.2", path = "acvm-repo/acvm" } -brillig = { version = "1.0.0-beta.2", path = "acvm-repo/brillig", default-features = false } -brillig_vm = { version = "1.0.0-beta.2", path = "acvm-repo/brillig_vm", default-features = false } -acvm_blackbox_solver = { version = "1.0.0-beta.2", path = "acvm-repo/blackbox_solver", default-features = false } -bn254_blackbox_solver = { version = "1.0.0-beta.2", path = "acvm-repo/bn254_blackbox_solver", default-features = false } +acir_field = { version = "1.0.0-beta.3", path = "acvm-repo/acir_field", default-features = false } +acir = { version = "1.0.0-beta.3", path = "acvm-repo/acir", default-features = false } +acvm = { version = "1.0.0-beta.3", path = "acvm-repo/acvm" } +brillig = { version = "1.0.0-beta.3", path = "acvm-repo/brillig", default-features = false } +brillig_vm = { version = "1.0.0-beta.3", path = "acvm-repo/brillig_vm", default-features = false } +acvm_blackbox_solver = { version = "1.0.0-beta.3", path = "acvm-repo/blackbox_solver", default-features = false } +bn254_blackbox_solver = { version = "1.0.0-beta.3", path = "acvm-repo/bn254_blackbox_solver", default-features = false } # Noir compiler workspace dependencies fm = { path = "compiler/fm" } @@ -116,9 +116,9 @@ lsp-types = "0.94.1" tower = "0.4" # Wasm -wasm-bindgen = { version = "=0.2.86", features = ["serde-serialize"] } -wasm-bindgen-test = "0.3.36" -wasm-bindgen-futures = "0.4.36" +wasm-bindgen = { version = "=0.2.100", features = ["serde-serialize"] } +wasm-bindgen-test = "0.3.50" +wasm-bindgen-futures = "0.4.50" console_error_panic_hook = "0.1.7" gloo-utils = { version = "0.1", features = ["serde"] } js-sys = "0.3.62" diff --git a/noir/noir-repo/EXTERNAL_NOIR_LIBRARIES.yml b/noir/noir-repo/EXTERNAL_NOIR_LIBRARIES.yml index fcb6885c1659..dc8af205bf3b 100644 --- a/noir/noir-repo/EXTERNAL_NOIR_LIBRARIES.yml +++ b/noir/noir-repo/EXTERNAL_NOIR_LIBRARIES.yml @@ -1,4 +1,4 @@ -define: &AZ_COMMIT 1350f93c3e9af8f601ca67ca3e67d0127c9767b6 +define: &AZ_COMMIT cca90e5e655ed9a2d2bb969f034e42ac15f87439 libraries: noir_check_shuffle: repo: noir-lang/noir_check_shuffle @@ -23,13 +23,13 @@ libraries: timeout: 2 noir-bignum: repo: noir-lang/noir-bignum - timeout: 380 + timeout: 90 noir_bigcurve: repo: noir-lang/noir_bigcurve - timeout: 330 + timeout: 250 noir_base64: repo: noir-lang/noir_base64 - timeout: 2 + timeout: 5 noir_string_search: repo: noir-lang/noir_string_search timeout: 2 diff --git a/noir/noir-repo/README.md b/noir/noir-repo/README.md index c2e41435b660..20f1f80d83ae 100644 --- a/noir/noir-repo/README.md +++ b/noir/noir-repo/README.md @@ -34,7 +34,7 @@ The current focus is to gather as much feedback as possible while in the alpha p ## Minimum Rust version -This workspace's minimum supported rustc version is 1.75.0. +This workspace's minimum supported rustc version is 1.85.0. ## License diff --git a/noir/noir-repo/acvm-repo/acir/Cargo.toml b/noir/noir-repo/acvm-repo/acir/Cargo.toml index de55860762e5..2b15c2abf095 100644 --- a/noir/noir-repo/acvm-repo/acir/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir/Cargo.toml @@ -2,7 +2,7 @@ name = "acir" description = "ACIR is the IR that the VM processes, it is analogous to LLVM IR" # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/acir/benches/serialization.rs b/noir/noir-repo/acvm-repo/acir/benches/serialization.rs index dd6a5c8b1cf3..2725b3e1dd0c 100644 --- a/noir/noir-repo/acvm-repo/acir/benches/serialization.rs +++ b/noir/noir-repo/acvm-repo/acir/benches/serialization.rs @@ -1,10 +1,10 @@ -use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; +use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main}; use std::{collections::BTreeSet, time::Duration}; use acir::{ + FieldElement, circuit::{Circuit, ExpressionWidth, Opcode, Program, PublicInputs}, native_types::{Expression, Witness}, - FieldElement, }; use pprof::criterion::{Output, PProfProfiler}; 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 091a3dcb0e52..68c3c832b5cd 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs @@ -11,7 +11,7 @@ use std::{io::prelude::*, num::ParseIntError, str::FromStr}; use base64::Engine; use flate2::Compression; -use serde::{de::Error as DeserializationError, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as DeserializationError}; use std::collections::BTreeSet; @@ -252,7 +252,7 @@ impl Program { program_bytes } - // Serialize and base64 encode program + /// Serialize and base64 encode program pub fn serialize_program_base64(program: &Self, s: S) -> Result where S: Serializer, @@ -277,7 +277,7 @@ impl Deserialize<'a>> Program { Program::read(serialized_circuit) } - // Deserialize and base64 decode program + /// Deserialize and base64 decode program pub fn deserialize_program_base64<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, @@ -375,8 +375,8 @@ mod tests { use std::collections::BTreeSet; use super::{ - opcodes::{BlackBoxFuncCall, FunctionInput}, Circuit, Compression, Opcode, PublicInputs, + opcodes::{BlackBoxFuncCall, FunctionInput}, }; use crate::{ circuit::{ExpressionWidth, Program}, diff --git a/noir/noir-repo/acvm-repo/acir/src/lib.rs b/noir/noir-repo/acvm-repo/acir/src/lib.rs index bb5b50c0daf2..e49ab60f9e06 100644 --- a/noir/noir-repo/acvm-repo/acir/src/lib.rs +++ b/noir/noir-repo/acvm-repo/acir/src/lib.rs @@ -41,10 +41,10 @@ mod reflection { use crate::{ circuit::{ - brillig::{BrilligInputs, BrilligOutputs}, - opcodes::{BlackBoxFuncCall, BlockType, ConstantOrWitnessEnum, FunctionInput}, AssertionPayload, Circuit, ExpressionOrMemory, ExpressionWidth, Opcode, OpcodeLocation, Program, + brillig::{BrilligInputs, BrilligOutputs}, + opcodes::{BlackBoxFuncCall, BlockType, ConstantOrWitnessEnum, FunctionInput}, }, native_types::{Witness, WitnessMap, WitnessStack}, }; diff --git a/noir/noir-repo/acvm-repo/acir/src/native_types/expression/ordering.rs b/noir/noir-repo/acvm-repo/acir/src/native_types/expression/ordering.rs index f1d217b3bd9d..968dc310bc11 100644 --- a/noir/noir-repo/acvm-repo/acir/src/native_types/expression/ordering.rs +++ b/noir/noir-repo/acvm-repo/acir/src/native_types/expression/ordering.rs @@ -85,11 +85,7 @@ impl Expression { fn cmp_max(m1: Option, m2: Option) -> Ordering { if let Some(m1) = m1 { - if let Some(m2) = m2 { - m1.cmp(&m2) - } else { - Ordering::Greater - } + if let Some(m2) = m2 { m1.cmp(&m2) } else { Ordering::Greater } } else if m2.is_some() { Ordering::Less } else { diff --git a/noir/noir-repo/acvm-repo/acir/src/native_types/witness_map.rs b/noir/noir-repo/acvm-repo/acir/src/native_types/witness_map.rs index e508fe5b1861..77745c714a39 100644 --- a/noir/noir-repo/acvm-repo/acir/src/native_types/witness_map.rs +++ b/noir/noir-repo/acvm-repo/acir/src/native_types/witness_map.rs @@ -1,12 +1,12 @@ use std::{ - collections::{btree_map, BTreeMap}, + collections::{BTreeMap, btree_map}, io::Read, ops::Index, }; +use flate2::Compression; use flate2::bufread::GzDecoder; use flate2::bufread::GzEncoder; -use flate2::Compression; use serde::{Deserialize, Serialize}; use thiserror::Error; diff --git a/noir/noir-repo/acvm-repo/acir/src/native_types/witness_stack.rs b/noir/noir-repo/acvm-repo/acir/src/native_types/witness_stack.rs index 8a4fffa15772..6338ad630d6c 100644 --- a/noir/noir-repo/acvm-repo/acir/src/native_types/witness_stack.rs +++ b/noir/noir-repo/acvm-repo/acir/src/native_types/witness_stack.rs @@ -1,8 +1,8 @@ use std::io::Read; +use flate2::Compression; use flate2::bufread::GzDecoder; use flate2::bufread::GzEncoder; -use flate2::Compression; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -12,6 +12,9 @@ use super::WitnessMap; enum SerializationError { #[error(transparent)] Deflate(#[from] std::io::Error), + + #[error(transparent)] + BincodeError(#[from] bincode::Error), } #[derive(Debug, Error)] @@ -57,11 +60,11 @@ impl From> for WitnessStack { } } -impl TryFrom> for Vec { +impl TryFrom<&WitnessStack> for Vec { type Error = WitnessStackError; - fn try_from(val: WitnessStack) -> Result { - let buf = bincode::serialize(&val).unwrap(); + fn try_from(val: &WitnessStack) -> Result { + let buf = bincode::serialize(val).map_err(|e| WitnessStackError(e.into()))?; let mut deflater = GzEncoder::new(buf.as_slice(), Compression::best()); let mut buf_c = Vec::new(); deflater.read_to_end(&mut buf_c).map_err(|err| WitnessStackError(err.into()))?; @@ -69,6 +72,14 @@ impl TryFrom> for Vec { } } +impl TryFrom> for Vec { + type Error = WitnessStackError; + + fn try_from(val: WitnessStack) -> Result { + Self::try_from(&val) + } +} + impl Deserialize<'a>> TryFrom<&[u8]> for WitnessStack { type Error = WitnessStackError; @@ -76,7 +87,8 @@ impl Deserialize<'a>> TryFrom<&[u8]> for WitnessStack { let mut deflater = GzDecoder::new(bytes); let mut buf_d = Vec::new(); deflater.read_to_end(&mut buf_d).map_err(|err| WitnessStackError(err.into()))?; - let witness_stack = bincode::deserialize(&buf_d).unwrap(); + let witness_stack = + bincode::deserialize(&buf_d).map_err(|e| WitnessStackError(e.into()))?; Ok(witness_stack) } } diff --git a/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs b/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs index 305d94abcee2..4ff571106a1b 100644 --- a/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs +++ b/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs @@ -13,9 +13,9 @@ use std::collections::BTreeSet; use acir::{ circuit::{ + Circuit, Opcode, Program, PublicInputs, brillig::{BrilligBytecode, BrilligFunctionId, BrilligInputs, BrilligOutputs}, opcodes::{AcirFunctionId, BlackBoxFuncCall, BlockId, FunctionInput, MemOp}, - Circuit, Opcode, Program, PublicInputs, }, native_types::{Expression, Witness}, }; diff --git a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml index bc4be250cfd4..719d2585485b 100644 --- a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml @@ -2,7 +2,7 @@ name = "acir_field" description = "The field implementation being used by ACIR." # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true @@ -23,12 +23,18 @@ serde.workspace = true ark-bn254.workspace = true ark-bls12-381 = { workspace = true, optional = true } ark-ff.workspace = true +ark-std.workspace = true cfg-if.workspace = true [dev-dependencies] proptest.workspace = true +criterion.workspace = true [features] bn254 = [] bls12_381 = ["dep:ark-bls12-381"] + +[[bench]] +name = "field_element" +harness = false diff --git a/noir/noir-repo/acvm-repo/acir_field/benches/field_element.rs b/noir/noir-repo/acvm-repo/acir_field/benches/field_element.rs new file mode 100644 index 000000000000..8560c10cd040 --- /dev/null +++ b/noir/noir-repo/acvm-repo/acir_field/benches/field_element.rs @@ -0,0 +1,11 @@ +use acir_field::{AcirField, FieldElement}; +use criterion::{Criterion, criterion_group, criterion_main}; +use std::hint::black_box; + +fn criterion_benchmark(c: &mut Criterion) { + let field_element = FieldElement::from(123456789_u128); + c.bench_function("FieldElement::num_bits", |b| b.iter(|| black_box(field_element).num_bits())); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs index 8afc76da9d89..fdac33836ad8 100644 --- a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs +++ b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs @@ -1,5 +1,6 @@ use ark_ff::PrimeField; use ark_ff::Zero; +use ark_std::io::Write; use num_bigint::BigUint; use serde::{Deserialize, Serialize}; use std::borrow::Cow; @@ -111,11 +112,7 @@ impl From for FieldElement { impl From for FieldElement { fn from(boolean: bool) -> FieldElement { - if boolean { - FieldElement::one() - } else { - FieldElement::zero() - } + if boolean { FieldElement::one() } else { FieldElement::zero() } } } @@ -182,11 +179,7 @@ impl AcirField for FieldElement { /// For example, a max bit size of 254 would give a max byte size of 32. fn max_num_bytes() -> u32 { let num_bytes = Self::max_num_bits() / 8; - if Self::max_num_bits() % 8 == 0 { - num_bytes - } else { - num_bytes + 1 - } + if Self::max_num_bits() % 8 == 0 { num_bytes } else { num_bytes + 1 } } fn modulus() -> BigUint { @@ -195,26 +188,9 @@ impl AcirField for FieldElement { /// This is the number of bits required to represent this specific field element fn num_bits(&self) -> u32 { - let bytes = self.to_be_bytes(); - - // Iterate through the byte decomposition and pop off all leading zeroes - let mut iter = bytes.iter().skip_while(|x| (**x) == 0); - - // The first non-zero byte in the decomposition may have some leading zero-bits. - let Some(head_byte) = iter.next() else { - // If we don't have a non-zero byte then the field element is zero, - // which we consider to require a single bit to represent. - return 1; - }; - let num_bits_for_head_byte = head_byte.ilog2(); - - // Each remaining byte in the byte decomposition requires 8 bits. - // - // Note: count will panic if it goes over usize::MAX. - // This may not be suitable for devices whose usize < u16 - let tail_length = iter.count() as u32; - - 8 * tail_length + num_bits_for_head_byte + 1 + let mut bit_counter = BitCounter::default(); + self.0.serialize_uncompressed(&mut bit_counter).unwrap(); + bit_counter.bits() } fn to_u128(self) -> u128 { @@ -363,6 +339,52 @@ impl SubAssign for FieldElement { } } +#[derive(Default, Debug)] +struct BitCounter { + /// Total number of non-zero bytes we found. + count: usize, + /// Total bytes we found. + total: usize, + /// The last non-zero byte we found. + head_byte: u8, +} + +impl BitCounter { + fn bits(&self) -> u32 { + // If we don't have a non-zero byte then the field element is zero, + // which we consider to require a single bit to represent. + if self.count == 0 { + return 1; + } + + let num_bits_for_head_byte = self.head_byte.ilog2(); + + // Each remaining byte in the byte decomposition requires 8 bits. + // + // Note: count will panic if it goes over usize::MAX. + // This may not be suitable for devices whose usize < u16 + let tail_length = (self.count - 1) as u32; + 8 * tail_length + num_bits_for_head_byte + 1 + } +} + +impl Write for BitCounter { + fn write(&mut self, buf: &[u8]) -> ark_std::io::Result { + for byte in buf { + self.total += 1; + if *byte != 0 { + self.count = self.total; + self.head_byte = *byte; + } + } + Ok(buf.len()) + } + + fn flush(&mut self) -> ark_std::io::Result<()> { + Ok(()) + } +} + #[cfg(test)] mod tests { use super::{AcirField, FieldElement}; diff --git a/noir/noir-repo/acvm-repo/acvm/Cargo.toml b/noir/noir-repo/acvm-repo/acvm/Cargo.toml index 902918475138..53e220db23e5 100644 --- a/noir/noir-repo/acvm-repo/acvm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm" description = "The virtual machine that processes ACIR given a backend/proof system." # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/mod.rs index ee84f7bf60bd..ee503e7e0d05 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/mod.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; use acir::{ - circuit::{AssertionPayload, Circuit, ExpressionWidth, OpcodeLocation}, AcirField, + circuit::{AssertionPayload, Circuit, ExpressionWidth, OpcodeLocation}, }; // The various passes that we can use over ACIR @@ -14,7 +14,7 @@ pub use optimizers::optimize; use optimizers::optimize_internal; pub use simulator::CircuitSimulator; use transformers::transform_internal; -pub use transformers::{transform, MIN_EXPRESSION_WIDTH}; +pub use transformers::{MIN_EXPRESSION_WIDTH, transform}; /// This module moves and decomposes acir opcodes. The transformation map allows consumers of this module to map /// metadata they had about the opcodes to the new opcode structure generated after the transformation. diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/general.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/general.rs index 39a01a38cace..0802f33185cc 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/general.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/general.rs @@ -1,6 +1,6 @@ use acir::{ - native_types::{Expression, Witness}, AcirField, + native_types::{Expression, Witness}, }; use indexmap::IndexMap; diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/merge_expressions.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/merge_expressions.rs index e95c6207c3c3..2590c5f208aa 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/merge_expressions.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/merge_expressions.rs @@ -1,13 +1,13 @@ use std::collections::{BTreeMap, BTreeSet, HashMap}; use acir::{ + AcirField, circuit::{ + Circuit, Opcode, brillig::{BrilligInputs, BrilligOutputs}, opcodes::BlockId, - Circuit, Opcode, }, native_types::{Expression, Witness}, - AcirField, }; use crate::compiler::CircuitSimulator; @@ -258,16 +258,16 @@ impl MergeExpressionsOptimizer { #[cfg(test)] mod tests { - use crate::compiler::{optimizers::MergeExpressionsOptimizer, CircuitSimulator}; + use crate::compiler::{CircuitSimulator, optimizers::MergeExpressionsOptimizer}; use acir::{ + FieldElement, acir_field::AcirField, circuit::{ + Circuit, ExpressionWidth, Opcode, PublicInputs, brillig::{BrilligFunctionId, BrilligOutputs}, opcodes::{BlackBoxFuncCall, FunctionInput}, - Circuit, ExpressionWidth, Opcode, PublicInputs, }, native_types::{Expression, Witness}, - FieldElement, }; use std::collections::BTreeSet; diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/mod.rs index 3531825c709d..3e085e4ba9b5 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/mod.rs @@ -1,6 +1,6 @@ use acir::{ - circuit::{Circuit, Opcode}, AcirField, + circuit::{Circuit, Opcode}, }; // mod constant_backpropagation; @@ -17,7 +17,7 @@ use tracing::info; // use self::constant_backpropagation::ConstantBackpropagationOptimizer; use self::unused_memory::UnusedMemoryOptimizer; -use super::{transform_assert_messages, AcirTransformationMap}; +use super::{AcirTransformationMap, transform_assert_messages}; /// Applies [`ProofSystemCompiler`][crate::ProofSystemCompiler] independent optimizations to a [`Circuit`]. pub fn optimize(acir: Circuit) -> (Circuit, AcirTransformationMap) { diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs index f9c715a277f4..67dce75411e8 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs @@ -1,10 +1,10 @@ use acir::{ + AcirField, circuit::{ - opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum}, Circuit, Opcode, + opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum}, }, native_types::Witness, - AcirField, }; use std::collections::{BTreeMap, HashSet}; @@ -163,12 +163,12 @@ mod tests { use crate::compiler::optimizers::redundant_range::RangeOptimizer; use acir::{ + FieldElement, circuit::{ - opcodes::{BlackBoxFuncCall, FunctionInput}, Circuit, ExpressionWidth, Opcode, PublicInputs, + opcodes::{BlackBoxFuncCall, FunctionInput}, }, native_types::{Expression, Witness}, - FieldElement, }; fn test_circuit(ranges: Vec<(Witness, u32)>) -> Circuit { diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/unused_memory.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/unused_memory.rs index 1325a3b03cdf..8b7e52d66f2c 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/unused_memory.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/unused_memory.rs @@ -1,4 +1,4 @@ -use acir::circuit::{brillig::BrilligInputs, opcodes::BlockId, Circuit, Opcode}; +use acir::circuit::{Circuit, Opcode, brillig::BrilligInputs, opcodes::BlockId}; use std::collections::HashSet; /// `UnusedMemoryOptimizer` will remove initializations of memory blocks which are unused. diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/simulator.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/simulator.rs index 893195f342a9..96134926f5e0 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/simulator.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/simulator.rs @@ -1,11 +1,11 @@ use acir::{ + AcirField, circuit::{ + Circuit, Opcode, brillig::{BrilligInputs, BrilligOutputs}, opcodes::{BlockId, FunctionInput}, - Circuit, Opcode, }, native_types::{Expression, Witness}, - AcirField, }; use std::collections::{BTreeSet, HashMap, HashSet}; @@ -215,10 +215,10 @@ mod tests { use crate::compiler::CircuitSimulator; use acir::{ + FieldElement, acir_field::AcirField, circuit::{Circuit, ExpressionWidth, Opcode, PublicInputs}, native_types::{Expression, Witness}, - FieldElement, }; fn test_circuit( 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 bdd6998835a2..8f890e4ca86b 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 @@ -1,8 +1,8 @@ use std::{cmp::Ordering, collections::HashSet}; use acir::{ - native_types::{Expression, Witness}, AcirField, + native_types::{Expression, Witness}, }; use indexmap::IndexMap; @@ -201,7 +201,7 @@ impl CSatTransformer { // Now we have used up 2 spaces in our assert-zero opcode. The width now dictates, how many more we can add let mut remaining_space = self.width - 2 - 1; // We minus 1 because we need an extra space to contain the intermediate variable - // Keep adding terms until we have no more left, or we reach the width + // Keep adding terms until we have no more left, or we reach the width let mut remaining_linear_terms = Vec::with_capacity(opcode.linear_combinations.len()); while remaining_space > 0 { diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/transformers/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/transformers/mod.rs index 77a5d2da34e0..fe9404b075c8 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/transformers/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/transformers/mod.rs @@ -1,12 +1,11 @@ use acir::{ + AcirField, circuit::{ - self, + self, Circuit, ExpressionWidth, Opcode, brillig::{BrilligInputs, BrilligOutputs}, opcodes::{BlackBoxFuncCall, FunctionInput, MemOp}, - Circuit, ExpressionWidth, Opcode, }, native_types::{Expression, Witness}, - AcirField, }; use indexmap::IndexMap; @@ -17,8 +16,9 @@ pub use csat::MIN_EXPRESSION_WIDTH; use tracing::info; use super::{ + AcirTransformationMap, optimizers::{MergeExpressionsOptimizer, RangeOptimizer}, - transform_assert_messages, AcirTransformationMap, + transform_assert_messages, }; /// We need multiple passes to stabilize the output. diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/arithmetic.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/arithmetic.rs index 5eeabd8a8332..a2921bcbc9b1 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/arithmetic.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/arithmetic.rs @@ -1,9 +1,9 @@ use acir::{ - native_types::{Expression, Witness, WitnessMap}, AcirField, + native_types::{Expression, Witness, WitnessMap}, }; -use super::{insert_value, ErrorLocation, OpcodeNotSolvable, OpcodeResolutionError}; +use super::{ErrorLocation, OpcodeNotSolvable, OpcodeResolutionError, insert_value}; /// An Expression solver will take a Circuit's assert-zero opcodes with witness assignments /// and create the other witness variables @@ -254,7 +254,52 @@ mod tests { use acir::FieldElement; #[test] - fn expression_solver_smoke_test() { + fn solves_simple_assignment() { + let a = Witness(0); + + // a - 1 == 0; + let opcode_a = Expression { + mul_terms: vec![], + linear_combinations: vec![(FieldElement::one(), a)], + q_c: -FieldElement::one(), + }; + + let mut values = WitnessMap::new(); + assert_eq!(ExpressionSolver::solve(&mut values, &opcode_a), Ok(())); + + assert_eq!(values.get(&a).unwrap(), &FieldElement::from(1_i128)); + } + + #[test] + fn solves_unknown_in_mul_term() { + let a = Witness(0); + let b = Witness(1); + let c = Witness(2); + let d = Witness(3); + + // a * b - b - c - d == 0; + let opcode_a = Expression { + mul_terms: vec![(FieldElement::one(), a, b)], + linear_combinations: vec![ + (-FieldElement::one(), b), + (-FieldElement::one(), c), + (-FieldElement::one(), d), + ], + q_c: FieldElement::zero(), + }; + + let mut values = WitnessMap::new(); + values.insert(b, FieldElement::from(2_i128)); + values.insert(c, FieldElement::from(1_i128)); + values.insert(d, FieldElement::from(1_i128)); + + assert_eq!(ExpressionSolver::solve(&mut values, &opcode_a), Ok(())); + + assert_eq!(values.get(&a).unwrap(), &FieldElement::from(2_i128)); + } + + #[test] + fn solves_unknown_in_linear_term() { let a = Witness(0); let b = Witness(1); let c = Witness(2); diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/aes128.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/aes128.rs index e3c8dc78aa62..e241a39f5af7 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/aes128.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/aes128.rs @@ -1,11 +1,11 @@ use acir::{ + AcirField, circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, - AcirField, }; use acvm_blackbox_solver::aes128_encrypt; -use crate::{pwg::insert_value, OpcodeResolutionError}; +use crate::{OpcodeResolutionError, pwg::insert_value}; use super::utils::{to_u8_array, to_u8_vec}; diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/bigint.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/bigint.rs index eb5a6f81e7d5..1c5a436fa110 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/bigint.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/bigint.rs @@ -1,8 +1,8 @@ use crate::pwg::input_to_value; use acir::{ + AcirField, BlackBoxFunc, circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, - AcirField, BlackBoxFunc, }; use acvm_blackbox_solver::BigIntSolver; diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs index 9e5115712751..2c728e77304c 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs @@ -1,11 +1,11 @@ use acir::{ + AcirField, circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, - AcirField, }; use acvm_blackbox_solver::BlackBoxFunctionSolver; -use crate::pwg::{input_to_value, insert_value, OpcodeResolutionError}; +use crate::pwg::{OpcodeResolutionError, input_to_value, insert_value}; pub(super) fn multi_scalar_mul( backend: &impl BlackBoxFunctionSolver, diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/hash.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/hash.rs index 7476b0dc2dcd..a4af9de55cfe 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/hash.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/hash.rs @@ -1,12 +1,12 @@ use acir::{ + AcirField, circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, - AcirField, }; -use acvm_blackbox_solver::{sha256_compression, BlackBoxFunctionSolver, BlackBoxResolutionError}; +use acvm_blackbox_solver::{BlackBoxFunctionSolver, BlackBoxResolutionError, sha256_compression}; -use crate::pwg::{input_to_value, insert_value}; use crate::OpcodeResolutionError; +use crate::pwg::{input_to_value, insert_value}; /// Attempts to solve a 256 bit hash function opcode. /// If successful, `initial_witness` will be mutated to contain the new witness assignment. @@ -49,9 +49,13 @@ fn get_hash_input( // in the message, then we error. if num_bytes_to_take > message_input.len() { return Err(OpcodeResolutionError::BlackBoxFunctionFailed( - acir::BlackBoxFunc::Blake2s, - format!("the number of bytes to take from the message is more than the number of bytes in the message. {} > {}", num_bytes_to_take, message_input.len()), - )); + acir::BlackBoxFunc::Blake2s, + format!( + "the number of bytes to take from the message is more than the number of bytes in the message. {} > {}", + num_bytes_to_take, + message_input.len() + ), + )); } let truncated_message = message_input[0..num_bytes_to_take].to_vec(); Ok(truncated_message) @@ -132,11 +136,10 @@ pub(crate) fn solve_poseidon2_permutation_opcode( } // Read witness assignments - let mut state = Vec::new(); - for input in inputs.iter() { - let witness_assignment = input_to_value(initial_witness, *input, false)?; - state.push(witness_assignment); - } + let state: Vec = inputs + .iter() + .map(|input| input_to_value(initial_witness, *input, false)) + .collect::>()?; let state = backend.poseidon2_permutation(&state, len)?; diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/logic.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/logic.rs index 3fa72d9b215b..587add11b810 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/logic.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/logic.rs @@ -1,9 +1,9 @@ -use crate::pwg::{input_to_value, insert_value}; use crate::OpcodeResolutionError; +use crate::pwg::{input_to_value, insert_value}; use acir::{ + AcirField, circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, - AcirField, }; use acvm_blackbox_solver::{bit_and, bit_xor}; diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs index 2f10e333c24b..ad064fefd7b8 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -1,7 +1,7 @@ use acir::{ + AcirField, circuit::opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput}, native_types::{Witness, WitnessMap}, - AcirField, }; use acvm_blackbox_solver::{blake2s, blake3, keccakf1600}; @@ -10,8 +10,8 @@ use self::{ hash::solve_poseidon2_permutation_opcode, }; -use super::{insert_value, OpcodeNotSolvable, OpcodeResolutionError}; -use crate::{pwg::input_to_value, BlackBoxFunctionSolver}; +use super::{OpcodeNotSolvable, OpcodeResolutionError, insert_value}; +use crate::{BlackBoxFunctionSolver, pwg::input_to_value}; mod aes128; pub(crate) mod bigint; @@ -37,12 +37,8 @@ fn first_missing_assignment( inputs: &[FunctionInput], ) -> Option { inputs.iter().find_map(|input| { - if let ConstantOrWitnessEnum::Witness(ref witness) = input.input_ref() { - if witness_assignments.contains_key(witness) { - None - } else { - Some(*witness) - } + if let ConstantOrWitnessEnum::Witness(witness) = input.input_ref() { + if witness_assignments.contains_key(witness) { None } else { Some(*witness) } } else { None } diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/range.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/range.rs index d7083e4b9c05..039a04b9063d 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/range.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/range.rs @@ -1,8 +1,8 @@ use crate::{ - pwg::{input_to_value, ErrorLocation}, OpcodeResolutionError, + pwg::{ErrorLocation, input_to_value}, }; -use acir::{circuit::opcodes::FunctionInput, native_types::WitnessMap, AcirField}; +use acir::{AcirField, circuit::opcodes::FunctionInput, native_types::WitnessMap}; pub(crate) fn solve_range_opcode( initial_witness: &WitnessMap, @@ -21,3 +21,36 @@ pub(crate) fn solve_range_opcode( } Ok(()) } + +#[cfg(test)] +mod tests { + use std::collections::BTreeMap; + + use acir::{ + FieldElement, + circuit::opcodes::FunctionInput, + native_types::{Witness, WitnessMap}, + }; + + use crate::pwg::blackbox::solve_range_opcode; + + #[test] + fn rejects_too_large_inputs() { + let witness_map = + WitnessMap::from(BTreeMap::from([(Witness(0), FieldElement::from(256u32))])); + let input: FunctionInput = FunctionInput::witness(Witness(0), 8); + assert!(solve_range_opcode(&witness_map, &input, false).is_err()); + } + + #[test] + fn accepts_valid_inputs() { + let values: [u32; 4] = [0, 1, 8, 255]; + + for value in values { + let witness_map = + WitnessMap::from(BTreeMap::from([(Witness(0), FieldElement::from(value))])); + let input: FunctionInput = FunctionInput::witness(Witness(0), 8); + assert!(solve_range_opcode(&witness_map, &input, false).is_ok()); + } + } +} diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/signature/ecdsa.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/signature/ecdsa.rs index db92d27b871e..4b53316e88f5 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/signature/ecdsa.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/signature/ecdsa.rs @@ -1,16 +1,16 @@ use acir::{ + AcirField, circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, - AcirField, }; use acvm_blackbox_solver::{ecdsa_secp256k1_verify, ecdsa_secp256r1_verify}; use crate::{ + OpcodeResolutionError, pwg::{ blackbox::utils::{to_u8_array, to_u8_vec}, insert_value, }, - OpcodeResolutionError, }; pub(crate) fn secp256k1_prehashed( diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/utils.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/utils.rs index b966cb0cc5d2..6c9c5a1025ad 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/utils.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/utils.rs @@ -1,6 +1,6 @@ -use acir::{circuit::opcodes::FunctionInput, native_types::WitnessMap, AcirField}; +use acir::{AcirField, circuit::opcodes::FunctionInput, native_types::WitnessMap}; -use crate::pwg::{input_to_value, OpcodeResolutionError}; +use crate::pwg::{OpcodeResolutionError, input_to_value}; pub(crate) fn to_u8_array( initial_witness: &WitnessMap, diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs index a635cd926159..136f358b9bb6 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs @@ -1,20 +1,20 @@ use std::collections::HashMap; use acir::{ + AcirField, brillig::{ForeignCallParam, ForeignCallResult, Opcode as BrilligOpcode}, circuit::{ + ErrorSelector, OpcodeLocation, RawAssertionPayload, ResolvedAssertionPayload, brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}, opcodes::BlockId, - ErrorSelector, OpcodeLocation, RawAssertionPayload, ResolvedAssertionPayload, }, native_types::WitnessMap, - AcirField, }; use acvm_blackbox_solver::BlackBoxFunctionSolver; -use brillig_vm::{BrilligProfilingSamples, FailureReason, MemoryValue, VMStatus, VM}; +use brillig_vm::{BrilligProfilingSamples, FailureReason, MemoryValue, VM, VMStatus}; use serde::{Deserialize, Serialize}; -use crate::{pwg::OpcodeNotSolvable, OpcodeResolutionError}; +use crate::{OpcodeResolutionError, pwg::OpcodeNotSolvable}; use super::{get_value, insert_value, memory_op::MemoryOpSolver}; @@ -100,7 +100,7 @@ impl<'b, B: BlackBoxFunctionSolver, F: AcirField> BrilligSolver<'b, F, B> { Err(_) => { return Err(OpcodeResolutionError::OpcodeNotSolvable( OpcodeNotSolvable::ExpressionHasTooManyUnknowns(expr.clone()), - )) + )); } }, BrilligInputs::Array(expr_arr) => { @@ -111,7 +111,7 @@ impl<'b, B: BlackBoxFunctionSolver, F: AcirField> BrilligSolver<'b, F, B> { Err(_) => { return Err(OpcodeResolutionError::OpcodeNotSolvable( OpcodeNotSolvable::ExpressionHasTooManyUnknowns(expr.clone()), - )) + )); } } } diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/memory_op.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/memory_op.rs index 2a83bf2531ca..aef18d3957ea 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/memory_op.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/memory_op.rs @@ -1,15 +1,15 @@ use std::collections::HashMap; use acir::{ + AcirField, circuit::opcodes::MemOp, native_types::{Expression, Witness, WitnessMap}, - AcirField, }; +use super::{ErrorLocation, OpcodeResolutionError}; use super::{ arithmetic::ExpressionSolver, get_value, insert_value, is_predicate_false, witness_to_value, }; -use super::{ErrorLocation, OpcodeResolutionError}; type MemoryIndex = u32; @@ -140,9 +140,9 @@ mod tests { use std::collections::BTreeMap; use acir::{ + AcirField, FieldElement, circuit::opcodes::MemOp, native_types::{Expression, Witness, WitnessMap}, - AcirField, FieldElement, }; use super::MemoryOpSolver; diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs index 6e0e28cf81d3..f5d56df17c1e 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs @@ -3,17 +3,17 @@ use std::collections::HashMap; use acir::{ + AcirField, BlackBoxFunc, brillig::ForeignCallResult, circuit::{ + AssertionPayload, ErrorSelector, ExpressionOrMemory, Opcode, OpcodeLocation, + RawAssertionPayload, ResolvedAssertionPayload, brillig::{BrilligBytecode, BrilligFunctionId}, opcodes::{ AcirFunctionId, BlockId, ConstantOrWitnessEnum, FunctionInput, InvalidInputBitSize, }, - AssertionPayload, ErrorSelector, ExpressionOrMemory, Opcode, OpcodeLocation, - RawAssertionPayload, ResolvedAssertionPayload, }, native_types::{Expression, Witness, WitnessMap}, - AcirField, BlackBoxFunc, }; use acvm_blackbox_solver::BlackBoxResolutionError; @@ -73,6 +73,7 @@ impl std::fmt::Display for ACVMStatus { } } +#[expect(clippy::large_enum_variant)] pub enum StepResult<'a, F, B: BlackBoxFunctionSolver> { Status(ACVMStatus), IntoBrillig(BrilligSolver<'a, F, B>), @@ -142,7 +143,9 @@ pub enum OpcodeResolutionError { }, #[error("Attempted to call `main` with a `Call` opcode")] AcirMainCallAttempted { opcode_location: ErrorLocation }, - #[error("{results_size:?} result values were provided for {outputs_size:?} call output witnesses, most likely due to bad ACIR codegen")] + #[error( + "{results_size:?} result values were provided for {outputs_size:?} call output witnesses, most likely due to bad ACIR codegen" + )] AcirCallOutputsMismatch { opcode_location: ErrorLocation, results_size: u32, outputs_size: u32 }, #[error("(--pedantic): Predicates are expected to be 0 or 1, but found: {pred_value}")] PredicateLargerThanOne { opcode_location: ErrorLocation, pred_value: F }, @@ -744,11 +747,7 @@ pub fn insert_value( // The function is used during partial witness generation to report unsolved witness fn any_witness_from_expression(expr: &Expression) -> Option { if expr.linear_combinations.is_empty() { - if expr.mul_terms.is_empty() { - None - } else { - Some(expr.mul_terms[0].1) - } + if expr.mul_terms.is_empty() { None } else { Some(expr.mul_terms[0].1) } } else { Some(expr.linear_combinations[0].1) } diff --git a/noir/noir-repo/acvm-repo/acvm/tests/solver.rs b/noir/noir-repo/acvm-repo/acvm/tests/solver.rs index 2fd610503779..ce75a7d20017 100644 --- a/noir/noir-repo/acvm-repo/acvm/tests/solver.rs +++ b/noir/noir-repo/acvm-repo/acvm/tests/solver.rs @@ -3,20 +3,20 @@ use std::sync::Arc; use acir::brillig::{BitSize, HeapVector, IntegerBitSize}; use acir::{ + AcirField, FieldElement, acir_field::GenericFieldElement, brillig::{BinaryFieldOp, MemoryAddress, Opcode as BrilligOpcode, ValueOrArray}, circuit::{ + Opcode, OpcodeLocation, brillig::{BrilligBytecode, BrilligFunctionId, BrilligInputs, BrilligOutputs}, opcodes::{BlackBoxFuncCall, BlockId, BlockType, FunctionInput, MemOp}, - Opcode, OpcodeLocation, }, native_types::{Expression, Witness, WitnessMap}, - AcirField, FieldElement, }; -use acvm::pwg::{ACVMStatus, ErrorLocation, ForeignCallWaitInfo, OpcodeResolutionError, ACVM}; +use acvm::pwg::{ACVM, ACVMStatus, ErrorLocation, ForeignCallWaitInfo, OpcodeResolutionError}; use acvm_blackbox_solver::{BigIntSolver, StubbedBlackBoxSolver}; -use bn254_blackbox_solver::{field_from_hex, Bn254BlackBoxSolver, POSEIDON2_CONFIG}; +use bn254_blackbox_solver::{Bn254BlackBoxSolver, POSEIDON2_CONFIG, field_from_hex}; use brillig_vm::brillig::HeapValueType; use num_bigint::BigUint; diff --git a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml index 308d0ef516bb..d8416b5d89f8 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm_js" description = "Typescript wrapper around the ACVM allowing execution of ACIR code" # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true @@ -40,7 +40,7 @@ getrandom = { workspace = true, features = ["js"] } build-data.workspace = true pkg-config = "0.3" -[dev-dependencies] +[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dev-dependencies] wasm-bindgen-test.workspace = true [features] 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/acvm-repo/acvm_js/package.json b/noir/noir-repo/acvm-repo/acvm_js/package.json index e54a952698b1..fc842da42360 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/package.json +++ b/noir/noir-repo/acvm-repo/acvm_js/package.json @@ -1,6 +1,6 @@ { "name": "@noir-lang/acvm_js", - "version": "1.0.0-beta.2", + "version": "1.0.0-beta.3", "publishConfig": { "access": "public" }, diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/black_box_solvers.rs b/noir/noir-repo/acvm-repo/acvm_js/src/black_box_solvers.rs index 0e35851ee788..02c026cd26fa 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/black_box_solvers.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/black_box_solvers.rs @@ -2,7 +2,7 @@ use js_sys::JsString; use wasm_bindgen::prelude::*; use crate::js_witness_map::{field_element_to_js_string, js_value_to_field_element}; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; /// Performs a bitwise AND operation between `lhs` and `rhs` #[wasm_bindgen] diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/execute.rs b/noir/noir-repo/acvm-repo/acvm_js/src/execute.rs index e4d52063977f..3efa6900f6ec 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/execute.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/execute.rs @@ -1,22 +1,22 @@ use std::{future::Future, pin::Pin}; -use acvm::acir::circuit::brillig::BrilligBytecode; use acvm::acir::circuit::ResolvedAssertionPayload; +use acvm::acir::circuit::brillig::BrilligBytecode; +use acvm::{BlackBoxFunctionSolver, FieldElement}; use acvm::{ acir::circuit::{Circuit, Program}, acir::native_types::{WitnessMap, WitnessStack}, - pwg::{ACVMStatus, ErrorLocation, OpcodeResolutionError, ACVM}, + pwg::{ACVM, ACVMStatus, ErrorLocation, OpcodeResolutionError}, }; -use acvm::{BlackBoxFunctionSolver, FieldElement}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use js_sys::Error; use wasm_bindgen::prelude::wasm_bindgen; use crate::{ - foreign_call::{resolve_brillig, ForeignCallHandler}, - public_witness::extract_indices, JsExecutionError, JsSolvedAndReturnWitness, JsWitnessMap, JsWitnessStack, + foreign_call::{ForeignCallHandler, resolve_brillig}, + public_witness::extract_indices, }; /// Executes an ACIR circuit to generate the solved witness from the initial witness. diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/inputs.rs b/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/inputs.rs index dd12bc639ece..eee36a0f01a9 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/inputs.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/inputs.rs @@ -1,4 +1,4 @@ -use acvm::{brillig_vm::brillig::ForeignCallParam, FieldElement}; +use acvm::{FieldElement, brillig_vm::brillig::ForeignCallParam}; use crate::js_witness_map::field_element_to_js_string; diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/mod.rs b/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/mod.rs index 4884c1173dc4..8b34b699a856 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/mod.rs @@ -1,7 +1,7 @@ -use acvm::{brillig_vm::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo, FieldElement}; +use acvm::{FieldElement, brillig_vm::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo}; use js_sys::{Error, JsString}; -use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; +use wasm_bindgen::{JsValue, prelude::wasm_bindgen}; mod inputs; mod outputs; diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/outputs.rs b/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/outputs.rs index 2b3f44fe98df..75b9c3aa3113 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/outputs.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/outputs.rs @@ -1,6 +1,6 @@ use acvm::{ - brillig_vm::brillig::{ForeignCallParam, ForeignCallResult}, FieldElement, + brillig_vm::brillig::{ForeignCallParam, ForeignCallResult}, }; use wasm_bindgen::JsValue; diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs b/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs index f6a00af79422..6aa7cf756786 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs @@ -1,10 +1,10 @@ use acvm::{ - acir::circuit::{brillig::BrilligFunctionId, OpcodeLocation, RawAssertionPayload}, FieldElement, + acir::circuit::{OpcodeLocation, RawAssertionPayload, brillig::BrilligFunctionId}, }; use gloo_utils::format::JsValueSerdeExt; use js_sys::{Array, Error, JsString, Reflect}; -use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; +use wasm_bindgen::prelude::{JsValue, wasm_bindgen}; #[wasm_bindgen(typescript_custom_section)] const EXECUTION_ERROR: &'static str = r#" diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_map.rs b/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_map.rs index 8316059e21a6..a09e8e1ade7a 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_map.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_map.rs @@ -1,10 +1,10 @@ use acvm::{ - acir::native_types::{Witness, WitnessMap}, - acir::AcirField, FieldElement, + acir::AcirField, + acir::native_types::{Witness, WitnessMap}, }; use js_sys::{JsString, Map, Object}; -use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; +use wasm_bindgen::prelude::{JsValue, wasm_bindgen}; #[wasm_bindgen(typescript_custom_section)] const WITNESS_MAP: &'static str = r#" @@ -106,21 +106,21 @@ pub(crate) fn field_element_to_js_string(field_element: &FieldElement) -> JsStri format!("0x{}", field_element.to_hex()).into() } -#[cfg(test)] +#[cfg(all(test, any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))] mod test { - use wasm_bindgen_test::wasm_bindgen_test as test; + use wasm_bindgen_test::*; use std::collections::BTreeMap; use acvm::{ - acir::native_types::{Witness, WitnessMap}, AcirField, FieldElement, + acir::native_types::{Witness, WitnessMap}, }; use wasm_bindgen::JsValue; use crate::JsWitnessMap; - #[test] + #[wasm_bindgen_test] fn test_witness_map_to_js() { let witness_map = BTreeMap::from([ (Witness(1), FieldElement::one()), diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_stack.rs b/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_stack.rs index d59ee508086d..e1baa4917274 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_stack.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_stack.rs @@ -1,6 +1,6 @@ -use acvm::{acir::native_types::WitnessStack, FieldElement}; +use acvm::{FieldElement, acir::native_types::WitnessStack}; use js_sys::{Array, Map, Object}; -use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; +use wasm_bindgen::prelude::{JsValue, wasm_bindgen}; use crate::JsWitnessMap; diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/logging.rs b/noir/noir-repo/acvm-repo/acvm_js/src/logging.rs index 483c23ab624e..f40e2fa1bd34 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/logging.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/logging.rs @@ -1,6 +1,6 @@ use js_sys::{Error, JsString}; -use tracing_subscriber::prelude::*; use tracing_subscriber::EnvFilter; +use tracing_subscriber::prelude::*; use tracing_web::MakeWebConsoleWriter; use wasm_bindgen::prelude::*; diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/public_witness.rs b/noir/noir-repo/acvm-repo/acvm_js/src/public_witness.rs index 245d5b4dd0ad..2ca794ba3a50 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/public_witness.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/public_witness.rs @@ -1,9 +1,9 @@ use acvm::{ + FieldElement, acir::{ circuit::Program, native_types::{Witness, WitnessMap}, }, - FieldElement, }; use js_sys::JsString; use wasm_bindgen::prelude::wasm_bindgen; @@ -44,7 +44,11 @@ pub fn get_return_witness( let circuit = match program.functions.len() { 0 => return Ok(JsWitnessMap::from(WitnessMap::new())), 1 => &program.functions[0], - _ => return Err(JsString::from("Program contains multiple circuits however ACVM currently only supports programs containing a single circuit")) + _ => { + return Err(JsString::from( + "Program contains multiple circuits however ACVM currently only supports programs containing a single circuit", + )); + } }; let witness_map = WitnessMap::from(witness_map); @@ -71,7 +75,11 @@ pub fn get_public_parameters_witness( let circuit = match program.functions.len() { 0 => return Ok(JsWitnessMap::from(WitnessMap::new())), 1 => &program.functions[0], - _ => return Err(JsString::from("Program contains multiple circuits however ACVM currently only supports programs containing a single circuit")) + _ => { + return Err(JsString::from( + "Program contains multiple circuits however ACVM currently only supports programs containing a single circuit", + )); + } }; let witness_map = WitnessMap::from(solved_witness); @@ -98,7 +106,11 @@ pub fn get_public_witness( let circuit = match program.functions.len() { 0 => return Ok(JsWitnessMap::from(WitnessMap::new())), 1 => &program.functions[0], - _ => return Err(JsString::from("Program contains multiple circuits however ACVM currently only supports programs containing a single circuit")) + _ => { + return Err(JsString::from( + "Program contains multiple circuits however ACVM currently only supports programs containing a single circuit", + )); + } }; let witness_map = WitnessMap::from(solved_witness); diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml index bcd5be3f43ab..38292dd1924d 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm_blackbox_solver" description = "A solver for the blackbox functions found in ACIR and Brillig" # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs b/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs index f7be1e80a556..d3531007f092 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs +++ b/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs @@ -220,8 +220,8 @@ impl BigIntSolverWithId { #[test] fn all_allowed_bigint_moduli_are_prime() { - use num_prime::nt_funcs::is_prime; use num_prime::Primality; + use num_prime::nt_funcs::is_prime; for modulus in BigIntSolver::allowed_bigint_moduli() { let modulus = BigUint::from_bytes_le(&modulus); @@ -231,7 +231,10 @@ fn all_allowed_bigint_moduli_are_prime() { Primality::No => panic!("not all allowed_bigint_moduli are prime: {modulus}"), Primality::Probable(probability) => { if probability < 0.90 { - panic!("not all allowed_bigint_moduli are prime within the allowed probability: {} < 0.90", probability); + panic!( + "not all allowed_bigint_moduli are prime within the allowed probability: {} < 0.90", + probability + ); } } } diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs b/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs index 37fe5d053633..af0104b54f0a 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs +++ b/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs @@ -25,8 +25,8 @@ pub trait BlackBoxFunctionSolver { ) -> Result<(F, F, F), BlackBoxResolutionError>; fn poseidon2_permutation( &self, - _inputs: &[F], - _len: u32, + inputs: &[F], + len: u32, ) -> Result, BlackBoxResolutionError>; } diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256k1.rs b/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256k1.rs index 17c51353f7fa..d090029b1cd5 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256k1.rs +++ b/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256k1.rs @@ -1,15 +1,15 @@ -use k256::elliptic_curve::sec1::FromEncodedPoint; use k256::elliptic_curve::PrimeField; +use k256::elliptic_curve::sec1::FromEncodedPoint; use blake2::digest::generic_array::GenericArray; -use k256::{ecdsa::Signature, Scalar}; use k256::{ + AffinePoint, EncodedPoint, ProjectivePoint, PublicKey, elliptic_curve::{ - sec1::{Coordinates, ToEncodedPoint}, IsHigh, + sec1::{Coordinates, ToEncodedPoint}, }, - AffinePoint, EncodedPoint, ProjectivePoint, PublicKey, }; +use k256::{Scalar, ecdsa::Signature}; pub(super) fn verify_signature( hashed_msg: &[u8], diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256r1.rs b/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256r1.rs index 54559d7c7744..7a162aa800ab 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256r1.rs +++ b/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256r1.rs @@ -1,15 +1,15 @@ -use p256::elliptic_curve::sec1::FromEncodedPoint; use p256::elliptic_curve::PrimeField; +use p256::elliptic_curve::sec1::FromEncodedPoint; use blake2::digest::generic_array::GenericArray; -use p256::{ecdsa::Signature, Scalar}; use p256::{ + AffinePoint, EncodedPoint, ProjectivePoint, PublicKey, elliptic_curve::{ - sec1::{Coordinates, ToEncodedPoint}, IsHigh, + sec1::{Coordinates, ToEncodedPoint}, }, - AffinePoint, EncodedPoint, ProjectivePoint, PublicKey, }; +use p256::{Scalar, ecdsa::Signature}; pub(super) fn verify_signature( hashed_msg: &[u8], diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml index 5ac93d9d8f68..d9367f6b52af 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml @@ -2,7 +2,7 @@ name = "bn254_blackbox_solver" description = "Solvers for black box functions which are specific for the bn254 curve" # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/benches/criterion.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/benches/criterion.rs index fc566b70a268..05211264beb6 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/benches/criterion.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/benches/criterion.rs @@ -1,4 +1,4 @@ -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{Criterion, criterion_group, criterion_main}; use std::{hint::black_box, time::Duration}; use acir::{AcirField, FieldElement}; diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/hash_to_curve.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/hash_to_curve.rs index 3c284fa811ca..028ca2e5d1cc 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/hash_to_curve.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/hash_to_curve.rs @@ -62,11 +62,7 @@ pub(crate) fn hash_to_curve(seed: &[u8], attempt_count: u8) -> Affine { if let Some(point) = Affine::get_point_from_x_unchecked(x, false) { let parity_bit = hash_hi[0] > 127; let y_bit_set = point.y().unwrap().into_bigint().get_bit(0); - if (parity_bit && !y_bit_set) || (!parity_bit && y_bit_set) { - -point - } else { - point - } + if (parity_bit && !y_bit_set) || (!parity_bit && y_bit_set) { -point } else { point } } else { hash_to_curve(seed, attempt_count + 1) } diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs index fc031faefa26..d0fece86d6f9 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs @@ -11,8 +11,8 @@ mod poseidon2; pub use embedded_curve_ops::{embedded_curve_add, multi_scalar_mul}; pub use generator::generators::derive_generators; pub use poseidon2::{ - field_from_hex, poseidon2_permutation, poseidon_hash, Poseidon2Config, Poseidon2Sponge, - POSEIDON2_CONFIG, + POSEIDON2_CONFIG, Poseidon2Config, Poseidon2Sponge, field_from_hex, poseidon_hash, + poseidon2_permutation, }; // Temporary hack, this ensure that we always use a bn254 field here diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs index 3aa735388ca8..d756f40a236f 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs @@ -430,7 +430,7 @@ lazy_static! { }; } -impl<'a> Poseidon2<'a> { +impl Poseidon2<'_> { pub(crate) fn new() -> Self { Poseidon2 { config: &POSEIDON2_CONFIG } } @@ -626,7 +626,7 @@ impl<'a> Poseidon2Sponge<'a> { mod test { use acir::AcirField; - use super::{field_from_hex, poseidon2_permutation, FieldElement}; + use super::{FieldElement, field_from_hex, poseidon2_permutation}; #[test] fn smoke_test() { diff --git a/noir/noir-repo/acvm-repo/brillig/Cargo.toml b/noir/noir-repo/acvm-repo/brillig/Cargo.toml index d037531b8ed5..5a9720238aca 100644 --- a/noir/noir-repo/acvm-repo/brillig/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig/Cargo.toml @@ -2,7 +2,7 @@ name = "brillig" description = "Brillig is the bytecode ACIR uses for non-determinism." # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/brillig/src/black_box.rs b/noir/noir-repo/acvm-repo/brillig/src/black_box.rs index be9ba20ed499..eb496d0f826c 100644 --- a/noir/noir-repo/acvm-repo/brillig/src/black_box.rs +++ b/noir/noir-repo/acvm-repo/brillig/src/black_box.rs @@ -1,4 +1,4 @@ -use crate::{opcodes::HeapVector, HeapArray, MemoryAddress}; +use crate::{HeapArray, MemoryAddress, opcodes::HeapVector}; use serde::{Deserialize, Serialize}; /// These opcodes provide an equivalent of ACIR blackbox functions. diff --git a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml index 531179f54d1f..55bfe486c9f3 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml @@ -2,7 +2,7 @@ name = "brillig_vm" description = "The virtual machine that processes Brillig bytecode, used to introduce non-determinism to the ACVM" # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/brillig_vm/src/arithmetic.rs b/noir/noir-repo/acvm-repo/brillig_vm/src/arithmetic.rs index c9417faaff30..24013d1fde7a 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/src/arithmetic.rs +++ b/noir/noir-repo/acvm-repo/brillig_vm/src/arithmetic.rs @@ -1,7 +1,7 @@ use std::ops::{BitAnd, BitOr, BitXor, Shl, Shr}; -use acir::brillig::{BinaryFieldOp, BinaryIntOp, BitSize, IntegerBitSize}; use acir::AcirField; +use acir::brillig::{BinaryFieldOp, BinaryIntOp, BitSize, IntegerBitSize}; use num_bigint::BigUint; use num_traits::{CheckedDiv, WrappingAdd, WrappingMul, WrappingSub, Zero}; @@ -231,21 +231,11 @@ fn evaluate_binary_int_op_shifts + Zero + Shl + Shr { let rhs_usize: usize = rhs as usize; - #[allow(unused_qualifications)] - if rhs_usize >= 8 * std::mem::size_of::() { - T::zero() - } else { - lhs << rhs.into() - } + if rhs_usize >= 8 * size_of::() { T::zero() } else { lhs << rhs.into() } } BinaryIntOp::Shr => { let rhs_usize: usize = rhs as usize; - #[allow(unused_qualifications)] - if rhs_usize >= 8 * std::mem::size_of::() { - T::zero() - } else { - lhs >> rhs.into() - } + if rhs_usize >= 8 * size_of::() { T::zero() } else { lhs >> rhs.into() } } _ => unreachable!("Operator not handled by this function: {op:?}"), } diff --git a/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs b/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs index aa3d0de55f47..74ea3568a1a2 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs +++ b/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs @@ -1,14 +1,14 @@ use acir::brillig::{BlackBoxOp, HeapArray, HeapVector}; use acir::{AcirField, BlackBoxFunc}; use acvm_blackbox_solver::{ - aes128_encrypt, blake2s, blake3, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify, keccakf1600, - sha256_compression, BigIntSolverWithId, BlackBoxFunctionSolver, BlackBoxResolutionError, + BigIntSolverWithId, BlackBoxFunctionSolver, BlackBoxResolutionError, aes128_encrypt, blake2s, + blake3, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify, keccakf1600, sha256_compression, }; use num_bigint::BigUint; use num_traits::Zero; -use crate::memory::MemoryValue; use crate::Memory; +use crate::memory::MemoryValue; fn read_heap_vector<'a, F: AcirField>( memory: &'a Memory, diff --git a/noir/noir-repo/acvm-repo/brillig_vm/src/lib.rs b/noir/noir-repo/acvm-repo/brillig_vm/src/lib.rs index 27759012335c..935c296d5ae8 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/src/lib.rs +++ b/noir/noir-repo/acvm-repo/brillig_vm/src/lib.rs @@ -10,19 +10,19 @@ //! [acir]: https://crates.io/crates/acir //! [acvm]: https://crates.io/crates/acvm +use acir::AcirField; use acir::brillig::{ BinaryFieldOp, BinaryIntOp, BitSize, ForeignCallParam, ForeignCallResult, HeapArray, HeapValueType, HeapVector, IntegerBitSize, MemoryAddress, Opcode, ValueOrArray, }; -use acir::AcirField; use acvm_blackbox_solver::BlackBoxFunctionSolver; -use arithmetic::{evaluate_binary_field_op, evaluate_binary_int_op, BrilligArithmeticError}; -use black_box::{evaluate_black_box, BrilligBigIntSolver}; +use arithmetic::{BrilligArithmeticError, evaluate_binary_field_op, evaluate_binary_int_op}; +use black_box::{BrilligBigIntSolver, evaluate_black_box}; // Re-export `brillig`. pub use acir::brillig; use memory::MemoryTypeError; -pub use memory::{Memory, MemoryValue, MEMORY_ADDRESSING_BIT_SIZE}; +pub use memory::{MEMORY_ADDRESSING_BIT_SIZE, Memory, MemoryValue}; mod arithmetic; mod black_box; @@ -548,69 +548,99 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver> VM<'a, F, B> { destinations.iter().zip(destination_value_types).zip(&values) { match (destination, value_type) { - (ValueOrArray::MemoryAddress(value_index), HeapValueType::Simple(bit_size)) => { - match output { - ForeignCallParam::Single(value) => { - self.write_value_to_memory(*value_index, value, *bit_size)?; - } - _ => return Err(format!( - "Function result size does not match brillig bytecode. Expected 1 result but got {output:?}") - ), - } - } - ( - ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }), - HeapValueType::Array { value_types, size: type_size }, - ) if size == type_size => { - if HeapValueType::all_simple(value_types) { + (ValueOrArray::MemoryAddress(value_index), HeapValueType::Simple(bit_size)) => { match output { - ForeignCallParam::Array(values) => { - if values.len() != *size { - // foreign call returning flattened values into a nested type, so the sizes do not match - let destination = self.memory.read_ref(*pointer_index); - let return_type = value_type; - let mut flatten_values_idx = 0; //index of values read from flatten_values - self.write_slice_of_values_to_memory(destination, &output.fields(), &mut flatten_values_idx, return_type)?; - } else { - self.write_values_to_memory_slice(*pointer_index, values, value_types)?; - } + ForeignCallParam::Single(value) => { + self.write_value_to_memory(*value_index, value, *bit_size)?; } _ => { - return Err("Function result size does not match brillig bytecode size".to_string()); + return Err(format!( + "Function result size does not match brillig bytecode. Expected 1 result but got {output:?}" + )); } } - } else { - // foreign call returning flattened values into a nested type, so the sizes do not match - let destination = self.memory.read_ref(*pointer_index); - let return_type = value_type; - let mut flatten_values_idx = 0; //index of values read from flatten_values - self.write_slice_of_values_to_memory(destination, &output.fields(), &mut flatten_values_idx, return_type)?; - } - } - ( - ValueOrArray::HeapVector(HeapVector {pointer: pointer_index, size: size_index }), - HeapValueType::Vector { value_types }, - ) => { - if HeapValueType::all_simple(value_types) { - match output { - ForeignCallParam::Array(values) => { - // Set our size in the size address - self.memory.write(*size_index, values.len().into()); - self.write_values_to_memory_slice(*pointer_index, values, value_types)?; - + } + ( + ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }), + HeapValueType::Array { value_types, size: type_size }, + ) if size == type_size => { + if HeapValueType::all_simple(value_types) { + match output { + ForeignCallParam::Array(values) => { + if values.len() != *size { + // foreign call returning flattened values into a nested type, so the sizes do not match + let destination = self.memory.read_ref(*pointer_index); + let return_type = value_type; + let mut flatten_values_idx = 0; //index of values read from flatten_values + self.write_slice_of_values_to_memory( + destination, + &output.fields(), + &mut flatten_values_idx, + return_type, + )?; + } else { + self.write_values_to_memory_slice( + *pointer_index, + values, + value_types, + )?; + } + } + _ => { + return Err( + "Function result size does not match brillig bytecode size" + .to_string(), + ); + } } - _ => { - return Err("Function result size does not match brillig bytecode size".to_string()); + } else { + // foreign call returning flattened values into a nested type, so the sizes do not match + let destination = self.memory.read_ref(*pointer_index); + let return_type = value_type; + let mut flatten_values_idx = 0; //index of values read from flatten_values + self.write_slice_of_values_to_memory( + destination, + &output.fields(), + &mut flatten_values_idx, + return_type, + )?; + } + } + ( + ValueOrArray::HeapVector(HeapVector { + pointer: pointer_index, + size: size_index, + }), + HeapValueType::Vector { value_types }, + ) => { + if HeapValueType::all_simple(value_types) { + match output { + ForeignCallParam::Array(values) => { + // Set our size in the size address + self.memory.write(*size_index, values.len().into()); + self.write_values_to_memory_slice( + *pointer_index, + values, + value_types, + )?; + } + _ => { + return Err( + "Function result size does not match brillig bytecode size" + .to_string(), + ); + } } + } else { + unimplemented!("deflattening heap vectors from foreign calls"); } - } else { - unimplemented!("deflattening heap vectors from foreign calls"); + } + _ => { + return Err(format!( + "Unexpected value type {value_type:?} for destination {destination:?}" + )); } } - _ => { - return Err(format!("Unexpected value type {value_type:?} for destination {destination:?}")); - } - } } let _ = diff --git a/noir/noir-repo/acvm-repo/brillig_vm/src/memory.rs b/noir/noir-repo/acvm-repo/brillig_vm/src/memory.rs index 73443384efa5..33a760b52f57 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/src/memory.rs +++ b/noir/noir-repo/acvm-repo/brillig_vm/src/memory.rs @@ -1,6 +1,6 @@ use acir::{ - brillig::{BitSize, IntegerBitSize, MemoryAddress}, AcirField, + brillig::{BitSize, IntegerBitSize, MemoryAddress}, }; pub const MEMORY_ADDRESSING_BIT_SIZE: IntegerBitSize = IntegerBitSize::U32; @@ -18,7 +18,9 @@ pub enum MemoryValue { #[derive(Debug, thiserror::Error)] pub enum MemoryTypeError { - #[error("Bit size for value {value_bit_size} does not match the expected bit size {expected_bit_size}")] + #[error( + "Bit size for value {value_bit_size} does not match the expected bit size {expected_bit_size}" + )] MismatchedBitSize { value_bit_size: u32, expected_bit_size: u32 }, } diff --git a/noir/noir-repo/compiler/integration-tests/package.json b/noir/noir-repo/compiler/integration-tests/package.json index e33179f31e7f..053e9efeed20 100644 --- a/noir/noir-repo/compiler/integration-tests/package.json +++ b/noir/noir-repo/compiler/integration-tests/package.json @@ -13,7 +13,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.72.1", "@noir-lang/noir_js": "workspace:*", "@noir-lang/noir_wasm": "workspace:*", "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", 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 9838a8af2102..3bbe2181798e 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs @@ -1,15 +1,15 @@ use std::collections::BTreeMap; -use acvm::acir::circuit::ErrorSelector; use acvm::AcirField; +use acvm::acir::circuit::ErrorSelector; use iter_extended::vecmap; use noirc_abi::{ Abi, AbiErrorType, AbiParameter, AbiReturnType, AbiType, AbiValue, AbiVisibility, Sign, }; -use noirc_errors::Span; +use noirc_errors::Location; use noirc_evaluator::ErrorType; -use noirc_frontend::ast::{Signedness, Visibility}; use noirc_frontend::TypeBinding; +use noirc_frontend::ast::{Signedness, Visibility}; use noirc_frontend::{ hir::Context, hir_def::{ @@ -42,11 +42,11 @@ pub(super) fn gen_abi( } // Get the Span of the root crate's main function, or else a dummy span if that fails -fn get_main_function_span(context: &Context) -> Span { +fn get_main_function_location(context: &Context) -> Location { if let Some(func_id) = context.get_main_function(context.root_crate_id()) { - context.function_meta(&func_id).location.span + context.function_meta(&func_id).location } else { - Span::default() + Location::dummy() } } @@ -54,7 +54,7 @@ fn build_abi_error_type(context: &Context, typ: ErrorType) -> AbiErrorType { match typ { ErrorType::Dynamic(typ) => { if let Type::FmtString(len, item_types) = typ { - let span = get_main_function_span(context); + let span = get_main_function_location(context); let length = len.evaluate_to_u32(span).expect("Cannot evaluate fmt length"); let Type::Tuple(item_types) = item_types.as_ref() else { unreachable!("FmtString items must be a tuple") @@ -74,7 +74,7 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType { match typ { Type::FieldElement => AbiType::Field, Type::Array(size, typ) => { - let span = get_main_function_span(context); + let span = get_main_function_location(context); let length = size .evaluate_to_u32(span) .expect("Cannot have variable sized arrays as a parameter to main"); @@ -103,7 +103,7 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType { } Type::Bool => AbiType::Boolean, Type::String(size) => { - let span = get_main_function_span(context); + let span = get_main_function_location(context); let size = size .evaluate_to_u32(span) .expect("Cannot have variable sized strings as a parameter to main"); @@ -226,7 +226,9 @@ pub(super) fn value_from_hir_expression(context: &Context, expression: HirExpres }, HirLiteral::Bool(value) => AbiValue::Boolean { value }, HirLiteral::Str(value) => AbiValue::String { value }, - HirLiteral::Integer(field, sign) => AbiValue::Integer { value: field.to_hex(), sign }, + HirLiteral::Integer(value) => { + AbiValue::Integer { value: value.field.to_hex(), sign: value.is_negative } + } _ => unreachable!("Literal cannot be used in the abi"), }, _ => unreachable!("Type cannot be used in the abi {:?}", expression), diff --git a/noir/noir-repo/compiler/noirc_driver/src/contract.rs b/noir/noir-repo/compiler/noirc_driver/src/contract.rs index 6253f3e1814e..b4c5e9a3fcae 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/contract.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/contract.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, HashMap}; -use acvm::{acir::circuit::Program, FieldElement}; +use acvm::{FieldElement, acir::circuit::Program}; use fm::FileId; use noirc_abi::{Abi, AbiType, AbiValue}; use noirc_errors::debug_info::DebugInfo; @@ -41,6 +41,8 @@ pub struct CompiledContract { pub struct ContractFunction { pub name: String, + pub hash: u64, + pub is_unconstrained: bool, pub custom_attributes: Vec, diff --git a/noir/noir-repo/compiler/noirc_driver/src/lib.rs b/noir/noir-repo/compiler/noirc_driver/src/lib.rs index 807ee7385a37..3712634d707f 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/lib.rs @@ -10,14 +10,15 @@ use clap::Args; use fm::{FileId, FileManager}; use iter_extended::vecmap; use noirc_abi::{AbiParameter, AbiType, AbiValue}; -use noirc_errors::{CustomDiagnostic, DiagnosticKind, FileDiagnostic}; +use noirc_errors::{CustomDiagnostic, DiagnosticKind}; use noirc_evaluator::brillig::BrilligOptions; use noirc_evaluator::create_program; use noirc_evaluator::errors::RuntimeError; use noirc_evaluator::ssa::{SsaLogging, SsaProgramArtifact}; use noirc_frontend::debug::build_debug_crate_file; -use noirc_frontend::hir::def_map::{Contract, CrateDefMap}; +use noirc_frontend::elaborator::{FrontendOptions, UnstableFeature}; use noirc_frontend::hir::Context; +use noirc_frontend::hir::def_map::{Contract, CrateDefMap}; use noirc_frontend::monomorphization::{ errors::MonomorphizationError, monomorphize, monomorphize_debug, }; @@ -105,10 +106,6 @@ pub struct CompileOptions { #[arg(long, conflicts_with = "deny_warnings")] pub silence_warnings: bool, - /// Disables the builtin Aztec macros being used in the compiler - #[arg(long, hide = true)] - pub disable_macros: bool, - /// Outputs the monomorphized IR to stdout for debugging #[arg(long, hide = true)] pub show_monomorphized: bool, @@ -136,20 +133,16 @@ pub struct CompileOptions { #[arg(long)] pub skip_underconstrained_check: bool, - /// Flag to turn on the compiler check for missing Brillig call constraints. - /// Warning: This can degrade compilation speed but will also find some correctness errors. + /// Flag to turn off the compiler check for missing Brillig call constraints. + /// Warning: This can improve compilation speed but can also lead to correctness errors. /// This check should always be run on production code. #[arg(long)] - pub enable_brillig_constraints_check: bool, + pub skip_brillig_constraints_check: bool, /// Flag to turn on extra Brillig bytecode to be generated to guard against invalid states in testing. #[arg(long, hide = true)] pub enable_brillig_debug_assertions: bool, - /// Hidden Brillig call check flag to maintain CI compatibility (currently ignored) - #[arg(long, hide = true)] - pub skip_brillig_constraints_check: bool, - /// Flag to turn on the lookback feature of the Brillig call constraints /// check, allowing tracking argument values before the call happens preventing /// certain rare false positives (leads to a slowdown on large rollout functions) @@ -179,6 +172,11 @@ pub struct CompileOptions { /// Used internally to test for non-determinism in the compiler. #[clap(long, hide = true)] pub check_non_determinism: bool, + + /// Unstable features to enable for this current build + #[arg(value_parser = clap::value_parser!(UnstableFeature))] + #[clap(long, short = 'Z', value_delimiter = ',')] + pub unstable_features: Vec, } pub fn parse_expression_width(input: &str) -> Result { @@ -197,6 +195,16 @@ pub fn parse_expression_width(input: &str) -> Result FrontendOptions { + FrontendOptions { + debug_comptime_in_file: self.debug_comptime_in_file.as_deref(), + pedantic_solving: self.pedantic_solving, + enabled_unstable_features: &self.unstable_features, + } + } +} + #[derive(Debug)] pub enum CompileError { MonomorphizationError(MonomorphizationError), @@ -215,8 +223,8 @@ impl From for CompileError { } } -impl From for FileDiagnostic { - fn from(error: CompileError) -> FileDiagnostic { +impl From for CustomDiagnostic { + fn from(error: CompileError) -> CustomDiagnostic { match error { CompileError::RuntimeError(err) => err.into(), CompileError::MonomorphizationError(err) => err.into(), @@ -225,10 +233,10 @@ impl From for FileDiagnostic { } /// Helper type used to signify where only warnings are expected in file diagnostics -pub type Warnings = Vec; +pub type Warnings = Vec; /// Helper type used to signify where errors or warnings are expected in file diagnostics -pub type ErrorsAndWarnings = Vec; +pub type ErrorsAndWarnings = Vec; /// Helper type for connecting a compilation artifact to the errors or warnings which were produced during compilation. pub type CompilationResult = Result<(T, Warnings), ErrorsAndWarnings>; @@ -336,30 +344,18 @@ pub fn check_crate( crate_id: CrateId, options: &CompileOptions, ) -> CompilationResult<()> { - let diagnostics = CrateDefMap::collect_defs( - crate_id, - context, - options.debug_comptime_in_file.as_deref(), - options.pedantic_solving, - ); + let diagnostics = CrateDefMap::collect_defs(crate_id, context, options.frontend_options()); let crate_files = context.crate_files(&crate_id); - let warnings_and_errors: Vec = diagnostics - .into_iter() - .map(|(error, file_id)| { - let diagnostic = CustomDiagnostic::from(&error); - diagnostic.in_file(file_id) - }) + let warnings_and_errors: Vec = diagnostics + .iter() + .map(CustomDiagnostic::from) .filter(|diagnostic| { // We filter out any warnings if they're going to be ignored later on to free up memory. - !options.silence_warnings || diagnostic.diagnostic.kind != DiagnosticKind::Warning + !options.silence_warnings || diagnostic.kind != DiagnosticKind::Warning }) .filter(|error| { // Only keep warnings from the crate we are checking - if error.diagnostic.is_warning() { - crate_files.contains(&error.file_id) - } else { - true - } + if error.is_warning() { crate_files.contains(&error.file) } else { true } }) .collect(); @@ -397,16 +393,16 @@ pub fn compile_main( // TODO(#2155): This error might be a better to exist in Nargo let err = CustomDiagnostic::from_message( "cannot compile crate into a program as it does not contain a `main` function", - ) - .in_file(FileId::default()); + FileId::default(), + ); vec![err] })?; let compiled_program = compile_no_check(context, options, main, cached_program, options.force_compile) - .map_err(FileDiagnostic::from)?; + .map_err(|error| vec![CustomDiagnostic::from(error)])?; - let compilation_warnings = vecmap(compiled_program.warnings.clone(), FileDiagnostic::from); + let compilation_warnings = vecmap(compiled_program.warnings.clone(), CustomDiagnostic::from); if options.deny_warnings && !compilation_warnings.is_empty() { return Err(compilation_warnings); } @@ -435,14 +431,16 @@ pub fn compile_contract( let mut errors = warnings; if contracts.len() > 1 { - let err = CustomDiagnostic::from_message("Packages are limited to a single contract") - .in_file(FileId::default()); + let err = CustomDiagnostic::from_message( + "Packages are limited to a single contract", + FileId::default(), + ); return Err(vec![err]); } else if contracts.is_empty() { let err = CustomDiagnostic::from_message( "cannot compile crate into a contract as it does not contain any contracts", - ) - .in_file(FileId::default()); + FileId::default(), + ); return Err(vec![err]); }; @@ -479,12 +477,8 @@ pub fn compile_contract( } /// True if there are (non-warning) errors present and we should halt compilation -fn has_errors(errors: &[FileDiagnostic], deny_warnings: bool) -> bool { - if deny_warnings { - !errors.is_empty() - } else { - errors.iter().any(|error| error.diagnostic.is_error()) - } +fn has_errors(errors: &[CustomDiagnostic], deny_warnings: bool) -> bool { + if deny_warnings { !errors.is_empty() } else { errors.iter().any(|error| error.is_error()) } } /// Compile all of the functions associated with a Noir contract. @@ -521,7 +515,7 @@ fn compile_contract_inner( let function = match compile_no_check(context, &options, function_id, None, true) { Ok(function) => function, Err(new_error) => { - errors.push(FileDiagnostic::from(new_error)); + errors.push(new_error.into()); continue; } }; @@ -541,6 +535,7 @@ fn compile_contract_inner( functions.push(ContractFunction { name, + hash: function.hash, custom_attributes, abi: function.abi, bytecode: function.program, @@ -699,7 +694,7 @@ pub fn compile_no_check( skip_underconstrained_check: options.skip_underconstrained_check, enable_brillig_constraints_check_lookback: options .enable_brillig_constraints_check_lookback, - enable_brillig_constraints_check: options.enable_brillig_constraints_check, + skip_brillig_constraints_check: options.skip_brillig_constraints_check, inliner_aggressiveness: options.inliner_aggressiveness, max_bytecode_increase_percent: options.max_bytecode_increase_percent, }; diff --git a/noir/noir-repo/compiler/noirc_driver/src/program.rs b/noir/noir-repo/compiler/noirc_driver/src/program.rs index 4b4d6662e8e7..b3d545339fc3 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/program.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/program.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use acvm::{acir::circuit::Program, FieldElement}; +use acvm::{FieldElement, acir::circuit::Program}; use fm::FileId; use noirc_errors::debug_info::DebugInfo; diff --git a/noir/noir-repo/compiler/noirc_driver/tests/contracts.rs b/noir/noir-repo/compiler/noirc_driver/tests/contracts.rs index c30412923527..0732a7728ca5 100644 --- a/noir/noir-repo/compiler/noirc_driver/tests/contracts.rs +++ b/noir/noir-repo/compiler/noirc_driver/tests/contracts.rs @@ -1,9 +1,9 @@ use std::path::Path; use fm::FileId; -use noirc_driver::{file_manager_with_stdlib, prepare_crate, CompileOptions, ErrorsAndWarnings}; +use noirc_driver::{CompileOptions, ErrorsAndWarnings, file_manager_with_stdlib, prepare_crate}; use noirc_errors::CustomDiagnostic; -use noirc_frontend::hir::{def_map::parse_file, Context}; +use noirc_frontend::hir::{Context, def_map::parse_file}; #[test] fn reject_crates_containing_multiple_contracts() -> Result<(), ErrorsAndWarnings> { @@ -33,8 +33,10 @@ contract Bar {}"; assert_eq!( errors, - vec![CustomDiagnostic::from_message("Packages are limited to a single contract") - .in_file(FileId::default())], + vec![CustomDiagnostic::from_message( + "Packages are limited to a single contract", + FileId::default() + )], "stdlib is producing warnings" ); diff --git a/noir/noir-repo/compiler/noirc_driver/tests/stdlib_warnings.rs b/noir/noir-repo/compiler/noirc_driver/tests/stdlib_warnings.rs index e290842480d0..411ea68fea7c 100644 --- a/noir/noir-repo/compiler/noirc_driver/tests/stdlib_warnings.rs +++ b/noir/noir-repo/compiler/noirc_driver/tests/stdlib_warnings.rs @@ -1,7 +1,7 @@ use std::path::Path; -use noirc_driver::{file_manager_with_stdlib, prepare_crate, ErrorsAndWarnings}; -use noirc_frontend::hir::{def_map::parse_file, Context}; +use noirc_driver::{ErrorsAndWarnings, file_manager_with_stdlib, prepare_crate}; +use noirc_frontend::hir::{Context, def_map::parse_file}; #[test] fn stdlib_does_not_produce_constant_warnings() -> Result<(), ErrorsAndWarnings> { diff --git a/noir/noir-repo/compiler/noirc_errors/src/debug_info.rs b/noir/noir-repo/compiler/noirc_errors/src/debug_info.rs index a5e12b37712d..4d25973835db 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/debug_info.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/debug_info.rs @@ -1,16 +1,16 @@ -use acvm::acir::circuit::brillig::BrilligFunctionId; use acvm::acir::circuit::BrilligOpcodeLocation; use acvm::acir::circuit::OpcodeLocation; +use acvm::acir::circuit::brillig::BrilligFunctionId; use acvm::compiler::AcirTransformationMap; use base64::Engine; +use flate2::Compression; use flate2::read::DeflateDecoder; use flate2::write::DeflateEncoder; -use flate2::Compression; use serde::Deserializer; use serde::Serializer; -use serde_with::serde_as; use serde_with::DisplayFromStr; +use serde_with::serde_as; use std::collections::BTreeMap; use std::io::Read; use std::io::Write; @@ -19,7 +19,7 @@ use std::mem; use crate::Location; use noirc_printable_type::PrintableType; use serde::{ - de::Error as DeserializationError, ser::Error as SerializationError, Deserialize, Serialize, + Deserialize, Serialize, de::Error as DeserializationError, ser::Error as SerializationError, }; #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, Deserialize, Serialize)] diff --git a/noir/noir-repo/compiler/noirc_errors/src/lib.rs b/noir/noir-repo/compiler/noirc_errors/src/lib.rs index bcdc0684099c..91d121603baa 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/lib.rs @@ -6,23 +6,5 @@ pub mod debug_info; mod position; pub mod reporter; -pub use position::{Location, Position, Span, Spanned}; +pub use position::{Located, Location, Position, Span, Spanned}; pub use reporter::{CustomDiagnostic, DiagnosticKind}; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct FileDiagnostic { - pub file_id: fm::FileId, - pub diagnostic: CustomDiagnostic, -} - -impl FileDiagnostic { - pub fn new(file_id: fm::FileId, diagnostic: CustomDiagnostic) -> FileDiagnostic { - FileDiagnostic { file_id, diagnostic } - } -} - -impl From for Vec { - fn from(value: FileDiagnostic) -> Self { - vec![value] - } -} diff --git a/noir/noir-repo/compiler/noirc_errors/src/position.rs b/noir/noir-repo/compiler/noirc_errors/src/position.rs index c7a64c4f4224..f87f78e1f309 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/position.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/position.rs @@ -2,12 +2,68 @@ use codespan::Span as ByteSpan; use fm::FileId; use serde::{Deserialize, Serialize}; use std::{ + cmp::Ordering, hash::{Hash, Hasher}, ops::Range, }; pub type Position = u32; +#[derive(Eq, Debug, Clone)] +pub struct Located { + pub contents: T, + location: Location, +} + +/// This is important for tests. Two Located objects are equal if their content is equal +/// They may not have the same location. Use `.location()` to test for Location being equal specifically +impl PartialEq> for Located { + fn eq(&self, other: &Located) -> bool { + self.contents == other.contents + } +} + +impl PartialOrd> for Located { + fn partial_cmp(&self, other: &Located) -> Option { + self.contents.partial_cmp(&other.contents) + } +} + +impl Ord for Located { + fn cmp(&self, other: &Located) -> Ordering { + self.contents.cmp(&other.contents) + } +} + +impl Default for Located { + fn default() -> Self { + Self { contents: Default::default(), location: Location::dummy() } + } +} + +/// Hash-based data structures (HashMap, HashSet) rely on the inverse of Hash +/// being injective, i.e. x.eq(y) => hash(x, H) == hash(y, H), we hence align +/// this with the above +impl Hash for Located { + fn hash(&self, state: &mut H) { + self.contents.hash(state); + } +} + +impl Located { + pub fn from(location: Location, contents: T) -> Located { + Located { location, contents } + } + + pub fn span(&self) -> Span { + self.location.span + } + + pub fn location(&self) -> Location { + self.location + } +} + #[derive(PartialOrd, Eq, Ord, Debug, Clone, Default)] pub struct Spanned { pub contents: T, @@ -36,17 +92,13 @@ impl Spanned { Spanned { span: Span::inclusive(start, end), contents } } - pub const fn from(t_span: Span, contents: T) -> Spanned { + pub fn from(t_span: Span, contents: T) -> Spanned { Spanned { span: t_span, contents } } pub fn span(&self) -> Span { self.span } - - pub fn set_span(&mut self, span: Span) { - self.span = span; - } } impl std::borrow::Borrow for Spanned { @@ -135,12 +187,33 @@ impl Location { } pub fn dummy() -> Self { - Self { span: Span::single_char(0), file: FileId::dummy() } + Self { span: Span::default(), file: FileId::dummy() } } pub fn contains(&self, other: &Location) -> bool { self.file == other.file && self.span.contains(&other.span) } + + #[must_use] + pub fn merge(self, other: Location) -> Location { + if self.file == other.file { + Location::new(self.span.merge(other.span), self.file) + } else { + self + } + } +} + +impl Ord for Location { + fn cmp(&self, other: &Self) -> Ordering { + (self.file, self.span).cmp(&(other.file, other.span)) + } +} + +impl PartialOrd for Location { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } } #[cfg(test)] diff --git a/noir/noir-repo/compiler/noirc_errors/src/reporter.rs b/noir/noir-repo/compiler/noirc_errors/src/reporter.rs index 30bf49348b6a..d406e897d656 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/reporter.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/reporter.rs @@ -1,6 +1,6 @@ use std::io::IsTerminal; -use crate::{FileDiagnostic, Location, Span}; +use crate::{Location, Span}; use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::Files; use codespan_reporting::term; @@ -8,6 +8,7 @@ use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct CustomDiagnostic { + pub file: fm::FileId, pub message: String, pub secondaries: Vec, pub notes: Vec, @@ -35,8 +36,9 @@ pub struct ReportedErrors { } impl CustomDiagnostic { - pub fn from_message(msg: &str) -> CustomDiagnostic { + pub fn from_message(msg: &str, file: fm::FileId) -> CustomDiagnostic { Self { + file, message: msg.to_owned(), secondaries: Vec::new(), notes: Vec::new(), @@ -50,12 +52,13 @@ impl CustomDiagnostic { fn simple_with_kind( primary_message: String, secondary_message: String, - secondary_span: Span, + secondary_location: Location, kind: DiagnosticKind, ) -> CustomDiagnostic { CustomDiagnostic { + file: secondary_location.file, message: primary_message, - secondaries: vec![CustomLabel::new(secondary_message, secondary_span, None)], + secondaries: vec![CustomLabel::new(secondary_message, secondary_location)], notes: Vec::new(), kind, deprecated: false, @@ -67,12 +70,12 @@ impl CustomDiagnostic { pub fn simple_error( primary_message: String, secondary_message: String, - secondary_span: Span, + secondary_location: Location, ) -> CustomDiagnostic { Self::simple_with_kind( primary_message, secondary_message, - secondary_span, + secondary_location, DiagnosticKind::Error, ) } @@ -80,12 +83,12 @@ impl CustomDiagnostic { pub fn simple_warning( primary_message: String, secondary_message: String, - secondary_span: Span, + secondary_location: Location, ) -> CustomDiagnostic { Self::simple_with_kind( primary_message, secondary_message, - secondary_span, + secondary_location, DiagnosticKind::Warning, ) } @@ -93,12 +96,12 @@ impl CustomDiagnostic { pub fn simple_info( primary_message: String, secondary_message: String, - secondary_span: Span, + secondary_location: Location, ) -> CustomDiagnostic { Self::simple_with_kind( primary_message, secondary_message, - secondary_span, + secondary_location, DiagnosticKind::Info, ) } @@ -106,11 +109,12 @@ impl CustomDiagnostic { pub fn simple_bug( primary_message: String, secondary_message: String, - secondary_span: Span, + secondary_location: Location, ) -> CustomDiagnostic { CustomDiagnostic { + file: secondary_location.file, message: primary_message, - secondaries: vec![CustomLabel::new(secondary_message, secondary_span, None)], + secondaries: vec![CustomLabel::new(secondary_message, secondary_location)], notes: Vec::new(), kind: DiagnosticKind::Bug, deprecated: false, @@ -119,10 +123,6 @@ impl CustomDiagnostic { } } - pub fn in_file(self, file_id: fm::FileId) -> FileDiagnostic { - FileDiagnostic::new(file_id, self) - } - pub fn with_call_stack(mut self, call_stack: Vec) -> Self { self.call_stack = call_stack; self @@ -132,12 +132,8 @@ impl CustomDiagnostic { self.notes.push(message); } - pub fn add_secondary(&mut self, message: String, span: Span) { - self.secondaries.push(CustomLabel::new(message, span, None)); - } - - pub fn add_secondary_with_file(&mut self, message: String, span: Span, file: fm::FileId) { - self.secondaries.push(CustomLabel::new(message, span, Some(file))); + pub fn add_secondary(&mut self, message: String, location: Location) { + self.secondaries.push(CustomLabel::new(message, location)); } pub fn is_error(&self) -> bool { @@ -176,13 +172,12 @@ impl std::fmt::Display for CustomDiagnostic { #[derive(Debug, Clone, PartialEq, Eq)] pub struct CustomLabel { pub message: String, - pub span: Span, - pub file: Option, + pub location: Location, } impl CustomLabel { - fn new(message: String, span: Span, file: Option) -> CustomLabel { - CustomLabel { message, span, file } + fn new(message: String, location: Location) -> CustomLabel { + CustomLabel { message, location } } } @@ -190,16 +185,16 @@ impl CustomLabel { /// of diagnostics that were errors. pub fn report_all<'files>( files: &'files impl Files<'files, FileId = fm::FileId>, - diagnostics: &[FileDiagnostic], + diagnostics: &[CustomDiagnostic], deny_warnings: bool, silence_warnings: bool, ) -> ReportedErrors { // Report warnings before any errors let (warnings_and_bugs, mut errors): (Vec<_>, _) = - diagnostics.iter().partition(|item| !item.diagnostic.is_error()); + diagnostics.iter().partition(|item| !item.is_error()); let (warnings, mut bugs): (Vec<_>, _) = - warnings_and_bugs.iter().partition(|item| item.diagnostic.is_warning()); + warnings_and_bugs.iter().partition(|item| item.is_warning()); let mut diagnostics = if silence_warnings { Vec::new() } else { warnings }; diagnostics.append(&mut bugs); diagnostics.append(&mut errors); @@ -210,14 +205,14 @@ pub fn report_all<'files>( ReportedErrors { error_count } } -impl FileDiagnostic { +impl CustomDiagnostic { /// Print the report; return true if it was an error. pub fn report<'files>( &self, files: &'files impl Files<'files, FileId = fm::FileId>, deny_warnings: bool, ) -> bool { - report(files, &self.diagnostic, Some(self.file_id), deny_warnings) + report(files, self, deny_warnings) } } @@ -225,7 +220,6 @@ impl FileDiagnostic { pub fn report<'files>( files: &'files impl Files<'files, FileId = fm::FileId>, custom_diagnostic: &CustomDiagnostic, - file: Option, deny_warnings: bool, ) -> bool { let color_choice = @@ -234,7 +228,7 @@ pub fn report<'files>( let config = term::Config::default(); let stack_trace = stack_trace(files, &custom_diagnostic.call_stack); - let diagnostic = convert_diagnostic(custom_diagnostic, file, stack_trace, deny_warnings); + let diagnostic = convert_diagnostic(custom_diagnostic, stack_trace, deny_warnings); term::emit(&mut writer.lock(), &config, files, &diagnostic).unwrap(); deny_warnings || custom_diagnostic.is_error() @@ -242,7 +236,6 @@ pub fn report<'files>( fn convert_diagnostic( cd: &CustomDiagnostic, - file: Option, stack_trace: String, deny_warnings: bool, ) -> Diagnostic { @@ -253,19 +246,18 @@ fn convert_diagnostic( _ => Diagnostic::error(), }; - let secondary_labels = if let Some(file_id) = file { - cd.secondaries - .iter() - .map(|sl| { - let start_span = sl.span.start() as usize; - let end_span = sl.span.end() as usize; - let file = sl.file.unwrap_or(file_id); - Label::secondary(file, start_span..end_span).with_message(&sl.message) - }) - .collect() - } else { - vec![] - }; + let secondary_labels = cd + .secondaries + .iter() + .map(|custom_label| { + let location = custom_label.location; + let span = location.span; + let start_span = span.start() as usize; + let end_span = span.end() as usize; + let file = location.file; + Label::secondary(file, start_span..end_span).with_message(&custom_label.message) + }) + .collect(); let mut notes = cd.notes.clone(); notes.push(stack_trace); diff --git a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml index 76aa0f50e8a2..f9cc2e7b3bf5 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml @@ -20,6 +20,7 @@ fxhash.workspace = true iter-extended.workspace = true thiserror.workspace = true num-bigint = "0.4" +num-traits.workspace = true im.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/acir_variable.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/acir_variable.rs index a4e81de3d5cd..b8f5edaa3cd6 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/acir_variable.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/acir_variable.rs @@ -1,13 +1,13 @@ use acvm::{ + BlackBoxFunctionSolver, acir::{ + AcirField, BlackBoxFunc, circuit::{ - opcodes::{AcirFunctionId, BlockId, BlockType, MemOp}, AssertionPayload, ExpressionOrMemory, ExpressionWidth, Opcode, + opcodes::{AcirFunctionId, BlockId, BlockType, MemOp}, }, native_types::{Expression, Witness}, - AcirField, BlackBoxFunc, }, - BlackBoxFunctionSolver, }; use fxhash::FxHashMap as HashMap; use iter_extended::{try_vecmap, vecmap}; @@ -22,7 +22,7 @@ use crate::ssa::ir::{ use super::big_int::BigIntContext; use super::generated_acir::{BrilligStdlibFunc, GeneratedAcir, PLACEHOLDER_BRILLIG_INDEX}; -use super::{brillig_directive, AcirDynamicArray, AcirValue}; +use super::{AcirDynamicArray, AcirValue, brillig_directive}; #[derive(Clone, Debug, PartialEq, Eq, Hash)] /// High level Type descriptor for Variables. @@ -80,7 +80,7 @@ impl From for AcirType { } } -impl<'a> From<&'a SsaType> for AcirType { +impl From<&SsaType> for AcirType { fn from(value: &SsaType) -> Self { match value { SsaType::Numeric(numeric_type) => AcirType::NumericType(*numeric_type), @@ -278,7 +278,7 @@ impl> AcirContext { let var_data = match self.vars.get(&var) { Some(var_data) => var_data, None => { - return Err(InternalError::UndeclaredAcirVar { call_stack: self.get_call_stack() }) + return Err(InternalError::UndeclaredAcirVar { call_stack: self.get_call_stack() }); } }; Ok(var_data.to_expression().into_owned()) @@ -777,7 +777,8 @@ impl> AcirContext { pub(crate) fn not_var(&mut self, x: AcirVar, typ: AcirType) -> Result { let bit_size = typ.bit_size::(); // Subtracting from max flips the bits - let max = self.add_constant((1_u128 << bit_size) - 1); + let max = power_of_two::(bit_size) - F::one(); + let max = self.add_constant(max); self.sub_var(max, x) } @@ -841,18 +842,19 @@ impl> AcirContext { } // maximum bit size for q and for [r and rhs] - let mut max_q_bits = bit_size; - let mut max_rhs_bits = bit_size; - // when rhs is constant, we can better estimate the maximum bit sizes - if let Some(rhs_const) = rhs_expr.to_const() { - max_rhs_bits = rhs_const.num_bits(); - if max_rhs_bits != 0 { - if max_rhs_bits > bit_size { - return Ok((zero, zero)); - } - max_q_bits = bit_size - max_rhs_bits + 1; - } - } + let (max_q_bits, max_rhs_bits) = if let Some(rhs_const) = rhs_expr.to_const() { + // when rhs is constant, we can better estimate the maximum bit sizes + let max_rhs_bits = rhs_const.num_bits(); + assert!( + max_rhs_bits <= bit_size, + "attempted to divide by constant larger than operand type" + ); + + let max_q_bits = bit_size - max_rhs_bits + 1; + (max_q_bits, max_rhs_bits) + } else { + (bit_size, bit_size) + }; let [q_value, r_value]: [AcirValue; 2] = self .brillig_call( @@ -908,19 +910,9 @@ impl> AcirContext { self.assert_eq_var(lhs_constraint, rhs_constraint, None)?; // Avoids overflow: 'q*b+r < 2^max_q_bits*2^max_rhs_bits' - let mut avoid_overflow = false; if max_q_bits + max_rhs_bits >= F::max_num_bits() - 1 { // q*b+r can overflow; we avoid this when b is constant - if rhs_expr.is_const() { - avoid_overflow = true; - } else { - // we do not support unbounded division - unreachable!("overflow in unbounded division"); - } - } - - if let Some(rhs_const) = rhs_expr.to_const() { - if avoid_overflow { + if let Some(rhs_const) = rhs_expr.to_const() { // we compute q0 = p/rhs let rhs_big = BigUint::from_bytes_be(&rhs_const.to_be_bytes()); let q0_big = F::modulus() / &rhs_big; @@ -944,6 +936,20 @@ impl> AcirContext { predicate, rhs_const.num_bits(), )?; + } else if bit_size == 128 { + // q and b are u128 and q*b could overflow so we check that either q or b are less than 2^64 + let two_pow_64: F = power_of_two(64); + let two_pow_64 = self.add_constant(two_pow_64); + + let (q_upper, _) = + self.euclidean_division_var(quotient_var, two_pow_64, bit_size, predicate)?; + let (rhs_upper, _) = + self.euclidean_division_var(rhs, two_pow_64, bit_size, predicate)?; + let mul_uppers = self.mul_var(q_upper, rhs_upper)?; + self.assert_eq_var(mul_uppers, zero, None)?; + } else { + // we do not support unbounded division + unreachable!("overflow in unbounded division"); } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/black_box.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/black_box.rs index 7b386d6c1886..a75d53cf10a3 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/black_box.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/black_box.rs @@ -1,9 +1,9 @@ use acvm::{ + BlackBoxFunctionSolver, acir::{ - circuit::opcodes::{ConstantOrWitnessEnum, FunctionInput}, AcirField, BlackBoxFunc, + circuit::opcodes::{ConstantOrWitnessEnum, FunctionInput}, }, - BlackBoxFunctionSolver, }; use iter_extended::vecmap; use num_bigint::BigUint; @@ -11,8 +11,8 @@ use num_bigint::BigUint; use crate::errors::{InternalError, RuntimeError}; use super::{ - acir_variable::{AcirContext, AcirVar}, AcirValue, + acir_variable::{AcirContext, AcirVar}, }; impl> AcirContext { @@ -35,7 +35,7 @@ impl> AcirContext { name: "poseidon_2_permutation call".to_string(), arg: "length".to_string(), call_stack: self.get_call_stack(), - })) + })); } }; @@ -45,7 +45,7 @@ impl> AcirContext { return Err(RuntimeError::InternalError(InternalError::NotAConstant { name: "length".to_string(), call_stack: self.get_call_stack(), - })) + })); } }; @@ -160,7 +160,7 @@ impl> AcirContext { name: "verify proof".to_string(), arg: "proof type".to_string(), call_stack: self.get_call_stack(), - })) + })); } }; @@ -170,7 +170,7 @@ impl> AcirContext { return Err(RuntimeError::InternalError(InternalError::NotAConstant { name: "proof type".to_string(), call_stack: self.get_call_stack(), - })) + })); } }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_call.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_call.rs index f027a7b62f80..020c60968a13 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_call.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_call.rs @@ -1,12 +1,12 @@ use acvm::{ + BlackBoxFunctionSolver, acir::{ + AcirField, brillig::Opcode as BrilligOpcode, circuit::brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}, native_types::{Expression, Witness}, - AcirField, }, - brillig_vm::{MemoryValue, VMStatus, VM}, - BlackBoxFunctionSolver, + brillig_vm::{MemoryValue, VM, VMStatus}, }; use iter_extended::{try_vecmap, vecmap}; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_directive.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_directive.rs index 89fc7f1eda5d..5fab9e345231 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_directive.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_directive.rs @@ -1,9 +1,9 @@ use acvm::acir::{ + AcirField, brillig::{ BinaryFieldOp, BinaryIntOp, BitSize, HeapVector, IntegerBitSize, MemoryAddress, Opcode as BrilligOpcode, }, - AcirField, }; use crate::brillig::brillig_ir::artifact::GeneratedBrillig; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/generated_acir.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/generated_acir.rs index 141c9f367c2c..25b4771048c9 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/generated_acir.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/generated_acir.rs @@ -3,21 +3,21 @@ use std::collections::BTreeMap; use acvm::acir::{ + AcirField, BlackBoxFunc, circuit::{ + AssertionPayload, BrilligOpcodeLocation, ErrorSelector, OpcodeLocation, brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}, opcodes::{BlackBoxFuncCall, FunctionInput, Opcode as AcirOpcode}, - AssertionPayload, BrilligOpcodeLocation, ErrorSelector, OpcodeLocation, }, native_types::{Expression, Witness}, - AcirField, BlackBoxFunc, }; use super::brillig_directive; use crate::{ + ErrorType, brillig::brillig_ir::artifact::GeneratedBrillig, errors::{InternalError, RuntimeError, SsaReport}, ssa::ir::call_stack::CallStack, - ErrorType, }; use iter_extended::vecmap; @@ -788,7 +788,10 @@ fn intrinsics_check_inputs(name: BlackBoxFunc, input_count: usize) { None => return, }; - assert_eq!(expected_num_inputs,input_count,"Tried to call black box function {name} with {input_count} inputs, but this function's definition requires {expected_num_inputs} inputs"); + assert_eq!( + expected_num_inputs, input_count, + "Tried to call black box function {name} with {input_count} inputs, but this function's definition requires {expected_num_inputs} inputs" + ); } /// Checks that the number of outputs being used to call the blackbox function @@ -818,5 +821,8 @@ fn intrinsics_check_outputs(name: BlackBoxFunc, output_count: usize) { None => return, }; - assert_eq!(expected_num_outputs,output_count,"Tried to call black box function {name} with {output_count} outputs, but this function's definition requires {expected_num_outputs} outputs"); + assert_eq!( + expected_num_outputs, output_count, + "Tried to call black box function {name} with {output_count} outputs, but this function's definition requires {expected_num_outputs} outputs" + ); } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/mod.rs index dc6b3826b60e..a2c4913b5e7a 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/mod.rs @@ -5,15 +5,15 @@ use std::collections::{BTreeMap, HashSet}; use std::fmt::Debug; use acvm::acir::{ + BlackBoxFunc, circuit::{ + AssertionPayload, ErrorSelector, ExpressionWidth, OpcodeLocation, brillig::{BrilligBytecode, BrilligFunctionId}, opcodes::{AcirFunctionId, BlockType}, - AssertionPayload, ErrorSelector, ExpressionWidth, OpcodeLocation, }, native_types::Witness, - BlackBoxFunc, }; -use acvm::{acir::circuit::opcodes::BlockId, acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField, acir::circuit::opcodes::BlockId}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use iter_extended::{try_vecmap, vecmap}; use noirc_frontend::monomorphization::ast::InlineType; @@ -25,12 +25,12 @@ mod brillig_call; mod brillig_directive; mod generated_acir; -use crate::brillig::brillig_gen::gen_brillig_for; use crate::brillig::BrilligOptions; +use crate::brillig::brillig_gen::gen_brillig_for; use crate::brillig::{ + Brillig, brillig_gen::brillig_fn::FunctionContext as BrilligFunctionContext, brillig_ir::artifact::{BrilligParameter, GeneratedBrillig}, - Brillig, }; use crate::errors::{InternalError, InternalWarning, RuntimeError, SsaReport}; use crate::ssa::ir::instruction::Hint; @@ -51,7 +51,7 @@ use crate::ssa::{ }, ssa_gen::Ssa, }; -use acir_variable::{power_of_two, AcirContext, AcirType, AcirVar}; +use acir_variable::{AcirContext, AcirType, AcirVar, power_of_two}; use generated_acir::BrilligStdlibFunc; pub(crate) use generated_acir::GeneratedAcir; use noirc_frontend::hir_def::types::Type as HirType; @@ -404,11 +404,15 @@ impl<'a> Context<'a> { match inline_type { InlineType::Inline | InlineType::InlineAlways => { if function.id() != ssa.main_id { - panic!("ACIR function should have been inlined earlier if not marked otherwise"); + panic!( + "ACIR function should have been inlined earlier if not marked otherwise" + ); } } InlineType::NoPredicates => { - panic!("All ACIR functions marked with #[no_predicates] should be inlined before ACIR gen. This is an SSA exclusive codegen attribute"); + panic!( + "All ACIR functions marked with #[no_predicates] should be inlined before ACIR gen. This is an SSA exclusive codegen attribute" + ); } InlineType::Fold => {} } @@ -863,7 +867,11 @@ impl<'a> Context<'a> { let func = &ssa.functions[id]; match func.runtime() { RuntimeType::Acir(inline_type) => { - assert!(!matches!(inline_type, InlineType::Inline), "ICE: Got an ACIR function named {} that should have already been inlined", func.name()); + assert!( + !matches!(inline_type, InlineType::Inline), + "ICE: Got an ACIR function named {} that should have already been inlined", + func.name() + ); let inputs = vecmap(arguments, |arg| self.convert_value(*arg, dfg)); let output_count = result_ids @@ -874,7 +882,9 @@ impl<'a> Context<'a> { .sum(); let Some(acir_function_id) = ssa.get_entry_point_index(id) else { - unreachable!("Expected an associated final index for call to acir function {id} with args {arguments:?}"); + unreachable!( + "Expected an associated final index for call to acir function {id} with args {arguments:?}" + ); }; let output_vars = self.acir_context.call_acir_function( @@ -956,7 +966,11 @@ impl<'a> Context<'a> { }; // Compiler sanity check - assert_eq!(result_ids.len(), output_values.len(), "ICE: The number of Brillig output values should match the result ids in SSA"); + assert_eq!( + result_ids.len(), + output_values.len(), + "ICE: The number of Brillig output values should match the result ids in SSA" + ); self.handle_ssa_call_outputs(result_ids, output_values, dfg)?; } @@ -1076,7 +1090,7 @@ impl<'a> Context<'a> { found: format!("Instead got {:?}", dfg[instruction]), call_stack: self.acir_context.get_call_stack(), } - .into()) + .into()); } }; // Ensure that array id is fully resolved. @@ -1477,7 +1491,7 @@ impl<'a> Context<'a> { found: format!("Instead got {:?}", dfg[instruction]), call_stack: self.acir_context.get_call_stack(), } - .into()) + .into()); } }; @@ -1525,7 +1539,11 @@ impl<'a> Context<'a> { let value_types = self.convert_value(array, dfg).flat_numeric_types(); // Compiler sanity check - assert_eq!(value_types.len(), array_len, "ICE: The length of the flattened type array should match the length of the dynamic array"); + assert_eq!( + value_types.len(), + array_len, + "ICE: The length of the flattened type array should match the length of the dynamic array" + ); let result_value = AcirValue::DynamicArray(AcirDynamicArray { block_id: result_block_id, @@ -1675,7 +1693,7 @@ impl<'a> Context<'a> { found: format!("{:?}", array_acir_value), call_stack: self.acir_context.get_call_stack(), } - .into()) + .into()); } } } @@ -1685,7 +1703,7 @@ impl<'a> Context<'a> { found: format!("{:?}", &dfg[array_id]), call_stack: self.acir_context.get_call_stack(), } - .into()) + .into()); } }; } @@ -1973,26 +1991,7 @@ impl<'a> Context<'a> { ) -> Result { let lhs = self.convert_numeric_value(binary.lhs, dfg)?; let rhs = self.convert_numeric_value(binary.rhs, dfg)?; - let binary_type = self.type_of_binary_operation(binary, dfg); - match &binary_type { - Type::Numeric(NumericType::Unsigned { bit_size }) - | Type::Numeric(NumericType::Signed { bit_size }) => { - // Conservative max bit size that is small enough such that two operands can be - // multiplied and still fit within the field modulus. This is necessary for the - // truncation technique: result % 2^bit_size to be valid. - let max_integer_bit_size = FieldElement::max_num_bits() / 2; - if *bit_size > max_integer_bit_size { - return Err(RuntimeError::UnsupportedIntegerSize { - num_bits: *bit_size, - max_num_bits: max_integer_bit_size, - call_stack: self.acir_context.get_call_stack(), - }); - } - } - _ => {} - } - let binary_type = AcirType::from(binary_type); let bit_count = binary_type.bit_size::(); let num_type = binary_type.to_numeric_type(); @@ -2113,6 +2112,12 @@ impl<'a> Context<'a> { max_bit_size: u32, dfg: &DataFlowGraph, ) -> Result { + assert_ne!(bit_size, max_bit_size, "Attempted to generate a noop truncation"); + assert!( + bit_size < max_bit_size, + "Attempted to generate a truncation into size larger than max input" + ); + let mut var = self.convert_numeric_value(value_id, dfg)?; match &dfg[value_id] { Value::Instruction { instruction, .. } => { @@ -2192,7 +2197,9 @@ impl<'a> Context<'a> { Ok(self.convert_vars_to_values(vars, dfg, result_ids)) } Intrinsic::ApplyRangeConstraint => { - unreachable!("ICE: `Intrinsic::ApplyRangeConstraint` calls should be transformed into an `Instruction::RangeCheck`"); + unreachable!( + "ICE: `Intrinsic::ApplyRangeConstraint` calls should be transformed into an `Instruction::RangeCheck`" + ); } Intrinsic::ToRadix(endian) => { let field = self.convert_value(arguments[0], dfg).into_var()?; @@ -2895,15 +2902,15 @@ fn can_omit_element_sizes_array(array_typ: &Type) -> bool { mod test { use acvm::{ + FieldElement, acir::{ circuit::{ + ExpressionWidth, Opcode, OpcodeLocation, brillig::BrilligFunctionId, opcodes::{AcirFunctionId, BlackBoxFuncCall}, - ExpressionWidth, Opcode, OpcodeLocation, }, native_types::Witness, }, - FieldElement, }; use noirc_errors::Location; use noirc_frontend::monomorphization::ast::InlineType; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs index 957ebc2b0695..64160528148d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs @@ -12,11 +12,11 @@ use fxhash::FxHashMap as HashMap; use self::{brillig_block::BrilligBlock, brillig_fn::FunctionContext}; use super::{ + Brillig, BrilligOptions, BrilligVariable, ValueId, brillig_ir::{ - artifact::{BrilligArtifact, BrilligParameter, GeneratedBrillig, Label}, BrilligContext, + artifact::{BrilligArtifact, BrilligParameter, GeneratedBrillig, Label}, }, - Brillig, BrilligOptions, BrilligVariable, ValueId, }; use crate::{ errors::InternalError, @@ -88,7 +88,7 @@ pub(crate) fn gen_brillig_for( return Err(InternalError::General { message: format!("Cannot find linked fn {unresolved_fn_label}"), call_stack: CallStack::new(), - }) + }); } }; entry_point.link_with(artifact); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs index 1fc39b582232..cdc6df262402 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs @@ -1,14 +1,14 @@ use acvm::{ + AcirField, acir::{ - brillig::{BlackBoxOp, HeapVector, ValueOrArray}, BlackBoxFunc, + brillig::{BlackBoxOp, HeapVector, ValueOrArray}, }, - AcirField, }; use crate::brillig::brillig_ir::{ - brillig_variable::BrilligVariable, debug_show::DebugToString, registers::RegisterAllocator, - BrilligContext, + BrilligContext, brillig_variable::BrilligVariable, debug_show::DebugToString, + registers::RegisterAllocator, }; /// Transforms SSA's black box function calls into the corresponding brillig instructions @@ -83,7 +83,12 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::BrilligArray(public_key_x), BrilligVariable::BrilligArray(public_key_y), BrilligVariable::BrilligArray(signature), message], + [ + BrilligVariable::BrilligArray(public_key_x), + BrilligVariable::BrilligArray(public_key_y), + BrilligVariable::BrilligArray(signature), + message, + ], [BrilligVariable::SingleAddr(result_register)], ) = (function_arguments, function_results) { @@ -114,7 +119,12 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::BrilligArray(public_key_x), BrilligVariable::BrilligArray(public_key_y), BrilligVariable::BrilligArray(signature), message], + [ + BrilligVariable::BrilligArray(public_key_x), + BrilligVariable::BrilligArray(public_key_y), + BrilligVariable::BrilligArray(signature), + message, + ], [BrilligVariable::SingleAddr(result_register)], ) = (function_arguments, function_results) { @@ -168,7 +178,14 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::SingleAddr(input1_x), BrilligVariable::SingleAddr(input1_y), BrilligVariable::SingleAddr(input1_infinite), BrilligVariable::SingleAddr(input2_x), BrilligVariable::SingleAddr(input2_y), BrilligVariable::SingleAddr(input2_infinite)], + [ + BrilligVariable::SingleAddr(input1_x), + BrilligVariable::SingleAddr(input1_y), + BrilligVariable::SingleAddr(input1_infinite), + BrilligVariable::SingleAddr(input2_x), + BrilligVariable::SingleAddr(input2_y), + BrilligVariable::SingleAddr(input2_infinite), + ], [BrilligVariable::BrilligArray(result_array)], ) = (function_arguments, function_results) { @@ -202,7 +219,12 @@ pub(crate) fn convert_black_box_call {} BlackBoxFunc::BigIntAdd => { if let ( - [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(_lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(_rhs_modulus)], + [ + BrilligVariable::SingleAddr(lhs), + BrilligVariable::SingleAddr(_lhs_modulus), + BrilligVariable::SingleAddr(rhs), + BrilligVariable::SingleAddr(_rhs_modulus), + ], [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(_modulus_id)], ) = (function_arguments, function_results) { @@ -219,7 +241,12 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(_lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(_rhs_modulus)], + [ + BrilligVariable::SingleAddr(lhs), + BrilligVariable::SingleAddr(_lhs_modulus), + BrilligVariable::SingleAddr(rhs), + BrilligVariable::SingleAddr(_rhs_modulus), + ], [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(_modulus_id)], ) = (function_arguments, function_results) { @@ -236,7 +263,12 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(_lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(_rhs_modulus)], + [ + BrilligVariable::SingleAddr(lhs), + BrilligVariable::SingleAddr(_lhs_modulus), + BrilligVariable::SingleAddr(rhs), + BrilligVariable::SingleAddr(_rhs_modulus), + ], [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(_modulus_id)], ) = (function_arguments, function_results) { @@ -253,7 +285,12 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(_lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(_rhs_modulus)], + [ + BrilligVariable::SingleAddr(lhs), + BrilligVariable::SingleAddr(_lhs_modulus), + BrilligVariable::SingleAddr(rhs), + BrilligVariable::SingleAddr(_rhs_modulus), + ], [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(_modulus_id)], ) = (function_arguments, function_results) { @@ -326,12 +363,17 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::BrilligArray(input_array), BrilligVariable::BrilligArray(hash_values)], + [ + BrilligVariable::BrilligArray(input_array), + BrilligVariable::BrilligArray(hash_values), + ], [BrilligVariable::BrilligArray(result_array)], ) = (function_arguments, function_results) { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 40dd825be35d..d8f1f9d0997e 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -1,11 +1,11 @@ use crate::brillig::brillig_ir::artifact::Label; use crate::brillig::brillig_ir::brillig_variable::{ - type_to_heap_value_type, BrilligArray, BrilligVariable, SingleAddrVariable, + BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable, type_to_heap_value_type, }; use crate::brillig::brillig_ir::registers::RegisterAllocator; use crate::brillig::brillig_ir::{ - BrilligBinaryOp, BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, + BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, BrilligBinaryOp, BrilligContext, ReservedRegisters, }; use crate::ssa::ir::call_stack::CallStack; use crate::ssa::ir::instruction::{ConstrainError, Hint}; @@ -21,7 +21,7 @@ use crate::ssa::ir::{ }; use acvm::acir::brillig::{MemoryAddress, ValueOrArray}; use acvm::brillig_vm::MEMORY_ADDRESSING_BIT_SIZE; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use iter_extended::vecmap; use num_bigint::BigUint; @@ -29,7 +29,7 @@ use std::collections::BTreeSet; use std::sync::Arc; use super::brillig_black_box::convert_black_box_call; -use super::brillig_block_variables::{allocate_value_with_type, BlockVariables}; +use super::brillig_block_variables::{BlockVariables, allocate_value_with_type}; use super::brillig_fn::FunctionContext; use super::brillig_globals::HoistedConstantsToBrilligGlobals; use super::constant_allocation::InstructionLocation; @@ -460,7 +460,9 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { element_size, ); } else { - unreachable!("ICE: a vector must be preceded by a register containing its length"); + unreachable!( + "ICE: a vector must be preceded by a register containing its length" + ); } self.brillig_context.deallocate_heap_vector(*heap_vector); } @@ -769,21 +771,28 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { // Slice access checks are generated separately against the slice's dynamic length field. if matches!(dfg.type_of_value(*array), Type::Array(..)) - && !dfg.is_safe_index(*index, *array) + && !dfg.is_safe_brillig_index(*index, *array) { self.validate_array_index(array_variable, index_variable); } - let items_pointer = - self.brillig_context.codegen_make_array_or_vector_items_pointer(array_variable); - - self.brillig_context.codegen_load_with_offset( - items_pointer, - index_variable, - destination_variable.extract_register(), - ); - - self.brillig_context.deallocate_register(items_pointer); + if dfg.is_constant(*index) { + self.brillig_context.codegen_load_with_offset( + array_variable.extract_register(), + index_variable, + destination_variable.extract_register(), + ); + } else { + let items_pointer = self + .brillig_context + .codegen_make_array_or_vector_items_pointer(array_variable); + self.brillig_context.codegen_load_with_offset( + items_pointer, + index_variable, + destination_variable.extract_register(), + ); + self.brillig_context.deallocate_register(items_pointer); + } } Instruction::ArraySet { array, index, value, mutable } => { let source_variable = self.convert_ssa_value(*array, dfg); @@ -926,8 +935,64 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { Instruction::EnableSideEffectsIf { .. } => { unreachable!("enable_side_effects not supported by brillig") } - Instruction::IfElse { .. } => { - unreachable!("IfElse instructions should not be possible in brillig") + Instruction::IfElse { then_condition, then_value, else_condition: _, else_value } => { + let then_condition = self.convert_ssa_single_addr_value(*then_condition, dfg); + let then_value = self.convert_ssa_value(*then_value, dfg); + let else_value = self.convert_ssa_value(*else_value, dfg); + let result = self.variables.define_variable( + self.function_context, + self.brillig_context, + dfg.instruction_results(instruction_id)[0], + dfg, + ); + match (then_value, else_value) { + ( + BrilligVariable::SingleAddr(then_address), + BrilligVariable::SingleAddr(else_address), + ) => { + self.brillig_context.conditional_move_instruction( + then_condition, + then_address, + else_address, + result.extract_single_addr(), + ); + } + ( + BrilligVariable::BrilligArray(then_array), + BrilligVariable::BrilligArray(else_array), + ) => { + // Pointer to the array which result from the if-else + let pointer = self.brillig_context.allocate_register(); + self.brillig_context.conditional_move_instruction( + then_condition, + SingleAddrVariable::new_usize(then_array.pointer), + SingleAddrVariable::new_usize(else_array.pointer), + SingleAddrVariable::new_usize(pointer), + ); + let if_else_array = BrilligArray { pointer, size: then_array.size }; + // Copy the if-else array to the result + self.brillig_context + .call_array_copy_procedure(if_else_array, result.extract_array()); + } + ( + BrilligVariable::BrilligVector(then_vector), + BrilligVariable::BrilligVector(else_vector), + ) => { + // Pointer to the vector which result from the if-else + let pointer = self.brillig_context.allocate_register(); + self.brillig_context.conditional_move_instruction( + then_condition, + SingleAddrVariable::new_usize(then_vector.pointer), + SingleAddrVariable::new_usize(else_vector.pointer), + SingleAddrVariable::new_usize(pointer), + ); + let if_else_vector = BrilligVector { pointer }; + // Copy the if-else vector to the result + self.brillig_context + .call_vector_copy_procedure(if_else_vector, result.extract_vector()); + } + _ => unreachable!("ICE - then and else values must have the same type"), + } } Instruction::MakeArray { elements: array, typ } => { let value_id = dfg.instruction_results(instruction_id)[0]; @@ -1432,7 +1497,9 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { NumericType::Unsigned { .. } => (false, false), NumericType::NativeField => (true, false), }, - _ => unreachable!("only numeric types are allowed in binary operations. References are handled separately"), + _ => unreachable!( + "only numeric types are allowed in binary operations. References are handled separately" + ), }; let brillig_binary_op = match binary.operator { @@ -1991,14 +2058,21 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { self.allocate_foreign_call_result_array(element_type, inner_array); // We add one since array.pointer points to [RC, ...items] - let idx = - self.brillig_context.make_usize_constant_instruction((index + 1).into() ); - self.brillig_context.codegen_store_with_offset(array.pointer, idx, inner_array.pointer); + let idx = self + .brillig_context + .make_usize_constant_instruction((index + 1).into()); + self.brillig_context.codegen_store_with_offset( + array.pointer, + idx, + inner_array.pointer, + ); self.brillig_context.deallocate_single_addr(idx); self.brillig_context.deallocate_register(inner_array.pointer); } - Type::Slice(_) => unreachable!("ICE: unsupported slice type in allocate_nested_array(), expects an array or a numeric type"), + Type::Slice(_) => unreachable!( + "ICE: unsupported slice type in allocate_nested_array(), expects an array or a numeric type" + ), _ => (), } index += 1; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs index 4cf8e9214833..027994a11de4 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs @@ -3,12 +3,12 @@ use fxhash::FxHashSet as HashSet; use crate::{ brillig::brillig_ir::{ + BrilligContext, brillig_variable::{ - get_bit_size_from_ssa_type, BrilligArray, BrilligVariable, BrilligVector, - SingleAddrVariable, + BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable, + get_bit_size_from_ssa_type, }, registers::RegisterAllocator, - BrilligContext, }, ssa::ir::{ dfg::DataFlowGraph, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs index d2656354b566..6ff85b9ebe8e 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs @@ -3,7 +3,7 @@ use iter_extended::vecmap; use crate::{ brillig::brillig_ir::{ artifact::BrilligParameter, - brillig_variable::{get_bit_size_from_ssa_type, BrilligVariable}, + brillig_variable::{BrilligVariable, get_bit_size_from_ssa_type}, }, ssa::ir::{ basic_block::BasicBlockId, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_globals.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_globals.rs index 66f0980ac639..317f763078e1 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_globals.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_globals.rs @@ -8,7 +8,7 @@ use super::{ }; use crate::brillig::{Brillig, BrilligOptions, FunctionId}; use crate::{ - brillig::{brillig_ir::BrilligContext, ConstantAllocation, DataFlowGraph}, + brillig::{ConstantAllocation, DataFlowGraph, brillig_ir::BrilligContext}, ssa::ir::types::NumericType, ssa::opt::brillig_entry_points::{build_inner_call_to_entry_points, get_brillig_entry_points}, }; @@ -147,11 +147,7 @@ impl BrilligGlobals { .iter() .filter_map( |(&value, &num_occurrences)| { - if num_occurrences > 1 { - Some(value) - } else { - None - } + if num_occurrences > 1 { Some(value) } else { None } }, ) .collect(); @@ -280,12 +276,12 @@ pub(crate) fn convert_ssa_globals( #[cfg(test)] mod tests { use acvm::{ - acir::brillig::{BitSize, IntegerBitSize, Opcode}, FieldElement, + acir::brillig::{BitSize, IntegerBitSize, Opcode}, }; use crate::brillig::{ - brillig_ir::registers::RegisterAllocator, BrilligOptions, GlobalSpace, LabelType, Ssa, + BrilligOptions, GlobalSpace, LabelType, Ssa, brillig_ir::registers::RegisterAllocator, }; use super::ConstantAllocation; @@ -434,6 +430,8 @@ mod tests { "; let ssa = Ssa::from_str(src).unwrap(); + // Need to run SSA pass that sets up Brillig array gets + let ssa = ssa.brillig_array_gets(); // Need to run DIE to generate the used globals map, which is necessary for Brillig globals generation. let mut ssa = ssa.dead_instruction_elimination(); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index 99645f84ed3d..6387dc3dd371 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -1,14 +1,14 @@ use acvm::acir::brillig::MemoryAddress; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, brillig_variable::{BrilligVariable, BrilligVector, SingleAddrVariable}, registers::RegisterAllocator, - BrilligBinaryOp, }; use super::brillig_block::BrilligBlock; -impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { +impl BrilligBlock<'_, Registers> { fn write_variables(&mut self, write_pointer: MemoryAddress, variables: &[BrilligVariable]) { for (index, variable) in variables.iter().enumerate() { self.brillig_context.store_instruction(write_pointer, variable.extract_register()); @@ -163,6 +163,7 @@ mod tests { use fxhash::FxHashMap as HashMap; use noirc_frontend::monomorphization::ast::InlineType; + use crate::brillig::ValueId; use crate::brillig::brillig_gen::brillig_block::BrilligBlock; use crate::brillig::brillig_gen::brillig_block_variables::BlockVariables; use crate::brillig::brillig_gen::brillig_fn::FunctionContext; @@ -174,8 +175,7 @@ mod tests { use crate::brillig::brillig_ir::tests::{ create_and_run_vm, create_context, create_entry_point_bytecode, }; - use crate::brillig::brillig_ir::{BrilligContext, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE}; - use crate::brillig::ValueId; + use crate::brillig::brillig_ir::{BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, BrilligContext}; use crate::ssa::function_builder::FunctionBuilder; use crate::ssa::ir::function::RuntimeType; use crate::ssa::ir::map::Id; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index 95e6c5175b2d..a0637e8e3c25 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -32,8 +32,8 @@ use registers::{RegisterAllocator, ScratchSpace}; use self::{artifact::BrilligArtifact, debug_show::DebugToString, registers::Stack}; use crate::ssa::ir::call_stack::CallStack; use acvm::{ - acir::brillig::{MemoryAddress, Opcode as BrilligOpcode}, AcirField, + acir::brillig::{MemoryAddress, Opcode as BrilligOpcode}, }; use debug_show::DebugShow; @@ -281,11 +281,11 @@ pub(crate) mod tests { ValueOrArray, }; use acvm::brillig_vm::brillig::HeapValueType; - use acvm::brillig_vm::{VMStatus, VM}; + use acvm::brillig_vm::{VM, VMStatus}; use acvm::{BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement}; - use crate::brillig::brillig_ir::{BrilligBinaryOp, BrilligContext}; use crate::brillig::BrilligOptions; + use crate::brillig::brillig_ir::{BrilligBinaryOp, BrilligContext}; use crate::ssa::ir::function::FunctionId; use super::artifact::{BrilligParameter, GeneratedBrillig, Label, LabelType}; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs index c9223715042f..42052c092306 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs @@ -2,8 +2,8 @@ use acvm::acir::brillig::Opcode as BrilligOpcode; use acvm::acir::circuit::ErrorSelector; use std::collections::{BTreeMap, HashMap}; -use crate::ssa::ir::{basic_block::BasicBlockId, call_stack::CallStack, function::FunctionId}; use crate::ErrorType; +use crate::ssa::ir::{basic_block::BasicBlockId, call_stack::CallStack, function::FunctionId}; use super::procedures::ProcedureId; @@ -304,25 +304,37 @@ impl BrilligArtifact { let jump_instruction = self.byte_code[*location_of_jump].clone(); match jump_instruction { BrilligOpcode::Jump { location } => { - assert_eq!(location, 0, "location is not zero, which means that the jump label does not need resolving"); + assert_eq!( + location, 0, + "location is not zero, which means that the jump label does not need resolving" + ); self.byte_code[*location_of_jump] = BrilligOpcode::Jump { location: resolved_location }; } BrilligOpcode::JumpIfNot { condition, location } => { - assert_eq!(location, 0, "location is not zero, which means that the jump label does not need resolving"); + assert_eq!( + location, 0, + "location is not zero, which means that the jump label does not need resolving" + ); self.byte_code[*location_of_jump] = BrilligOpcode::JumpIfNot { condition, location: resolved_location }; } BrilligOpcode::JumpIf { condition, location } => { - assert_eq!(location, 0, "location is not zero, which means that the jump label does not need resolving"); + assert_eq!( + location, 0, + "location is not zero, which means that the jump label does not need resolving" + ); self.byte_code[*location_of_jump] = BrilligOpcode::JumpIf { condition, location: resolved_location }; } BrilligOpcode::Call { location } => { - assert_eq!(location, 0, "location is not zero, which means that the call label does not need resolving"); + assert_eq!( + location, 0, + "location is not zero, which means that the call label does not need resolving" + ); self.byte_code[*location_of_jump] = BrilligOpcode::Call { location: resolved_location }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs index 0bb18448670a..2a46cae2d241 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs @@ -1,7 +1,7 @@ use acvm::{ - acir::{brillig::BitSize, AcirField}, - brillig_vm::brillig::{HeapValueType, MemoryAddress}, FieldElement, + acir::{AcirField, brillig::BitSize}, + brillig_vm::brillig::{HeapValueType, MemoryAddress}, }; use serde::{Deserialize, Serialize}; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs index 9f573543e2a8..1fc37882e482 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs @@ -1,8 +1,8 @@ -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::{ - debug_show::DebugToString, instructions::BrilligBinaryOp, registers::RegisterAllocator, - BrilligContext, ReservedRegisters, + BrilligContext, ReservedRegisters, debug_show::DebugToString, instructions::BrilligBinaryOp, + registers::RegisterAllocator, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs index 4da3aa4d6d2e..5036326fbc97 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs @@ -1,12 +1,12 @@ -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use crate::ssa::ir::function::FunctionId; use super::{ + BrilligBinaryOp, BrilligContext, ReservedRegisters, brillig_variable::{BrilligVariable, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, Stack}, - BrilligBinaryOp, BrilligContext, ReservedRegisters, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs index 9512a60e6998..388c5f7a343e 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs @@ -1,19 +1,19 @@ use acvm::{ + AcirField, acir::{ brillig::{HeapVector, MemoryAddress}, circuit::ErrorSelector, }, - AcirField, }; use crate::ssa::ir::instruction::ErrorType; use super::{ + BrilligBinaryOp, BrilligContext, ReservedRegisters, artifact::BrilligParameter, brillig_variable::{BrilligArray, BrilligVariable, SingleAddrVariable}, debug_show::DebugToString, registers::RegisterAllocator, - BrilligBinaryOp, BrilligContext, ReservedRegisters, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs index 42c13a3c2355..3daf04861107 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs @@ -1,15 +1,15 @@ use acvm::acir::{ - brillig::{BlackBoxOp, IntegerBitSize}, AcirField, + brillig::{BlackBoxOp, IntegerBitSize}, }; use crate::brillig::brillig_ir::BrilligBinaryOp; use super::{ + BrilligContext, brillig_variable::{BrilligArray, SingleAddrVariable}, debug_show::DebugToString, registers::RegisterAllocator, - BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs index a34034bb5505..2936aea486ed 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs @@ -1,15 +1,15 @@ use acvm::{ - acir::brillig::{HeapArray, HeapVector, MemoryAddress, ValueOrArray}, AcirField, + acir::brillig::{HeapArray, HeapVector, MemoryAddress, ValueOrArray}, }; use crate::brillig::brillig_ir::BrilligBinaryOp; use super::{ + BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, BrilligContext, ReservedRegisters, brillig_variable::{BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::RegisterAllocator, - BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs index f609c3422886..a5dbc2811c3c 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs @@ -1,7 +1,7 @@ -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; -use super::{debug_show::DebugToString, registers::RegisterAllocator, BrilligContext}; +use super::{BrilligContext, debug_show::DebugToString, registers::RegisterAllocator}; impl BrilligContext { /// This function moves values from a set of registers to another set of registers. @@ -126,15 +126,15 @@ impl LoopDetector { #[cfg(test)] mod tests { use acvm::{ - acir::brillig::{MemoryAddress, Opcode}, FieldElement, + acir::brillig::{MemoryAddress, Opcode}, }; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::{ brillig::{ - brillig_ir::{artifact::Label, registers::Stack, BrilligContext}, BrilligOptions, + brillig_ir::{BrilligContext, artifact::Label, registers::Stack}, }, ssa::ir::function::FunctionId, }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index 283c0d67eb8b..c0b6492618c1 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -3,8 +3,8 @@ use super::BrilligBinaryOp; use crate::brillig::brillig_ir::ReservedRegisters; use acvm::{ - acir::brillig::{BlackBoxOp, HeapArray, HeapVector, MemoryAddress, ValueOrArray}, FieldElement, + acir::brillig::{BlackBoxOp, HeapArray, HeapVector, MemoryAddress, ValueOrArray}, }; /// Trait for converting values into debug-friendly strings. @@ -126,6 +126,24 @@ impl DebugShow { debug_println!(self.enable_debug_trace, " MOV {}, {}", destination, source); } + /// Emits a `conditional mov` instruction. + pub(crate) fn conditional_mov_instruction( + &self, + destination: MemoryAddress, + source_then: MemoryAddress, + source_else: MemoryAddress, + condition: MemoryAddress, + ) { + debug_println!( + self.enable_debug_trace, + " {} = MOV if {} then {}, else {}", + destination, + condition, + source_then, + source_else + ); + } + /// Emits a `cast` instruction. pub(crate) fn cast_instruction( &self, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs index 4a5be8bb19c6..e1450f0f1188 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs @@ -1,15 +1,15 @@ use crate::{brillig::BrilligOptions, ssa::ir::function::FunctionId}; use super::{ + BrilligBinaryOp, BrilligContext, ReservedRegisters, artifact::{BrilligArtifact, BrilligParameter}, brillig_variable::{BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::Stack, - BrilligBinaryOp, BrilligContext, ReservedRegisters, }; use acvm::acir::{ - brillig::{HeapVector, MemoryAddress}, AcirField, + brillig::{HeapVector, MemoryAddress}, }; pub(crate) const MAX_STACK_SIZE: usize = 16 * MAX_STACK_FRAME_SIZE; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs index 9dd541c71807..fad9892cfb16 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs @@ -1,23 +1,23 @@ use acvm::{ + FieldElement, acir::{ + AcirField, brillig::{ BinaryFieldOp, BinaryIntOp, BitSize, BlackBoxOp, HeapValueType, HeapVector, MemoryAddress, Opcode as BrilligOpcode, ValueOrArray, }, - AcirField, }, - FieldElement, }; use crate::ssa::ir::function::FunctionId; use super::{ + BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, BrilligContext, ReservedRegisters, artifact::{Label, UnresolvedJumpLocation}, brillig_variable::SingleAddrVariable, debug_show::DebugToString, procedures::ProcedureId, registers::RegisterAllocator, - BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; /// Low level instructions of the brillig IR, used by the brillig ir codegens and brillig_gen @@ -75,6 +75,28 @@ impl BrilligContext< ); } + /// Insert a conditional move instruction + pub(crate) fn conditional_move_instruction( + &mut self, + condition: SingleAddrVariable, + then_address: SingleAddrVariable, + else_address: SingleAddrVariable, + destination: SingleAddrVariable, + ) { + self.debug_show.conditional_mov_instruction( + destination.address, + then_address.address, + else_address.address, + condition.address, + ); + self.push_opcode(BrilligOpcode::ConditionalMov { + destination: destination.address, + source_a: then_address.address, + source_b: else_address.address, + condition: condition.address, + }); + } + fn binary( &mut self, lhs: SingleAddrVariable, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_copy.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_copy.rs index 0a6e8824223e..cccd08fe2d50 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_copy.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_copy.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligArray, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_reverse.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_reverse.rs index a5a11d61bef7..aa5d1cfeece8 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_reverse.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_reverse.rs @@ -1,12 +1,12 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligContext, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/check_max_stack_depth.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/check_max_stack_depth.rs index 4d5abe934209..fd8552903c71 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/check_max_stack_depth.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/check_max_stack_depth.rs @@ -2,11 +2,11 @@ use acvm::AcirField; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, BrilligContext, ReservedRegisters, brillig_variable::SingleAddrVariable, debug_show::DebugToString, entry_point::{MAX_STACK_FRAME_SIZE, MAX_STACK_SIZE}, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, ReservedRegisters, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mem_copy.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mem_copy.rs index cdd995424836..9f3f1daa2ac9 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mem_copy.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mem_copy.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligContext, brillig_variable::SingleAddrVariable, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mod.rs index 8e1761d0eee4..a2d00786ff8c 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mod.rs @@ -25,12 +25,12 @@ use vector_pop_back::compile_vector_pop_back_procedure; use vector_pop_front::compile_vector_pop_front_procedure; use vector_remove::compile_vector_remove_procedure; -use crate::brillig::{brillig_ir::AcirField, BrilligOptions}; +use crate::brillig::{BrilligOptions, brillig_ir::AcirField}; use super::{ + BrilligContext, artifact::{BrilligArtifact, Label}, debug_show::DebugToString, - BrilligContext, }; /// Procedures are a set of complex operations that are common in the noir language. diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_insert.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_insert.rs index 1c1a738509cb..dced68922ef3 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_insert.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_insert.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; -use super::{prepare_vector_push::reallocate_vector_for_insertion, ProcedureId}; +use super::{ProcedureId, prepare_vector_push::reallocate_vector_for_insertion}; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_push.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_push.rs index 798cf2385e5b..4d985ccb12b7 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_push.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_push.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/revert_with_string.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/revert_with_string.rs index 83d7dfc618d9..1601451ef50e 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/revert_with_string.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/revert_with_string.rs @@ -2,9 +2,9 @@ use acvm::AcirField; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligContext, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_copy.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_copy.rs index 6d2f9c4afb4f..8e53fb8d01cd 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_copy.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_copy.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_back.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_back.rs index bfc9d5128526..bd71b39f9e71 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_back.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_back.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_front.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_front.rs index 49123ca2f500..8df8f1c6b12f 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_front.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_front.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_remove.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_remove.rs index 7abc43286ee7..686fc74210a6 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_remove.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_remove.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs index 093c99dec3b5..2fc7f6fc7115 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs @@ -6,9 +6,9 @@ use iter_extended::vecmap; use crate::brillig::brillig_ir::entry_point::MAX_STACK_SIZE; use super::{ + BrilligContext, ReservedRegisters, brillig_variable::SingleAddrVariable, entry_point::{MAX_SCRATCH_SPACE, MAX_STACK_FRAME_SIZE}, - BrilligContext, ReservedRegisters, }; pub(crate) trait RegisterAllocator { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs b/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs index 94c0e0554a4d..deaefd40ae32 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs @@ -7,9 +7,9 @@ //! An Error of the former is a user Error //! //! An Error of the latter is an error in the implementation of the compiler -use acvm::FieldElement; use iter_extended::vecmap; -use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic}; +use noirc_errors::{CustomDiagnostic, Location}; +use noirc_frontend::signed_field::SignedField; use thiserror::Error; use crate::ssa::ir::{call_stack::CallStack, types::NumericType}; @@ -23,7 +23,7 @@ pub enum RuntimeError { InvalidRangeConstraint { num_bits: u32, call_stack: CallStack }, #[error("The value `{value:?}` cannot fit into `{typ}` which has range `{range}`")] IntegerOutOfBounds { - value: FieldElement, + value: SignedField, typ: NumericType, range: String, call_stack: CallStack, @@ -34,7 +34,9 @@ pub enum RuntimeError { UnInitialized { name: String, call_stack: CallStack }, #[error("Integer sized {num_bits:?} is over the max supported size of {max_num_bits:?}")] UnsupportedIntegerSize { num_bits: u32, max_num_bits: u32, call_stack: CallStack }, - #[error("Integer {value}, sized {num_bits:?}, is over the max supported size of {max_num_bits:?} for the blackbox function's inputs")] + #[error( + "Integer {value}, sized {num_bits:?}, is over the max supported size of {max_num_bits:?} for the blackbox function's inputs" + )] InvalidBlackBoxInputBitSize { value: String, num_bits: u32, @@ -59,7 +61,9 @@ pub enum RuntimeError { UnconstrainedSliceReturnToConstrained { call_stack: CallStack }, #[error("All `oracle` methods should be wrapped in an unconstrained fn")] UnconstrainedOracleReturnToConstrained { call_stack: CallStack }, - #[error("Could not resolve some references to the array. All references must be resolved at compile time")] + #[error( + "Could not resolve some references to the array. All references must be resolved at compile time" + )] UnknownReference { call_stack: CallStack }, } @@ -69,8 +73,8 @@ pub enum SsaReport { Bug(InternalBug), } -impl From for FileDiagnostic { - fn from(error: SsaReport) -> FileDiagnostic { +impl From for CustomDiagnostic { + fn from(error: SsaReport) -> CustomDiagnostic { match error { SsaReport::Warning(warning) => { let message = warning.to_string(); @@ -83,11 +87,10 @@ impl From for FileDiagnostic { }, }; let call_stack = vecmap(call_stack, |location| location); - let file_id = call_stack.last().map(|location| location.file).unwrap_or_default(); let location = call_stack.last().expect("Expected RuntimeError to have a location"); let diagnostic = - Diagnostic::simple_warning(message, secondary_message, location.span); - diagnostic.with_call_stack(call_stack).in_file(file_id) + CustomDiagnostic::simple_warning(message, secondary_message, *location); + diagnostic.with_call_stack(call_stack) } SsaReport::Bug(bug) => { let message = bug.to_string(); @@ -101,10 +104,10 @@ impl From for FileDiagnostic { InternalBug::AssertFailed { call_stack } => ("As a result, the compiled circuit is ensured to fail. Other assertions may also fail during execution".to_string(), call_stack) }; let call_stack = vecmap(call_stack, |location| location); - let file_id = call_stack.last().map(|location| location.file).unwrap_or_default(); let location = call_stack.last().expect("Expected RuntimeError to have a location"); - let diagnostic = Diagnostic::simple_bug(message, secondary_message, location.span); - diagnostic.with_call_stack(call_stack).in_file(file_id) + let diagnostic = + CustomDiagnostic::simple_bug(message, secondary_message, *location); + diagnostic.with_call_stack(call_stack) } } } @@ -178,24 +181,23 @@ impl RuntimeError { } } -impl From for FileDiagnostic { - fn from(error: RuntimeError) -> FileDiagnostic { +impl From for CustomDiagnostic { + fn from(error: RuntimeError) -> CustomDiagnostic { let call_stack = vecmap(error.call_stack(), |location| *location); - let file_id = call_stack.last().map(|location| location.file).unwrap_or_default(); let diagnostic = error.into_diagnostic(); - diagnostic.with_call_stack(call_stack).in_file(file_id) + diagnostic.with_call_stack(call_stack) } } impl RuntimeError { - fn into_diagnostic(self) -> Diagnostic { + fn into_diagnostic(self) -> CustomDiagnostic { match self { RuntimeError::InternalError(cause) => { - Diagnostic::simple_error( + CustomDiagnostic::simple_error( "Internal Consistency Evaluators Errors: \n This is likely a bug. Consider opening an issue at https://github.com/noir-lang/noir/issues".to_owned(), cause.to_string(), - noirc_errors::Span::inclusive(0, 0) + Location::dummy(), ) } RuntimeError::UnknownLoopBound { .. } => { @@ -203,10 +205,10 @@ impl RuntimeError { let location = self.call_stack().last().expect("Expected RuntimeError to have a location"); - Diagnostic::simple_error( + CustomDiagnostic::simple_error( primary_message, "If attempting to fetch the length of a slice, try converting to an array. Slices only use dynamic lengths.".to_string(), - location.span, + *location, ) } _ => { @@ -214,7 +216,7 @@ impl RuntimeError { let location = self.call_stack().last().unwrap_or_else(|| panic!("Expected RuntimeError to have a location. Error message: {message}")); - Diagnostic::simple_error(message, String::new(), location.span) + CustomDiagnostic::simple_error(message, String::new(), *location) } } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs index 74196d9a7666..935918c6b7e8 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs @@ -18,14 +18,14 @@ use crate::{ errors::{RuntimeError, SsaReport}, }; use acvm::{ + FieldElement, acir::{ circuit::{ - brillig::BrilligBytecode, Circuit, ErrorSelector, ExpressionWidth, - Program as AcirProgram, PublicInputs, + Circuit, ErrorSelector, ExpressionWidth, Program as AcirProgram, PublicInputs, + brillig::BrilligBytecode, }, native_types::Witness, }, - FieldElement, }; use ir::instruction::ErrorType; @@ -34,7 +34,7 @@ use noirc_errors::debug_info::{DebugFunctions, DebugInfo, DebugTypes, DebugVaria use noirc_frontend::ast::Visibility; use noirc_frontend::{hir_def::function::FunctionSignature, monomorphization::ast::Program}; use ssa_gen::Ssa; -use tracing::{span, Level}; +use tracing::{Level, span}; use crate::acir::{Artifacts, GeneratedAcir}; @@ -72,8 +72,8 @@ pub struct SsaEvaluatorOptions { /// Skip the check for under constrained values pub skip_underconstrained_check: bool, - /// Enable the missing Brillig call constraints check - pub enable_brillig_constraints_check: bool, + /// Skip the missing Brillig call constraints check + pub skip_brillig_constraints_check: bool, /// Enable the lookback feature of the Brillig call constraints /// check (prevents some rare false positives, leads to a slowdown @@ -132,7 +132,7 @@ pub(crate) fn optimize_into_acir( // It could happen that we inlined all calls to a given brillig function. // In that case it's unused so we can remove it. This is what we check next. .run_pass(Ssa::remove_unreachable_functions, "Removing Unreachable Functions (4th)") - .run_pass(Ssa::dead_instruction_elimination, "Dead Instruction Elimination (3rd)") + .run_pass(Ssa::dead_instruction_elimination_acir, "Dead Instruction Elimination (3rd)") .finish(); if !options.skip_underconstrained_check { @@ -143,7 +143,7 @@ pub(crate) fn optimize_into_acir( )); } - if options.enable_brillig_constraints_check { + if !options.skip_brillig_constraints_check { ssa_level_warnings.extend(time( "After Check for Missing Brillig Call Constraints", options.print_codegen_timings, @@ -211,9 +211,11 @@ fn optimize_all(builder: SsaBuilder, options: &SsaEvaluatorOptions) -> Result Result, // Map of argument value ids to the Brillig call ids employing them call_arguments: HashMap>, - // Maintains count of calls being tracked - tracking_count: usize, + // The set of calls currently being tracked + tracking: HashSet, // Opt-in to use the lookback feature (tracking the argument values // of a Brillig call before the call happens if their usage precedes // it). Can prevent certain false positives, at the cost of @@ -138,8 +138,6 @@ struct BrilligTaintedIds { array_elements: HashMap>, // Initial result value ids, along with element ids for arrays root_results: HashSet, - // The flag signaling that the call should be now tracked - tracking: bool, } #[derive(Clone, Debug)] @@ -195,7 +193,6 @@ impl BrilligTaintedIds { results: results_status, array_elements, root_results: HashSet::from_iter(results.iter().copied()), - tracking: false, } } @@ -327,7 +324,7 @@ impl DependencyContext { function.dfg[block].instructions().iter().for_each(|instruction| { if let Instruction::Call { func, arguments } = &function.dfg[*instruction] { if let Value::Function(callee) = &function.dfg[*func] { - if all_functions[&callee].runtime().is_brillig() { + if all_functions[callee].runtime().is_brillig() { // Skip already visited locations (happens often in unrolled functions) let call_stack = function.dfg.get_instruction_call_stack(*instruction); let location = call_stack.last(); @@ -394,23 +391,19 @@ impl DependencyContext { for argument in &arguments { if let Some(calls) = self.call_arguments.get(argument) { for call in calls { - if let Some(tainted_ids) = self.tainted.get_mut(call) { - tainted_ids.tracking = true; - self.tracking_count += 1; + if self.tainted.contains_key(call) { + self.tracking.insert(*call); } } } } } - if let Some(tainted_ids) = self.tainted.get_mut(instruction) { - if !tainted_ids.tracking { - tainted_ids.tracking = true; - self.tracking_count += 1; - } + if self.tainted.contains_key(instruction) { + self.tracking.insert(*instruction); } // We can skip over instructions while nothing is being tracked - if self.tracking_count > 0 { + if !self.tracking.is_empty() { let mut results = Vec::new(); // Collect non-constant instruction results @@ -431,8 +424,10 @@ impl DependencyContext { if let Some(value_id) = self.memory_slots.get(address) { self.update_children(&[*value_id], &results); } else { - panic!("load instruction {} has attempted to access previously unused memory location", - instruction); + panic!( + "load instruction {} has attempted to access previously unused memory location", + instruction + ); } } // Record the condition to set as future parent for the following values @@ -502,7 +497,10 @@ impl DependencyContext { RuntimeType::Brillig(..) => {} }, Value::ForeignFunction(..) => { - panic!("should not be able to reach foreign function from non-Brillig functions, {func_id} in function {}", function.name()); + panic!( + "should not be able to reach foreign function from non-Brillig functions, {func_id} in function {}", + function.name() + ); } Value::Instruction { .. } | Value::NumericConstant { .. } @@ -519,7 +517,7 @@ impl DependencyContext { // results involving the array in question, to properly // populate the array element tainted sets Instruction::ArrayGet { array, index } => { - self.process_array_get(function, *array, *index, &results); + self.process_array_get(*array, *index, &results, function); // Record all the used arguments as parents of the results self.update_children(&arguments, &results); } @@ -558,7 +556,10 @@ impl DependencyContext { .tainted .keys() .map(|brillig_call| { - trace!("tainted structure for {}: {:?}", brillig_call, self.tainted[brillig_call]); + trace!( + "tainted structure for {:?}: {:?}", + brillig_call, self.tainted[brillig_call] + ); SsaReport::Bug(InternalBug::UncheckedBrilligCall { call_stack: function.dfg.get_instruction_call_stack(*brillig_call), }) @@ -582,8 +583,8 @@ impl DependencyContext { self.side_effects_condition.map(|v| parents.insert(v)); // Don't update sets for the calls not yet being tracked - for (_, tainted_ids) in self.tainted.iter_mut() { - if tainted_ids.tracking { + for call in &self.tracking { + if let Some(tainted_ids) = self.tainted.get_mut(call) { tainted_ids.update_children(&parents, children); } } @@ -600,15 +601,15 @@ impl DependencyContext { .collect(); // Skip untracked calls - for (_, tainted_ids) in self.tainted.iter_mut() { - if tainted_ids.tracking { + for call in &self.tracking { + if let Some(tainted_ids) = self.tainted.get_mut(call) { tainted_ids.store_partial_constraints(&constrained_values); } } - self.tainted.retain(|_, tainted_ids| { + self.tainted.retain(|call, tainted_ids| { if tainted_ids.check_constrained() { - self.tracking_count -= 1; + self.tracking.remove(call); false } else { true @@ -619,10 +620,10 @@ impl DependencyContext { /// Process ArrayGet instruction for tracked Brillig calls fn process_array_get( &mut self, - function: &Function, array: ValueId, index: ValueId, element_results: &[ValueId], + function: &Function, ) { use acvm::acir::AcirField; @@ -630,8 +631,8 @@ impl DependencyContext { if let Some(value) = function.dfg.get_numeric_constant(index) { if let Some(index) = value.try_to_u32() { // Skip untracked calls - for (_, tainted_ids) in self.tainted.iter_mut() { - if tainted_ids.tracking { + for call in &self.tracking { + if let Some(tainted_ids) = self.tainted.get_mut(call) { tainted_ids.process_array_get(array, index as usize, element_results); } } @@ -826,13 +827,18 @@ impl Context { } }, Value::ForeignFunction(..) => { - panic!("Should not be able to reach foreign function from non-Brillig functions, {func_id} in function {}", function.name()); + panic!( + "Should not be able to reach foreign function from non-Brillig functions, {func_id} in function {}", + function.name() + ); } Value::Instruction { .. } | Value::NumericConstant { .. } | Value::Param { .. } | Value::Global(_) => { - panic!("At the point we are running disconnect there shouldn't be any other values as arguments") + panic!( + "At the point we are running disconnect there shouldn't be any other values as arguments" + ) } } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index e4c00358c8c0..95944129c4eb 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -2,7 +2,7 @@ pub(crate) mod data_bus; use std::{borrow::Cow, collections::BTreeMap, sync::Arc}; -use acvm::{acir::circuit::ErrorSelector, FieldElement}; +use acvm::{FieldElement, acir::circuit::ErrorSelector}; use noirc_errors::Location; use noirc_frontend::hir_def::types::Type as HirType; use noirc_frontend::monomorphization::ast::InlineType; @@ -85,7 +85,11 @@ impl FunctionBuilder { /// This should only be used immediately following construction of a FunctionBuilder /// and will panic if there are any already finished functions. pub(crate) fn set_runtime(&mut self, runtime: RuntimeType) { - assert_eq!(self.finished_functions.len(), 0, "Attempted to set runtime on a FunctionBuilder with finished functions. A FunctionBuilder's runtime should only be set on its initial function"); + assert_eq!( + self.finished_functions.len(), + 0, + "Attempted to set runtime on a FunctionBuilder with finished functions. A FunctionBuilder's runtime should only be set on its initial function" + ); self.current_function.set_runtime(runtime); } @@ -584,7 +588,7 @@ impl std::ops::Index for FunctionBuilder { mod tests { use std::sync::Arc; - use acvm::{acir::AcirField, FieldElement}; + use acvm::{FieldElement, acir::AcirField}; use crate::ssa::ir::{ instruction::{Endian, Intrinsic}, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index aa517d431047..132985830ebf 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -18,12 +18,12 @@ use super::{ value::{Value, ValueId}, }; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use fxhash::FxHashMap as HashMap; use iter_extended::vecmap; use serde::{Deserialize, Serialize}; -use serde_with::serde_as; use serde_with::DisplayFromStr; +use serde_with::serde_as; /// The DataFlowGraph contains most of the actual data in a function including /// its blocks, instructions, and values. This struct is largely responsible for @@ -239,10 +239,9 @@ impl DataFlowGraph { instruction, Instruction::IncrementRc { .. } | Instruction::DecrementRc { .. } ), - RuntimeType::Brillig(_) => !matches!( - instruction, - Instruction::EnableSideEffectsIf { .. } | Instruction::IfElse { .. } - ), + RuntimeType::Brillig(_) => { + !matches!(instruction, Instruction::EnableSideEffectsIf { .. }) + } } } @@ -340,14 +339,19 @@ impl DataFlowGraph { } } let mut instructions = instructions.unwrap_or(vec![instruction]); - assert!(!instructions.is_empty(), "`SimplifyResult::SimplifiedToInstructionMultiple` must not return empty vector"); + assert!( + !instructions.is_empty(), + "`SimplifyResult::SimplifiedToInstructionMultiple` must not return empty vector" + ); if instructions.len() > 1 { // There's currently no way to pass results from one instruction in `instructions` on to the next. // We then restrict this to only support multiple instructions if they're all `Instruction::Constrain` // as this instruction type does not have any results. assert!( - instructions.iter().all(|instruction| matches!(instruction, Instruction::Constrain(..))), + instructions + .iter() + .all(|instruction| matches!(instruction, Instruction::Constrain(..))), "`SimplifyResult::SimplifiedToInstructionMultiple` only supports `Constrain` instructions" ); } @@ -372,6 +376,11 @@ impl DataFlowGraph { } } + /// Replace an existing instruction with a new one. + pub(crate) fn set_instruction(&mut self, id: InstructionId, instruction: Instruction) { + self.instructions[id] = instruction; + } + /// Set the value of value_to_replace to refer to the value referred to by new_value. /// /// This is the preferred method to call for optimizations simplifying @@ -658,10 +667,28 @@ impl DataFlowGraph { pub(crate) fn is_safe_index(&self, index: ValueId, array: ValueId) -> bool { #[allow(clippy::match_like_matches_macro)] match (self.type_of_value(array), self.get_numeric_constant(index)) { - (Type::Array(_, len), Some(index)) if index.to_u128() < (len as u128) => true, + (Type::Array(elements, len), Some(index)) + if index.to_u128() < (len as u128 * elements.len() as u128) => + { + true + } + _ => false, + } + } + + /// Arrays are represented as `[RC, ...items]` where RC stands for reference count. + /// By the time of Brillig generation we expect all constant indices + /// to already account for the extra offset from the RC. + pub(crate) fn is_safe_brillig_index(&self, index: ValueId, array: ValueId) -> bool { + #[allow(clippy::match_like_matches_macro)] + match (self.type_of_value(array), self.get_numeric_constant(index)) { + (Type::Array(elements, len), Some(index)) => { + (index.to_u128() - 1) < (len as u128 * elements.len() as u128) + } _ => false, } } + /// Sets the terminator instruction for the given basic block pub(crate) fn set_block_terminator( &mut self, @@ -880,7 +907,7 @@ impl<'dfg> InsertInstructionResult<'dfg> { } } -impl<'dfg> std::ops::Index for InsertInstructionResult<'dfg> { +impl std::ops::Index for InsertInstructionResult<'_> { type Output = ValueId; fn index(&self, index: usize) -> &Self::Output { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dom.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dom.rs index 3dde6240e183..a6f878dafbf0 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dom.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dom.rs @@ -136,10 +136,7 @@ impl DominatorTree { if let Some(value) = f(block_id) { return Some(value); } - block_id = match self.immediate_dominator(block_id) { - Some(immediate_dominator) => immediate_dominator, - None => return None, - } + block_id = self.immediate_dominator(block_id)?; } } @@ -295,8 +292,8 @@ mod tests { } // Testing setup for a function with an unreachable block2 - fn unreachable_node_setup( - ) -> (DominatorTree, BasicBlockId, BasicBlockId, BasicBlockId, BasicBlockId) { + fn unreachable_node_setup() + -> (DominatorTree, BasicBlockId, BasicBlockId, BasicBlockId, BasicBlockId) { // func() { // block0(cond: u1): // jmpif v0 block2() block3() diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs index 9e4557e06a66..13b5ead5eb61 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs @@ -79,6 +79,13 @@ impl<'f> FunctionInserter<'f> { (instruction, self.function.dfg.get_instruction_call_stack_id(id)) } + /// Get an instruction, map all its values, and replace it with the resolved instruction. + pub(crate) fn map_instruction_in_place(&mut self, id: InstructionId) { + let mut instruction = self.function.dfg[id].clone(); + instruction.map_values_mut(|id| self.resolve(id)); + self.function.dfg.set_instruction(id, instruction); + } + /// Maps a terminator in place, replacing any ValueId in the terminator with the /// resolved version of that value id from this FunctionInserter's internal value mapping. pub(crate) fn map_terminator_in_place(&mut self, block: BasicBlockId) { @@ -251,4 +258,22 @@ impl<'f> FunctionInserter<'f> { self.values.entry(*param).or_insert(*new_param); } } + + /// Merge the internal mapping into the given mapping + /// The merge is guaranteed to be coherent because ambiguous cases are prevented + pub(crate) fn extract_mapping(&self, mapping: &mut HashMap) { + for (k, v) in &self.values { + if mapping.contains_key(k) { + unreachable!("cannot merge key"); + } + if mapping.contains_key(v) { + unreachable!("cannot merge value"); + } + mapping.insert(*k, *v); + } + } + + pub(crate) fn set_mapping(&mut self, mapping: HashMap) { + self.values = mapping; + } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 178e15e4e322..1bef9079eb82 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -1,11 +1,11 @@ -use binary::truncate_field; +use binary::{truncate, truncate_field}; use serde::{Deserialize, Serialize}; use std::hash::{Hash, Hasher}; use acvm::{ - acir::AcirField, - acir::{circuit::ErrorSelector, BlackBoxFunc}, FieldElement, + acir::AcirField, + acir::{BlackBoxFunc, circuit::ErrorSelector}, }; use fxhash::FxHasher64; use iter_extended::vecmap; @@ -911,8 +911,10 @@ impl Instruction { // would be incorrect however since the extra bits on the field would not be flipped. Value::NumericConstant { constant, typ } if typ.is_unsigned() => { // As we're casting to a `u128`, we need to clear out any upper bits that the NOT fills. - let value = !constant.to_u128() % (1 << typ.bit_size()); - SimplifiedTo(dfg.make_constant(value.into(), *typ)) + let bit_size = typ.bit_size(); + assert!(bit_size <= 128); + let not_value: u128 = truncate(!constant.to_u128(), bit_size); + SimplifiedTo(dfg.make_constant(not_value.into(), *typ)) } Value::Instruction { instruction, .. } => { // !!v => v @@ -1001,11 +1003,7 @@ impl Instruction { // // In order for the truncation to be a noop, we then require `max_quotient_bits < bit_size`. let max_quotient_bits = max_numerator_bits - divisor_bits; - if max_quotient_bits < *bit_size { - SimplifiedTo(*value) - } else { - None - } + if max_quotient_bits < *bit_size { SimplifiedTo(*value) } else { None } } _ => None, @@ -1034,11 +1032,7 @@ impl Instruction { Instruction::DecrementRc { .. } => None, Instruction::RangeCheck { value, max_bit_size, .. } => { let max_potential_bits = dfg.get_value_max_num_bits(*value); - if max_potential_bits < *max_bit_size { - Remove - } else { - None - } + if max_potential_bits <= *max_bit_size { Remove } else { None } } Instruction::IfElse { then_condition, then_value, else_condition, else_value } => { let then_condition = dfg.resolve(*then_condition); @@ -1470,3 +1464,32 @@ impl SimplifyResult { } } } + +#[cfg(test)] +mod tests { + use crate::ssa::{opt::assert_normalized_ssa_equals, ssa_gen::Ssa}; + + #[test] + fn removes_range_constraints_on_constants() { + let src = " + acir(inline) fn main f0 { + b0(v0: Field): + range_check Field 0 to 1 bits + range_check Field 1 to 1 bits + range_check Field 255 to 8 bits + range_check Field 256 to 8 bits + return + } + "; + let ssa = Ssa::from_str_simplifying(src).unwrap(); + + let expected = " + acir(inline) fn main f0 { + b0(v0: Field): + range_check Field 256 to 8 bits + return + } + "; + assert_normalized_ssa_equals(ssa, expected); + } +} diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs index 1550c9ea050e..1ec2e33ee8d6 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs @@ -1,4 +1,5 @@ -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; +use num_traits::ToPrimitive as _; use serde::{Deserialize, Serialize}; use super::{ @@ -139,11 +140,11 @@ impl Binary { }; } - let lhs_is_zero = lhs_value.map_or(false, |lhs| lhs.is_zero()); - let rhs_is_zero = rhs_value.map_or(false, |rhs| rhs.is_zero()); + let lhs_is_zero = lhs_value.is_some_and(|lhs| lhs.is_zero()); + let rhs_is_zero = rhs_value.is_some_and(|rhs| rhs.is_zero()); - let lhs_is_one = lhs_value.map_or(false, |lhs| lhs.is_one()); - let rhs_is_one = rhs_value.map_or(false, |rhs| rhs.is_one()); + let lhs_is_one = lhs_value.is_some_and(|lhs| lhs.is_one()); + let rhs_is_one = rhs_value.is_some_and(|rhs| rhs.is_one()); match self.operator { BinaryOp::Add { .. } => { @@ -306,14 +307,18 @@ impl Binary { let bitmask_plus_one = bitmask.to_u128() + 1; if bitmask_plus_one.is_power_of_two() { let value = if lhs_value.is_some() { rhs } else { lhs }; - let num_bits = bitmask_plus_one.ilog2(); - return SimplifyResult::SimplifiedToInstruction( - Instruction::Truncate { - value, - bit_size: num_bits, - max_bit_size: lhs_type.bit_size(), - }, - ); + let bit_size = bitmask_plus_one.ilog2(); + let max_bit_size = lhs_type.bit_size(); + + if bit_size == max_bit_size { + // If we're truncating a value into the full size of its type then + // the truncation is a noop. + return SimplifyResult::SimplifiedTo(value); + } else { + return SimplifyResult::SimplifiedToInstruction( + Instruction::Truncate { value, bit_size, max_bit_size }, + ); + } } } @@ -509,7 +514,7 @@ fn convert_signed_integer_to_field_element(int: i128, bit_size: u32) -> FieldEle } /// Truncates `int` to fit within `bit_size` bits. -fn truncate(int: u128, bit_size: u32) -> u128 { +pub(super) fn truncate(int: u128, bit_size: u32) -> u128 { if bit_size == 128 { int } else { @@ -574,8 +579,8 @@ impl BinaryOp { BinaryOp::Xor => |x, y| Some(x ^ y), BinaryOp::Eq => |x, y| Some((x == y) as u128), BinaryOp::Lt => |x, y| Some((x < y) as u128), - BinaryOp::Shl => |x, y| Some(x << y), - BinaryOp::Shr => |x, y| Some(x >> y), + BinaryOp::Shl => |x, y| y.to_u32().and_then(|y| x.checked_shl(y)), + BinaryOp::Shr => |x, y| y.to_u32().and_then(|y| x.checked_shr(y)), } } @@ -591,8 +596,8 @@ impl BinaryOp { BinaryOp::Xor => |x, y| Some(x ^ y), BinaryOp::Eq => |x, y| Some((x == y) as i128), BinaryOp::Lt => |x, y| Some((x < y) as i128), - BinaryOp::Shl => |x, y| Some(x << y), - BinaryOp::Shr => |x, y| Some(x >> y), + BinaryOp::Shl => |x, y| y.to_u32().and_then(|y| x.checked_shl(y)), + BinaryOp::Shr => |x, y| y.to_u32().and_then(|y| x.checked_shr(y)), } } @@ -620,7 +625,7 @@ mod test { use proptest::prelude::*; use super::{ - convert_signed_integer_to_field_element, truncate_field, + BinaryOp, convert_signed_integer_to_field_element, truncate_field, try_convert_field_element_to_signed_integer, }; use acvm::{AcirField, FieldElement}; @@ -649,6 +654,17 @@ mod test { let truncated_as_bigint = FieldElement::from_be_bytes_reduce(&truncated_as_bigint.to_bytes_be()); prop_assert_eq!(truncated_as_field, truncated_as_bigint); } + } + + #[test] + fn get_u128_function_shift_works_with_values_larger_than_127() { + assert!(BinaryOp::Shr.get_u128_function()(1, 128).is_none()); + assert!(BinaryOp::Shl.get_u128_function()(1, 128).is_none()); + } + #[test] + fn get_i128_function_shift_works_with_values_larger_than_127() { + assert!(BinaryOp::Shr.get_i128_function()(1, 128).is_none()); + assert!(BinaryOp::Shl.get_i128_function()(1, 128).is_none()); } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index 6ee7aa0192ce..d32a562a037e 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -2,8 +2,8 @@ use fxhash::FxHashMap as HashMap; use std::{collections::VecDeque, sync::Arc}; use acvm::{ - acir::{AcirField, BlackBoxFunc}, FieldElement, + acir::{AcirField, BlackBoxFunc}, }; use bn254_blackbox_solver::derive_generators; use iter_extended::vecmap; @@ -170,7 +170,7 @@ pub(super) fn simplify_call( } Intrinsic::SlicePopBack => { let length = dfg.get_numeric_constant(arguments[0]); - if length.map_or(true, |length| length.is_zero()) { + if length.is_none_or(|length| length.is_zero()) { // If the length is zero then we're trying to pop the last element from an empty slice. // Defer the error to acir_gen. return SimplifyResult::None; @@ -185,7 +185,7 @@ pub(super) fn simplify_call( } Intrinsic::SlicePopFront => { let length = dfg.get_numeric_constant(arguments[0]); - if length.map_or(true, |length| length.is_zero()) { + if length.is_none_or(|length| length.is_zero()) { // If the length is zero then we're trying to pop the first element from an empty slice. // Defer the error to acir_gen. return SimplifyResult::None; @@ -243,7 +243,7 @@ pub(super) fn simplify_call( } Intrinsic::SliceRemove => { let length = dfg.get_numeric_constant(arguments[0]); - if length.map_or(true, |length| length.is_zero()) { + if length.is_none_or(|length| length.is_zero()) { // If the length is zero then we're trying to remove an element from an empty slice. // Defer the error to acir_gen. return SimplifyResult::None; @@ -610,7 +610,9 @@ fn simplify_black_box_func( "ICE: `BlackBoxFunc::RANGE` calls should be transformed into a `Instruction::Cast`" ) } - BlackBoxFunc::Sha256Compression => SimplifyResult::None, //TODO(Guillaume) + BlackBoxFunc::Sha256Compression => { + blackbox::simplify_sha256_compression(dfg, arguments, block, call_stack) + } BlackBoxFunc::AES128Encrypt => SimplifyResult::None, } } @@ -718,10 +720,10 @@ fn simplify_derive_generators( ); let is_infinite = dfg.make_constant(FieldElement::zero(), NumericType::bool()); let mut results = Vec::new(); - for gen in generators { - let x_big: BigUint = gen.x.into(); + for generator in generators { + let x_big: BigUint = generator.x.into(); let x = FieldElement::from_be_bytes_reduce(&x_big.to_bytes_be()); - let y_big: BigUint = gen.y.into(); + let y_big: BigUint = generator.y.into(); let y = FieldElement::from_be_bytes_reduce(&y_big.to_bytes_be()); results.push(dfg.make_constant(x, NumericType::NativeField)); results.push(dfg.make_constant(y, NumericType::NativeField)); @@ -742,7 +744,7 @@ fn simplify_derive_generators( #[cfg(test)] mod tests { - use crate::ssa::{opt::assert_normalized_ssa_equals, Ssa}; + use crate::ssa::{Ssa, opt::assert_normalized_ssa_equals}; #[test] fn simplify_derive_generators_has_correct_type() { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs index fac2e8b4d5a1..e2aabc62fa61 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs @@ -1,6 +1,7 @@ use std::sync::Arc; -use acvm::{acir::AcirField, BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement}; +use acvm::blackbox_solver::sha256_compression; +use acvm::{BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement, acir::AcirField}; use crate::ssa::ir::call_stack::CallStackId; use crate::ssa::ir::instruction::BlackBoxFunc; @@ -233,6 +234,55 @@ pub(super) fn simplify_poseidon2_permutation( } } +pub(super) fn simplify_sha256_compression( + dfg: &mut DataFlowGraph, + arguments: &[ValueId], + block: BasicBlockId, + call_stack: CallStackId, +) -> SimplifyResult { + match (dfg.get_array_constant(arguments[0]), dfg.get_array_constant(arguments[1])) { + (Some((state, _)), Some((msg_blocks, _))) + if array_is_constant(dfg, &state) && array_is_constant(dfg, &msg_blocks) => + { + let state: Option> = state + .iter() + .map(|id| { + dfg.get_numeric_constant(*id) + .expect("value id from array should point at constant") + .try_to_u32() + }) + .collect(); + + let Some(mut state) = state.and_then(|vec| <[u32; 8]>::try_from(vec).ok()) else { + return SimplifyResult::None; + }; + + let msg_blocks: Option> = msg_blocks + .iter() + .map(|id| { + dfg.get_numeric_constant(*id) + .expect("value id from array should point at constant") + .try_to_u32() + }) + .collect(); + + let Some(msg_blocks) = msg_blocks.and_then(|vec| <[u32; 16]>::try_from(vec).ok()) + else { + return SimplifyResult::None; + }; + + sha256_compression(&mut state, &msg_blocks); + + let new_state = state.into_iter().map(FieldElement::from); + let typ = NumericType::Unsigned { bit_size: 32 }; + let result_array = make_constant_array(dfg, new_state, typ, block, call_stack); + + SimplifyResult::SimplifiedTo(result_array) + } + _ => SimplifyResult::None, + } +} + pub(super) fn simplify_hash( dfg: &mut DataFlowGraph, arguments: &[ValueId], @@ -308,9 +358,9 @@ pub(super) fn simplify_signature( #[cfg(feature = "bn254")] #[cfg(test)] -mod test { - use crate::ssa::opt::assert_normalized_ssa_equals; +mod multi_scalar_mul { use crate::ssa::Ssa; + use crate::ssa::opt::assert_normalized_ssa_equals; #[cfg(feature = "bn254")] #[test] @@ -395,3 +445,32 @@ mod test { assert_normalized_ssa_equals(ssa, expected_src); } } + +#[cfg(test)] +mod sha256_compression { + use crate::ssa::Ssa; + use crate::ssa::opt::assert_normalized_ssa_equals; + + #[test] + fn is_optimized_out_with_constant_arguments() { + let src = r#" + acir(inline) fn main f0 { + b0(): + v0 = make_array [u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0] : [u32; 8] + v1 = make_array [u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0] : [u32; 16] + v2 = call sha256_compression(v0, v1) -> [u32; 8] + return v2 + }"#; + let ssa = Ssa::from_str_simplifying(src).unwrap(); + let expected_src = r#" + acir(inline) fn main f0 { + b0(): + v1 = make_array [u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0] : [u32; 8] + v2 = make_array [u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0] : [u32; 16] + v11 = make_array [u32 2091193876, u32 1113340840, u32 3461668143, u32 3254913767, u32 3068490961, u32 2551409935, u32 2927503052, u32 3205228454] : [u32; 8] + return v11 + } + "#; + assert_normalized_ssa_equals(ssa, expected_src); + } +} diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs index ee2ab43aa5d5..189fd20ca531 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs @@ -1,4 +1,4 @@ -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use num_bigint::BigUint; use super::{DataFlowGraph, Instruction, NumericType, SimplifyResult, Type, Value, ValueId}; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs index 8b3b897088a2..48587cb4b7bc 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs @@ -1,4 +1,4 @@ -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use crate::ssa::ir::types::NumericType; @@ -204,4 +204,26 @@ mod tests { "; assert_normalized_ssa_equals(ssa, expected); } + + #[test] + fn simplifies_out_noop_bitwise_ands() { + // Regression test for https://github.com/noir-lang/noir/issues/7451 + let src = " + acir(inline) predicate_pure fn main f0 { + b0(v0: u8): + v1 = and u8 255, v0 + return v1 + } + "; + + let ssa = Ssa::from_str_simplifying(src).unwrap(); + + let expected = " + acir(inline) fn main f0 { + b0(v0: u8): + return v0 + } + "; + assert_normalized_ssa_equals(ssa, expected); + } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/printer.rs index 06f2ecd70eaf..0fac2242abd7 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -6,8 +6,8 @@ use im::Vector; use iter_extended::vecmap; use crate::ssa::{ - ir::types::{NumericType, Type}, Ssa, + ir::types::{NumericType, Type}, }; use super::{ @@ -344,11 +344,7 @@ pub(crate) fn try_to_extract_string_from_error_payload( values: &[ValueId], dfg: &DataFlowGraph, ) -> Option { - if is_string_type && values.len() == 1 { - dfg.get_string(values[0]) - } else { - None - } + if is_string_type && values.len() == 1 { dfg.get_string(values[0]) } else { None } } fn display_constrain_error( diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/types.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/types.rs index 0dd7fd92ee53..ca6242d51c21 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/types.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/types.rs @@ -1,7 +1,8 @@ +use noirc_frontend::signed_field::SignedField; use serde::{Deserialize, Serialize}; use std::sync::Arc; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use iter_extended::vecmap; use crate::ssa::ssa_gen::SSA_WORD_SIZE; @@ -58,28 +59,20 @@ impl NumericType { /// Returns None if the given Field value is within the numeric limits /// for the current NumericType. Otherwise returns a string describing /// the limits, as a range. - pub(crate) fn value_is_outside_limits( - self, - field: FieldElement, - negative: bool, - ) -> Option { + pub(crate) fn value_is_outside_limits(self, value: SignedField) -> Option { match self { NumericType::Unsigned { bit_size } => { - let max = 2u128.pow(bit_size) - 1; - if negative { + let max = if bit_size == 128 { u128::MAX } else { 2u128.pow(bit_size) - 1 }; + if value.is_negative { return Some(format!("0..={}", max)); } - if field <= max.into() { - None - } else { - Some(format!("0..={}", max)) - } + if value.field <= max.into() { None } else { Some(format!("0..={}", max)) } } NumericType::Signed { bit_size } => { let min = 2u128.pow(bit_size - 1); let max = 2u128.pow(bit_size - 1) - 1; - let target_max = if negative { min } else { max }; - if field <= target_max.into() { + let target_max = if value.is_negative { min } else { max }; + if value.field <= target_max.into() { None } else { Some(format!("-{}..={}", min, max)) @@ -307,19 +300,19 @@ mod tests { #[test] fn test_u8_value_is_outside_limits() { let u8 = NumericType::Unsigned { bit_size: 8 }; - assert!(u8.value_is_outside_limits(FieldElement::from(1_i128), true).is_some()); - assert!(u8.value_is_outside_limits(FieldElement::from(0_i128), false).is_none()); - assert!(u8.value_is_outside_limits(FieldElement::from(255_i128), false).is_none()); - assert!(u8.value_is_outside_limits(FieldElement::from(256_i128), false).is_some()); + assert!(u8.value_is_outside_limits(SignedField::negative(1_i128)).is_some()); + assert!(u8.value_is_outside_limits(SignedField::positive(0_i128)).is_none()); + assert!(u8.value_is_outside_limits(SignedField::positive(255_i128)).is_none()); + assert!(u8.value_is_outside_limits(SignedField::positive(256_i128)).is_some()); } #[test] fn test_i8_value_is_outside_limits() { let i8 = NumericType::Signed { bit_size: 8 }; - assert!(i8.value_is_outside_limits(FieldElement::from(129_i128), true).is_some()); - assert!(i8.value_is_outside_limits(FieldElement::from(128_i128), true).is_none()); - assert!(i8.value_is_outside_limits(FieldElement::from(0_i128), false).is_none()); - assert!(i8.value_is_outside_limits(FieldElement::from(127_i128), false).is_none()); - assert!(i8.value_is_outside_limits(FieldElement::from(128_i128), false).is_some()); + assert!(i8.value_is_outside_limits(SignedField::negative(129_i128)).is_some()); + assert!(i8.value_is_outside_limits(SignedField::negative(128_i128)).is_none()); + assert!(i8.value_is_outside_limits(SignedField::positive(0_i128)).is_none()); + assert!(i8.value_is_outside_limits(SignedField::positive(127_i128)).is_none()); + assert!(i8.value_is_outside_limits(SignedField::positive(128_i128)).is_some()); } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/array_set.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/array_set.rs index 05ceafcf4509..7bef8e7b1ae2 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/array_set.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/array_set.rs @@ -39,7 +39,11 @@ impl Function { let reachable_blocks = self.reachable_blocks(); if !self.runtime().is_entry_point() { - assert_eq!(reachable_blocks.len(), 1, "Expected there to be 1 block remaining in Acir function for array_set optimization"); + assert_eq!( + reachable_blocks.len(), + 1, + "Expected there to be 1 block remaining in Acir function for array_set optimization" + ); } let mut context = Context::new(&self.dfg); @@ -187,7 +191,7 @@ fn make_mutable( #[cfg(test)] mod tests { - use crate::ssa::{opt::assert_normalized_ssa_equals, Ssa}; + use crate::ssa::{Ssa, opt::assert_normalized_ssa_equals}; #[test] fn array_set_in_loop_with_conditional_clone() { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/basic_conditional.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/basic_conditional.rs new file mode 100644 index 000000000000..cbc36787a254 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/basic_conditional.rs @@ -0,0 +1,526 @@ +use std::collections::HashSet; + +use acvm::AcirField; +use fxhash::FxHashMap as HashMap; +use iter_extended::vecmap; + +use crate::ssa::{ + Ssa, + ir::{ + basic_block::BasicBlockId, + cfg::ControlFlowGraph, + dfg::DataFlowGraph, + function::{Function, FunctionId}, + function_inserter::FunctionInserter, + instruction::{BinaryOp, Instruction, TerminatorInstruction}, + post_order::PostOrder, + value::ValueId, + }, +}; + +use super::flatten_cfg::Context; +#[derive(Debug, Clone)] +struct BasicConditional { + block_entry: BasicBlockId, + block_then: Option, + block_else: Option, + block_exit: BasicBlockId, +} + +impl Ssa { + #[tracing::instrument(level = "trace", skip(self))] + /// This pass flatten simple IF-THEN-ELSE statements + /// This optimization pass identifies simple conditional control flow patterns in unconstrained code + /// and flattens them to reduce the number of basic blocks and improve performance. + /// + /// e.g: if c {a} else {b} would be flattened to c*(a-b)+b + /// A simple conditional pattern is defined as an IF-THEN (with optional ELSE) statement, with no nested conditional nor loop statements + /// Performance improvement is based on a simple execution cost metric + pub(crate) fn flatten_basic_conditionals(mut self) -> Ssa { + // Retrieve the 'no_predicates' attribute of the functions in a map, to avoid problems with borrowing + let mut no_predicates = HashMap::default(); + for function in self.functions.values() { + no_predicates.insert(function.id(), function.is_no_predicates()); + } + for function in self.functions.values_mut() { + flatten_function(function, &mut no_predicates); + } + self + } +} + +/// Returns the blocks of the simple conditional sub-graph whose input block is the entry. +/// Returns None if the input block is not the entry block of a simple conditional. +fn is_conditional( + block: BasicBlockId, + cfg: &ControlFlowGraph, + function: &Function, +) -> Option { + // jump overhead is the cost for doing the conditional and jump around the blocks + // We use 10 as a rough estimate, the real cost is less. + let jump_overhead = 10; + let mut successors = cfg.successors(block); + let mut result = None; + // a conditional must have 2 branches + if successors.len() != 2 { + return None; + } + let left = successors.next().unwrap(); + let right = successors.next().unwrap(); + let mut left_successors = cfg.successors(left); + let mut right_successors = cfg.successors(right); + let left_successors_len = left_successors.len(); + let right_successors_len = right_successors.len(); + let next_left = left_successors.next(); + let next_right = right_successors.next(); + if next_left == Some(block) || next_right == Some(block) { + // this is a loop, not a conditional + return None; + } + if left_successors_len == 1 && right_successors_len == 1 && next_left == next_right { + // The branches join on one block so it is a non-nested conditional + let cost_left = block_cost(left, &function.dfg); + let cost_right = block_cost(right, &function.dfg); + // For the flattening to be valuable, we compare the cost of the flattened code with the average cost of the 2 branches, + // including an overhead to take into account the jumps between the blocks. + let cost = cost_right.saturating_add(cost_left); + if cost < cost / 2 + jump_overhead { + if let Some(TerminatorInstruction::JmpIf { + condition: _, + then_destination, + else_destination, + call_stack: _, + }) = function.dfg[block].terminator() + { + result = Some(BasicConditional { + block_entry: block, + block_then: Some(*then_destination), + block_else: Some(*else_destination), + block_exit: next_left.unwrap(), + }); + } + } + } else if left_successors_len == 1 && next_left == Some(right) { + // Left branch joins the right branch, e.g if/then statement with no else + // This case may not happen (i.e not generated), but it is safer to handle it (e.g in case it happens due to some optimizations) + let cost = block_cost(left, &function.dfg); + if cost < cost / 2 + jump_overhead { + if let Some(TerminatorInstruction::JmpIf { + condition: _, + then_destination, + else_destination, + call_stack: _, + }) = function.dfg[block].terminator() + { + let (block_then, block_else) = if left == *then_destination { + (Some(left), None) + } else if left == *else_destination { + (None, Some(left)) + } else { + return None; + }; + + result = Some(BasicConditional { + block_entry: block, + block_then, + block_else, + block_exit: right, + }); + } + } + } else if right_successors_len == 1 && next_right == Some(left) { + // Right branch joins the left branch, e.g if/else statement with no then + // This case may not happen (i.e not generated), but it is safer to handle it (e.g in case it happens due to some optimizations) + let cost = block_cost(right, &function.dfg); + if cost < cost / 2 + jump_overhead { + if let Some(TerminatorInstruction::JmpIf { + condition: _, + then_destination, + else_destination, + call_stack: _, + }) = function.dfg[block].terminator() + { + let (block_then, block_else) = if right == *then_destination { + (Some(right), None) + } else if right == *else_destination { + (None, Some(right)) + } else { + return None; + }; + result = Some(BasicConditional { + block_entry: block, + block_then, + block_else, + block_exit: right, + }); + } + } + } + // A conditional exit would have exactly 2 predecessors + result.filter(|result| cfg.predecessors(result.block_exit).len() == 2) +} + +/// Computes a cost estimate of a basic block +/// returns u32::MAX if the block has side-effect instructions +/// WARNING: these are estimates of the runtime cost of each instruction, +/// 1 being the cost of the simplest instruction. These numbers can be improved. +fn block_cost(block: BasicBlockId, dfg: &DataFlowGraph) -> u32 { + let mut cost: u32 = 0; + for instruction in dfg[block].instructions() { + let instruction_cost = match &dfg[*instruction] { + Instruction::Binary(binary) => { + match binary.operator { + BinaryOp::Add { unchecked } + | BinaryOp::Sub { unchecked } + | BinaryOp::Mul { unchecked } => if unchecked { 3 } else { return u32::MAX }, + BinaryOp::Div + | BinaryOp::Mod => return u32::MAX, + BinaryOp::Eq => 1, + BinaryOp::Lt => 5, + BinaryOp::And + | BinaryOp::Or + | BinaryOp::Xor => 1, + BinaryOp::Shl + | BinaryOp::Shr => return u32::MAX, + } + }, + // A Cast can be either simplified, or lead to a truncate + Instruction::Cast(_, _) => 3, + Instruction::Not(_) => 1, + Instruction::Truncate { .. } => 7, + + Instruction::Constrain(_,_,_) + | Instruction::ConstrainNotEqual(_,_,_) + | Instruction::RangeCheck { .. } + // Calls with no-predicate set to true could be supported, but + // they are likely to be too costly anyways. Simple calls would + // have been inlined already. + | Instruction::Call { .. } + | Instruction::Load { .. } + | Instruction::Store { .. } + | Instruction::ArraySet { .. } => return u32::MAX, + + Instruction::ArrayGet { array, index } => { + // A get can fail because of out-of-bound index + let mut in_bound = false; + // check if index is in bound + if let (Some(index), Some(len)) = (dfg.get_numeric_constant(*index), dfg.try_get_array_length(*array)) { + // The index is in-bounds + if index.to_u128() < len as u128 { + in_bound = true; + } + } + if !in_bound { + return u32::MAX; + } + 1 + }, + // if less than 10 elements, it is translated into a store for each element + // if more than 10, it is a loop, so 20 should be a good estimate, worst case being 10 stores and ~10 index increments + Instruction::MakeArray { .. } => 20, + + Instruction::Allocate + | Instruction::EnableSideEffectsIf { .. } + | Instruction::IncrementRc { .. } + | Instruction::DecrementRc { .. } + | Instruction::Noop => 0, + Instruction::IfElse { .. } => 1, + }; + cost += instruction_cost; + } + cost +} + +/// Identifies all simple conditionals in the function and flattens them +fn flatten_function(function: &mut Function, no_predicates: &mut HashMap) { + // This pass is dedicated to brillig functions + if !function.runtime().is_brillig() { + return; + } + let cfg = ControlFlowGraph::with_function(function); + let mut stack = vec![function.entry_block()]; + let mut processed = HashSet::new(); + let mut conditionals = Vec::new(); + + // 1. Process all blocks of the cfg, starting from the root and following the successors + while let Some(block) = stack.pop() { + // Avoid cycles + if processed.contains(&block) { + continue; + } + processed.insert(block); + + // Identify the simple conditionals + if let Some(conditional) = is_conditional(block, &cfg, function) { + // no need to check the branches, process the join block directly + stack.push(conditional.block_exit); + conditionals.push(conditional); + } else { + stack.extend(cfg.successors(block)); + } + } + + // 2. Flatten all simple conditionals + // process basic conditionals in reverse order so that + // a conditional does not impact the previous ones + conditionals.reverse(); + flatten_multiple(&conditionals, function, no_predicates); +} + +fn flatten_multiple( + conditionals: &Vec, + function: &mut Function, + no_predicates: &mut HashMap, +) { + // 1. process each basic conditional, using a new context per conditional + let post_order = PostOrder::with_function(function); + + let mut mapping = HashMap::default(); + for conditional in conditionals { + let cfg = ControlFlowGraph::with_function(function); + let cfg_root = function.entry_block(); + let mut branch_ends = HashMap::default(); + branch_ends.insert(conditional.block_entry, conditional.block_exit); + let mut context = Context::new(function, cfg, branch_ends, cfg_root); + context.flatten_single_conditional(conditional, no_predicates); + // extract the mapping into 'mapping + context.inserter.extract_mapping(&mut mapping); + } + // 2. re-map the full program for values that may been simplified. + if !mapping.is_empty() { + for block in post_order.as_slice() { + Context::map_block_with_mapping(mapping.clone(), function, *block); + } + } +} + +impl Context<'_> { + fn flatten_single_conditional( + &mut self, + conditional: &BasicConditional, + no_predicates: &mut HashMap, + ) { + // Manually inline 'then', 'else' and 'exit' into the entry block + //0. initialize the context for flattening a 'single conditional' + let old_target = self.target_block; + let old_no_predicate = self.no_predicate; + let mut queue = vec![]; + self.target_block = conditional.block_entry; + self.no_predicate = true; + //1. process 'then' branch + self.inline_block(conditional.block_entry, no_predicates); + let to_process = self.handle_terminator(conditional.block_entry, &queue); + queue.extend(to_process); + if let Some(then) = conditional.block_then { + assert_eq!(queue.pop(), conditional.block_then); + self.inline_block(then, no_predicates); + let to_process = self.handle_terminator(then, &queue); + + for incoming_block in to_process { + if !queue.contains(&incoming_block) { + queue.push(incoming_block); + } + } + } + + //2. process 'else' branch, in case there is no 'then' + let next = queue.pop(); + if next == conditional.block_else { + let next = next.unwrap(); + self.inline_block(next, no_predicates); + let _ = self.handle_terminator(next, &queue); + } else { + assert_eq!(next, Some(conditional.block_exit)); + } + + //3. process 'exit' block + self.inline_block(conditional.block_exit, no_predicates); + // Manually set the terminator of the entry block to the one of the exit block + let terminator = + self.inserter.function.dfg[conditional.block_exit].terminator().unwrap().clone(); + let new_terminator = match terminator { + TerminatorInstruction::JmpIf { + condition, + then_destination, + else_destination, + call_stack, + } => { + let condition = self.inserter.resolve(condition); + TerminatorInstruction::JmpIf { + condition, + then_destination, + else_destination, + call_stack, + } + } + TerminatorInstruction::Jmp { destination, arguments, call_stack } => { + let arguments = vecmap(arguments, |value| self.inserter.resolve(value)); + TerminatorInstruction::Jmp { destination, arguments, call_stack } + } + TerminatorInstruction::Return { return_values, call_stack } => { + let return_values = vecmap(return_values, |value| self.inserter.resolve(value)); + TerminatorInstruction::Return { return_values, call_stack } + } + }; + self.inserter.function.dfg.set_block_terminator(conditional.block_entry, new_terminator); + self.inserter.map_data_bus_in_place(); + //4. restore the context, in case it is re-used. + self.target_block = old_target; + self.no_predicate = old_no_predicate; + } + + fn map_block_with_mapping( + mapping: HashMap, + func: &mut Function, + block: BasicBlockId, + ) { + // Map all instructions in the block + let mut inserter = FunctionInserter::new(func); + inserter.set_mapping(mapping); + let instructions = inserter.function.dfg[block].instructions().to_vec(); + for instruction in instructions { + inserter.map_instruction_in_place(instruction); + } + inserter.map_terminator_in_place(block); + } +} + +#[cfg(test)] +mod test { + use crate::ssa::{Ssa, opt::assert_normalized_ssa_equals}; + + #[test] + fn basic_jmpif() { + let src = " + brillig(inline) fn foo f0 { + b0(v0: u32): + v3 = eq v0, u32 0 + jmpif v3 then: b2, else: b1 + b1(): + jmp b3(u32 5) + b2(): + jmp b3(u32 3) + b3(v1: u32): + return v1 + } + "; + let ssa = Ssa::from_str(src).unwrap(); + assert_eq!(ssa.main().reachable_blocks().len(), 4); + + let expected = " + brillig(inline) fn foo f0 { + b0(v0: u32): + v2 = eq v0, u32 0 + v3 = not v2 + v4 = cast v2 as u32 + v5 = cast v3 as u32 + v7 = unchecked_mul v4, u32 3 + v9 = unchecked_mul v5, u32 5 + v10 = unchecked_add v7, v9 + return v10 + } + "; + + let ssa = ssa.flatten_basic_conditionals(); + assert_normalized_ssa_equals(ssa, expected); + } + + #[test] + fn array_jmpif() { + let src = r#" + brillig(inline) fn foo f0 { + b0(v0: u32): + v3 = eq v0, u32 5 + jmpif v3 then: b2, else: b1 + b1(): + v6 = make_array b"foo" + jmp b3(v6) + b2(): + v10 = make_array b"bar" + jmp b3(v10) + b3(v1: [u8; 3]): + return v1 + } + "#; + let ssa = Ssa::from_str(src).unwrap(); + assert_eq!(ssa.main().reachable_blocks().len(), 4); + let ssa = ssa.flatten_basic_conditionals(); + // make_array is not simplified + assert_normalized_ssa_equals(ssa, src); + } + + #[test] + fn nested_jmpifs() { + let src = " + brillig(inline) fn foo f0 { + b0(v0: u32): + v5 = eq v0, u32 5 + v6 = not v5 + jmpif v5 then: b5, else: b1 + b1(): + v8 = lt v0, u32 3 + jmpif v8 then: b3, else: b2 + b2(): + v9 = truncate v0 to 2 bits, max_bit_size: 32 + jmp b4(v9) + b3(): + v10 = truncate v0 to 1 bits, max_bit_size: 32 + jmp b4(v10) + b4(v1: u32): + jmp b9(v1) + b5(): + v12 = lt u32 2, v0 + jmpif v12 then: b7, else: b6 + b6(): + v13 = truncate v0 to 3 bits, max_bit_size: 32 + jmp b8(v13) + b7(): + v14 = and v0, u32 2 + jmp b8(v14) + b8(v2: u32): + jmp b9(v2) + b9(v3: u32): + return v3 + } + "; + let ssa = Ssa::from_str(src).unwrap(); + assert_eq!(ssa.main().reachable_blocks().len(), 10); + + let expected = " + brillig(inline) fn foo f0 { + b0(v0: u32): + v3 = eq v0, u32 5 + v4 = not v3 + jmpif v3 then: b2, else: b1 + b1(): + v6 = lt v0, u32 3 + v7 = truncate v0 to 1 bits, max_bit_size: 32 + v8 = not v6 + v9 = truncate v0 to 2 bits, max_bit_size: 32 + v10 = cast v6 as u32 + v11 = cast v8 as u32 + v12 = unchecked_mul v10, v7 + v13 = unchecked_mul v11, v9 + v14 = unchecked_add v12, v13 + jmp b3(v14) + b2(): + v16 = lt u32 2, v0 + v17 = and v0, u32 2 + v18 = not v16 + v19 = truncate v0 to 3 bits, max_bit_size: 32 + v20 = cast v16 as u32 + v21 = cast v18 as u32 + v22 = unchecked_mul v20, v17 + v23 = unchecked_mul v21, v19 + v24 = unchecked_add v22, v23 + jmp b3(v24) + b3(v1: u32): + return v1 + } + "; + + let ssa = ssa.flatten_basic_conditionals(); + assert_eq!(ssa.main().reachable_blocks().len(), 4); + assert_normalized_ssa_equals(ssa, expected); + } +} diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_array_gets.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_array_gets.rs new file mode 100644 index 000000000000..c5d485dd1613 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_array_gets.rs @@ -0,0 +1,168 @@ +//! In the Brillig runtime arrays are represented as [RC, ...items], +//! Certain operations such as array gets only utilize the items pointer. +//! Without handling the items pointer offset in SSA, it is left to Brillig generation +//! to offset the array pointer. +//! +//! Slices are represented as Brillig vectors, where the items pointer instead starts at three rather than one. +//! A Brillig vector is represented as [RC, Size, Capacity, ...items]. +//! +//! For array operations with constant indices adding an instruction to offset the pointer +//! is unnecessary as we already know the index. This pass looks for such array operations +//! with constant indices and replaces their index with the appropriate offset. + +use fxhash::FxHashMap as HashMap; + +use crate::{ + brillig::brillig_ir::BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, + ssa::{ + Ssa, + ir::{ + function::Function, + instruction::Instruction, + types::{NumericType, Type}, + }, + }, +}; + +impl Ssa { + #[tracing::instrument(level = "trace", skip(self))] + pub(crate) fn brillig_array_gets(mut self) -> Ssa { + let brillig_functions = + self.functions.values_mut().filter(|function| function.runtime().is_brillig()); + for function in brillig_functions { + function.brillig_array_gets(); + } + + self + } +} + +impl Function { + pub(super) fn brillig_array_gets(&mut self) { + let reachable_blocks = self.reachable_blocks(); + + let mut instructions_to_update = HashMap::default(); + for block_id in reachable_blocks.into_iter() { + for instruction_id in self.dfg[block_id].instructions() { + if let Instruction::ArrayGet { array, index } = self.dfg[*instruction_id] { + if self.dfg.is_constant(index) { + instructions_to_update.insert( + *instruction_id, + (Instruction::ArrayGet { array, index }, block_id), + ); + } + } + } + } + + for (instruction_id, _) in instructions_to_update { + let new_instruction = match self.dfg[instruction_id] { + Instruction::ArrayGet { array, index } => { + let index_constant = + self.dfg.get_numeric_constant(index).expect("ICE: Expected constant index"); + let offset = if matches!(self.dfg.type_of_value(array), Type::Array(..)) { + // Brillig arrays are [RC, ...items] + 1u128 + } else { + // Brillig vectors are [RC, Size, Capacity, ...items] + 3u128 + }; + let index = self.dfg.make_constant( + index_constant + offset.into(), + NumericType::unsigned(BRILLIG_MEMORY_ADDRESSING_BIT_SIZE), + ); + Instruction::ArrayGet { array, index } + } + _ => { + continue; + } + }; + self.dfg[instruction_id] = new_instruction; + } + } +} + +#[cfg(test)] +mod tests { + use crate::ssa::opt::assert_normalized_ssa_equals; + + use super::Ssa; + + #[test] + fn offset_array_get_constant_index() { + let src = " + brillig(inline) fn main f0 { + b0(v0: [Field; 3]): + v2 = array_get v0, index u32 0 -> Field + return v2 + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.brillig_array_gets(); + + let expected = " + brillig(inline) fn main f0 { + b0(v0: [Field; 3]): + v2 = array_get v0, index u32 1 -> Field + return v2 + } + "; + + assert_normalized_ssa_equals(ssa, expected); + } + + #[test] + fn do_not_offset_dynamic_array_get() { + let src = " + brillig(inline) fn main f0 { + b0(v0: [Field; 3], v1: u32): + v2 = array_get v0, index v1 -> Field + return v2 + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.brillig_array_gets(); + assert_normalized_ssa_equals(ssa, src); + } + + #[test] + fn do_not_offset_array_get_in_acir() { + let src = " + acir(inline) fn main f0 { + b0(v0: [Field; 3]): + v2 = array_get v0, index u32 0 -> Field + return v2 + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.brillig_array_gets(); + assert_normalized_ssa_equals(ssa, src); + } + + #[test] + fn offset_slice_array_get_constant_index() { + let src = " + brillig(inline) fn main f0 { + b0(v0: [Field]): + v2 = array_get v0, index u32 0 -> Field + return v2 + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.brillig_array_gets(); + + let expected = " + brillig(inline) fn main f0 { + b0(v0: [Field]): + v2 = array_get v0, index u32 3 -> Field + return v2 + } + "; + + assert_normalized_ssa_equals(ssa, expected); + } +} diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_entry_points.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_entry_points.rs index e03f14b2721f..380daabee6cf 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_entry_points.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_entry_points.rs @@ -64,12 +64,12 @@ use std::collections::{BTreeMap, BTreeSet}; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::ssa::{ + Ssa, ir::{ function::{Function, FunctionId}, instruction::Instruction, value::Value, }, - Ssa, }; use super::inlining::called_functions_vec; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/check_u128_mul_overflow.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/check_u128_mul_overflow.rs new file mode 100644 index 000000000000..ae0c0f1e1033 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/check_u128_mul_overflow.rs @@ -0,0 +1,285 @@ +use acvm::{AcirField, FieldElement}; + +use crate::ssa::{ + ir::{ + basic_block::BasicBlockId, + call_stack::CallStackId, + dfg::DataFlowGraph, + function::Function, + instruction::{Binary, BinaryOp, ConstrainError, Instruction}, + types::NumericType, + value::ValueId, + }, + ssa_gen::Ssa, +}; + +impl Ssa { + /// An SSA pass that checks that multiplying two u128 doesn't overflow because + /// both operands are greater or equal than 2^64. + /// If both are, then the result is surely greater or equal than 2^128 so it would overflow. + /// The operands can still overflow if just one of them is less than 2^64, but in that case the result + /// will be less than 2^192 so it fits in a Field value, and acir will check that it fits in a u128. + #[tracing::instrument(level = "trace", skip(self))] + pub(crate) fn check_u128_mul_overflow(mut self) -> Ssa { + for function in self.functions.values_mut() { + function.check_u128_mul_overflow(); + } + self + } +} + +impl Function { + pub(crate) fn check_u128_mul_overflow(&mut self) { + if !self.runtime().is_acir() { + return; + } + + for block in self.reachable_blocks() { + let instructions = self.dfg[block].take_instructions(); + + for instruction in instructions { + self.dfg[block].insert_instruction(instruction); + + let Instruction::Binary(Binary { + lhs, + rhs, + operator: BinaryOp::Mul { unchecked: false }, + }) = &self.dfg[instruction] + else { + continue; + }; + + let binary_type = self.dfg.type_of_value(*lhs).unwrap_numeric(); + let NumericType::Unsigned { bit_size: 128 } = binary_type else { + continue; + }; + + let call_stack = self.dfg.get_instruction_call_stack_id(instruction); + check_u128_mul_overflow(*lhs, *rhs, block, &mut self.dfg, call_stack); + } + } + } +} + +fn check_u128_mul_overflow( + lhs: ValueId, + rhs: ValueId, + block: BasicBlockId, + dfg: &mut DataFlowGraph, + call_stack: CallStackId, +) { + let lhs_value = dfg.get_numeric_constant(lhs); + let rhs_value = dfg.get_numeric_constant(rhs); + + let two_pow_64 = 1_u128 << 64; + + // If lhs is less than 2^64 then the condition trivially holds. + if let Some(value) = lhs_value { + if value.to_u128() < two_pow_64 { + return; + } + } + + // Same goes for rhs + if let Some(value) = rhs_value { + if value.to_u128() < two_pow_64 { + return; + } + } + + let u128 = NumericType::unsigned(128); + let two_pow_64 = dfg.make_constant(two_pow_64.into(), u128); + + let res = if lhs_value.is_some() && rhs_value.is_some() { + // If both values are known at compile time, at this point we know it overflows + dfg.make_constant(FieldElement::one(), u128) + } else if lhs_value.is_some() { + // If only the left-hand side is known we just need to check that the right-hand side + // isn't greater than 2^64 + let instruction = + Instruction::Binary(Binary { lhs: rhs, rhs: two_pow_64, operator: BinaryOp::Div }); + dfg.insert_instruction_and_results(instruction, block, None, call_stack).first() + } else if rhs_value.is_some() { + // Same goes for the other side + let instruction = + Instruction::Binary(Binary { lhs, rhs: two_pow_64, operator: BinaryOp::Div }); + dfg.insert_instruction_and_results(instruction, block, None, call_stack).first() + } else { + // Check both sides + let instruction = + Instruction::Binary(Binary { lhs, rhs: two_pow_64, operator: BinaryOp::Div }); + let divided_lhs = + dfg.insert_instruction_and_results(instruction, block, None, call_stack).first(); + + let instruction = + Instruction::Binary(Binary { lhs: rhs, rhs: two_pow_64, operator: BinaryOp::Div }); + let divided_rhs = + dfg.insert_instruction_and_results(instruction, block, None, call_stack).first(); + + // Unchecked as operands are restricted to be less than 2^64 so multiplying them cannot overflow. + let mul = BinaryOp::Mul { unchecked: true }; + let instruction = + Instruction::Binary(Binary { lhs: divided_lhs, rhs: divided_rhs, operator: mul }); + dfg.insert_instruction_and_results(instruction, block, None, call_stack).first() + }; + + let zero = dfg.make_constant(FieldElement::zero(), u128); + let instruction = Instruction::Constrain( + res, + zero, + Some(ConstrainError::StaticString("attempt to multiply with overflow".to_string())), + ); + dfg.insert_instruction_and_results(instruction, block, None, call_stack); +} + +#[cfg(test)] +mod tests { + use crate::ssa::{opt::assert_normalized_ssa_equals, ssa_gen::Ssa}; + + #[test] + fn does_not_insert_check_if_lhs_is_less_than_two_pow_64() { + let src = " + acir(inline) fn main f0 { + b0(v0: u128): + v2 = mul u128 18446744073709551615, v0 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, src); + } + + #[test] + fn does_not_insert_check_if_rhs_is_less_than_two_pow_64() { + let src = " + acir(inline) fn main f0 { + b0(v0: u128): + v2 = mul v0, u128 18446744073709551615 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, src); + } + + #[test] + fn inserts_check_for_lhs() { + let src = " + acir(inline) fn main f0 { + b0(v0: u128): + v2 = mul v0, u128 18446744073709551617 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let expected = r#" + acir(inline) fn main f0 { + b0(v0: u128): + v2 = mul v0, u128 18446744073709551617 + v4 = div v0, u128 18446744073709551616 + constrain v4 == u128 0, "attempt to multiply with overflow" + return + } + "#; + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, expected); + } + + #[test] + fn inserts_check_for_rhs() { + let src = " + acir(inline) fn main f0 { + b0(v0: u128): + v2 = mul u128 18446744073709551617, v0 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let expected = r#" + acir(inline) fn main f0 { + b0(v0: u128): + v2 = mul u128 18446744073709551617, v0 + v4 = div v0, u128 18446744073709551616 + constrain v4 == u128 0, "attempt to multiply with overflow" + return + } + "#; + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, expected); + } + + #[test] + fn inserts_check_for_both_operands() { + let src = " + acir(inline) fn main f0 { + b0(v0: u128, v1: u128): + v2 = mul v0, v1 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let expected = r#" + acir(inline) fn main f0 { + b0(v0: u128, v1: u128): + v2 = mul v0, v1 + v4 = div v0, u128 18446744073709551616 + v5 = div v1, u128 18446744073709551616 + v6 = unchecked_mul v4, v5 + constrain v6 == u128 0, "attempt to multiply with overflow" + return + } + "#; + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, expected); + } + + #[test] + fn inserts_assertion_failure_if_overflow_is_guaranteed() { + let src = " + acir(inline) fn main f0 { + b0(): + v2 = mul u128 18446744073709551617, u128 18446744073709551616 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + // The multiplication remains, but it will be later removed by DIE + let expected = r#" + acir(inline) fn main f0 { + b0(): + v2 = mul u128 18446744073709551617, u128 18446744073709551616 + constrain u128 1 == u128 0, "attempt to multiply with overflow" + return + } + "#; + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, expected); + } + + #[test] + fn does_nothing_for_brillig() { + let src = " + brillig(inline) fn main f0 { + b0(): + v2 = mul u128 18446744073709551617, u128 18446744073709551616 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, src); + } +} diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index 05bd48b88306..3bde1d7530f0 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -22,9 +22,9 @@ use std::collections::{BTreeMap, HashSet, VecDeque}; use acvm::{ - acir::AcirField, - brillig_vm::{MemoryValue, VMStatus, VM}, FieldElement, + acir::AcirField, + brillig_vm::{MemoryValue, VM, VMStatus}, }; use bn254_blackbox_solver::Bn254BlackBoxSolver; use im::Vector; @@ -32,9 +32,9 @@ use iter_extended::vecmap; use crate::{ brillig::{ + Brillig, BrilligOptions, brillig_gen::gen_brillig_for, brillig_ir::{artifact::BrilligParameter, brillig_variable::get_bit_size_from_ssa_type}, - Brillig, BrilligOptions, }, ssa::{ ir::{ @@ -95,6 +95,11 @@ impl Ssa { let brillig_info = Some(BrilligInfo { brillig, brillig_functions: &brillig_functions }); for function in self.functions.values_mut() { + // We have already performed our final Brillig generation, so constant folding + // Brillig functions is unnecessary work. + if function.dfg.runtime().is_brillig() { + continue; + } function.constant_fold(false, brillig_info); } @@ -808,6 +813,7 @@ mod test { use crate::{ brillig::BrilligOptions, ssa::{ + Ssa, function_builder::FunctionBuilder, ir::{ function::RuntimeType, @@ -815,7 +821,6 @@ mod test { types::{NumericType, Type}, }, opt::assert_normalized_ssa_equals, - Ssa, }, }; @@ -1446,6 +1451,8 @@ mod test { } "; let ssa = Ssa::from_str(src).unwrap(); + // Need to run SSA pass that sets up Brillig array gets + let ssa = ssa.brillig_array_gets(); let brillig = ssa.to_brillig(&BrilligOptions::default()); let expected = " 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 d23cfee8a14f..e742ad4aa5d8 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 @@ -17,7 +17,7 @@ use crate::ssa::{ ssa_gen::Ssa, }; -use super::rc::{pop_rc_for, RcInstruction}; +use super::rc::{RcInstruction, pop_rc_for}; impl Ssa { /// Performs Dead Instruction Elimination (DIE) to remove any instructions with @@ -26,20 +26,22 @@ impl Ssa { /// This step should come after the flattening of the CFG and mem2reg. #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn dead_instruction_elimination(self) -> Ssa { - self.dead_instruction_elimination_inner(true) + self.dead_instruction_elimination_inner(true, false) } - fn dead_instruction_elimination_inner(mut self, flattened: bool) -> Ssa { + /// Post the Brillig generation we do not need to run this pass on Brillig functions. + #[tracing::instrument(level = "trace", skip(self))] + pub(crate) fn dead_instruction_elimination_acir(self) -> Ssa { + self.dead_instruction_elimination_inner(true, true) + } + + fn dead_instruction_elimination_inner(mut self, flattened: bool, skip_brillig: bool) -> Ssa { let mut used_globals_map: HashMap<_, _> = self .functions .par_iter_mut() .filter_map(|(id, func)| { - let set = func.dead_instruction_elimination(true, flattened); - if func.runtime().is_brillig() { - Some((*id, set)) - } else { - None - } + let set = func.dead_instruction_elimination(true, flattened, skip_brillig); + if func.runtime().is_brillig() { Some((*id, set)) } else { None } }) .collect(); @@ -79,7 +81,12 @@ impl Function { &mut self, insert_out_of_bounds_checks: bool, flattened: bool, + skip_brillig: bool, ) -> HashSet { + if skip_brillig && self.dfg.runtime().is_brillig() { + return HashSet::default(); + } + let mut context = Context { flattened, ..Default::default() }; context.mark_function_parameter_arrays_as_used(self); @@ -103,7 +110,7 @@ impl Function { // instructions (we don't want to remove those checks, or instructions that are // dependencies of those checks) if inserted_out_of_bounds_checks { - return self.dead_instruction_elimination(false, flattened); + return self.dead_instruction_elimination(false, flattened, skip_brillig); } context.remove_rc_instructions(&mut self.dfg); @@ -128,9 +135,8 @@ struct Context { /// them just yet. flattened: bool, - // When tracking mutations we consider arrays with the same type as all being possibly mutated. - // This we consider to span all blocks of the functions. - mutated_array_types: HashSet, + /// Track IncrementRc instructions per block to determine whether they are useless. + rc_tracker: RcTracker, } impl Context { @@ -160,10 +166,8 @@ impl Context { let block = &function.dfg[block_id]; self.mark_terminator_values_as_used(function, block); - // Lend the shared array type to the tracker. - let mut mutated_array_types = std::mem::take(&mut self.mutated_array_types); - let mut rc_tracker = RcTracker::new(&mut mutated_array_types); - rc_tracker.mark_terminator_arrays_as_used(function, block); + self.rc_tracker.new_block(); + self.rc_tracker.mark_terminator_arrays_as_used(function, block); let instructions_len = block.instructions().len(); @@ -196,12 +200,11 @@ impl Context { } } - rc_tracker.track_inc_rcs_to_remove(*instruction_id, function); + self.rc_tracker.track_inc_rcs_to_remove(*instruction_id, function); } - self.instructions_to_remove.extend(rc_tracker.get_non_mutated_arrays(&function.dfg)); - self.instructions_to_remove.extend(rc_tracker.rc_pairs_to_remove); - + self.instructions_to_remove.extend(self.rc_tracker.get_non_mutated_arrays(&function.dfg)); + self.instructions_to_remove.extend(self.rc_tracker.rc_pairs_to_remove.drain()); // If there are some instructions that might trigger an out of bounds error, // first add constrain checks. Then run the DIE pass again, which will remove those // but leave the constrains (any any value needed by those constrains) @@ -221,9 +224,6 @@ impl Context { .instructions_mut() .retain(|instruction| !self.instructions_to_remove.contains(instruction)); - // Take the mutated array back. - self.mutated_array_types = mutated_array_types; - false } @@ -272,11 +272,15 @@ impl Context { let typ = typ.get_contained_array(); // Want to store the array type which is being referenced, // because it's the underlying array that the `inc_rc` is associated with. - self.mutated_array_types.insert(typ.clone()); + self.add_mutated_array_type(typ.clone()); } } } + fn add_mutated_array_type(&mut self, typ: Type) { + self.rc_tracker.mutated_array_types.insert(typ.get_contained_array().clone()); + } + /// Go through the RC instructions collected when we figured out which values were unused; /// for each RC that refers to an unused value, remove the RC as well. fn remove_rc_instructions(&self, dfg: &mut DataFlowGraph) { @@ -608,8 +612,9 @@ fn apply_side_effects( (lhs, rhs) } +#[derive(Default)] /// Per block RC tracker. -struct RcTracker<'a> { +struct RcTracker { // We can track IncrementRc instructions per block to determine whether they are useless. // IncrementRc and DecrementRc instructions are normally side effectual instructions, but we remove // them if their value is not used anywhere in the function. However, even when their value is used, their existence @@ -624,7 +629,8 @@ struct RcTracker<'a> { // If an array is the same type as one of those non-mutated array types, we can safely remove all IncrementRc instructions on that array. inc_rcs: HashMap>, // Mutated arrays shared across the blocks of the function. - mutated_array_types: &'a mut HashSet, + // When tracking mutations we consider arrays with the same type as all being possibly mutated. + mutated_array_types: HashSet, // The SSA often creates patterns where after simplifications we end up with repeat // IncrementRc instructions on the same value. We track whether the previous instruction was an IncrementRc, // and if the current instruction is also an IncrementRc on the same value we remove the current instruction. @@ -632,15 +638,12 @@ struct RcTracker<'a> { previous_inc_rc: Option, } -impl<'a> RcTracker<'a> { - fn new(mutated_array_types: &'a mut HashSet) -> Self { - Self { - rcs_with_possible_pairs: Default::default(), - rc_pairs_to_remove: Default::default(), - inc_rcs: Default::default(), - previous_inc_rc: Default::default(), - mutated_array_types, - } +impl RcTracker { + fn new_block(&mut self) { + self.rcs_with_possible_pairs.clear(); + self.rc_pairs_to_remove.clear(); + self.inc_rcs.clear(); + self.previous_inc_rc = Default::default(); } fn mark_terminator_arrays_as_used(&mut self, function: &Function, block: &BasicBlock) { @@ -751,6 +754,7 @@ mod test { use noirc_frontend::monomorphization::ast::InlineType; use crate::ssa::{ + Ssa, function_builder::FunctionBuilder, ir::{ function::RuntimeType, @@ -758,7 +762,6 @@ mod test { types::{NumericType, Type}, }, opt::assert_normalized_ssa_equals, - Ssa, }; #[test] @@ -1099,7 +1102,7 @@ mod test { let ssa = Ssa::from_str(src).unwrap(); // Even though these ACIR functions only have 1 block, we have not inlined and flattened anything yet. - let ssa = ssa.dead_instruction_elimination_inner(false); + let ssa = ssa.dead_instruction_elimination_inner(false, false); let expected = " acir(inline) fn main f0 { @@ -1121,4 +1124,38 @@ mod test { "; assert_normalized_ssa_equals(ssa, expected); } + + #[test] + fn do_not_remove_inc_rc_if_mutated_in_other_block() { + let src = " + brillig(inline) fn main f0 { + b0(v0: &mut [Field; 3]): + v1 = load v0 -> [Field; 3] + inc_rc v1 + jmp b1() + b1(): + v2 = load v0 -> [Field; 3] + v3 = array_set v2, index u32 0, value u32 0 + store v3 at v0 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let expected = " + brillig(inline) fn main f0 { + b0(v0: &mut [Field; 3]): + v1 = load v0 -> [Field; 3] + inc_rc v1 + jmp b1() + b1(): + v2 = load v0 -> [Field; 3] + v4 = array_set v2, index u32 0, value u32 0 + store v4 at v0 + return + } + "; + let ssa = ssa.dead_instruction_elimination(); + assert_normalized_ssa_equals(ssa, expected); + } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 76f8495c0094..a25e3db2b080 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -133,7 +133,7 @@ //! store v12 at v5 (new store) use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; -use acvm::{acir::AcirField, acir::BlackBoxFunc, FieldElement}; +use acvm::{FieldElement, acir::AcirField, acir::BlackBoxFunc}; use iter_extended::vecmap; use crate::ssa::{ @@ -176,12 +176,15 @@ impl Ssa { } } -struct Context<'f> { - inserter: FunctionInserter<'f>, +pub(crate) struct Context<'f> { + pub(crate) inserter: FunctionInserter<'f>, /// This ControlFlowGraph is the graph from before the function was modified by this flattening pass. cfg: ControlFlowGraph, + /// Target block of the flattening + pub(crate) target_block: BasicBlockId, + /// Maps start of branch -> end of branch branch_ends: HashMap, @@ -213,6 +216,10 @@ struct Context<'f> { /// us from unnecessarily inserting extra instructions, and keeps ids unique which /// helps simplifications. not_instructions: HashMap, + + /// Flag to tell the context to not issue 'enable_side_effect' instructions during flattening. + /// This should be set to true only by flatten_single(), when no instruction is known to fail. + pub(crate) no_predicate: bool, } #[derive(Clone)] @@ -249,6 +256,7 @@ fn flatten_function_cfg(function: &mut Function, no_predicates: &HashMap Context<'f> { - fn flatten(&mut self, no_predicates: &HashMap) { + //impl Context<'_> { + pub(crate) fn new( + function: &'f mut Function, + cfg: ControlFlowGraph, + branch_ends: HashMap, + target_block: BasicBlockId, + ) -> Self { + Context { + inserter: FunctionInserter::new(function), + cfg, + branch_ends, + condition_stack: Vec::new(), + arguments_stack: Vec::new(), + local_allocations: HashSet::default(), + not_instructions: HashMap::default(), + target_block, + no_predicate: false, + } + } + + pub(crate) fn flatten(&mut self, no_predicates: &HashMap) { // Flatten the CFG by inlining all instructions from the queued blocks // until all blocks have been flattened. // We follow the terminator of each block to determine which blocks to // process next - let mut queue = vec![self.inserter.function.entry_block()]; + let mut queue = vec![self.target_block]; while let Some(block) = queue.pop() { self.inline_block(block, no_predicates); let to_process = self.handle_terminator(block, &queue); @@ -318,10 +348,14 @@ impl<'f> Context<'f> { result } - // Inline all instructions from the given block into the entry block, and track slice capacities - fn inline_block(&mut self, block: BasicBlockId, no_predicates: &HashMap) { - if self.inserter.function.entry_block() == block { - // we do not inline the entry block into itself + // Inline all instructions from the given block into the target block, and track slice capacities + pub(crate) fn inline_block( + &mut self, + block: BasicBlockId, + no_predicates: &HashMap, + ) { + if self.target_block == block { + // we do not inline the target block into itself // for the outer block before we start inlining return; } @@ -354,7 +388,7 @@ impl<'f> Context<'f> { /// For a normal block, it would be its successor /// For blocks related to a conditional statement, we ensure to process /// the 'then-branch', then the 'else-branch' (if it exists), and finally the end block - fn handle_terminator( + pub(crate) fn handle_terminator( &mut self, block: BasicBlockId, work_list: &[BasicBlockId], @@ -388,9 +422,9 @@ impl<'f> Context<'f> { let return_values = vecmap(return_values.clone(), |value| self.inserter.resolve(value)); let new_return = TerminatorInstruction::Return { return_values, call_stack }; - let entry = self.inserter.function.entry_block(); + let target = self.target_block; - self.inserter.function.dfg.set_block_terminator(entry, new_return); + self.inserter.function.dfg.set_block_terminator(target, new_return); vec![] } } @@ -544,7 +578,7 @@ impl<'f> Context<'f> { } else { self.inserter.function.dfg.make_constant(FieldElement::zero(), NumericType::bool()) }; - let block = self.inserter.function.entry_block(); + let block = self.target_block; // Cannot include this in the previous vecmap since it requires exclusive access to self let args = vecmap(args, |(then_arg, else_arg)| { @@ -568,11 +602,11 @@ impl<'f> Context<'f> { destination } - /// Insert a new instruction into the function's entry block. + /// Insert a new instruction into the target block. /// Unlike push_instruction, this function will not map any ValueIds. /// within the given instruction, nor will it modify self.values in any way. fn insert_instruction(&mut self, instruction: Instruction, call_stack: CallStackId) -> ValueId { - let block = self.inserter.function.entry_block(); + let block = self.target_block; self.inserter .function .dfg @@ -580,7 +614,7 @@ impl<'f> Context<'f> { .first() } - /// Inserts a new instruction into the function's entry block, using the given + /// Inserts a new instruction into the target block, using the given /// control type variables to specify result types if needed. /// Unlike push_instruction, this function will not map any ValueIds. /// within the given instruction, nor will it modify self.values in any way. @@ -590,7 +624,7 @@ impl<'f> Context<'f> { ctrl_typevars: Option>, call_stack: CallStackId, ) -> InsertInstructionResult { - let block = self.inserter.function.entry_block(); + let block = self.target_block; self.inserter.function.dfg.insert_instruction_and_results( instruction, block, @@ -600,11 +634,14 @@ impl<'f> Context<'f> { } /// Checks the branch condition on the top of the stack and uses it to build and insert an - /// `EnableSideEffectsIf` instruction into the entry block. + /// `EnableSideEffectsIf` instruction into the target block. /// /// If the stack is empty, a "true" u1 constant is taken to be the active condition. This is /// necessary for re-enabling side-effects when re-emerging to a branch depth of 0. fn insert_current_side_effects_enabled(&mut self) { + if self.no_predicate { + return; + } let condition = match self.get_last_condition() { Some(cond) => cond, None => { @@ -616,7 +653,7 @@ impl<'f> Context<'f> { self.insert_instruction_with_typevars(enable_side_effects, None, call_stack); } - /// Push the given instruction to the end of the entry block of the current function. + /// Push the given instruction to the end of the target block of the current function. /// /// Note that each ValueId of the instruction will be mapped via self.inserter.resolve. /// As a result, the instruction that will be pushed will actually be a new instruction @@ -631,8 +668,8 @@ impl<'f> Context<'f> { let instruction = self.handle_instruction_side_effects(instruction, call_stack); let instruction_is_allocate = matches!(&instruction, Instruction::Allocate); - let entry = self.inserter.function.entry_block(); - let results = self.inserter.push_instruction_value(instruction, id, entry, call_stack); + let results = + self.inserter.push_instruction_value(instruction, id, self.target_block, call_stack); // Remember an allocate was created local to this branch so that we do not try to merge store // values across branches for it later. @@ -816,13 +853,13 @@ mod test { use acvm::acir::AcirField; use crate::ssa::{ + Ssa, ir::{ dfg::DataFlowGraph, instruction::{Instruction, TerminatorInstruction}, value::{Value, ValueId}, }, opt::assert_normalized_ssa_equals, - Ssa, }; #[test] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs index 780912852085..7aad174d327d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs @@ -102,7 +102,9 @@ impl<'cfg> Context<'cfg> { } else if successors.len() == 1 { self.find_join_point(successors.next().unwrap()) } else if successors.len() == 0 { - unreachable!("return encountered before a join point was found. This can only happen if early-return was added to the language without implementing it by jmping to a join block first") + unreachable!( + "return encountered before a join point was found. This can only happen if early-return was added to the language without implementing it by jmping to a join block first" + ) } else { unreachable!("A block can only have 0, 1, or 2 successors"); } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs index f4638cf85e45..6e8c7df2bba5 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs @@ -1,4 +1,4 @@ -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use fxhash::{FxHashMap as HashMap, FxHashSet}; use crate::ssa::ir::{ diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/hint.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/hint.rs index be088c3da948..911554e5d6dd 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/hint.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/hint.rs @@ -6,8 +6,8 @@ mod tests { brillig::BrilligOptions, errors::RuntimeError, ssa::{ - opt::assert_normalized_ssa_equals, optimize_all, Ssa, SsaBuilder, SsaEvaluatorOptions, - SsaLogging, + Ssa, SsaBuilder, SsaEvaluatorOptions, SsaLogging, opt::assert_normalized_ssa_equals, + optimize_all, }, }; @@ -20,7 +20,7 @@ mod tests { emit_ssa: None, skip_underconstrained_check: true, enable_brillig_constraints_check_lookback: false, - enable_brillig_constraints_check: false, + skip_brillig_constraints_check: true, inliner_aggressiveness: 0, max_bytecode_increase_percent: None, }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining.rs index e5753aeba4e8..15414e92eff6 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining.rs @@ -23,7 +23,7 @@ use crate::ssa::{ pub(super) mod inline_info; -pub(super) use inline_info::{compute_inline_infos, InlineInfo, InlineInfos}; +pub(super) use inline_info::{InlineInfo, InlineInfos, compute_inline_infos}; /// An arbitrary limit to the maximum number of recursive call /// frames at any point in time. @@ -278,7 +278,9 @@ impl InlineContext { if self.recursion_level > RECURSION_LIMIT { panic!( - "Attempted to recur more than {RECURSION_LIMIT} times during inlining function '{}':\n{}", source_function.name(), source_function + "Attempted to recur more than {RECURSION_LIMIT} times during inlining function '{}':\n{}", + source_function.name(), + source_function ); } @@ -349,10 +351,14 @@ impl<'function> PerFunctionContext<'function> { return id; } } - unreachable!("All Value::Instructions should already be known during inlining after creating the original inlined instruction. Unknown value {id} = {value:?}") + unreachable!( + "All Value::Instructions should already be known during inlining after creating the original inlined instruction. Unknown value {id} = {value:?}" + ) } value @ Value::Param { .. } => { - unreachable!("All Value::Params should already be known from previous calls to translate_block. Unknown value {id} = {value:?}") + unreachable!( + "All Value::Params should already be known from previous calls to translate_block. Unknown value {id} = {value:?}" + ) } Value::NumericConstant { constant, typ } => { // The dfg indexes a global's inner value directly, so we need to check here @@ -778,10 +784,11 @@ impl<'function> PerFunctionContext<'function> { mod test { use std::cmp::max; - use acvm::{acir::AcirField, FieldElement}; + use acvm::{FieldElement, acir::AcirField}; use noirc_frontend::monomorphization::ast::InlineType; use crate::ssa::{ + Ssa, function_builder::FunctionBuilder, ir::{ basic_block::BasicBlockId, @@ -791,7 +798,6 @@ mod test { types::{NumericType, Type}, }, opt::{assert_normalized_ssa_equals, inlining::inline_info::compute_bottom_up_order}, - Ssa, }; #[test] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining/inline_info.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining/inline_info.rs index 26bb2cad6752..d40baaae6a35 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining/inline_info.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining/inline_info.rs @@ -311,11 +311,7 @@ fn mark_functions_to_retain_recursive( let inlined_function_weights: i64 = called_functions.iter().fold(0, |acc, callee| { let info = &inline_infos[callee]; // If the callee is not going to be inlined then we can ignore its cost. - if info.should_inline { - acc.saturating_add(info.weight) - } else { - acc - } + if info.should_inline { acc.saturating_add(info.weight) } else { acc } }); let this_function_weight = inlined_function_weights diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs index 19fc6a7f5a20..f68afc55efa0 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs @@ -7,22 +7,22 @@ //! - Already marked as loop invariants //! //! We also check that we are not hoisting instructions with side effects. -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::ssa::{ + Ssa, ir::{ basic_block::BasicBlockId, function::Function, function_inserter::FunctionInserter, instruction::{ - binary::eval_constant_binary_op, Binary, BinaryOp, Instruction, InstructionId, + Binary, BinaryOp, Instruction, InstructionId, binary::eval_constant_binary_op, }, post_order::PostOrder, types::Type, value::ValueId, }, - Ssa, }; use super::unrolling::{Loop, Loops}; @@ -373,8 +373,8 @@ impl<'f> LoopInvariantContext<'f> { #[cfg(test)] mod test { - use crate::ssa::opt::assert_normalized_ssa_equals; use crate::ssa::Ssa; + use crate::ssa::opt::assert_normalized_ssa_equals; #[test] fn simple_loop_invariant_code_motion() { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/make_constrain_not_equal.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/make_constrain_not_equal.rs index 21f536eba2d4..28e59f924292 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/make_constrain_not_equal.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/make_constrain_not_equal.rs @@ -39,7 +39,7 @@ impl Function { if self .dfg .get_numeric_constant(*rhs) - .map_or(false, |constant| constant.is_zero()) + .is_some_and(|constant| constant.is_zero()) { if let Value::Instruction { instruction, .. } = &self.dfg[self.dfg.resolve(*lhs)] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index ce76825877a6..9b58a0de3294 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -201,7 +201,7 @@ impl<'f> PerFunctionContext<'f> { let is_dereference = block .expressions .get(store_address) - .map_or(false, |expression| matches!(expression, Expression::Dereference(_))); + .is_some_and(|expression| matches!(expression, Expression::Dereference(_))); if !self.last_loads.contains_key(store_address) && !store_alias_used @@ -668,10 +668,11 @@ impl<'f> PerFunctionContext<'f> { mod tests { use std::sync::Arc; - use acvm::{acir::AcirField, FieldElement}; + use acvm::{FieldElement, acir::AcirField}; use im::vector; use crate::ssa::{ + Ssa, function_builder::FunctionBuilder, ir::{ basic_block::BasicBlockId, @@ -681,7 +682,6 @@ mod tests { types::Type, }, opt::assert_normalized_ssa_equals, - Ssa, }; #[test] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs index 91e27f07b8e9..8c74852b53bf 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs @@ -58,11 +58,7 @@ pub(super) enum ReferenceValue { impl ReferenceValue { fn unify(self, other: Self) -> Self { - if self == other { - self - } else { - ReferenceValue::Unknown - } + if self == other { self } else { ReferenceValue::Unknown } } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mod.rs index 4d8a652b94d9..a9784d4c7cfb 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mod.rs @@ -7,7 +7,10 @@ mod array_set; mod as_slice_length; mod assert_constant; +mod basic_conditional; +mod brillig_array_gets; pub(crate) mod brillig_entry_points; +mod check_u128_mul_overflow; mod constant_folding; mod defunctionalize; mod die; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/preprocess_fns.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/preprocess_fns.rs index 764fb6dd65b5..6fa1e88ee2d4 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/preprocess_fns.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/preprocess_fns.rs @@ -1,8 +1,8 @@ //! Pre-process functions before inlining them into others. use crate::ssa::{ - ir::function::{Function, RuntimeType}, Ssa, + ir::function::{Function, RuntimeType}, }; use super::inlining::{self, InlineInfo}; @@ -59,7 +59,7 @@ impl Ssa { // Try to reduce the number of blocks. function.simplify_function(); // Remove leftover instructions. - function.dead_instruction_elimination(true, false); + function.dead_instruction_elimination(true, false, false); // Put it back into the SSA, so the next functions can pick it up. self.functions.insert(id, function); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/pure.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/pure.rs index d790d035eb05..b975413fc2e5 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/pure.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/pure.rs @@ -171,7 +171,7 @@ impl Function { | Value::Instruction { .. } | Value::Param { .. } | Value::NumericConstant { .. } => { - return (Purity::Impure, BTreeSet::new()) + return (Purity::Impure, BTreeSet::new()); } } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs index e36be71aeea2..b4427a1c91bf 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, sync::Arc}; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use crate::ssa::{ ir::{ @@ -167,6 +167,7 @@ impl Context<'_> { let lhs_typ = self.function.dfg.type_of_value(lhs).unwrap_numeric(); let base = self.field_constant(FieldElement::from(2_u128)); let pow = self.pow(base, rhs); + let pow = self.pow_or_max_for_bit_size(pow, rhs, bit_size, lhs_typ); let pow = self.insert_cast(pow, lhs_typ); if lhs_typ.is_unsigned() { // unsigned right bit shift is just a normal division @@ -205,6 +206,53 @@ impl Context<'_> { } } + /// Returns `pow` or the maximum value allowed for `typ` if 2^rhs is guaranteed to exceed that maximum. + fn pow_or_max_for_bit_size( + &mut self, + pow: ValueId, + rhs: ValueId, + bit_size: u32, + typ: NumericType, + ) -> ValueId { + let max = if typ.is_unsigned() { + if bit_size == 128 { u128::MAX } else { (1_u128 << bit_size) - 1 } + } else { + 1_u128 << (bit_size - 1) + }; + let max = self.field_constant(FieldElement::from(max)); + + // Here we check whether rhs is less than the bit_size: if it's not then it will overflow. + // Then we do: + // + // rhs_is_less_than_bit_size = lt rhs, bit_size + // rhs_is_not_less_than_bit_size = not rhs_is_less_than_bit_size + // pow_when_is_less_than_bit_size = rhs_is_less_than_bit_size * pow + // pow_when_is_not_less_than_bit_size = rhs_is_not_less_than_bit_size * max + // pow = add pow_when_is_less_than_bit_size, pow_when_is_not_less_than_bit_size + // + // All operations here are unchecked because they work on field types. + let rhs_typ = self.function.dfg.type_of_value(rhs).unwrap_numeric(); + let bit_size = self.numeric_constant(bit_size as u128, rhs_typ); + let rhs_is_less_than_bit_size = self.insert_binary(rhs, BinaryOp::Lt, bit_size); + let rhs_is_not_less_than_bit_size = self.insert_not(rhs_is_less_than_bit_size); + let rhs_is_less_than_bit_size = + self.insert_cast(rhs_is_less_than_bit_size, NumericType::NativeField); + let rhs_is_not_less_than_bit_size = + self.insert_cast(rhs_is_not_less_than_bit_size, NumericType::NativeField); + let pow_when_is_less_than_bit_size = + self.insert_binary(rhs_is_less_than_bit_size, BinaryOp::Mul { unchecked: true }, pow); + let pow_when_is_not_less_than_bit_size = self.insert_binary( + rhs_is_not_less_than_bit_size, + BinaryOp::Mul { unchecked: true }, + max, + ); + self.insert_binary( + pow_when_is_less_than_bit_size, + BinaryOp::Add { unchecked: true }, + pow_when_is_not_less_than_bit_size, + ) + } + /// Computes lhs^rhs via square&multiply, using the bits decomposition of rhs /// Pseudo-code of the computation: /// let mut r = 1; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs index 942fe67b5d5c..44a1a0b7acf2 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs @@ -10,7 +10,7 @@ //! before the [Instruction]. Continue inserting instructions until the next [Instruction::EnableSideEffectsIf] is encountered. use std::collections::HashSet; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use crate::ssa::{ ir::{ @@ -92,7 +92,7 @@ impl Context { let condition_is_one = function .dfg .get_numeric_constant(*condition) - .map_or(false, |condition| condition.is_one()); + .is_some_and(|condition| condition.is_one()); if condition_is_one { new_instructions.push(instruction_id); last_side_effects_enabled_instruction = None; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs index 8dde79a3c60d..94b0c1d0a851 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs @@ -1,6 +1,6 @@ use std::collections::hash_map::Entry; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use fxhash::FxHashMap as HashMap; use crate::ssa::ir::function::RuntimeType; @@ -8,6 +8,7 @@ use crate::ssa::ir::instruction::Hint; use crate::ssa::ir::types::NumericType; use crate::ssa::ir::value::ValueId; use crate::ssa::{ + Ssa, ir::{ dfg::DataFlowGraph, function::Function, @@ -16,7 +17,6 @@ use crate::ssa::{ value::Value, }, opt::flatten_cfg::value_merger::ValueMerger, - Ssa, }; impl Ssa { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs index 22fdf0a79872..3d812870c064 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs @@ -254,7 +254,9 @@ fn remove_block_parameters( let jump_args = match function.dfg[predecessor].unwrap_terminator_mut() { TerminatorInstruction::Jmp { arguments, .. } => std::mem::take(arguments), - TerminatorInstruction::JmpIf { .. } => unreachable!("If jmpif instructions are modified to support block arguments in the future, this match will need to be updated"), + TerminatorInstruction::JmpIf { .. } => unreachable!( + "If jmpif instructions are modified to support block arguments in the future, this match will need to be updated" + ), _ => unreachable!( "Predecessor was already validated to have only a single jmp destination" ), @@ -293,6 +295,7 @@ fn try_inline_into_predecessor( #[cfg(test)] mod test { use crate::ssa::{ + Ssa, function_builder::FunctionBuilder, ir::{ instruction::{BinaryOp, TerminatorInstruction}, @@ -300,7 +303,6 @@ mod test { types::Type, }, opt::assert_normalized_ssa_equals, - Ssa, }; use acvm::acir::AcirField; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs index 547a8a042c65..43afb9fa41a7 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -20,7 +20,7 @@ //! only used by Brillig bytecode. use std::collections::BTreeSet; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use im::HashSet; use crate::{ @@ -490,9 +490,19 @@ impl Loop { context.inline_instructions_from_block(); // Mutate the terminator if possible so that it points at the iteration block. match context.dfg()[fresh_block].unwrap_terminator() { - TerminatorInstruction::JmpIf { condition, then_destination, else_destination, call_stack } => { + TerminatorInstruction::JmpIf { + condition, + then_destination, + else_destination, + call_stack, + } => { let condition = *condition; - let next_blocks = context.handle_jmpif(condition, *then_destination, *else_destination, *call_stack); + let next_blocks = context.handle_jmpif( + condition, + *then_destination, + *else_destination, + *call_stack, + ); // If there is only 1 next block the jmpif evaluated to a single known block. // This is the expected case and lets us know if we should loop again or not. @@ -515,7 +525,9 @@ impl Loop { Err(context.inserter.function.dfg.get_value_call_stack(condition)) } } - other => unreachable!("Expected loop header to terminate in a JmpIf to the loop body, but found {other:?} instead"), + other => unreachable!( + "Expected loop header to terminate in a JmpIf to the loop body, but found {other:?} instead" + ), } } @@ -1021,9 +1033,9 @@ mod tests { use test_case::test_case; use crate::errors::RuntimeError; - use crate::ssa::{ir::value::ValueId, opt::assert_normalized_ssa_equals, Ssa}; + use crate::ssa::{Ssa, ir::value::ValueId, opt::assert_normalized_ssa_equals}; - use super::{is_new_size_ok, BoilerplateStats, Loops}; + use super::{BoilerplateStats, Loops, is_new_size_ok}; /// Tries to unroll all loops in each SSA function once, calling the `Function` directly, /// bypassing the iterative loop done by the SSA which does further optimisations. diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index fc9b1ae98bcd..d7ca5832f064 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -15,8 +15,8 @@ use crate::ssa::{ }; use super::{ - ast::AssertMessage, Identifier, ParsedBlock, ParsedFunction, ParsedGlobal, ParsedGlobalValue, - ParsedInstruction, ParsedSsa, ParsedTerminator, ParsedValue, RuntimeType, Ssa, SsaError, Type, + Identifier, ParsedBlock, ParsedFunction, ParsedGlobal, ParsedGlobalValue, ParsedInstruction, + ParsedSsa, ParsedTerminator, ParsedValue, RuntimeType, Ssa, SsaError, Type, ast::AssertMessage, }; impl ParsedSsa { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/lexer.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/lexer.rs index e22b6a661de0..e6e15d1559d3 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/lexer.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/lexer.rs @@ -171,7 +171,7 @@ impl<'a> Lexer<'a> { return Err(LexerError::InvalidIntegerLiteral { span: Span::inclusive(start, end), found: integer_str, - }) + }); } }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/mod.rs index 4c5f6334430d..9f4d38648e20 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/mod.rs @@ -5,11 +5,11 @@ use std::{ }; use super::{ + Ssa, ir::{ instruction::BinaryOp, types::{NumericType, Type}, }, - Ssa, }; use acvm::{AcirField, FieldElement}; @@ -675,11 +675,7 @@ impl<'a> Parser<'a> { } fn parse_value_or_error(&mut self) -> ParseResult { - if let Some(value) = self.parse_value()? { - Ok(value) - } else { - self.expected_value() - } + if let Some(value) = self.parse_value()? { Ok(value) } else { self.expected_value() } } fn parse_value(&mut self) -> ParseResult> { @@ -818,7 +814,7 @@ impl<'a> Parser<'a> { } fn eat_identifier(&mut self) -> ParseResult> { - let span = self.token.to_span(); + let span = self.token.span(); if let Some(name) = self.eat_ident()? { Ok(Some(Identifier::new(name, span))) } else { @@ -852,11 +848,7 @@ impl<'a> Parser<'a> { } fn eat_ident_or_error(&mut self) -> ParseResult { - if let Some(ident) = self.eat_ident()? { - Ok(ident) - } else { - self.expected_identifier() - } + if let Some(ident) = self.eat_ident()? { Ok(ident) } else { self.expected_identifier() } } fn eat_int(&mut self) -> ParseResult> { @@ -879,11 +871,7 @@ impl<'a> Parser<'a> { } fn eat_int_or_error(&mut self) -> ParseResult { - if let Some(int) = self.eat_int()? { - Ok(int) - } else { - self.expected_int() - } + if let Some(int) = self.eat_int()? { Ok(int) } else { self.expected_int() } } fn eat_int_type(&mut self) -> ParseResult> { @@ -933,11 +921,7 @@ impl<'a> Parser<'a> { } fn eat_or_error(&mut self, token: Token) -> ParseResult<()> { - if self.eat(token.clone())? { - Ok(()) - } else { - self.expected_token(token) - } + if self.eat(token.clone())? { Ok(()) } else { self.expected_token(token) } } fn at(&self, token: Token) -> bool { @@ -960,56 +944,53 @@ impl<'a> Parser<'a> { fn expected_instruction_or_terminator(&mut self) -> ParseResult { Err(ParserError::ExpectedInstructionOrTerminator { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } fn expected_string_or_data(&mut self) -> ParseResult { Err(ParserError::ExpectedStringOrData { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } fn expected_byte_string(&mut self) -> ParseResult { Err(ParserError::ExpectedByteString { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } fn expected_identifier(&mut self) -> ParseResult { Err(ParserError::ExpectedIdentifier { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } fn expected_int(&mut self) -> ParseResult { - Err(ParserError::ExpectedInt { - found: self.token.token().clone(), - span: self.token.to_span(), - }) + Err(ParserError::ExpectedInt { found: self.token.token().clone(), span: self.token.span() }) } fn expected_type(&mut self) -> ParseResult { Err(ParserError::ExpectedType { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } fn expected_value(&mut self) -> ParseResult { Err(ParserError::ExpectedValue { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } fn expected_global_value(&mut self) -> ParseResult { Err(ParserError::ExpectedGlobalValue { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } @@ -1017,7 +998,7 @@ impl<'a> Parser<'a> { Err(ParserError::ExpectedToken { token, found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } @@ -1025,7 +1006,7 @@ impl<'a> Parser<'a> { Err(ParserError::ExpectedOneOfTokens { tokens: tokens.to_vec(), found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 358c2e89a411..a865a31a060d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -1,7 +1,7 @@ #![cfg(test)] use crate::{ - ssa::{opt::assert_normalized_ssa_equals, Ssa}, + ssa::{Ssa, opt::assert_normalized_ssa_equals}, trim_leading_whitespace_from_lines, }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/token.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/token.rs index eb09209466d4..280a2ca95a7c 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -12,7 +12,7 @@ impl SpannedToken { SpannedToken(Spanned::from(span, token)) } - pub(crate) fn to_span(&self) -> Span { + pub(crate) fn span(&self) -> Span { self.0.span() } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index cbac4bd6d840..5db3ecb91b7f 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -1,12 +1,13 @@ use std::collections::BTreeMap; use std::sync::{Arc, Mutex, RwLock}; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use iter_extended::vecmap; use noirc_errors::Location; use noirc_frontend::ast::{BinaryOpKind, Signedness}; use noirc_frontend::monomorphization::ast::{self, GlobalId, InlineType, LocalId, Parameters}; use noirc_frontend::monomorphization::ast::{FuncId, Program}; +use noirc_frontend::signed_field::SignedField; use crate::errors::RuntimeError; use crate::ssa::function_builder::FunctionBuilder; @@ -19,8 +20,8 @@ use crate::ssa::ir::map::AtomicCounter; use crate::ssa::ir::types::{NumericType, Type}; use crate::ssa::ir::value::ValueId; -use super::value::{Tree, Value, Values}; use super::GlobalsGraph; +use super::value::{Tree, Value, Values}; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; /// The FunctionContext is the main context object for translating a @@ -289,32 +290,30 @@ impl<'a> FunctionContext<'a> { /// otherwise values like 2^128 can be assigned to a u8 without error or wrapping. pub(super) fn checked_numeric_constant( &mut self, - value: impl Into, - negative: bool, + value: SignedField, numeric_type: NumericType, ) -> Result { - let value = value.into(); - - if let Some(range) = numeric_type.value_is_outside_limits(value, negative) { + if let Some(range) = numeric_type.value_is_outside_limits(value) { let call_stack = self.builder.get_call_stack(); return Err(RuntimeError::IntegerOutOfBounds { - value: if negative { -value } else { value }, + value, typ: numeric_type, range, call_stack, }); } - let value = if negative { + let value = if value.is_negative { match numeric_type { - NumericType::NativeField => -value, + NumericType::NativeField => -value.field, NumericType::Signed { bit_size } | NumericType::Unsigned { bit_size } => { + assert!(bit_size < 128); let base = 1_u128 << bit_size; - FieldElement::from(base) - value + FieldElement::from(base) - value.field } } } else { - value + value.field }; Ok(self.builder.numeric_constant(value, numeric_type)) @@ -355,7 +354,8 @@ impl<'a> FunctionContext<'a> { /// Insert constraints ensuring that the operation does not overflow the bit size of the result /// - /// If the result is unsigned, overflow will be checked during acir-gen (cf. issue #4456), except for bit-shifts, because we will convert them to field multiplication + /// If the result is unsigned, overflow will be checked during acir-gen (cf. issue #4456), except for + /// bit-shifts, because we will convert them to field multiplication /// /// If the result is signed, we just prepare it for check_signed_overflow() by casting it to /// an unsigned value representing the signed integer. @@ -755,11 +755,7 @@ impl<'a> FunctionContext<'a> { Ok(match lvalue { ast::LValue::Ident(ident) => { let (reference, should_auto_deref) = self.ident_lvalue(ident); - if should_auto_deref { - LValue::Dereference { reference } - } else { - LValue::Ident - } + if should_auto_deref { LValue::Dereference { reference } } else { LValue::Ident } } ast::LValue::Index { array, index, location, .. } => { self.index_lvalue(array, index, location)?.2 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 91aa8b2914d2..a954ac3ab93c 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 @@ -130,7 +130,7 @@ pub(crate) fn generate_ssa(program: Program) -> Result { Ok(ssa) } -impl<'a> FunctionContext<'a> { +impl FunctionContext<'_> { /// Codegen a function's body and set its return value to that of its last parameter. /// For functions returning nothing, this will be an empty list. fn codegen_function_body(&mut self, body: &Expression) -> Result<(), RuntimeError> { @@ -233,10 +233,10 @@ impl<'a> FunctionContext<'a> { _ => unreachable!("ICE: unexpected slice literal type, got {}", array.typ), }) } - ast::Literal::Integer(value, negative, typ, location) => { + ast::Literal::Integer(value, typ, location) => { self.builder.set_location(*location); let typ = Self::convert_non_tuple_type(typ).unwrap_numeric(); - self.checked_numeric_constant(*value, *negative, typ).map(Into::into) + self.checked_numeric_constant(*value, typ).map(Into::into) } ast::Literal::Bool(value) => { // Don't need to call checked_numeric_constant here since `value` can only be true or false @@ -252,7 +252,7 @@ impl<'a> FunctionContext<'a> { let value = value.replace('{', "{{").replace('}', "}}"); string.push_str(&value); } - FmtStrFragment::Interpolation(value, _span) => { + FmtStrFragment::Interpolation(value, _) => { string.push('{'); string.push_str(value); string.push('}'); @@ -820,9 +820,7 @@ impl<'a> FunctionContext<'a> { typ: NumericType, ) -> Result { match constructor { - Constructor::Int(value) => { - self.checked_numeric_constant(value.field, value.is_negative, typ) - } + Constructor::Int(value) => self.checked_numeric_constant(*value, typ), other => Ok(self.builder.numeric_constant(other.variant_index(), typ)), } } diff --git a/noir/noir-repo/compiler/noirc_frontend/Cargo.toml b/noir/noir-repo/compiler/noirc_frontend/Cargo.toml index f9e36f7f3e0e..e5231565041c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_frontend/Cargo.toml @@ -33,7 +33,6 @@ strum.workspace = true strum_macros.workspace = true fxhash.workspace = true - [dev-dependencies] base64.workspace = true proptest.workspace = true diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/enumeration.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/enumeration.rs index 6789a200e6aa..5ac60be5fba5 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/enumeration.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/enumeration.rs @@ -4,7 +4,7 @@ use crate::ast::{Ident, UnresolvedGenerics, UnresolvedType}; use crate::token::SecondaryAttribute; use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::Location; use super::{Documented, ItemVisibility}; @@ -16,7 +16,7 @@ pub struct NoirEnumeration { pub visibility: ItemVisibility, pub generics: UnresolvedGenerics, pub variants: Vec>, - pub span: Span, + pub location: Location, } impl NoirEnumeration { 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 01705e0af881..398e52676950 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs @@ -7,16 +7,15 @@ use crate::ast::{ Ident, ItemVisibility, Path, Pattern, Statement, StatementKind, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, Visibility, }; -use crate::node_interner::{ - ExprId, InternedExpressionKind, InternedStatementKind, QuotedTypeId, TypeId, -}; +use crate::node_interner::{ExprId, InternedExpressionKind, InternedStatementKind, QuotedTypeId}; +use crate::signed_field::SignedField; use crate::token::{Attributes, FmtStrFragment, FunctionAttribute, Token, Tokens}; use crate::{Kind, Type}; -use acvm::{acir::AcirField, FieldElement}; +use acvm::FieldElement; use iter_extended::vecmap; -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Located, Location, Span}; -use super::{AsTraitPath, TypePath}; +use super::{AsTraitPath, TypePath, UnsafeExpression}; #[derive(Debug, PartialEq, Eq, Clone)] pub enum ExpressionKind { @@ -39,8 +38,8 @@ pub enum ExpressionKind { Parenthesized(Box), Quote(Tokens), Unquote(Box), - Comptime(BlockExpression, Span), - Unsafe(BlockExpression, Span), + Comptime(BlockExpression, Location), + Unsafe(UnsafeExpression), AsTraitPath(AsTraitPath), TypePath(TypePath), @@ -76,7 +75,7 @@ pub enum UnresolvedGeneric { /// splices existing types into a generic list. In this case we have /// to validate the type refers to a named generic and treat that /// as a ResolvedGeneric when this is resolved. - Resolved(QuotedTypeId, Span), + Resolved(QuotedTypeId, Location), } #[derive(Error, PartialEq, Eq, Debug, Clone)] @@ -87,14 +86,18 @@ pub struct UnsupportedNumericGenericType { } impl UnresolvedGeneric { - pub fn span(&self) -> Span { + pub fn location(&self) -> Location { match self { - UnresolvedGeneric::Variable(ident) => ident.0.span(), - UnresolvedGeneric::Numeric { ident, typ } => ident.0.span().merge(typ.span), - UnresolvedGeneric::Resolved(_, span) => *span, + UnresolvedGeneric::Variable(ident) => ident.0.location(), + UnresolvedGeneric::Numeric { ident, typ } => ident.location().merge(typ.location), + UnresolvedGeneric::Resolved(_, location) => *location, } } + pub fn span(&self) -> Span { + self.location().span + } + pub fn kind(&self) -> Result { match self { UnresolvedGeneric::Variable(_) => Ok(Kind::Normal), @@ -168,8 +171,8 @@ impl ExpressionKind { match (operator, &rhs) { ( UnaryOp::Minus, - Expression { kind: ExpressionKind::Literal(Literal::Integer(field, sign)), .. }, - ) => ExpressionKind::Literal(Literal::Integer(*field, !sign)), + Expression { kind: ExpressionKind::Literal(Literal::Integer(field)), .. }, + ) => ExpressionKind::Literal(Literal::Integer(-*field)), _ => ExpressionKind::Prefix(Box::new(PrefixExpression { operator, rhs })), } } @@ -197,7 +200,7 @@ impl ExpressionKind { } pub fn integer(contents: FieldElement) -> ExpressionKind { - ExpressionKind::Literal(Literal::Integer(contents, false)) + ExpressionKind::Literal(Literal::Integer(SignedField::positive(contents))) } pub fn boolean(contents: bool) -> ExpressionKind { @@ -219,18 +222,14 @@ impl ExpressionKind { pub fn constructor( (typ, fields): (UnresolvedType, Vec<(Ident, Expression)>), ) -> ExpressionKind { - ExpressionKind::Constructor(Box::new(ConstructorExpression { - typ, - fields, - struct_type: None, - })) + ExpressionKind::Constructor(Box::new(ConstructorExpression { typ, fields })) } } #[derive(Debug, Eq, Clone)] pub struct Expression { pub kind: ExpressionKind, - pub span: Span, + pub location: Location, } // This is important for tests. Two expressions are the same, if their Kind is the same @@ -242,23 +241,23 @@ impl PartialEq for Expression { } impl Expression { - pub fn new(kind: ExpressionKind, span: Span) -> Expression { - Expression { kind, span } + pub fn new(kind: ExpressionKind, location: Location) -> Expression { + Expression { kind, location } } - /// Returns the innermost span that gives this expression its type. - pub fn type_span(&self) -> Span { + /// Returns the innermost location that gives this expression its type. + pub fn type_location(&self) -> Location { match &self.kind { ExpressionKind::Block(block_expression) | ExpressionKind::Comptime(block_expression, _) - | ExpressionKind::Unsafe(block_expression, _) => { + | ExpressionKind::Unsafe(UnsafeExpression { block: block_expression, .. }) => { if let Some(statement) = block_expression.statements.last() { - statement.type_span() + statement.type_location() } else { - self.span + self.location } } - ExpressionKind::Parenthesized(expression) => expression.type_span(), + ExpressionKind::Parenthesized(expression) => expression.type_location(), ExpressionKind::Literal(..) | ExpressionKind::Prefix(..) | ExpressionKind::Index(..) @@ -281,12 +280,12 @@ impl Expression { | ExpressionKind::Resolved(..) | ExpressionKind::Interned(..) | ExpressionKind::InternedStatement(..) - | ExpressionKind::Error => self.span, + | ExpressionKind::Error => self.location, } } } -pub type BinaryOp = Spanned; +pub type BinaryOp = Located; #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Copy, Clone)] #[cfg_attr(test, derive(strum_macros::EnumIter))] @@ -411,7 +410,7 @@ pub enum Literal { Array(ArrayLiteral), Slice(ArrayLiteral), Bool(bool), - Integer(FieldElement, /*sign*/ bool), // false for positive integer and true for negative + Integer(SignedField), Str(String), RawStr(String, u8), FmtStr(Vec, u32 /* length */), @@ -478,7 +477,7 @@ pub struct FunctionDefinition { pub generics: UnresolvedGenerics, pub parameters: Vec, pub body: BlockExpression, - pub span: Span, + pub location: Location, pub where_clause: Vec, pub return_type: FunctionReturnType, pub return_visibility: Visibility, @@ -503,13 +502,13 @@ pub struct Param { pub visibility: Visibility, pub pattern: Pattern, pub typ: UnresolvedType, - pub span: Span, + pub location: Location, } #[derive(Debug, PartialEq, Eq, Clone)] pub enum FunctionReturnType { /// Returns type is not specified. - Default(Span), + Default(Location), /// Everything else. Ty(UnresolvedType), } @@ -541,11 +540,6 @@ pub struct MethodCallExpression { pub struct ConstructorExpression { pub typ: UnresolvedType, pub fields: Vec<(Ident, Expression)>, - - /// This may be filled out during macro expansion - /// so that we can skip re-resolving the type name since it - /// would be lost at that point. - pub struct_type: Option, } #[derive(Debug, PartialEq, Eq, Clone)] @@ -583,7 +577,7 @@ impl BlockExpression { pub struct ConstrainExpression { pub kind: ConstrainKind, pub arguments: Vec, - pub span: Span, + pub location: Location, } impl Display for ConstrainExpression { @@ -659,7 +653,7 @@ impl Display for ExpressionKind { Lambda(lambda) => lambda.fmt(f), Parenthesized(sub_expr) => write!(f, "({sub_expr})"), Comptime(block, _) => write!(f, "comptime {block}"), - Unsafe(block, _) => write!(f, "unsafe {block}"), + Unsafe(UnsafeExpression { block, .. }) => write!(f, "unsafe {block}"), Error => write!(f, "Error"), Resolved(_) => write!(f, "?Resolved"), Interned(_) => write!(f, "?Interned"), @@ -693,12 +687,8 @@ impl Display for Literal { write!(f, "&[{repeated_element}; {length}]") } Literal::Bool(boolean) => write!(f, "{}", if *boolean { "true" } else { "false" }), - Literal::Integer(integer, sign) => { - if *sign { - write!(f, "-{}", integer.to_u128()) - } else { - write!(f, "{}", integer.to_u128()) - } + Literal::Integer(signed_field) => { + write!(f, "{signed_field}") } Literal::Str(string) => write!(f, "\"{string}\""), Literal::RawStr(string, num_hashes) => { @@ -877,7 +867,7 @@ impl FunctionDefinition { visibility: Visibility::Private, pattern: Pattern::Identifier(ident.clone()), typ: unresolved_type.clone(), - span: ident.span().merge(unresolved_type.span), + location: ident.location().merge(unresolved_type.location), }) .collect(); @@ -890,7 +880,7 @@ impl FunctionDefinition { generics: generics.clone(), parameters: p, body, - span: name.span(), + location: name.location(), where_clause, return_type: return_type.clone(), return_visibility: Visibility::Private, @@ -898,13 +888,14 @@ impl FunctionDefinition { } pub fn signature(&self) -> String { - let parameters = vecmap(&self.parameters, |Param { visibility, pattern, typ, span: _ }| { - if *visibility == Visibility::Public { - format!("{pattern}: {visibility} {typ}") - } else { - format!("{pattern}: {typ}") - } - }); + let parameters = + vecmap(&self.parameters, |Param { visibility, pattern, typ, location: _ }| { + if *visibility == Visibility::Public { + format!("{pattern}: {visibility} {typ}") + } else { + format!("{pattern}: {typ}") + } + }); let where_clause = vecmap(&self.where_clause, ToString::to_string); let where_clause_str = if !where_clause.is_empty() { @@ -933,12 +924,19 @@ impl Display for FunctionDefinition { impl FunctionReturnType { pub fn get_type(&self) -> Cow { match self { - FunctionReturnType::Default(span) => { - Cow::Owned(UnresolvedType { typ: UnresolvedTypeData::Unit, span: *span }) + FunctionReturnType::Default(location) => { + Cow::Owned(UnresolvedType { typ: UnresolvedTypeData::Unit, location: *location }) } FunctionReturnType::Ty(typ) => Cow::Borrowed(typ), } } + + pub fn location(&self) -> Location { + match self { + FunctionReturnType::Default(location) => *location, + FunctionReturnType::Ty(typ) => typ.location, + } + } } impl Display for FunctionReturnType { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/function.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/function.rs index 8957564e0d63..f0e4ac812cae 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/function.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/function.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use noirc_errors::Span; +use noirc_errors::{Location, Span}; use crate::{ ast::{FunctionReturnType, Ident, Param, Visibility}, @@ -65,7 +65,9 @@ impl NoirFunction { pub fn return_type(&self) -> UnresolvedType { match &self.def.return_type { - FunctionReturnType::Default(span) => UnresolvedTypeData::Unit.with_span(*span), + FunctionReturnType::Default(location) => { + UnresolvedTypeData::Unit.with_location(*location) + } FunctionReturnType::Ty(ty) => ty.clone(), } } @@ -96,8 +98,11 @@ impl NoirFunction { pub fn number_of_statements(&self) -> usize { self.def.body.statements.len() } + pub fn location(&self) -> Location { + self.def.location + } pub fn span(&self) -> Span { - self.def.span + self.location().span } pub fn foreign(&self) -> Option<&FunctionDefinition> { 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 33c73eb2482e..8e74ce8877e9 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs @@ -14,6 +14,7 @@ mod traits; mod type_alias; mod visitor; +use noirc_errors::Location; pub use visitor::AttributeTarget; pub use visitor::Visitor; @@ -34,10 +35,10 @@ pub use traits::*; pub use type_alias::*; use crate::{ + BinaryTypeOperator, node_interner::{InternedUnresolvedTypeData, QuotedTypeId}, parser::{ParserError, ParserErrorReason}, token::IntType, - BinaryTypeOperator, }; use acvm::acir::AcirField; use iter_extended::vecmap; @@ -50,6 +51,7 @@ pub enum IntegerBitSize { Sixteen, ThirtyTwo, SixtyFour, + HundredTwentyEight, } impl IntegerBitSize { @@ -60,6 +62,7 @@ impl IntegerBitSize { IntegerBitSize::Sixteen => 16, IntegerBitSize::ThirtyTwo => 32, IntegerBitSize::SixtyFour => 64, + IntegerBitSize::HundredTwentyEight => 128, } } } @@ -79,6 +82,7 @@ impl From for u32 { Sixteen => 16, ThirtyTwo => 32, SixtyFour => 64, + HundredTwentyEight => 128, } } } @@ -96,6 +100,7 @@ impl TryFrom for IntegerBitSize { 16 => Ok(Sixteen), 32 => Ok(ThirtyTwo), 64 => Ok(SixtyFour), + 128 => Ok(HundredTwentyEight), _ => Err(InvalidIntegerBitSizeError(value)), } } @@ -165,7 +170,7 @@ pub enum UnresolvedTypeData { #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub struct UnresolvedType { pub typ: UnresolvedTypeData, - pub span: Span, + pub location: Location, } /// An argument to a generic type or trait. @@ -231,12 +236,12 @@ impl From> for GenericTypeArgs { #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum UnresolvedTypeExpression { Variable(Path), - Constant(FieldElement, Span), + Constant(FieldElement, Location), BinaryOperation( Box, BinaryTypeOperator, Box, - Span, + Location, ), AsTraitPath(Box), } @@ -352,7 +357,7 @@ impl UnresolvedType { } pub fn from_path(mut path: Path) -> Self { - let span = path.span; + let location = path.location; let last_segment = path.segments.last_mut().unwrap(); let generics = last_segment.generics.take(); let generic_type_args = if let Some(generics) = generics { @@ -365,7 +370,7 @@ impl UnresolvedType { GenericTypeArgs::default() }; let typ = UnresolvedTypeData::Named(path, generic_type_args, true); - UnresolvedType { typ, span } + UnresolvedType { typ, location } } pub(crate) fn contains_unspecified(&self) -> bool { @@ -380,7 +385,11 @@ impl UnresolvedTypeData { use {IntType::*, UnresolvedTypeData::Integer}; match token { Signed(num_bits) => { - Ok(Integer(Signedness::Signed, IntegerBitSize::try_from(num_bits)?)) + if num_bits == 128 { + Err(InvalidIntegerBitSizeError(128)) + } else { + Ok(Integer(Signedness::Signed, IntegerBitSize::try_from(num_bits)?)) + } } Unsigned(num_bits) => { Ok(Integer(Signedness::Unsigned, IntegerBitSize::try_from(num_bits)?)) @@ -388,8 +397,12 @@ impl UnresolvedTypeData { } } - pub fn with_span(&self, span: Span) -> UnresolvedType { - UnresolvedType { typ: self.clone(), span } + pub fn with_location(&self, location: Location) -> UnresolvedType { + UnresolvedType { typ: self.clone(), location } + } + + pub fn with_dummy_location(&self) -> UnresolvedType { + self.with_location(Location::dummy()) } fn contains_unspecified(&self) -> bool { @@ -455,37 +468,43 @@ impl UnresolvedTypeExpression { #[allow(clippy::result_large_err)] pub(crate) fn from_expr( expr: Expression, - span: Span, + location: Location, ) -> Result { Self::from_expr_helper(expr).map_err(|err_expr| { - ParserError::with_reason(ParserErrorReason::InvalidTypeExpression(err_expr), span) + ParserError::with_reason(ParserErrorReason::InvalidTypeExpression(err_expr), location) }) } - pub fn span(&self) -> Span { + pub fn location(&self) -> Location { match self { - UnresolvedTypeExpression::Variable(path) => path.span(), - UnresolvedTypeExpression::Constant(_, span) => *span, - UnresolvedTypeExpression::BinaryOperation(_, _, _, span) => *span, + UnresolvedTypeExpression::Variable(path) => path.location, + UnresolvedTypeExpression::Constant(_, location) => *location, + UnresolvedTypeExpression::BinaryOperation(_, _, _, location) => *location, UnresolvedTypeExpression::AsTraitPath(path) => { - path.trait_path.span.merge(path.impl_item.span()) + path.trait_path.location.merge(path.impl_item.location()) } } } + pub fn span(&self) -> Span { + self.location().span + } + fn from_expr_helper(expr: Expression) -> Result { match expr.kind { - ExpressionKind::Literal(Literal::Integer(int, _)) => match int.try_to_u32() { - Some(int) => Ok(UnresolvedTypeExpression::Constant(int.into(), expr.span)), + ExpressionKind::Literal(Literal::Integer(int)) => match int.try_to_unsigned::() { + Some(int) => Ok(UnresolvedTypeExpression::Constant(int.into(), expr.location)), None => Err(expr), }, ExpressionKind::Variable(path) => Ok(UnresolvedTypeExpression::Variable(path)), ExpressionKind::Prefix(prefix) if prefix.operator == UnaryOp::Minus => { - let lhs = - Box::new(UnresolvedTypeExpression::Constant(FieldElement::zero(), expr.span)); + let lhs = Box::new(UnresolvedTypeExpression::Constant( + FieldElement::zero(), + expr.location, + )); let rhs = Box::new(UnresolvedTypeExpression::from_expr_helper(prefix.rhs)?); let op = BinaryTypeOperator::Subtraction; - Ok(UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, expr.span)) + Ok(UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, expr.location)) } ExpressionKind::Infix(infix) if Self::operator_allowed(infix.operator.contents) => { let lhs = Box::new(UnresolvedTypeExpression::from_expr_helper(infix.lhs)?); @@ -511,7 +530,7 @@ impl UnresolvedTypeExpression { unreachable!("impossible via `operator_allowed` check") } }; - Ok(UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, expr.span)) + Ok(UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, expr.location)) } ExpressionKind::AsTraitPath(path) => { Ok(UnresolvedTypeExpression::AsTraitPath(Box::new(path))) 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 372f20f87800..78b29b8c1fdd 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs @@ -1,9 +1,9 @@ use std::fmt::Display; -use acvm::acir::AcirField; use acvm::FieldElement; +use acvm::acir::AcirField; use iter_extended::vecmap; -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Located, Location, Span}; use super::{ BinaryOpKind, BlockExpression, ConstructorExpression, Expression, ExpressionKind, @@ -11,14 +11,13 @@ use super::{ MethodCallExpression, UnresolvedType, }; use crate::ast::UnresolvedTypeData; -use crate::elaborator::types::SELF_TYPE_NAME; use crate::elaborator::Turbofish; -use crate::lexer::token::SpannedToken; +use crate::elaborator::types::SELF_TYPE_NAME; use crate::node_interner::{ InternedExpressionKind, InternedPattern, InternedStatementKind, NodeInterner, }; use crate::parser::{ParserError, ParserErrorReason}; -use crate::token::{SecondaryAttribute, Token}; +use crate::token::{LocatedToken, SecondaryAttribute, Token}; /// This is used when an identifier fails to parse in the parser. /// Instead of failing the parse, we can often recover using this @@ -33,7 +32,7 @@ pub const WILDCARD_TYPE: &str = "_"; #[derive(Debug, PartialEq, Eq, Clone)] pub struct Statement { pub kind: StatementKind, - pub span: Span, + pub location: Location, } /// Ast node for statements in noir. Statements are always within a block { } @@ -45,7 +44,7 @@ pub enum StatementKind { Expression(Expression), Assign(AssignStatement), For(ForLoopStatement), - Loop(Expression, Span /* loop keyword span */), + Loop(Expression, Location /* loop keyword location */), While(WhileStatement), Break, Continue, @@ -66,19 +65,19 @@ impl Statement { pub fn add_semicolon( mut self, semi: Option, - span: Span, + location: Location, last_statement_in_block: bool, emit_error: &mut dyn FnMut(ParserError), ) -> Self { - self.kind = self.kind.add_semicolon(semi, span, last_statement_in_block, emit_error); + self.kind = self.kind.add_semicolon(semi, location, last_statement_in_block, emit_error); self } - /// Returns the innermost span that gives this statement its type. - pub fn type_span(&self) -> Span { + /// Returns the innermost location that gives this statement its type. + pub fn type_location(&self) -> Location { match &self.kind { - StatementKind::Expression(expression) => expression.type_span(), - StatementKind::Comptime(statement) => statement.type_span(), + StatementKind::Expression(expression) => expression.type_location(), + StatementKind::Comptime(statement) => statement.type_location(), StatementKind::Let(..) | StatementKind::Assign(..) | StatementKind::For(..) @@ -88,7 +87,7 @@ impl Statement { | StatementKind::Continue | StatementKind::Semi(..) | StatementKind::Interned(..) - | StatementKind::Error => self.span, + | StatementKind::Error => self.location, } } } @@ -97,12 +96,12 @@ impl StatementKind { pub fn add_semicolon( self, semi: Option, - span: Span, + location: Location, last_statement_in_block: bool, emit_error: &mut dyn FnMut(ParserError), ) -> Self { let missing_semicolon = - ParserError::with_reason(ParserErrorReason::MissingSeparatingSemi, span); + ParserError::with_reason(ParserErrorReason::MissingSeparatingSemi, location); match self { StatementKind::Let(_) @@ -119,7 +118,7 @@ impl StatementKind { } StatementKind::Comptime(mut statement) => { *statement = - statement.add_semicolon(semi, span, last_statement_in_block, emit_error); + statement.add_semicolon(semi, location, last_statement_in_block, emit_error); StatementKind::Comptime(statement) } // A semicolon on a for loop, loop or while is optional and does nothing @@ -174,7 +173,7 @@ impl StatementKind { } #[derive(Eq, Debug, Clone, Default)] -pub struct Ident(pub Spanned); +pub struct Ident(pub Located); impl Ident { pub fn is_self_type_name(&self) -> bool { @@ -218,15 +217,15 @@ impl Display for Ident { } } -impl From> for Ident { - fn from(a: Spanned) -> Ident { +impl From> for Ident { + fn from(a: Located) -> Ident { Ident(a) } } impl From for Ident { fn from(a: String) -> Ident { - Spanned::from_position(Default::default(), Default::default(), a).into() + Located::from(Location::dummy(), a).into() } } impl From<&str> for Ident { @@ -235,37 +234,32 @@ impl From<&str> for Ident { } } -impl From for Ident { - fn from(st: SpannedToken) -> Ident { - let spanned_str = Spanned::from(st.to_span(), st.token().to_string()); - Ident(spanned_str) +impl From for Ident { + fn from(lt: LocatedToken) -> Ident { + let located_str = Located::from(lt.location(), lt.token().to_string()); + Ident(located_str) } } impl From for Expression { fn from(i: Ident) -> Expression { - Expression { - span: i.0.span(), - kind: ExpressionKind::Variable(Path { - span: i.span(), - segments: vec![PathSegment::from(i)], - kind: PathKind::Plain, - }), - } + let location = i.location(); + let kind = ExpressionKind::Variable(Path::plain(vec![PathSegment::from(i)], location)); + Expression { location, kind } } } impl Ident { - pub fn span(&self) -> Span { - self.0.span() + pub fn location(&self) -> Location { + self.0.location() } - pub fn from_token(token: Token, span: Span) -> Ident { - Ident::from(SpannedToken::new(token, span)) + pub fn span(&self) -> Span { + self.0.span() } - pub fn new(text: String, span: Span) -> Ident { - Ident(Spanned::from(span, text)) + pub fn new(text: String, location: Location) -> Ident { + Ident(Located::from(location, text)) } } @@ -302,7 +296,7 @@ pub enum PathKind { pub struct UseTree { pub prefix: Path, pub kind: UseTreeKind, - pub span: Span, + pub location: Location, } impl Display for UseTree { @@ -361,6 +355,12 @@ impl UseTree { } } +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct UnsafeExpression { + pub block: BlockExpression, + pub unsafe_keyword_location: Location, +} + /// A special kind of path in the form `::ident`. /// Note that this path must consist of exactly two segments. /// @@ -390,10 +390,16 @@ pub struct TypePath { pub struct Path { pub segments: Vec, pub kind: PathKind, - pub span: Span, + pub location: Location, + // The location of `kind` (this is the same as `location` for plain kinds) + pub kind_location: Location, } impl Path { + pub fn plain(segments: Vec, location: Location) -> Self { + Self { segments, location, kind: PathKind::Plain, kind_location: location } + } + pub fn pop(&mut self) -> PathSegment { self.segments.pop().unwrap() } @@ -404,17 +410,18 @@ impl Path { } /// Construct a PathKind::Plain from this single - pub fn from_single(name: String, span: Span) -> Path { - let segment = Ident::from(Spanned::from(span, name)); + pub fn from_single(name: String, location: Location) -> Path { + let segment = Ident::from(Located::from(location, name)); Path::from_ident(segment) } pub fn from_ident(name: Ident) -> Path { - Path { span: name.span(), segments: vec![PathSegment::from(name)], kind: PathKind::Plain } + let location = name.location(); + Path::plain(vec![PathSegment::from(name)], location) } pub fn span(&self) -> Span { - self.span + self.location.span } pub fn last_segment(&self) -> PathSegment { @@ -487,7 +494,7 @@ impl Path { pub struct PathSegment { pub ident: Ident, pub generics: Option>, - pub span: Span, + pub location: Location, } impl PathSegment { @@ -498,20 +505,29 @@ impl PathSegment { /// /// 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()) + if self.ident.location().file == self.location.file { + Span::from(self.ident.span().end()..self.location.span.end()) + } else { + self.location.span + } + } + + pub fn turbofish_location(&self) -> Location { + Location::new(self.turbofish_span(), self.location.file) } pub fn turbofish(&self) -> Option { - self.generics - .as_ref() - .map(|generics| Turbofish { span: self.turbofish_span(), generics: generics.clone() }) + self.generics.as_ref().map(|generics| Turbofish { + location: self.turbofish_location(), + generics: generics.clone(), + }) } } impl From for PathSegment { fn from(ident: Ident) -> PathSegment { - let span = ident.span(); - PathSegment { ident, generics: None, span } + let location = ident.location(); + PathSegment { ident, generics: None, location } } } @@ -550,35 +566,39 @@ pub struct AssignStatement { #[derive(Debug, PartialEq, Eq, Clone)] pub enum LValue { Ident(Ident), - MemberAccess { object: Box, field_name: Ident, span: Span }, - Index { array: Box, index: Expression, span: Span }, - Dereference(Box, Span), - Interned(InternedExpressionKind, Span), + MemberAccess { object: Box, field_name: Ident, location: Location }, + Index { array: Box, index: Expression, location: Location }, + Dereference(Box, Location), + Interned(InternedExpressionKind, Location), } #[derive(Debug, PartialEq, Eq, Clone)] pub enum Pattern { Identifier(Ident), - Mutable(Box, Span, /*is_synthesized*/ bool), - Tuple(Vec, Span), - Struct(Path, Vec<(Ident, Pattern)>, Span), - Interned(InternedPattern, Span), + Mutable(Box, Location, /*is_synthesized*/ bool), + Tuple(Vec, Location), + Struct(Path, Vec<(Ident, Pattern)>, Location), + Interned(InternedPattern, Location), } impl Pattern { pub fn is_synthesized(&self) -> bool { matches!(self, Pattern::Mutable(_, _, true)) } - - pub fn span(&self) -> Span { + pub fn location(&self) -> Location { match self { - Pattern::Identifier(ident) => ident.span(), - Pattern::Mutable(_, span, _) - | Pattern::Tuple(_, span) - | Pattern::Struct(_, _, span) - | Pattern::Interned(_, span) => *span, + Pattern::Identifier(ident) => ident.location(), + Pattern::Mutable(_, location, _) + | Pattern::Tuple(_, location) + | Pattern::Struct(_, _, location) + | Pattern::Interned(_, location) => *location, } } + + pub fn span(&self) -> Span { + self.location().span + } + pub fn name_ident(&self) -> &Ident { match self { Pattern::Identifier(name_ident) => name_ident, @@ -591,17 +611,17 @@ impl Pattern { match self { Pattern::Identifier(ident) => Some(Expression { kind: ExpressionKind::Variable(Path::from_ident(ident.clone())), - span: ident.span(), + location: ident.location(), }), Pattern::Mutable(_, _, _) => None, - Pattern::Tuple(patterns, span) => { + Pattern::Tuple(patterns, location) => { let mut expressions = Vec::new(); for pattern in patterns { expressions.push(pattern.try_as_expression(interner)?); } - Some(Expression { kind: ExpressionKind::Tuple(expressions), span: *span }) + Some(Expression { kind: ExpressionKind::Tuple(expressions), location: *location }) } - Pattern::Struct(path, patterns, span) => { + Pattern::Struct(path, patterns, location) => { let mut fields = Vec::new(); for (field, pattern) in patterns { let expression = pattern.try_as_expression(interner)?; @@ -611,9 +631,8 @@ impl Pattern { kind: ExpressionKind::Constructor(Box::new(ConstructorExpression { typ: UnresolvedType::from_path(path.clone()), fields, - struct_type: None, })), - span: *span, + location: *location, }) } Pattern::Interned(id, _) => interner.get_pattern(*id).try_as_expression(interner), @@ -625,13 +644,13 @@ impl LValue { pub fn as_expression(&self) -> Expression { let kind = match self { LValue::Ident(ident) => ExpressionKind::Variable(Path::from_ident(ident.clone())), - LValue::MemberAccess { object, field_name, span: _ } => { + LValue::MemberAccess { object, field_name, location: _ } => { ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { lhs: object.as_expression(), rhs: field_name.clone(), })) } - LValue::Index { array, index, span: _ } => { + LValue::Index { array, index, location: _ } => { ExpressionKind::Index(Box::new(IndexExpression { collection: array.as_expression(), index: index.clone(), @@ -645,52 +664,58 @@ impl LValue { } LValue::Interned(id, _) => ExpressionKind::Interned(*id), }; - let span = self.span(); - Expression::new(kind, span) + Expression::new(kind, self.location()) } pub fn from_expression(expr: Expression) -> Option { - LValue::from_expression_kind(expr.kind, expr.span) + LValue::from_expression_kind(expr.kind, expr.location) } - pub fn from_expression_kind(expr: ExpressionKind, span: Span) -> Option { + pub fn from_expression_kind(expr: ExpressionKind, location: Location) -> Option { match expr { ExpressionKind::Variable(path) => Some(LValue::Ident(path.as_ident().unwrap().clone())), ExpressionKind::MemberAccess(member_access) => Some(LValue::MemberAccess { object: Box::new(LValue::from_expression(member_access.lhs)?), field_name: member_access.rhs, - span, + location, }), ExpressionKind::Index(index) => Some(LValue::Index { array: Box::new(LValue::from_expression(index.collection)?), index: index.index, - span, + location, }), ExpressionKind::Prefix(prefix) => { if matches!( prefix.operator, crate::ast::UnaryOp::Dereference { implicitly_added: false } ) { - Some(LValue::Dereference(Box::new(LValue::from_expression(prefix.rhs)?), span)) + Some(LValue::Dereference( + Box::new(LValue::from_expression(prefix.rhs)?), + location, + )) } else { None } } ExpressionKind::Parenthesized(expr) => LValue::from_expression(*expr), - ExpressionKind::Interned(id) => Some(LValue::Interned(id, span)), + ExpressionKind::Interned(id) => Some(LValue::Interned(id, location)), _ => None, } } - pub fn span(&self) -> Span { + pub fn location(&self) -> Location { match self { - LValue::Ident(ident) => ident.span(), - LValue::MemberAccess { span, .. } - | LValue::Index { span, .. } - | LValue::Dereference(_, span) => *span, - LValue::Interned(_, span) => *span, + LValue::Ident(ident) => ident.location(), + LValue::MemberAccess { location, .. } + | LValue::Index { location, .. } + | LValue::Dereference(_, location) + | LValue::Interned(_, location) => *location, } } + + pub fn span(&self) -> Span { + self.location().span + } } #[derive(Debug, PartialEq, Eq, Clone)] @@ -707,13 +732,16 @@ impl ForBounds { /// Returns the `start` and `end` expressions. pub(crate) fn into_half_open(self) -> (Expression, Expression) { let end = if self.inclusive { - let end_span = self.end.span; + let end_location = self.end.location; let end = ExpressionKind::Infix(Box::new(InfixExpression { lhs: self.end, - operator: Spanned::from(end_span, BinaryOpKind::Add), - rhs: Expression::new(ExpressionKind::integer(FieldElement::from(1u32)), end_span), + operator: Located::from(end_location, BinaryOpKind::Add), + rhs: Expression::new( + ExpressionKind::integer(FieldElement::from(1u32)), + end_location, + ), })); - Expression::new(end, end_span) + Expression::new(end, end_location) } else { self.end }; @@ -758,7 +786,7 @@ impl ForRange { self, identifier: Ident, block: Expression, - for_loop_span: Span, + for_loop_location: Location, ) -> Statement { // Counter used to generate unique names when desugaring // code in the parser requires the creation of fresh variables. @@ -769,96 +797,91 @@ impl ForRange { unreachable!() } ForRange::Array(array) => { - let array_span = array.span; + let array_location = array.location; let start_range = ExpressionKind::integer(FieldElement::zero()); - let start_range = Expression::new(start_range, array_span); + let start_range = Expression::new(start_range, array_location); let next_unique_id = unique_name_counter; unique_name_counter += 1; let array_name = format!("$i{next_unique_id}"); - let array_span = array.span; - let array_ident = Ident::new(array_name, array_span); + let array_location = array.location; + let array_ident = Ident::new(array_name, array_location); // let fresh1 = array; let let_array = Statement { kind: StatementKind::new_let( Pattern::Identifier(array_ident.clone()), - UnresolvedTypeData::Unspecified.with_span(Default::default()), + UnresolvedTypeData::Unspecified.with_dummy_location(), array, vec![], ), - span: array_span, + location: array_location, }; // array.len() let segments = vec![PathSegment::from(array_ident)]; - let array_ident = ExpressionKind::Variable(Path { - segments, - kind: PathKind::Plain, - span: array_span, - }); + let array_ident = ExpressionKind::Variable(Path::plain(segments, array_location)); let end_range = ExpressionKind::MethodCall(Box::new(MethodCallExpression { - object: Expression::new(array_ident.clone(), array_span), - method_name: Ident::new("len".to_string(), array_span), + object: Expression::new(array_ident.clone(), array_location), + method_name: Ident::new("len".to_string(), array_location), generics: None, is_macro_call: false, arguments: vec![], })); - let end_range = Expression::new(end_range, array_span); + let end_range = Expression::new(end_range, array_location); let next_unique_id = unique_name_counter; let index_name = format!("$i{next_unique_id}"); - let fresh_identifier = Ident::new(index_name.clone(), array_span); + let fresh_identifier = Ident::new(index_name.clone(), array_location); // array[i] - 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 segments = vec![PathSegment::from(Ident::new(index_name, array_location))]; + let index_ident = ExpressionKind::Variable(Path::plain(segments, array_location)); let loop_element = ExpressionKind::Index(Box::new(IndexExpression { - collection: Expression::new(array_ident, array_span), - index: Expression::new(index_ident, array_span), + collection: Expression::new(array_ident, array_location), + index: Expression::new(index_ident, array_location), })); // let elem = array[i]; let let_elem = Statement { kind: StatementKind::new_let( Pattern::Identifier(identifier), - UnresolvedTypeData::Unspecified.with_span(Default::default()), - Expression::new(loop_element, array_span), + UnresolvedTypeData::Unspecified.with_dummy_location(), + Expression::new(loop_element, array_location), vec![], ), - span: array_span, + location: array_location, }; - let block_span = block.span; + let block_location = block.location; let new_block = BlockExpression { statements: vec![ let_elem, - Statement { kind: StatementKind::Expression(block), span: block_span }, + Statement { + kind: StatementKind::Expression(block), + location: block_location, + }, ], }; - let new_block = Expression::new(ExpressionKind::Block(new_block), block_span); + let new_block = Expression::new(ExpressionKind::Block(new_block), block_location); let for_loop = Statement { kind: StatementKind::For(ForLoopStatement { identifier: fresh_identifier, range: ForRange::range(start_range, end_range), block: new_block, - span: for_loop_span, + location: for_loop_location, }), - span: for_loop_span, + location: for_loop_location, }; let block = ExpressionKind::Block(BlockExpression { statements: vec![let_array, for_loop], }); Statement { - kind: StatementKind::Expression(Expression::new(block, for_loop_span)), - span: for_loop_span, + kind: StatementKind::Expression(Expression::new(block, for_loop_location)), + location: for_loop_location, } } } @@ -870,14 +893,14 @@ pub struct ForLoopStatement { pub identifier: Ident, pub range: ForRange, pub block: Expression, - pub span: Span, + pub location: Location, } #[derive(Debug, PartialEq, Eq, Clone)] pub struct WhileStatement { pub condition: Expression, pub body: Expression, - pub while_keyword_span: Span, + pub while_keyword_location: Location, } impl Display for StatementKind { @@ -921,10 +944,10 @@ impl Display for LValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { LValue::Ident(ident) => ident.fmt(f), - LValue::MemberAccess { object, field_name, span: _ } => { + LValue::MemberAccess { object, field_name, location: _ } => { write!(f, "{object}.{field_name}") } - LValue::Index { array, index, span: _ } => write!(f, "{array}[{index}]"), + LValue::Index { array, index, location: _ } => write!(f, "{array}[{index}]"), LValue::Dereference(lvalue, _span) => write!(f, "*{lvalue}"), LValue::Interned(_, _) => write!(f, "?Interned"), } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs index 8c8fe6e63872..7162a2d3ba68 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs @@ -4,7 +4,7 @@ use crate::ast::{Ident, UnresolvedGenerics, UnresolvedType}; use crate::token::SecondaryAttribute; use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::Location; use super::{Documented, ItemVisibility}; @@ -16,7 +16,7 @@ pub struct NoirStruct { pub visibility: ItemVisibility, pub generics: UnresolvedGenerics, pub fields: Vec>, - pub span: Span, + pub location: Location, } impl 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 e5040463d174..d7cc48ec7421 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/traits.rs @@ -1,7 +1,7 @@ use std::fmt::Display; use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::Location; use crate::ast::{ BlockExpression, Expression, FunctionReturnType, Ident, NoirFunction, Path, UnresolvedGenerics, @@ -20,7 +20,7 @@ pub struct NoirTrait { pub generics: UnresolvedGenerics, pub bounds: Vec, pub where_clause: Vec, - pub span: Span, + pub location: Location, pub items: Vec>, pub attributes: Vec, pub visibility: ItemVisibility, @@ -57,10 +57,10 @@ pub enum TraitItem { #[derive(Clone, Debug)] pub struct TypeImpl { pub object_type: UnresolvedType, - pub type_span: Span, + pub type_location: Location, pub generics: UnresolvedGenerics, pub where_clause: Vec, - pub methods: Vec<(Documented, Span)>, + pub methods: Vec<(Documented, Location)>, } /// Ast node for an implementation of a trait for a particular type @@ -104,7 +104,7 @@ pub struct TraitBound { #[derive(Clone, Debug)] pub struct TraitImplItem { pub kind: TraitImplItemKind, - pub span: Span, + pub location: Location, } #[derive(Clone, Debug)] @@ -197,11 +197,7 @@ impl Display for TraitItem { "{unconstrained}{visibility}{is_comptime}fn {name}<{generics}>({parameters}) -> {return_type} where {where_clause}" )?; - if let Some(body) = body { - write!(f, "{body}") - } else { - write!(f, ";") - } + if let Some(body) = body { write!(f, "{body}") } else { write!(f, ";") } } TraitItem::Constant { name, typ, default_value } => { write!(f, "let {name}: {typ}")?; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/type_alias.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/type_alias.rs index 5b26044c3289..532593fad0b0 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/type_alias.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/type_alias.rs @@ -1,6 +1,6 @@ use super::{Ident, ItemVisibility, UnresolvedGenerics, UnresolvedType}; use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::Location; use std::fmt::Display; /// Ast node for type aliases @@ -10,7 +10,7 @@ pub struct NoirTypeAlias { pub generics: UnresolvedGenerics, pub typ: UnresolvedType, pub visibility: ItemVisibility, - pub span: Span, + pub location: Location, } impl Display for NoirTypeAlias { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs index 7fe89489c3cf..b2142f266551 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs @@ -1,7 +1,7 @@ -use acvm::FieldElement; use noirc_errors::Span; use crate::{ + ParsedModule, QuotedType, ast::{ ArrayLiteral, AsTraitPath, AssignStatement, BlockExpression, CallExpression, CastExpression, ConstrainExpression, ConstructorExpression, Expression, ExpressionKind, @@ -16,15 +16,15 @@ use crate::{ InternedUnresolvedTypeData, QuotedTypeId, }, parser::{Item, ItemKind, ParsedSubModule}, + signed_field::SignedField, token::{FmtStrFragment, MetaAttribute, SecondaryAttribute, Tokens}, - ParsedModule, QuotedType, }; use super::{ ForBounds, FunctionReturnType, GenericTypeArgs, IntegerBitSize, ItemVisibility, MatchExpression, NoirEnumeration, Pattern, Signedness, TraitBound, TraitImplItemKind, TypePath, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, - UnresolvedTypeExpression, + UnresolvedTypeExpression, UnsafeExpression, }; #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -172,7 +172,7 @@ pub trait Visitor { fn visit_literal_bool(&mut self, _: bool, _: Span) {} - fn visit_literal_integer(&mut self, _value: FieldElement, _negative: bool, _: Span) {} + fn visit_literal_integer(&mut self, _value: SignedField, _: Span) {} fn visit_literal_str(&mut self, _: &str, _: Span) {} @@ -242,7 +242,7 @@ pub trait Visitor { true } - fn visit_unsafe(&mut self, _: &BlockExpression, _: Span) -> bool { + fn visit_unsafe_expression(&mut self, _: &UnsafeExpression, _: Span) -> bool { true } @@ -520,31 +520,33 @@ impl Item { } pub fn accept_children(&self, visitor: &mut impl Visitor) { + let span = self.location.span; + match &self.kind { ItemKind::Submodules(parsed_sub_module) => { - parsed_sub_module.accept(self.span, visitor); + parsed_sub_module.accept(span, visitor); } - ItemKind::Function(noir_function) => noir_function.accept(self.span, visitor), + ItemKind::Function(noir_function) => noir_function.accept(span, visitor), ItemKind::TraitImpl(noir_trait_impl) => { - noir_trait_impl.accept(self.span, visitor); + noir_trait_impl.accept(span, visitor); } - ItemKind::Impl(type_impl) => type_impl.accept(self.span, visitor), + ItemKind::Impl(type_impl) => type_impl.accept(span, visitor), ItemKind::Global(let_statement, _visibility) => { - if visitor.visit_global(let_statement, self.span) { + if visitor.visit_global(let_statement, span) { let_statement.accept(visitor); } } - ItemKind::Trait(noir_trait) => noir_trait.accept(self.span, visitor), + ItemKind::Trait(noir_trait) => noir_trait.accept(span, visitor), ItemKind::Import(use_tree, visibility) => { - if visitor.visit_import(use_tree, self.span, *visibility) { + if visitor.visit_import(use_tree, span, *visibility) { use_tree.accept(visitor); } } - ItemKind::TypeAlias(noir_type_alias) => noir_type_alias.accept(self.span, visitor), - ItemKind::Struct(noir_struct) => noir_struct.accept(self.span, visitor), - ItemKind::Enum(noir_enum) => noir_enum.accept(self.span, visitor), + ItemKind::TypeAlias(noir_type_alias) => noir_type_alias.accept(span, visitor), + ItemKind::Struct(noir_struct) => noir_struct.accept(span, visitor), + ItemKind::Enum(noir_enum) => noir_enum.accept(span, visitor), ItemKind::ModuleDecl(module_declaration) => { - module_declaration.accept(self.span, visitor); + module_declaration.accept(span, visitor); } ItemKind::InnerAttribute(attribute) => { attribute.accept(AttributeTarget::Module, visitor); @@ -620,7 +622,7 @@ impl TraitImplItem { } pub fn accept_children(&self, visitor: &mut impl Visitor) { - self.kind.accept(self.span, visitor); + self.kind.accept(self.location.span, visitor); } } @@ -663,8 +665,8 @@ impl TypeImpl { pub fn accept_children(&self, visitor: &mut impl Visitor) { self.object_type.accept(visitor); - for (method, span) in &self.methods { - method.item.accept(*span, visitor); + for (method, location) in &self.methods { + method.item.accept(location.span, visitor); } } } @@ -843,79 +845,78 @@ impl Expression { } pub fn accept_children(&self, visitor: &mut impl Visitor) { + let span = self.location.span; match &self.kind { - ExpressionKind::Literal(literal) => literal.accept(self.span, visitor), + ExpressionKind::Literal(literal) => literal.accept(span, visitor), ExpressionKind::Block(block_expression) => { - block_expression.accept(Some(self.span), visitor); + block_expression.accept(Some(span), visitor); } ExpressionKind::Prefix(prefix_expression) => { - prefix_expression.accept(self.span, visitor); + prefix_expression.accept(span, visitor); } ExpressionKind::Index(index_expression) => { - index_expression.accept(self.span, visitor); + index_expression.accept(span, visitor); } ExpressionKind::Call(call_expression) => { - call_expression.accept(self.span, visitor); + call_expression.accept(span, visitor); } ExpressionKind::MethodCall(method_call_expression) => { - method_call_expression.accept(self.span, visitor); + method_call_expression.accept(span, visitor); } ExpressionKind::Constrain(constrain) => { constrain.accept(visitor); } ExpressionKind::Constructor(constructor_expression) => { - constructor_expression.accept(self.span, visitor); + constructor_expression.accept(span, visitor); } ExpressionKind::MemberAccess(member_access_expression) => { - member_access_expression.accept(self.span, visitor); + member_access_expression.accept(span, visitor); } ExpressionKind::Cast(cast_expression) => { - cast_expression.accept(self.span, visitor); + cast_expression.accept(span, visitor); } ExpressionKind::Infix(infix_expression) => { - infix_expression.accept(self.span, visitor); + infix_expression.accept(span, visitor); } ExpressionKind::If(if_expression) => { - if_expression.accept(self.span, visitor); + if_expression.accept(span, visitor); } ExpressionKind::Match(match_expression) => { - match_expression.accept(self.span, visitor); + match_expression.accept(span, visitor); } ExpressionKind::Tuple(expressions) => { - if visitor.visit_tuple(expressions, self.span) { + if visitor.visit_tuple(expressions, span) { visit_expressions(expressions, visitor); } } - ExpressionKind::Lambda(lambda) => lambda.accept(self.span, visitor), + ExpressionKind::Lambda(lambda) => lambda.accept(span, visitor), ExpressionKind::Parenthesized(expression) => { - if visitor.visit_parenthesized(expression, self.span) { + if visitor.visit_parenthesized(expression, span) { expression.accept(visitor); } } ExpressionKind::Unquote(expression) => { - if visitor.visit_unquote(expression, self.span) { + if visitor.visit_unquote(expression, span) { expression.accept(visitor); } } ExpressionKind::Comptime(block_expression, _) => { - if visitor.visit_comptime_expression(block_expression, self.span) { + if visitor.visit_comptime_expression(block_expression, span) { block_expression.accept(None, visitor); } } - ExpressionKind::Unsafe(block_expression, _) => { - if visitor.visit_unsafe(block_expression, self.span) { - block_expression.accept(None, visitor); - } + ExpressionKind::Unsafe(unsafe_expression) => { + unsafe_expression.accept(span, visitor); } ExpressionKind::Variable(path) => { - if visitor.visit_variable(path, self.span) { + if visitor.visit_variable(path, span) { path.accept(visitor); } } ExpressionKind::AsTraitPath(as_trait_path) => { - as_trait_path.accept(self.span, visitor); + as_trait_path.accept(span, visitor); } - ExpressionKind::TypePath(path) => path.accept(self.span, visitor), + ExpressionKind::TypePath(path) => path.accept(span, visitor), ExpressionKind::Quote(tokens) => visitor.visit_quote(tokens), ExpressionKind::Resolved(expr_id) => visitor.visit_resolved_expression(*expr_id), ExpressionKind::Interned(id) => visitor.visit_interned_expression(*id), @@ -945,8 +946,8 @@ impl Literal { } } Literal::Bool(value) => visitor.visit_literal_bool(*value, span), - Literal::Integer(value, negative) => { - visitor.visit_literal_integer(*value, *negative, span); + Literal::Integer(value) => { + visitor.visit_literal_integer(*value, span); } Literal::Str(str) => visitor.visit_literal_str(str, span), Literal::RawStr(str, length) => visitor.visit_literal_raw_str(str, *length, span), @@ -1262,23 +1263,23 @@ impl LValue { pub fn accept_children(&self, visitor: &mut impl Visitor) { match self { LValue::Ident(ident) => visitor.visit_lvalue_ident(ident), - LValue::MemberAccess { object, field_name, span } => { - if visitor.visit_lvalue_member_access(object, field_name, *span) { + LValue::MemberAccess { object, field_name, location } => { + if visitor.visit_lvalue_member_access(object, field_name, location.span) { object.accept(visitor); } } - LValue::Index { array, index, span } => { - if visitor.visit_lvalue_index(array, index, *span) { + LValue::Index { array, index, location } => { + if visitor.visit_lvalue_index(array, index, location.span) { array.accept(visitor); index.accept(visitor); } } - LValue::Dereference(lvalue, span) => { - if visitor.visit_lvalue_dereference(lvalue, *span) { + LValue::Dereference(lvalue, location) => { + if visitor.visit_lvalue_dereference(lvalue, location.span) { lvalue.accept(visitor); } } - LValue::Interned(id, span) => visitor.visit_lvalue_interned(*id, *span), + LValue::Interned(id, location) => visitor.visit_lvalue_interned(*id, location.span), } } } @@ -1301,6 +1302,18 @@ impl ForRange { } } +impl UnsafeExpression { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_unsafe_expression(self, span) { + self.accept_children(span, visitor); + } + } + + pub fn accept_children(&self, span: Span, visitor: &mut impl Visitor) { + self.block.accept(Some(span), visitor); + } +} + impl AsTraitPath { pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { if visitor.visit_as_trait_path(self, span) { @@ -1339,73 +1352,84 @@ impl UnresolvedType { pub fn accept_children(&self, visitor: &mut impl Visitor) { match &self.typ { UnresolvedTypeData::Array(unresolved_type_expression, unresolved_type) => { - if visitor.visit_array_type(unresolved_type_expression, unresolved_type, self.span) - { + if visitor.visit_array_type( + unresolved_type_expression, + unresolved_type, + self.location.span, + ) { unresolved_type.accept(visitor); } } UnresolvedTypeData::Slice(unresolved_type) => { - if visitor.visit_slice_type(unresolved_type, self.span) { + if visitor.visit_slice_type(unresolved_type, self.location.span) { unresolved_type.accept(visitor); } } UnresolvedTypeData::Parenthesized(unresolved_type) => { - if visitor.visit_parenthesized_type(unresolved_type, self.span) { + if visitor.visit_parenthesized_type(unresolved_type, self.location.span) { unresolved_type.accept(visitor); } } UnresolvedTypeData::Named(path, generic_type_args, _) => { - if visitor.visit_named_type(path, generic_type_args, self.span) { + if visitor.visit_named_type(path, generic_type_args, self.location.span) { path.accept(visitor); generic_type_args.accept(visitor); } } UnresolvedTypeData::TraitAsType(path, generic_type_args) => { - if visitor.visit_trait_as_type(path, generic_type_args, self.span) { + if visitor.visit_trait_as_type(path, generic_type_args, self.location.span) { path.accept(visitor); generic_type_args.accept(visitor); } } UnresolvedTypeData::MutableReference(unresolved_type) => { - if visitor.visit_mutable_reference_type(unresolved_type, self.span) { + if visitor.visit_mutable_reference_type(unresolved_type, self.location.span) { unresolved_type.accept(visitor); } } UnresolvedTypeData::Tuple(unresolved_types) => { - if visitor.visit_tuple_type(unresolved_types, self.span) { + if visitor.visit_tuple_type(unresolved_types, self.location.span) { visit_unresolved_types(unresolved_types, visitor); } } UnresolvedTypeData::Function(args, ret, env, unconstrained) => { - if visitor.visit_function_type(args, ret, env, *unconstrained, self.span) { + if visitor.visit_function_type(args, ret, env, *unconstrained, self.location.span) { visit_unresolved_types(args, visitor); ret.accept(visitor); env.accept(visitor); } } UnresolvedTypeData::AsTraitPath(as_trait_path) => { - if visitor.visit_as_trait_path_type(as_trait_path, self.span) { - as_trait_path.accept(self.span, visitor); + if visitor.visit_as_trait_path_type(as_trait_path, self.location.span) { + as_trait_path.accept(self.location.span, visitor); } } - UnresolvedTypeData::Expression(expr) => visitor.visit_expression_type(expr, self.span), + UnresolvedTypeData::Expression(expr) => { + visitor.visit_expression_type(expr, self.location.span); + } UnresolvedTypeData::FormatString(expr, typ) => { - if visitor.visit_format_string_type(expr, typ, self.span) { + if visitor.visit_format_string_type(expr, typ, self.location.span) { typ.accept(visitor); } } - UnresolvedTypeData::String(expr) => visitor.visit_string_type(expr, self.span), - UnresolvedTypeData::Unspecified => visitor.visit_unspecified_type(self.span), - UnresolvedTypeData::Quoted(typ) => visitor.visit_quoted_type(typ, self.span), - UnresolvedTypeData::FieldElement => visitor.visit_field_element_type(self.span), - UnresolvedTypeData::Integer(signdness, size) => { - visitor.visit_integer_type(*signdness, *size, self.span); + UnresolvedTypeData::String(expr) => visitor.visit_string_type(expr, self.location.span), + UnresolvedTypeData::Unspecified => visitor.visit_unspecified_type(self.location.span), + UnresolvedTypeData::Quoted(typ) => visitor.visit_quoted_type(typ, self.location.span), + UnresolvedTypeData::FieldElement => { + visitor.visit_field_element_type(self.location.span); + } + UnresolvedTypeData::Integer(signedness, size) => { + visitor.visit_integer_type(*signedness, *size, self.location.span); + } + UnresolvedTypeData::Bool => visitor.visit_bool_type(self.location.span), + UnresolvedTypeData::Unit => visitor.visit_unit_type(self.location.span), + UnresolvedTypeData::Resolved(id) => { + visitor.visit_resolved_type(*id, self.location.span); + } + UnresolvedTypeData::Interned(id) => { + visitor.visit_interned_type(*id, self.location.span); } - UnresolvedTypeData::Bool => visitor.visit_bool_type(self.span), - UnresolvedTypeData::Unit => visitor.visit_unit_type(self.span), - UnresolvedTypeData::Resolved(id) => visitor.visit_resolved_type(*id, self.span), - UnresolvedTypeData::Interned(id) => visitor.visit_interned_type(*id, self.span), - UnresolvedTypeData::Error => visitor.visit_error_type(self.span), + UnresolvedTypeData::Error => visitor.visit_error_type(self.location.span), } } } @@ -1484,28 +1508,28 @@ impl Pattern { pub fn accept_children(&self, visitor: &mut impl Visitor) { match self { Pattern::Identifier(ident) => visitor.visit_identifier_pattern(ident), - Pattern::Mutable(pattern, span, is_synthesized) => { - if visitor.visit_mutable_pattern(pattern, *span, *is_synthesized) { + Pattern::Mutable(pattern, location, is_synthesized) => { + if visitor.visit_mutable_pattern(pattern, location.span, *is_synthesized) { pattern.accept(visitor); } } - Pattern::Tuple(patterns, span) => { - if visitor.visit_tuple_pattern(patterns, *span) { + Pattern::Tuple(patterns, location) => { + if visitor.visit_tuple_pattern(patterns, location.span) { for pattern in patterns { pattern.accept(visitor); } } } - Pattern::Struct(path, fields, span) => { - if visitor.visit_struct_pattern(path, fields, *span) { + Pattern::Struct(path, fields, location) => { + if visitor.visit_struct_pattern(path, fields, location.span) { path.accept(visitor); for (_, pattern) in fields { pattern.accept(visitor); } } } - Pattern::Interned(id, span) => { - visitor.visit_interned_pattern(id, *span); + Pattern::Interned(id, location) => { + visitor.visit_interned_pattern(id, location.span); } } } 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 d80302401e74..48bed1c41997 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs @@ -1,13 +1,15 @@ use crate::ast::PathSegment; use crate::parse_program; use crate::parser::ParsedModule; +use crate::signed_field::SignedField; use crate::{ ast, - ast::{Path, PathKind}, + ast::Path, parser::{Item, ItemKind}, }; +use fm::FileId; use noirc_errors::debug_info::{DebugFnId, DebugFunction}; -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Location, Span}; use std::collections::HashMap; use std::collections::VecDeque; use std::mem::take; @@ -56,7 +58,7 @@ impl Default for DebugInstrumenter { } impl DebugInstrumenter { - pub fn instrument_module(&mut self, module: &mut ParsedModule) { + pub fn instrument_module(&mut self, module: &mut ParsedModule, file: FileId) { module.items.iter_mut().for_each(|item| { if let Item { kind: ItemKind::Function(f), .. } = item { self.walk_fn(&mut f.def); @@ -64,7 +66,7 @@ impl DebugInstrumenter { }); // this part absolutely must happen after ast traversal above // so that oracle functions don't get wrapped, resulting in infinite recursion: - self.insert_state_set_oracle(module, 8); + self.insert_state_set_oracle(module, 8, file); } fn insert_var(&mut self, var_name: &str) -> Option { @@ -102,7 +104,7 @@ impl DebugInstrumenter { let func_args = func.parameters.iter().map(|param| pattern_to_string(¶m.pattern)).collect(); let fn_id = self.insert_function(func_name, func_args); - let enter_stmt = build_debug_call_stmt("enter", fn_id, func.span); + let enter_stmt = build_debug_call_stmt("enter", fn_id, func.location); self.scope.push(HashMap::default()); let set_fn_params: Vec<_> = func @@ -122,11 +124,11 @@ impl DebugInstrumenter { let func_body = &mut func.body.statements; let mut statements = take(func_body); - self.walk_scope(&mut statements, func.span); + self.walk_scope(&mut statements, func.location); // walk_scope ensures that the last statement is the return value of the function let last_stmt = statements.pop().expect("at least one statement after walk_scope"); - let exit_stmt = build_debug_call_stmt("exit", fn_id, last_stmt.span); + let exit_stmt = build_debug_call_stmt("exit", fn_id, last_stmt.location); // rebuild function body func_body.push(enter_stmt); @@ -138,7 +140,7 @@ impl DebugInstrumenter { // Modify a vector of statements in-place, adding instrumentation for sets and drops. // This function will consume a scope level. - fn walk_scope(&mut self, statements: &mut Vec, span: Span) { + fn walk_scope(&mut self, statements: &mut Vec, location: Location) { statements.iter_mut().for_each(|stmt| self.walk_statement(stmt)); // extract and save the return value from the scope if there is one @@ -148,12 +150,12 @@ impl DebugInstrumenter { Some(ast::Statement { kind: ast::StatementKind::Expression(ret_expr), .. }) => { let save_ret_expr = ast::Statement { kind: ast::StatementKind::new_let( - ast::Pattern::Identifier(ident("__debug_expr", ret_expr.span)), - ast::UnresolvedTypeData::Unspecified.with_span(Default::default()), + ast::Pattern::Identifier(ident("__debug_expr", ret_expr.location)), + ast::UnresolvedTypeData::Unspecified.with_dummy_location(), ret_expr.clone(), vec![], ), - span: ret_expr.span, + location: ret_expr.location, }; statements.push(save_ret_expr); true @@ -165,39 +167,44 @@ impl DebugInstrumenter { } }; - let span = Span::empty(span.end()); + let span = Span::empty(location.span.end()); + let location = Location::new(span, location.file); // drop scope variables let scope_vars = self.scope.pop().unwrap_or_default(); - let drop_vars_stmts = scope_vars.values().map(|var_id| build_drop_var_stmt(*var_id, span)); + let drop_vars_stmts = + scope_vars.values().map(|var_id| build_drop_var_stmt(*var_id, location)); statements.extend(drop_vars_stmts); // return the saved value in __debug_expr, or unit otherwise let last_stmt = if has_ret_expr { ast::Statement { kind: ast::StatementKind::Expression(ast::Expression { - kind: ast::ExpressionKind::Variable(ast::Path { - segments: vec![PathSegment::from(ident("__debug_expr", span))], - kind: PathKind::Plain, - span, - }), - span, + kind: ast::ExpressionKind::Variable(ast::Path::plain( + vec![PathSegment::from(ident("__debug_expr", location))], + location, + )), + location, }), - span, + location, } } else { ast::Statement { kind: ast::StatementKind::Expression(ast::Expression { kind: ast::ExpressionKind::Literal(ast::Literal::Unit), - span, + location, }), - span, + location, } }; statements.push(last_stmt); } - fn walk_let_statement(&mut self, let_stmt: &ast::LetStatement, span: &Span) -> ast::Statement { + fn walk_let_statement( + &mut self, + let_stmt: &ast::LetStatement, + location: Location, + ) -> ast::Statement { // rewrites let statements written like this: // let (((a,b,c),D { d }),e,f) = x; // @@ -221,7 +228,7 @@ impl DebugInstrumenter { if *is_mut { ast::Pattern::Mutable( Box::new(ast::Pattern::Identifier(id.clone())), - id.span(), + id.location(), true, ) } else { @@ -238,7 +245,7 @@ impl DebugInstrumenter { if id.0.contents == "_" { ast::Expression { kind: ast::ExpressionKind::Literal(ast::Literal::Unit), - span: id.span(), + location: id.location(), } } else { id_expr(id) @@ -247,7 +254,7 @@ impl DebugInstrumenter { .collect(); let mut block_stmts = - vec![ast::Statement { kind: ast::StatementKind::Let(let_stmt.clone()), span: *span }]; + vec![ast::Statement { kind: ast::StatementKind::Let(let_stmt.clone()), location }]; block_stmts.extend(vars.iter().filter_map(|(id, _)| { let var_id = self.insert_var(&id.0.contents)?; Some(build_assign_var_stmt(var_id, id_expr(id))) @@ -255,31 +262,31 @@ impl DebugInstrumenter { block_stmts.push(ast::Statement { kind: ast::StatementKind::Expression(ast::Expression { kind: ast::ExpressionKind::Tuple(vars_exprs), - span: let_stmt.pattern.span(), + location: let_stmt.pattern.location(), }), - span: let_stmt.pattern.span(), + location: let_stmt.pattern.location(), }); ast::Statement { kind: ast::StatementKind::new_let( - ast::Pattern::Tuple(vars_pattern, let_stmt.pattern.span()), - ast::UnresolvedTypeData::Unspecified.with_span(Default::default()), + ast::Pattern::Tuple(vars_pattern, let_stmt.pattern.location()), + ast::UnresolvedTypeData::Unspecified.with_dummy_location(), ast::Expression { kind: ast::ExpressionKind::Block(ast::BlockExpression { statements: block_stmts, }), - span: let_stmt.expression.span, + location: let_stmt.expression.location, }, vec![], ), - span: *span, + location, } } fn walk_assign_statement( &mut self, assign_stmt: &ast::AssignStatement, - span: &Span, + location: Location, ) -> ast::Statement { // X = Y becomes: // X = { @@ -293,26 +300,26 @@ impl DebugInstrumenter { // }; let let_kind = ast::StatementKind::new_let( - ast::Pattern::Identifier(ident("__debug_expr", assign_stmt.expression.span)), - ast::UnresolvedTypeData::Unspecified.with_span(Default::default()), + ast::Pattern::Identifier(ident("__debug_expr", assign_stmt.expression.location)), + ast::UnresolvedTypeData::Unspecified.with_dummy_location(), assign_stmt.expression.clone(), vec![], ); - let expression_span = assign_stmt.expression.span; + let expression_location = assign_stmt.expression.location; let new_assign_stmt = match &assign_stmt.lvalue { ast::LValue::Ident(id) => { let var_id = self .lookup_var(&id.0.contents) .unwrap_or_else(|| panic!("var lookup failed for var_name={}", &id.0.contents)); - build_assign_var_stmt(var_id, id_expr(&ident("__debug_expr", id.span()))) + build_assign_var_stmt(var_id, id_expr(&ident("__debug_expr", id.location()))) } - ast::LValue::Dereference(_lv, span) => { + ast::LValue::Dereference(_lv, location) => { // TODO: this is a dummy statement for now, but we should // somehow track the derefence and update the pointed to // variable ast::Statement { - kind: ast::StatementKind::Expression(uint_expr(0, *span)), - span: *span, + kind: ast::StatementKind::Expression(uint_expr(0, *location)), + location: *location, } } _ => { @@ -327,12 +334,12 @@ impl DebugInstrumenter { }); break; } - ast::LValue::MemberAccess { object, field_name, span } => { + ast::LValue::MemberAccess { object, field_name, location } => { cursor = object; let field_name_id = self.insert_field_name(&field_name.0.contents); - indexes.push(sint_expr(-(field_name_id.0 as i128), *span)); + indexes.push(sint_expr(-(field_name_id.0 as i128), *location)); } - ast::LValue::Index { index, array, span: _ } => { + ast::LValue::Index { index, array, location: _ } => { cursor = array; indexes.push(index.clone()); } @@ -347,13 +354,13 @@ impl DebugInstrumenter { build_assign_member_stmt( var_id, &indexes, - &id_expr(&ident("__debug_expr", expression_span)), + &id_expr(&ident("__debug_expr", expression_location)), ) } }; let ret_kind = - ast::StatementKind::Expression(id_expr(&ident("__debug_expr", expression_span))); + ast::StatementKind::Expression(id_expr(&ident("__debug_expr", expression_location))); ast::Statement { kind: ast::StatementKind::Assign(ast::AssignStatement { @@ -361,23 +368,23 @@ impl DebugInstrumenter { expression: ast::Expression { kind: ast::ExpressionKind::Block(ast::BlockExpression { statements: vec![ - ast::Statement { kind: let_kind, span: expression_span }, + ast::Statement { kind: let_kind, location: expression_location }, new_assign_stmt, - ast::Statement { kind: ret_kind, span: expression_span }, + ast::Statement { kind: ret_kind, location: expression_location }, ], }), - span: expression_span, + location: expression_location, }, }), - span: *span, + location, } } fn walk_expr(&mut self, expr: &mut ast::Expression) { match &mut expr.kind { - ast::ExpressionKind::Block(ast::BlockExpression { ref mut statements, .. }) => { + ast::ExpressionKind::Block(ast::BlockExpression { statements, .. }) => { self.scope.push(HashMap::default()); - self.walk_scope(statements, expr.span); + self.walk_scope(statements, expr.location); } ast::ExpressionKind::Prefix(prefix_expr) => { self.walk_expr(&mut prefix_expr.rhs); @@ -389,19 +396,19 @@ impl DebugInstrumenter { ast::ExpressionKind::Call(call_expr) => { // TODO: push a stack frame or something here? self.walk_expr(&mut call_expr.func); - call_expr.arguments.iter_mut().for_each(|ref mut expr| { + call_expr.arguments.iter_mut().for_each(|expr| { self.walk_expr(expr); }); } ast::ExpressionKind::MethodCall(mc_expr) => { // TODO: also push a stack frame here self.walk_expr(&mut mc_expr.object); - mc_expr.arguments.iter_mut().for_each(|ref mut expr| { + mc_expr.arguments.iter_mut().for_each(|expr| { self.walk_expr(expr); }); } ast::ExpressionKind::Constructor(c_expr) => { - c_expr.fields.iter_mut().for_each(|(_id, ref mut expr)| { + c_expr.fields.iter_mut().for_each(|(_id, expr)| { self.walk_expr(expr); }); } @@ -442,9 +449,10 @@ impl DebugInstrumenter { let var_id = self.insert_var(var_name); let set_and_drop_stmt = var_id.map(|var_id| { + let span = Span::empty(for_stmt.location.span.end()); ( build_assign_var_stmt(var_id, id_expr(&for_stmt.identifier)), - build_drop_var_stmt(var_id, Span::empty(for_stmt.span.end())), + build_drop_var_stmt(var_id, Location::new(span, for_stmt.location.file)), ) }); @@ -453,7 +461,7 @@ impl DebugInstrumenter { let mut statements = Vec::new(); let block_statement = ast::Statement { kind: ast::StatementKind::Semi(for_stmt.block.clone()), - span: for_stmt.block.span, + location: for_stmt.block.location, }; if let Some((set_stmt, drop_stmt)) = set_and_drop_stmt { @@ -466,17 +474,17 @@ impl DebugInstrumenter { for_stmt.block = ast::Expression { kind: ast::ExpressionKind::Block(ast::BlockExpression { statements }), - span: for_stmt.span, + location: for_stmt.location, }; } fn walk_statement(&mut self, stmt: &mut ast::Statement) { match &mut stmt.kind { ast::StatementKind::Let(let_stmt) => { - *stmt = self.walk_let_statement(let_stmt, &stmt.span); + *stmt = self.walk_let_statement(let_stmt, stmt.location); } ast::StatementKind::Assign(assign_stmt) => { - *stmt = self.walk_assign_statement(assign_stmt, &stmt.span); + *stmt = self.walk_assign_statement(assign_stmt, stmt.location); } ast::StatementKind::Expression(expr) => { self.walk_expr(expr); @@ -484,20 +492,21 @@ impl DebugInstrumenter { ast::StatementKind::Semi(expr) => { self.walk_expr(expr); } - ast::StatementKind::For(ref mut for_stmt) => { + ast::StatementKind::For(for_stmt) => { self.walk_for(for_stmt); } _ => {} // Constrain, Error } } - fn insert_state_set_oracle(&self, module: &mut ParsedModule, n: u32) { + fn insert_state_set_oracle(&self, module: &mut ParsedModule, n: u32, file: FileId) { let member_assigns = (1..=n) .map(|i| format!["__debug_member_assign_{i}"]) .collect::>() .join(",\n"); - let (program, errors) = parse_program(&format!( - r#" + let (program, errors) = parse_program( + &format!( + r#" use __debug::{{ __debug_var_assign, __debug_var_drop, @@ -506,7 +515,9 @@ impl DebugInstrumenter { __debug_dereference_assign, {member_assigns}, }};"# - )); + ), + file, + ); if !errors.is_empty() { panic!("errors parsing internal oracle definitions: {errors:?}") } @@ -619,36 +630,34 @@ pub fn build_debug_crate_file() -> String { } fn build_assign_var_stmt(var_id: SourceVarId, expr: ast::Expression) -> ast::Statement { - let span = expr.span; + let location = expr.location; let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression { func: Box::new(ast::Expression { - kind: ast::ExpressionKind::Variable(ast::Path { - segments: vec![PathSegment::from(ident("__debug_var_assign", span))], - kind: PathKind::Plain, - span, - }), - span, + kind: ast::ExpressionKind::Variable(ast::Path::plain( + vec![PathSegment::from(ident("__debug_var_assign", location))], + location, + )), + location, }), is_macro_call: false, - arguments: vec![uint_expr(var_id.0 as u128, span), expr], + arguments: vec![uint_expr(var_id.0 as u128, location), expr], })); - ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } + ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, location }), location } } -fn build_drop_var_stmt(var_id: SourceVarId, span: Span) -> ast::Statement { +fn build_drop_var_stmt(var_id: SourceVarId, location: Location) -> ast::Statement { let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression { func: Box::new(ast::Expression { - kind: ast::ExpressionKind::Variable(ast::Path { - segments: vec![PathSegment::from(ident("__debug_var_drop", span))], - kind: PathKind::Plain, - span, - }), - span, + kind: ast::ExpressionKind::Variable(ast::Path::plain( + vec![PathSegment::from(ident("__debug_var_drop", location))], + location, + )), + location, }), is_macro_call: false, - arguments: vec![uint_expr(var_id.0 as u128, span)], + arguments: vec![uint_expr(var_id.0 as u128, location)], })); - ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } + ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, location }), location } } fn build_assign_member_stmt( @@ -660,44 +669,39 @@ fn build_assign_member_stmt( if arity > MAX_MEMBER_ASSIGN_DEPTH { unreachable!("Assignment to member exceeds maximum depth for debugging"); } - let span = expr.span; + let location = expr.location; let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression { func: Box::new(ast::Expression { - kind: ast::ExpressionKind::Variable(ast::Path { - segments: vec![PathSegment::from(ident( - &format!["__debug_member_assign_{arity}"], - span, - ))], - kind: PathKind::Plain, - span, - }), - span, + kind: ast::ExpressionKind::Variable(ast::Path::plain( + vec![PathSegment::from(ident(&format!["__debug_member_assign_{arity}"], location))], + location, + )), + location, }), is_macro_call: false, arguments: [ - vec![uint_expr(var_id.0 as u128, span)], + vec![uint_expr(var_id.0 as u128, location)], vec![expr.clone()], indexes.iter().rev().cloned().collect(), ] .concat(), })); - ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } + ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, location }), location } } -fn build_debug_call_stmt(fname: &str, fn_id: DebugFnId, span: Span) -> ast::Statement { +fn build_debug_call_stmt(fname: &str, fn_id: DebugFnId, location: Location) -> ast::Statement { let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression { func: Box::new(ast::Expression { - kind: ast::ExpressionKind::Variable(ast::Path { - segments: vec![PathSegment::from(ident(&format!["__debug_fn_{fname}"], span))], - kind: PathKind::Plain, - span, - }), - span, + kind: ast::ExpressionKind::Variable(ast::Path::plain( + vec![PathSegment::from(ident(&format!["__debug_fn_{fname}"], location))], + location, + )), + location, }), is_macro_call: false, - arguments: vec![uint_expr(fn_id.0 as u128, span)], + arguments: vec![uint_expr(fn_id.0 as u128, location)], })); - ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } + ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, location }), location } } fn pattern_vars(pattern: &ast::Pattern) -> Vec<(ast::Ident, bool)> { @@ -750,31 +754,28 @@ fn pattern_to_string(pattern: &ast::Pattern) -> String { } } -fn ident(s: &str, span: Span) -> ast::Ident { - ast::Ident(Spanned::from(span, s.to_string())) +fn ident(s: &str, location: Location) -> ast::Ident { + ast::Ident::new(s.to_string(), location) } fn id_expr(id: &ast::Ident) -> ast::Expression { ast::Expression { - kind: ast::ExpressionKind::Variable(Path { - segments: vec![PathSegment::from(id.clone())], - kind: PathKind::Plain, - span: id.span(), - }), - span: id.span(), + kind: ast::ExpressionKind::Variable(Path::plain( + vec![PathSegment::from(id.clone())], + id.location(), + )), + location: id.location(), } } -fn uint_expr(x: u128, span: Span) -> ast::Expression { - ast::Expression { - kind: ast::ExpressionKind::Literal(ast::Literal::Integer(x.into(), false)), - span, - } +fn uint_expr(x: u128, location: Location) -> ast::Expression { + let value = SignedField::positive(x); + let kind = ast::ExpressionKind::Literal(ast::Literal::Integer(value)); + ast::Expression { kind, location } } -fn sint_expr(x: i128, span: Span) -> ast::Expression { - ast::Expression { - kind: ast::ExpressionKind::Literal(ast::Literal::Integer(x.abs().into(), x < 0)), - span, - } +fn sint_expr(x: i128, location: Location) -> ast::Expression { + let value = SignedField::from_signed(x); + let kind = ast::ExpressionKind::Literal(ast::Literal::Integer(value)); + ast::Expression { kind, location } } 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 b78787249cb7..7091f7b261cd 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -1,10 +1,10 @@ use std::{collections::BTreeMap, fmt::Display}; -use fm::FileId; use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use crate::{ + Type, TypeBindings, UnificationError, ast::{Documented, Expression, ExpressionKind}, hir::{ comptime::{Interpreter, InterpreterError, Value}, @@ -22,28 +22,23 @@ use crate::{ node_interner::{DefinitionKind, DependencyId, FuncId, NodeInterner, TraitId, TypeId}, parser::{Item, ItemKind}, token::{MetaAttribute, SecondaryAttribute}, - Type, TypeBindings, UnificationError, }; -use super::{Elaborator, FunctionContext, ResolverMeta}; +use super::{ElaborateReason, Elaborator, FunctionContext, ResolverMeta}; #[derive(Debug, Copy, Clone)] struct AttributeContext { - // The file where generated items should be added - file: FileId, // The module where generated items should be added module: LocalModuleId, - // The file where the attribute is located - attribute_file: FileId, // The module where the attribute is located attribute_module: LocalModuleId, } -type CollectedAttributes = Vec<(FuncId, Value, Vec, AttributeContext, Span)>; +type CollectedAttributes = Vec<(FuncId, Value, Vec, AttributeContext, Location)>; impl AttributeContext { - fn new(file: FileId, module: LocalModuleId) -> Self { - Self { file, module, attribute_file: file, attribute_module: module } + fn new(module: LocalModuleId) -> Self { + Self { module, attribute_module: module } } } @@ -62,7 +57,6 @@ impl<'context> Elaborator<'context> { 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()); } }) @@ -71,14 +65,12 @@ impl<'context> Elaborator<'context> { pub fn elaborate_item_from_comptime_in_module<'a, T>( &'a mut self, module: ModuleId, - file: FileId, f: impl FnOnce(&mut Elaborator<'a>) -> T, ) -> T { self.elaborate_item_from_comptime(f, |elaborator| { elaborator.current_item = None; elaborator.crate_id = module.krate; elaborator.local_module = module.local_id; - elaborator.file = file; }) } @@ -95,16 +87,15 @@ impl<'context> Elaborator<'context> { self.usage_tracker, self.crate_graph, self.crate_id, - self.debug_comptime_in_file, self.interpreter_call_stack.clone(), - self.pedantic_solving, + self.options, + self.elaborate_reasons.clone(), ); elaborator.function_context.push(FunctionContext::default()); elaborator.scopes.start_function(); elaborator.local_module = self.local_module; - elaborator.file = self.file; setup(&mut elaborator); @@ -167,7 +158,7 @@ impl<'context> Elaborator<'context> { attribute_context, attributes_to_run, ) { - this.errors.push(error); + this.push_err(error); } }); } @@ -180,12 +171,12 @@ impl<'context> Elaborator<'context> { item: Value, attribute_context: AttributeContext, attributes_to_run: &mut CollectedAttributes, - ) -> Result<(), (CompilationError, FileId)> { - self.file = attribute_context.attribute_file; + ) -> Result<(), CompilationError> { self.local_module = attribute_context.attribute_module; - let span = attribute.span; + let location = attribute.location; - let function = Expression { kind: ExpressionKind::Variable(attribute.name.clone()), span }; + let function = + Expression { kind: ExpressionKind::Variable(attribute.name.clone()), location }; let arguments = attribute.arguments.clone(); // Elaborate the function, rolling back any errors generated in case it is unknown @@ -197,22 +188,25 @@ impl<'context> Elaborator<'context> { let definition_id = match self.interner.expression(&function) { HirExpression::Ident(ident, _) => ident.id, _ => { - let error = - ResolverError::AttributeFunctionIsNotAPath { function: function_string, span }; - return Err((error.into(), self.file)); + let error = ResolverError::AttributeFunctionIsNotAPath { + function: function_string, + location, + }; + return Err(error.into()); } }; let Some(definition) = self.interner.try_definition(definition_id) else { - let error = ResolverError::AttributeFunctionNotInScope { name: function_string, span }; - return Err((error.into(), self.file)); + let error = + ResolverError::AttributeFunctionNotInScope { name: function_string, location }; + return Err(error.into()); }; let DefinitionKind::Function(function) = definition.kind else { - return Err((ResolverError::NonFunctionInAnnotation { span }.into(), self.file)); + return Err(ResolverError::NonFunctionInAnnotation { location }.into()); }; - attributes_to_run.push((function, item, arguments, attribute_context, span)); + attributes_to_run.push((function, item, arguments, attribute_context, location)); Ok(()) } @@ -224,8 +218,7 @@ impl<'context> Elaborator<'context> { item: Value, location: Location, generated_items: &mut CollectedItems, - ) -> Result<(), (CompilationError, FileId)> { - self.file = attribute_context.file; + ) -> Result<(), CompilationError> { self.local_module = attribute_context.module; let mut interpreter = self.setup_interpreter(); @@ -236,20 +229,19 @@ impl<'context> Elaborator<'context> { arguments, location, ) - .map_err(|error| error.into_compilation_error_pair())?; + .map_err(CompilationError::from)?; arguments.insert(0, (item, location)); let value = interpreter .call_function(function, arguments, TypeBindings::new(), location) - .map_err(|error| error.into_compilation_error_pair())?; + .map_err(CompilationError::from)?; self.debug_comptime(location, |interner| value.display(interner).to_string()); if value != Value::Unit { - let items = value - .into_top_level_items(location, self) - .map_err(|error| error.into_compilation_error_pair())?; + let items = + value.into_top_level_items(location, self).map_err(CompilationError::from)?; self.add_items(items, generated_items, location); } @@ -303,7 +295,7 @@ impl<'context> Elaborator<'context> { let mut varargs = im::Vector::new(); for (i, arg) in arguments.into_iter().enumerate() { - let arg_location = Location::new(arg.span, location.file); + let arg_location = arg.location; let param_type = parameters.get(i).or(varargs_elem_type).unwrap_or(&Type::Error); let mut push_arg = |arg| { @@ -350,9 +342,14 @@ impl<'context> Elaborator<'context> { generated_items: &mut CollectedItems, location: Location, ) { + let previous_errors = + self.push_elaborate_reason_and_take_errors(ElaborateReason::RunningAttribute, location); + for item in items { self.add_item(item, generated_items, location); } + + self.pop_elaborate_reason(previous_errors); } pub(crate) fn add_item( @@ -371,13 +368,12 @@ impl<'context> Elaborator<'context> { self.usage_tracker, &function, module_id, - self.file, item.doc_comments, &mut self.errors, ) { let functions = vec![(self.local_module, id, function)]; generated_items.functions.push(UnresolvedFunctions { - file_id: self.file, + file_id: location.file, functions, trait_id: None, self_type: None, @@ -390,12 +386,12 @@ impl<'context> Elaborator<'context> { self.interner, &mut trait_impl, self.crate_id, - self.file, + location.file, self.local_module, ); generated_items.trait_impls.push(UnresolvedTraitImpl { - file_id: self.file, + file_id: location.file, module_id: self.local_module, r#trait: trait_impl.r#trait, object_type: trait_impl.object_type, @@ -420,14 +416,14 @@ impl<'context> Elaborator<'context> { self.usage_tracker, Documented::new(global, item.doc_comments), visibility, - self.file, + location.file, self.local_module, self.crate_id, ); generated_items.globals.push(global); if let Some(error) = error { - self.errors.push(error); + self.push_err(error); } } ItemKind::Struct(struct_def) => { @@ -436,7 +432,6 @@ impl<'context> Elaborator<'context> { self.def_maps.get_mut(&self.crate_id).unwrap(), self.usage_tracker, Documented::new(struct_def, item.doc_comments), - self.file, self.local_module, self.crate_id, &mut self.errors, @@ -450,7 +445,7 @@ impl<'context> Elaborator<'context> { self.def_maps.get_mut(&self.crate_id).unwrap(), self.usage_tracker, Documented::new(enum_def, item.doc_comments), - self.file, + location.file, self.local_module, self.crate_id, &mut self.errors, @@ -464,7 +459,7 @@ impl<'context> Elaborator<'context> { self.interner, generated_items, r#impl, - self.file, + location.file, module, &mut self.errors, ); @@ -476,9 +471,10 @@ impl<'context> Elaborator<'context> { | ItemKind::TypeAlias(_) | ItemKind::Submodules(_) | ItemKind::InnerAttribute(_) => { + let location = item.location; let item = item.kind.to_string(); let error = InterpreterError::UnsupportedTopLevelItemUnquote { item, location }; - self.errors.push(error.into_compilation_error_pair()); + self.push_err(error); } } } @@ -488,7 +484,7 @@ impl<'context> Elaborator<'context> { Some(DependencyId::Function(function)) => Some(function), _ => None, }; - Interpreter::new(self, self.crate_id, current_function, self.pedantic_solving) + Interpreter::new(self, self.crate_id, current_function) } pub(super) fn debug_comptime T>( @@ -496,12 +492,11 @@ impl<'context> Elaborator<'context> { location: Location, mut expr_f: F, ) { - if Some(location.file) == self.debug_comptime_in_file { + if Some(location.file) == self.options.debug_comptime_in_file { let displayed_expr = expr_f(self.interner); - self.errors.push(( - InterpreterError::debug_evaluate_comptime(displayed_expr, location).into(), - location.file, - )); + let error: CompilationError = + InterpreterError::debug_evaluate_comptime(displayed_expr, location).into(); + self.push_err(error); } } @@ -520,7 +515,7 @@ impl<'context> Elaborator<'context> { for (trait_id, trait_) in traits { let attributes = &trait_.trait_def.attributes; let item = Value::TraitDefinition(*trait_id); - let context = AttributeContext::new(trait_.file_id, trait_.module_id); + let context = AttributeContext::new(trait_.module_id); self.collect_comptime_attributes_on_item( attributes, item, @@ -532,7 +527,7 @@ impl<'context> Elaborator<'context> { for (struct_id, struct_def) in types { let attributes = &struct_def.struct_def.attributes; let item = Value::StructDefinition(*struct_id); - let context = AttributeContext::new(struct_def.file_id, struct_def.module_id); + let context = AttributeContext::new(struct_def.module_id); self.collect_comptime_attributes_on_item( attributes, item, @@ -547,9 +542,7 @@ impl<'context> Elaborator<'context> { self.sort_attributes_by_run_order(&mut attributes_to_run); // run - for (attribute, item, args, context, span) in attributes_to_run { - let location = Location::new(span, context.attribute_file); - + for (attribute, item, args, context, location) in attributes_to_run { let mut generated_items = CollectedItems::default(); self.elaborate_in_comptime_context(|this| { if let Err(error) = this.run_attribute( @@ -560,12 +553,19 @@ impl<'context> Elaborator<'context> { location, &mut generated_items, ) { - this.errors.push(error); + this.push_err(error); } }); if !generated_items.is_empty() { + let previous_errors = self.push_elaborate_reason_and_take_errors( + ElaborateReason::RunningAttribute, + location, + ); + self.elaborate_items(generated_items); + + self.pop_elaborate_reason(previous_errors); } } } @@ -575,8 +575,8 @@ impl<'context> Elaborator<'context> { // Sort each attribute by (module, location in file) so that we can execute in // the order they were defined in, running attributes in child modules first. - attributes.sort_by_key(|(_, _, _, ctx, span)| { - (module_order[&ctx.attribute_module], span.start()) + attributes.sort_by_key(|(_, _, _, ctx, location)| { + (module_order[&ctx.attribute_module], location.span.start()) }); } @@ -592,9 +592,7 @@ impl<'context> Elaborator<'context> { let attribute = &module_attribute.attribute; let context = AttributeContext { - file: module_attribute.file_id, module: module_attribute.module_id, - attribute_file: module_attribute.attribute_file_id, attribute_module: module_attribute.attribute_module_id, }; @@ -611,7 +609,7 @@ impl<'context> Elaborator<'context> { self.self_type = function_set.self_type.clone(); for (local_module, function_id, function) in &function_set.functions { - let context = AttributeContext::new(function_set.file_id, *local_module); + let context = AttributeContext::new(*local_module); let attributes = function.secondary_attributes(); let item = Value::FunctionDefinition(*function_id); self.collect_comptime_attributes_on_item( @@ -653,4 +651,35 @@ impl<'context> Elaborator<'context> { _ => false, } } + + /// Pushes an ElaborateReason but also `std::mem::take`s the current errors and returns them. + pub(crate) fn push_elaborate_reason_and_take_errors( + &mut self, + reason: ElaborateReason, + location: Location, + ) -> Vec { + self.elaborate_reasons.push_back((reason, location)); + std::mem::take(&mut self.errors) + } + + /// Pops en ElaborateREason. Receives the errors that were returned by `push_elaborate_reason` + /// so they are restored, while also wrapping errors in the current Elaborator in a ComptimeError. + pub(crate) fn pop_elaborate_reason(&mut self, previous_errors: Vec) { + let new_errors = std::mem::take(&mut self.errors); + let new_errors = self.wrap_errors_in_macro_error(new_errors); + self.errors = previous_errors; + self.push_errors(new_errors); + self.elaborate_reasons.pop_back(); + } + + fn wrap_errors_in_macro_error(&self, errors: Vec) -> Vec { + vecmap(errors, |error| self.wrap_error_in_macro_error(error)) + } + + fn wrap_error_in_macro_error(&self, mut error: CompilationError) -> CompilationError { + for (reason, location) in self.elaborate_reasons.iter().rev() { + error = CompilationError::ComptimeError(reason.to_macro_error(error, *location)); + } + error + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/enums.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/enums.rs index 02d9bfae4946..90849a750d56 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/enums.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/enums.rs @@ -1,25 +1,28 @@ +use std::collections::BTreeMap; + use fxhash::FxHashMap as HashMap; use iter_extended::{try_vecmap, vecmap}; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use crate::{ + DataType, Kind, Shared, Type, ast::{ - EnumVariant, Expression, ExpressionKind, FunctionKind, Literal, NoirEnumeration, - StatementKind, UnresolvedType, Visibility, + ConstructorExpression, EnumVariant, Expression, ExpressionKind, FunctionKind, Ident, + Literal, NoirEnumeration, StatementKind, UnresolvedType, Visibility, }, elaborator::path_resolution::PathResolutionItem, hir::{comptime::Value, resolution::errors::ResolverError, type_check::TypeCheckError}, hir_def::{ expr::{ Case, Constructor, HirBlockExpression, HirEnumConstructorExpression, HirExpression, - HirIdent, HirMatch, SignedField, + HirIdent, HirMatch, }, function::{FuncMeta, FunctionBody, HirFunction, Parameters}, stmt::{HirLetStatement, HirPattern, HirStatement}, }, node_interner::{DefinitionId, DefinitionKind, ExprId, FunctionModifiers, GlobalValue, TypeId}, + signed_field::SignedField, token::Attributes, - DataType, Kind, Shared, Type, }; use super::Elaborator; @@ -75,13 +78,13 @@ impl Elaborator<'_> { self_type: &Type, ) { let name = &variant.name; - let location = Location::new(variant.name.span(), self.file); + let location = variant.name.location(); let global_id = self.interner.push_empty_global( name.clone(), type_id.local_module_id(), type_id.krate(), - self.file, + name.location().file, Vec::new(), false, false, @@ -127,7 +130,7 @@ impl Elaborator<'_> { ) { let name_string = variant.name.to_string(); let datatype_ref = datatype.borrow(); - let location = Location::new(variant.name.span(), self.file); + let location = variant.name.location(); let id = self.interner.push_empty_fn(); @@ -176,7 +179,7 @@ impl Elaborator<'_> { function_body: FunctionBody::Resolved, source_crate: self.crate_id, source_module: type_id.local_module_id(), - source_file: self.file, + source_file: variant.name.location().file, self_type: None, }; @@ -219,7 +222,7 @@ impl Elaborator<'_> { HirPattern::Identifier(ident) => { let id = self.interner.push_expr(HirExpression::Ident(ident.clone(), None)); self.interner.push_expr_type(id, typ.clone()); - self.interner.push_expr_location(id, location.span, location.file); + self.interner.push_expr_location(id, location); id } _ => unreachable!(), @@ -235,7 +238,7 @@ impl Elaborator<'_> { let enum_generics = self_type.borrow().generic_types(); let typ = Type::DataType(self_type.clone(), enum_generics); self.interner.push_expr_type(body, typ); - self.interner.push_expr_location(body, location.span, location.file); + self.interner.push_expr_location(body, location); body } @@ -270,17 +273,18 @@ impl Elaborator<'_> { let rows = vecmap(rules, |(pattern, branch)| { self.push_scope(); - let pattern = self.expression_to_pattern(pattern, &expected_pattern_type); + let pattern = + self.expression_to_pattern(pattern, &expected_pattern_type, &mut Vec::new()); let columns = vec![Column::new(variable_to_match, pattern)]; let guard = None; - let body_span = branch.type_span(); + let body_location = branch.type_location(); let (body, body_type) = self.elaborate_expression(branch); self.unify(&body_type, &result_type, || TypeCheckError::TypeMismatch { expected_typ: result_type.to_string(), expr_typ: body_type.to_string(), - expr_span: body_span, + expr_location: body_location, }); self.pop_scope(); @@ -290,21 +294,33 @@ impl Elaborator<'_> { } /// Convert an expression into a Pattern, defining any variables within. - fn expression_to_pattern(&mut self, expression: Expression, expected_type: &Type) -> Pattern { - let expr_span = expression.type_span(); + fn expression_to_pattern( + &mut self, + expression: Expression, + expected_type: &Type, + variables_defined: &mut Vec, + ) -> Pattern { + let expr_location = expression.type_location(); let unify_with_expected_type = |this: &mut Self, actual| { this.unify(actual, expected_type, || TypeCheckError::TypeMismatch { expected_typ: expected_type.to_string(), expr_typ: actual.to_string(), - expr_span, + expr_location, }); }; + // We want the actual expression's location here, not the innermost one from `type_location()` + let syntax_error = |this: &mut Self| { + let errors = ResolverError::InvalidSyntaxInPattern { location: expression.location }; + this.push_err(errors); + Pattern::Error + }; + match expression.kind { - ExpressionKind::Literal(Literal::Integer(value, negative)) => { + ExpressionKind::Literal(Literal::Integer(value)) => { let actual = self.interner.next_type_variable_with_kind(Kind::IntegerOrField); unify_with_expected_type(self, &actual); - Pattern::Int(SignedField::new(value, negative)) + Pattern::Int(value) } ExpressionKind::Literal(Literal::Bool(value)) => { unify_with_expected_type(self, &Type::Bool); @@ -318,39 +334,42 @@ impl Elaborator<'_> { // - Possible diagnostics improvement: warn if `a` is defined as a variable // when there is a matching enum variant with name `Foo::a` which can // be imported. The user likely intended to reference the enum variant. - let path_len = path.segments.len(); - let location = Location::new(path.span(), self.file); + let location = path.location; let last_ident = path.last_ident(); + // Setting this to `Some` allows us to shadow globals with the same name. + // We should avoid this if there is a `::` in the path since that means the + // user is trying to resolve to a non-local item. + let shadow_existing = path.is_ident().then_some(last_ident); + match self.resolve_path_or_error(path) { Ok(resolution) => self.path_resolution_to_constructor( resolution, + shadow_existing, Vec::new(), expected_type, - location.span, + location, + variables_defined, ), - Err(_) if path_len == 1 => { - // Define the variable - let kind = DefinitionKind::Local(None); - // TODO: `allow_shadowing` is false while I'm too lazy to add a check that we - // don't define the same name multiple times in one pattern. - let id = self.add_variable_decl(last_ident, false, false, true, kind).id; - self.interner.push_definition_type(id, expected_type.clone()); - Pattern::Binding(id) - } Err(error) => { - self.push_err(error); - // Default to defining a variable of the same name although this could - // cause further match warnings/errors (e.g. redundant cases). - let id = self.fresh_match_variable(expected_type.clone(), location); - Pattern::Binding(id) + if let Some(name) = shadow_existing { + self.define_pattern_variable(name, expected_type, variables_defined) + } else { + self.push_err(error); + Pattern::Error + } } } } - ExpressionKind::Call(call) => { - self.expression_to_constructor(*call.func, call.arguments, expected_type) + ExpressionKind::Call(call) => self.expression_to_constructor( + *call.func, + call.arguments, + expected_type, + variables_defined, + ), + ExpressionKind::Constructor(constructor) => { + self.constructor_to_pattern(*constructor, variables_defined) } - ExpressionKind::Constructor(_) => todo!("handle constructors"), ExpressionKind::Tuple(fields) => { let field_types = vecmap(0..fields.len(), |_| self.interner.next_type_variable()); let actual = Type::Tuple(field_types.clone()); @@ -358,23 +377,25 @@ impl Elaborator<'_> { let fields = vecmap(fields.into_iter().enumerate(), |(i, field)| { let expected = field_types.get(i).unwrap_or(&Type::Error); - self.expression_to_pattern(field, expected) + self.expression_to_pattern(field, expected, variables_defined) }); Pattern::Constructor(Constructor::Tuple(field_types.clone()), fields) } - ExpressionKind::Parenthesized(expr) => self.expression_to_pattern(*expr, expected_type), + ExpressionKind::Parenthesized(expr) => { + self.expression_to_pattern(*expr, expected_type, variables_defined) + } ExpressionKind::Interned(id) => { let kind = self.interner.get_expression_kind(id); - let expr = Expression::new(kind.clone(), expression.span); - self.expression_to_pattern(expr, expected_type) + let expr = Expression::new(kind.clone(), expression.location); + self.expression_to_pattern(expr, expected_type, variables_defined) } ExpressionKind::InternedStatement(id) => { if let StatementKind::Expression(expr) = self.interner.get_statement_kind(id) { - self.expression_to_pattern(expr.clone(), expected_type) + self.expression_to_pattern(expr.clone(), expected_type, variables_defined) } else { - panic!("Invalid expr kind {expression}") + syntax_error(self) } } @@ -393,14 +414,85 @@ impl Elaborator<'_> { | ExpressionKind::Quote(_) | ExpressionKind::Unquote(_) | ExpressionKind::Comptime(_, _) - | ExpressionKind::Unsafe(_, _) + | ExpressionKind::Unsafe(_) | ExpressionKind::AsTraitPath(_) | ExpressionKind::TypePath(_) | ExpressionKind::Resolved(_) - | ExpressionKind::Error => { - panic!("Invalid expr kind {expression}") + | ExpressionKind::Error => syntax_error(self), + } + } + + fn define_pattern_variable( + &mut self, + name: Ident, + expected_type: &Type, + variables_defined: &mut Vec, + ) -> Pattern { + // Define the variable + let kind = DefinitionKind::Local(None); + + if let Some(existing) = variables_defined.iter().find(|elem| *elem == &name) { + // Allow redefinition of `_` only, to ignore variables + if name.0.contents != "_" { + self.push_err(ResolverError::VariableAlreadyDefinedInPattern { + existing: existing.clone(), + new_location: name.location(), + }); } + } else { + variables_defined.push(name.clone()); } + + let id = self.add_variable_decl(name, false, true, true, kind).id; + self.interner.push_definition_type(id, expected_type.clone()); + Pattern::Binding(id) + } + + fn constructor_to_pattern( + &mut self, + constructor: ConstructorExpression, + variables_defined: &mut Vec, + ) -> Pattern { + let location = constructor.typ.location; + let typ = self.resolve_type(constructor.typ); + + let Some((struct_name, mut expected_field_types)) = + self.struct_name_and_field_types(&typ, location) + else { + return Pattern::Error; + }; + + let mut fields = BTreeMap::default(); + for (field_name, field) in constructor.fields { + let Some(field_index) = + expected_field_types.iter().position(|(name, _)| *name == field_name.0.contents) + else { + let error = if fields.contains_key(&field_name.0.contents) { + ResolverError::DuplicateField { field: field_name } + } else { + let struct_definition = struct_name.clone(); + ResolverError::NoSuchField { field: field_name, struct_definition } + }; + self.push_err(error); + continue; + }; + + let (field_name, expected_field_type) = expected_field_types.swap_remove(field_index); + let pattern = + self.expression_to_pattern(field, &expected_field_type, variables_defined); + fields.insert(field_name, pattern); + } + + if !expected_field_types.is_empty() { + let struct_definition = struct_name; + let missing_fields = vecmap(expected_field_types, |(name, _)| name); + let error = + ResolverError::MissingFields { location, missing_fields, struct_definition }; + self.push_err(error); + } + + let args = vecmap(fields, |(_name, field)| field); + Pattern::Constructor(Constructor::Variant(typ, 0), args) } fn expression_to_constructor( @@ -408,16 +500,28 @@ impl Elaborator<'_> { name: Expression, args: Vec, expected_type: &Type, + variables_defined: &mut Vec, ) -> Pattern { + let syntax_error = |this: &mut Self| { + this.push_err(ResolverError::InvalidSyntaxInPattern { location: name.location }); + Pattern::Error + }; + match name.kind { ExpressionKind::Variable(path) => { - let span = path.span(); - let location = Location::new(span, self.file); + let location = path.location; match self.resolve_path_or_error(path) { - Ok(resolution) => { - self.path_resolution_to_constructor(resolution, args, expected_type, span) - } + // Use None for `name` here - we don't want to define a variable if this + // resolves to an existing item. + Ok(resolution) => self.path_resolution_to_constructor( + resolution, + None, + args, + expected_type, + location, + variables_defined, + ), Err(error) => { self.push_err(error); let id = self.fresh_match_variable(expected_type.clone(), location); @@ -426,38 +530,63 @@ impl Elaborator<'_> { } } ExpressionKind::Parenthesized(expr) => { - self.expression_to_constructor(*expr, args, expected_type) + self.expression_to_constructor(*expr, args, expected_type, variables_defined) } ExpressionKind::Interned(id) => { let kind = self.interner.get_expression_kind(id); - let expr = Expression::new(kind.clone(), name.span); - self.expression_to_constructor(expr, args, expected_type) + let expr = Expression::new(kind.clone(), name.location); + self.expression_to_constructor(expr, args, expected_type, variables_defined) } ExpressionKind::InternedStatement(id) => { if let StatementKind::Expression(expr) = self.interner.get_statement_kind(id) { - self.expression_to_constructor(expr.clone(), args, expected_type) + self.expression_to_constructor( + expr.clone(), + args, + expected_type, + variables_defined, + ) } else { - panic!("Invalid expr kind {name}") + syntax_error(self) } } - other => todo!("invalid constructor `{other}`"), + _ => syntax_error(self), } } + /// Convert a PathResolutionItem - usually an enum variant or global - to a Constructor. + /// If `name` is `Some`, we'll define a Pattern::Binding instead of erroring if the + /// item doesn't resolve to a variant or global. This would shadow an existing + /// value such as a free function. Generally this is desired unless the variable was + /// a path with multiple components such as `foo::bar` which should always be treated as + /// a path to an existing item. fn path_resolution_to_constructor( &mut self, - name: PathResolutionItem, + resolution: PathResolutionItem, + name: Option, args: Vec, expected_type: &Type, - span: Span, + location: Location, + variables_defined: &mut Vec, ) -> Pattern { - let (actual_type, expected_arg_types, variant_index) = match name { + let (actual_type, expected_arg_types, variant_index) = match &resolution { PathResolutionItem::Global(id) => { // variant constant - let global = self.interner.get_global(id); - let variant_index = match global.value { - GlobalValue::Resolved(Value::Enum(tag, ..)) => tag, - _ => todo!("Value is not an enum constant"), + self.elaborate_global_if_unresolved(id); + let global = self.interner.get_global(*id); + let variant_index = match &global.value { + GlobalValue::Resolved(Value::Enum(tag, ..)) => *tag, + // This may be a global constant. Treat it like a normal constant + GlobalValue::Resolved(value) => { + let value = value.clone(); + return self.global_constant_to_integer_constructor( + value, + expected_type, + location, + ); + } + // We tried to resolve this value above so there must have been an error + // in doing so. Avoid reporting an additional error. + _ => return Pattern::Error, }; let global_type = self.interner.definition_type(global.definition_id); @@ -466,8 +595,12 @@ impl Elaborator<'_> { } PathResolutionItem::Method(_type_id, _type_turbofish, func_id) => { // TODO(#7430): Take type_turbofish into account when instantiating the function's type - let meta = self.interner.function_meta(&func_id); - let Some(variant_index) = meta.enum_variant_index else { todo!("not a variant") }; + let meta = self.interner.function_meta(func_id); + let Some(variant_index) = meta.enum_variant_index else { + let item = resolution.description(); + self.push_err(ResolverError::UnexpectedItemInPattern { location, item }); + return Pattern::Error; + }; let (actual_type, expected_arg_types) = match meta.typ.instantiate(self.interner).0 { @@ -477,18 +610,22 @@ impl Elaborator<'_> { (actual_type, expected_arg_types, variant_index) } - PathResolutionItem::Module(_) => todo!("path_resolution_to_constructor {name:?}"), - PathResolutionItem::Type(_) => todo!("path_resolution_to_constructor {name:?}"), - PathResolutionItem::TypeAlias(_) => todo!("path_resolution_to_constructor {name:?}"), - PathResolutionItem::Trait(_) => todo!("path_resolution_to_constructor {name:?}"), - PathResolutionItem::ModuleFunction(_) => { - todo!("path_resolution_to_constructor {name:?}") - } - PathResolutionItem::TypeAliasFunction(_, _, _) => { - todo!("path_resolution_to_constructor {name:?}") - } - PathResolutionItem::TraitFunction(_, _, _) => { - todo!("path_resolution_to_constructor {name:?}") + PathResolutionItem::Module(_) + | PathResolutionItem::Type(_) + | PathResolutionItem::TypeAlias(_) + | PathResolutionItem::Trait(_) + | PathResolutionItem::ModuleFunction(_) + | PathResolutionItem::TypeAliasFunction(_, _, _) + | PathResolutionItem::TraitFunction(_, _, _) => { + // This variable refers to an existing item + if let Some(name) = name { + // If name is set, shadow the existing item + return self.define_pattern_variable(name, expected_type, variables_defined); + } else { + let item = resolution.description(); + self.push_err(ResolverError::UnexpectedItemInPattern { location, item }); + return Pattern::Error; + } } }; @@ -497,21 +634,89 @@ impl Elaborator<'_> { self.unify(&actual_type, expected_type, || TypeCheckError::TypeMismatch { expected_typ: expected_type.to_string(), expr_typ: actual_type.to_string(), - expr_span: span, + expr_location: location, }); if args.len() != expected_arg_types.len() { - // error expected N args, found M? + let expected = expected_arg_types.len(); + let found = args.len(); + self.push_err(TypeCheckError::ArityMisMatch { expected, found, location }); + return Pattern::Error; } let args = args.into_iter().zip(expected_arg_types); let args = vecmap(args, |(arg, expected_arg_type)| { - self.expression_to_pattern(arg, &expected_arg_type) + self.expression_to_pattern(arg, &expected_arg_type, variables_defined) }); let constructor = Constructor::Variant(actual_type, variant_index); Pattern::Constructor(constructor, args) } + fn global_constant_to_integer_constructor( + &mut self, + constant: Value, + expected_type: &Type, + location: Location, + ) -> Pattern { + let actual_type = constant.get_type(); + self.unify(&actual_type, expected_type, || TypeCheckError::TypeMismatch { + expected_typ: expected_type.to_string(), + expr_typ: actual_type.to_string(), + expr_location: location, + }); + + // Convert a signed integer type like i32 to SignedField + macro_rules! signed_to_signed_field { + ($value:expr) => {{ + let negative = $value < 0; + // Widen the value so that SignedType::MIN does not wrap to 0 when negated below + let mut widened = $value as i128; + if negative { + widened = -widened; + } + SignedField::new(widened.into(), negative) + }}; + } + + let value = match constant { + Value::Bool(value) => SignedField::positive(value), + Value::Field(value) => SignedField::positive(value), + Value::I8(value) => signed_to_signed_field!(value), + Value::I16(value) => signed_to_signed_field!(value), + Value::I32(value) => signed_to_signed_field!(value), + Value::I64(value) => signed_to_signed_field!(value), + Value::U1(value) => SignedField::positive(value), + Value::U8(value) => SignedField::positive(value as u128), + Value::U16(value) => SignedField::positive(value as u128), + Value::U32(value) => SignedField::positive(value), + Value::U64(value) => SignedField::positive(value), + Value::U128(value) => SignedField::positive(value), + Value::Zeroed(_) => SignedField::positive(0u32), + _ => { + self.push_err(ResolverError::NonIntegerGlobalUsedInPattern { location }); + return Pattern::Error; + } + }; + + Pattern::Int(value) + } + + fn struct_name_and_field_types( + &mut self, + typ: &Type, + location: Location, + ) -> Option<(Ident, Vec<(String, Type)>)> { + if let Type::DataType(typ, generics) = typ.follow_bindings_shallow().as_ref() { + if let Some(fields) = typ.borrow().get_fields(generics) { + return Some((typ.borrow().name.clone(), fields)); + } + } + + let error = ResolverError::NonStructUsedInConstructor { typ: typ.to_string(), location }; + self.push_err(error); + None + } + /// Compiles the rows of a match expression, outputting a decision tree for the match. /// /// This is an adaptation of https://github.com/yorickpeterse/pattern-matching-in-rust/tree/main/jacobs2021 @@ -532,7 +737,7 @@ impl Elaborator<'_> { self.push_tests_against_bare_variables(&mut rows); // If the first row is a match-all we match it and the remaining rows are ignored. - if rows.first().map_or(false, |row| row.columns.is_empty()) { + if rows.first().is_some_and(|row| row.columns.is_empty()) { let row = rows.remove(0); return Ok(match row.guard { @@ -557,8 +762,6 @@ impl Elaborator<'_> { Ok(HirMatch::Switch(branch_var, cases, Some(fallback))) } - Type::Array(_, _) => todo!(), - Type::Slice(_) => todo!(), Type::Bool => { let cases = vec![ (Constructor::False, Vec::new(), Vec::new()), @@ -609,12 +812,16 @@ impl Elaborator<'_> { } else { drop(def); let typ = Type::DataType(type_def, generics); - todo!("Cannot match on type {typ}") + Err(ResolverError::TypeUnsupportedInMatch { typ, location }) } } - typ @ (Type::Alias(_, _) - | Type::TypeVariable(_) + // We could match on these types in the future + typ @ (Type::Array(_, _) + | Type::Slice(_) | Type::String(_) + // But we'll never be able to match on these + | Type::Alias(_, _) + | Type::TypeVariable(_) | Type::FmtString(_, _) | Type::TraitAsType(_, _, _) | Type::NamedGeneric(_, _) @@ -625,7 +832,9 @@ impl Elaborator<'_> { | Type::Constant(_, _) | Type::Quoted(_) | Type::InfixExpr(_, _, _, _) - | Type::Error) => todo!("Cannot match on type {typ:?}"), + | Type::Error) => { + Err(ResolverError::TypeUnsupportedInMatch { typ, location }) + }, } } @@ -663,10 +872,9 @@ impl Elaborator<'_> { let (key, cons) = match col.pattern { Pattern::Int(val) => ((val, val), Constructor::Int(val)), Pattern::Range(start, stop) => ((start, stop), Constructor::Range(start, stop)), - pattern => { - eprintln!("Unexpected pattern for integer type: {pattern:?}"); - continue; - } + // Any other pattern shouldn't have an integer type and we expect a type + // check error to already have been issued. + _ => continue, }; if let Some(index) = tested.get(&key) { @@ -721,6 +929,7 @@ impl Elaborator<'_> { /// /// Types with infinite constructors (e.g. int and string) are handled /// separately; they don't need most of this work anyway. + #[allow(clippy::type_complexity)] fn compile_constructor_cases( &mut self, rows: Vec, @@ -845,7 +1054,7 @@ impl Elaborator<'_> { let rhs = HirExpression::Ident(HirIdent::non_trait_method(rhs, location), None); let rhs = self.interner.push_expr(rhs); self.interner.push_expr_type(rhs, rhs_type); - self.interner.push_expr_location(rhs, location.span, location.file); + self.interner.push_expr_location(rhs, location); let let_ = HirStatement::Let(HirLetStatement { pattern: HirPattern::Identifier(variable), @@ -860,13 +1069,13 @@ impl Elaborator<'_> { let let_ = self.interner.push_stmt(let_); let body = self.interner.push_stmt(HirStatement::Expression(body)); - self.interner.push_stmt_location(let_, location.span, location.file); - self.interner.push_stmt_location(body, location.span, location.file); + self.interner.push_stmt_location(let_, location); + self.interner.push_stmt_location(body, location); let block = HirExpression::Block(HirBlockExpression { statements: vec![let_, body] }); let block = self.interner.push_expr(block); self.interner.push_expr_type(block, body_type); - self.interner.push_expr_location(block, location.span, location.file); + self.interner.push_expr_location(block, location); block } } @@ -890,6 +1099,11 @@ enum Pattern { /// 1 <= n < 20. #[allow(unused)] Range(SignedField, SignedField), + + /// An error occurred while translating this pattern. This Pattern kind always translates + /// to a Fail branch in the decision tree, although the compiler is expected to halt + /// with errors before execution. + Error, } #[derive(Clone)] 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 3b25f85a25c2..17f256715183 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -1,29 +1,32 @@ use acvm::{AcirField, FieldElement}; use iter_extended::vecmap; -use noirc_errors::{Location, Span, Spanned}; +use noirc_errors::{Located, Location}; use rustc_hash::FxHashSet as HashSet; use crate::{ + DataType, Kind, QuotedType, Shared, Type, ast::{ - ArrayLiteral, BinaryOpKind, BlockExpression, CallExpression, CastExpression, + ArrayLiteral, AsTraitPath, BinaryOpKind, BlockExpression, CallExpression, CastExpression, ConstrainExpression, ConstrainKind, ConstructorExpression, Expression, ExpressionKind, Ident, IfExpression, IndexExpression, InfixExpression, ItemVisibility, Lambda, Literal, MatchExpression, MemberAccessExpression, MethodCallExpression, Path, PathSegment, - PrefixExpression, StatementKind, UnaryOp, UnresolvedTypeData, UnresolvedTypeExpression, + PrefixExpression, StatementKind, TraitBound, UnaryOp, UnresolvedTraitConstraint, + UnresolvedTypeData, UnresolvedTypeExpression, UnsafeExpression, }, hir::{ comptime::{self, InterpreterError}, + def_collector::dc_crate::CompilationError, resolution::{ errors::ResolverError, import::PathResolutionError, visibility::method_call_is_visible, }, - type_check::{generics::TraitGenerics, TypeCheckError}, + type_check::{TypeCheckError, generics::TraitGenerics}, }, hir_def::{ expr::{ HirArrayLiteral, HirBinaryOp, HirBlockExpression, HirCallExpression, HirCastExpression, HirConstrainExpression, HirConstructorExpression, HirExpression, HirIdent, HirIfExpression, HirIndexExpression, HirInfixExpression, HirLambda, HirLiteral, - HirMemberAccess, HirMethodCallExpression, HirPrefixExpression, + HirMemberAccess, HirMethodCallExpression, HirPrefixExpression, ImplKind, TraitMethod, }, stmt::{HirLetStatement, HirPattern, HirStatement}, traits::{ResolvedTraitBound, TraitConstraint}, @@ -32,12 +35,11 @@ use crate::{ DefinitionId, DefinitionKind, ExprId, FuncId, InternedStatementKind, StmtId, TraitMethodId, }, token::{FmtStrFragment, Tokens}, - DataType, Kind, QuotedType, Shared, Type, }; use super::{Elaborator, LambdaContext, UnsafeBlockStatus}; -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { pub(crate) fn elaborate_expression(&mut self, expr: Expression) -> (ExprId, Type) { self.elaborate_expression_with_target_type(expr, None) } @@ -48,58 +50,57 @@ impl<'context> Elaborator<'context> { target_type: Option<&Type>, ) -> (ExprId, Type) { let (hir_expr, typ) = match expr.kind { - ExpressionKind::Literal(literal) => self.elaborate_literal(literal, expr.span), + ExpressionKind::Literal(literal) => self.elaborate_literal(literal, expr.location), ExpressionKind::Block(block) => self.elaborate_block(block, target_type), - ExpressionKind::Prefix(prefix) => return self.elaborate_prefix(*prefix, expr.span), + ExpressionKind::Prefix(prefix) => return self.elaborate_prefix(*prefix, expr.location), 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), + ExpressionKind::Call(call) => self.elaborate_call(*call, expr.location), + ExpressionKind::MethodCall(call) => self.elaborate_method_call(*call, expr.location), ExpressionKind::Constrain(constrain) => self.elaborate_constrain(constrain), ExpressionKind::Constructor(constructor) => self.elaborate_constructor(*constructor), ExpressionKind::MemberAccess(access) => { - return self.elaborate_member_access(*access, expr.span) + return self.elaborate_member_access(*access, expr.location); } - ExpressionKind::Cast(cast) => self.elaborate_cast(*cast, expr.span), - ExpressionKind::Infix(infix) => return self.elaborate_infix(*infix, expr.span), + ExpressionKind::Cast(cast) => self.elaborate_cast(*cast, expr.location), + ExpressionKind::Infix(infix) => return self.elaborate_infix(*infix, expr.location), ExpressionKind::If(if_) => self.elaborate_if(*if_, target_type), - ExpressionKind::Match(match_) => self.elaborate_match(*match_, expr.span), + ExpressionKind::Match(match_) => self.elaborate_match(*match_, expr.location), ExpressionKind::Variable(variable) => return self.elaborate_variable(variable), ExpressionKind::Tuple(tuple) => self.elaborate_tuple(tuple, target_type), ExpressionKind::Lambda(lambda) => { self.elaborate_lambda_with_target_type(*lambda, target_type) } ExpressionKind::Parenthesized(expr) => { - return self.elaborate_expression_with_target_type(*expr, target_type) + return self.elaborate_expression_with_target_type(*expr, target_type); } - ExpressionKind::Quote(quote) => self.elaborate_quote(quote, expr.span), + ExpressionKind::Quote(quote) => self.elaborate_quote(quote, expr.location), ExpressionKind::Comptime(comptime, _) => { - return self.elaborate_comptime_block(comptime, expr.span, target_type) + return self.elaborate_comptime_block(comptime, expr.location, target_type); } - ExpressionKind::Unsafe(block_expression, span) => { - self.elaborate_unsafe_block(block_expression, span, target_type) + ExpressionKind::Unsafe(unsafe_expression) => { + self.elaborate_unsafe_block(unsafe_expression, target_type) } ExpressionKind::Resolved(id) => return (id, self.interner.id_type(id)), ExpressionKind::Interned(id) => { let expr_kind = self.interner.get_expression_kind(id); - let expr = Expression::new(expr_kind.clone(), expr.span); + let expr = Expression::new(expr_kind.clone(), expr.location); return self.elaborate_expression(expr); } ExpressionKind::InternedStatement(id) => { - return self.elaborate_interned_statement_as_expr(id, expr.span); + return self.elaborate_interned_statement_as_expr(id, expr.location); } ExpressionKind::Error => (HirExpression::Error, Type::Error), ExpressionKind::Unquote(_) => { - self.push_err(ResolverError::UnquoteUsedOutsideQuote { span: expr.span }); + self.push_err(ResolverError::UnquoteUsedOutsideQuote { location: expr.location }); (HirExpression::Error, Type::Error) } - ExpressionKind::AsTraitPath(_) => { - self.push_err(ResolverError::AsTraitPathNotYetImplemented { span: expr.span }); - (HirExpression::Error, Type::Error) + ExpressionKind::AsTraitPath(path) => { + return self.elaborate_as_trait_path(path); } ExpressionKind::TypePath(path) => return self.elaborate_type_path(path), }; let id = self.interner.push_expr(hir_expr); - self.interner.push_expr_location(id, expr.span, self.file); + self.interner.push_expr_location(id, expr.location); self.interner.push_expr_type(id, typ.clone()); (id, typ) } @@ -107,21 +108,24 @@ impl<'context> Elaborator<'context> { fn elaborate_interned_statement_as_expr( &mut self, id: InternedStatementKind, - span: Span, + location: Location, ) -> (ExprId, Type) { match self.interner.get_statement_kind(id) { StatementKind::Expression(expr) | StatementKind::Semi(expr) => { self.elaborate_expression(expr.clone()) } - StatementKind::Interned(id) => self.elaborate_interned_statement_as_expr(*id, span), + StatementKind::Interned(id) => self.elaborate_interned_statement_as_expr(*id, location), StatementKind::Error => { - let expr = Expression::new(ExpressionKind::Error, span); + let expr = Expression::new(ExpressionKind::Error, location); self.elaborate_expression(expr) } other => { let statement = other.to_string(); - self.push_err(ResolverError::InvalidInternedStatementInExpr { statement, span }); - let expr = Expression::new(ExpressionKind::Error, span); + self.push_err(ResolverError::InvalidInternedStatementInExpr { + statement, + location, + }); + let expr = Expression::new(ExpressionKind::Error, location); self.elaborate_expression(expr) } } @@ -154,11 +158,11 @@ impl<'context> Elaborator<'context> { if let HirStatement::Semi(expr) = self.interner.statement(&id) { let inner_expr_type = self.interner.id_type(expr); - let span = self.interner.expr_span(&expr); + let location = self.interner.expr_location(&expr); self.unify(&inner_expr_type, &Type::Unit, || TypeCheckError::UnusedResultError { expr_type: inner_expr_type.clone(), - expr_span: span, + expr_location: location, }); } @@ -173,8 +177,7 @@ impl<'context> Elaborator<'context> { fn elaborate_unsafe_block( &mut self, - block: BlockExpression, - span: Span, + unsafe_expression: UnsafeExpression, target_type: Option<&Type>, ) -> (HirExpression, Type) { // Before entering the block we cache the old value of `in_unsafe_block` so it can be restored. @@ -182,18 +185,21 @@ impl<'context> Elaborator<'context> { let is_nested_unsafe_block = !matches!(old_in_unsafe_block, UnsafeBlockStatus::NotInUnsafeBlock); if is_nested_unsafe_block { - let span = Span::from(span.start()..span.start() + 6); // Only highlight the `unsafe` keyword - self.push_err(TypeCheckError::NestedUnsafeBlock { span }); + self.push_err(TypeCheckError::NestedUnsafeBlock { + location: unsafe_expression.unsafe_keyword_location, + }); } self.unsafe_block_status = UnsafeBlockStatus::InUnsafeBlockWithoutUnconstrainedCalls; - let (hir_block_expression, typ) = self.elaborate_block_expression(block, target_type); + let (hir_block_expression, typ) = + self.elaborate_block_expression(unsafe_expression.block, target_type); if let UnsafeBlockStatus::InUnsafeBlockWithoutUnconstrainedCalls = self.unsafe_block_status { - let span = Span::from(span.start()..span.start() + 6); // Only highlight the `unsafe` keyword - self.push_err(TypeCheckError::UnnecessaryUnsafeBlock { span }); + self.push_err(TypeCheckError::UnnecessaryUnsafeBlock { + location: unsafe_expression.unsafe_keyword_location, + }); } // Finally, we restore the original value of `self.in_unsafe_block`, @@ -206,14 +212,13 @@ impl<'context> Elaborator<'context> { (HirExpression::Unsafe(hir_block_expression), typ) } - fn elaborate_literal(&mut self, literal: Literal, span: Span) -> (HirExpression, Type) { + fn elaborate_literal(&mut self, literal: Literal, location: Location) -> (HirExpression, Type) { use HirExpression::Literal as Lit; match literal { Literal::Unit => (Lit(HirLiteral::Unit), Type::Unit), Literal::Bool(b) => (Lit(HirLiteral::Bool(b)), Type::Bool), - Literal::Integer(integer, sign) => { - let int = HirLiteral::Integer(integer, sign); - (Lit(int), self.polymorphic_integer_or_field()) + Literal::Integer(integer) => { + (Lit(HirLiteral::Integer(integer)), self.polymorphic_integer_or_field()) } Literal::Str(str) | Literal::RawStr(str, _) => { let len = Type::Constant(str.len().into(), Kind::u32()); @@ -221,10 +226,10 @@ impl<'context> Elaborator<'context> { } Literal::FmtStr(fragments, length) => self.elaborate_fmt_string(fragments, length), Literal::Array(array_literal) => { - self.elaborate_array_literal(array_literal, span, true) + self.elaborate_array_literal(array_literal, location, true) } Literal::Slice(array_literal) => { - self.elaborate_array_literal(array_literal, span, false) + self.elaborate_array_literal(array_literal, location, false) } } } @@ -232,24 +237,24 @@ impl<'context> Elaborator<'context> { fn elaborate_array_literal( &mut self, array_literal: ArrayLiteral, - span: Span, + location: Location, is_array: bool, ) -> (HirExpression, Type) { let (expr, elem_type, length) = match array_literal { ArrayLiteral::Standard(elements) => { let first_elem_type = self.interner.next_type_variable(); - let first_span = elements.first().map(|elem| elem.span).unwrap_or(span); + let first_location = elements.first().map(|elem| elem.location).unwrap_or(location); let elements = vecmap(elements.into_iter().enumerate(), |(i, elem)| { - let span = elem.span; + let location = elem.location; let (elem_id, elem_type) = self.elaborate_expression(elem); self.unify(&elem_type, &first_elem_type, || { TypeCheckError::NonHomogeneousArray { - first_span, + first_location, first_type: first_elem_type.to_string(), first_index: 0, - second_span: span, + second_location: location, second_type: elem_type.to_string(), second_index: i, } @@ -262,14 +267,15 @@ impl<'context> Elaborator<'context> { (HirArrayLiteral::Standard(elements), first_elem_type, length) } ArrayLiteral::Repeated { repeated_element, length } => { - let span = length.span; - let length = - UnresolvedTypeExpression::from_expr(*length, span).unwrap_or_else(|error| { + let location = length.location; + let length = UnresolvedTypeExpression::from_expr(*length, location).unwrap_or_else( + |error| { self.push_err(ResolverError::ParserError(Box::new(error))); - UnresolvedTypeExpression::Constant(FieldElement::zero(), span) - }); + UnresolvedTypeExpression::Constant(FieldElement::zero(), location) + }, + ); - let length = self.convert_expression_type(length, &Kind::u32(), span); + let length = self.convert_expression_type(length, &Kind::u32(), location); let (repeated_element, elem_type) = self.elaborate_expression(*repeated_element); let length_clone = length.clone(); @@ -295,7 +301,7 @@ impl<'context> Elaborator<'context> { let mut capture_types = Vec::new(); for fragment in &fragments { - if let FmtStrFragment::Interpolation(ident_name, string_span) = fragment { + if let FmtStrFragment::Interpolation(ident_name, location) = fragment { let scope_tree = self.scopes.current_scope_tree(); let variable = scope_tree.find(ident_name); @@ -303,23 +309,20 @@ impl<'context> Elaborator<'context> { old_value.num_times_used += 1; old_value.ident.clone() } else if let Ok((definition_id, _)) = - self.lookup_global(Path::from_single(ident_name.to_string(), *string_span)) + self.lookup_global(Path::from_single(ident_name.to_string(), *location)) { - HirIdent::non_trait_method( - definition_id, - Location::new(*string_span, self.file), - ) + HirIdent::non_trait_method(definition_id, *location) } else { self.push_err(ResolverError::VariableNotDeclared { name: ident_name.to_owned(), - span: *string_span, + location: *location, }); continue; }; let hir_expr = HirExpression::Ident(hir_ident.clone(), None); let expr_id = self.interner.push_expr(hir_expr); - self.interner.push_expr_location(expr_id, *string_span, self.file); + self.interner.push_expr_location(expr_id, *location); let typ = self.type_check_variable(hir_ident, expr_id, None); self.interner.push_expr_type(expr_id, typ.clone()); capture_types.push(typ); @@ -332,8 +335,8 @@ impl<'context> Elaborator<'context> { (HirExpression::Literal(HirLiteral::FmtStr(fragments, fmt_str_idents, length)), typ) } - fn elaborate_prefix(&mut self, prefix: PrefixExpression, span: Span) -> (ExprId, Type) { - let rhs_span = prefix.rhs.span; + fn elaborate_prefix(&mut self, prefix: PrefixExpression, location: Location) -> (ExprId, Type) { + let rhs_location = prefix.rhs.location; let (rhs, rhs_type) = self.elaborate_expression(prefix.rhs); let trait_id = self.interner.get_prefix_operator_trait_method(&prefix.operator); @@ -341,55 +344,81 @@ impl<'context> Elaborator<'context> { let operator = prefix.operator; if let UnaryOp::MutableReference = operator { - self.check_can_mutate(rhs, rhs_span); + self.check_can_mutate(rhs, rhs_location); } let expr = HirExpression::Prefix(HirPrefixExpression { operator, rhs, trait_method_id: trait_id }); let expr_id = self.interner.push_expr(expr); - self.interner.push_expr_location(expr_id, span, self.file); + self.interner.push_expr_location(expr_id, location); - let result = self.prefix_operand_type_rules(&operator, &rhs_type, span); - let typ = self.handle_operand_type_rules_result(result, &rhs_type, trait_id, expr_id, span); + let result = self.prefix_operand_type_rules(&operator, &rhs_type, location); + let typ = + self.handle_operand_type_rules_result(result, &rhs_type, trait_id, expr_id, location); self.interner.push_expr_type(expr_id, typ.clone()); (expr_id, typ) } - fn check_can_mutate(&mut self, expr_id: ExprId, span: Span) { + pub(super) fn check_can_mutate(&mut self, expr_id: ExprId, location: Location) { let expr = self.interner.expression(&expr_id); match expr { HirExpression::Ident(hir_ident, _) => { if let Some(definition) = self.interner.try_definition(hir_ident.id) { + let name = definition.name.clone(); if !definition.mutable { self.push_err(TypeCheckError::CannotMutateImmutableVariable { - name: definition.name.clone(), - span, + name, + location, }); + } else { + self.check_can_mutate_lambda_capture(hir_ident.id, name, location); } } } + HirExpression::Index(_) => { + self.push_err(TypeCheckError::MutableReferenceToArrayElement { location }); + } HirExpression::MemberAccess(member_access) => { - self.check_can_mutate(member_access.lhs, span); + self.check_can_mutate(member_access.lhs, location); } _ => (), } } + // We must check whether the mutable variable we are attempting to mutate + // comes from a lambda capture. All captures are immutable so we want to error + // if the user attempts to mutate a captured variable inside of a lambda without mutable references. + pub(super) fn check_can_mutate_lambda_capture( + &mut self, + id: DefinitionId, + name: String, + location: Location, + ) { + if let Some(lambda_context) = self.lambda_stack.last() { + let typ = self.interner.definition_type(id); + if !typ.is_mutable_ref() && lambda_context.captures.iter().any(|var| var.ident.id == id) + { + self.push_err(TypeCheckError::MutableCaptureWithoutRef { name, location }); + } + } + } + fn elaborate_index(&mut self, index_expr: IndexExpression) -> (HirExpression, Type) { - let span = index_expr.index.span; + let location = index_expr.index.location; + let (index, index_type) = self.elaborate_expression(index_expr.index); let expected = self.polymorphic_integer_or_field(); self.unify(&index_type, &expected, || TypeCheckError::TypeMismatch { expected_typ: "an integer".to_owned(), expr_typ: index_type.to_string(), - expr_span: span, + expr_location: location, }); // When writing `a[i]`, if `a : &mut ...` then automatically dereference `a` as many // times as needed to get the underlying array. - let lhs_span = index_expr.collection.span; + let lhs_location = index_expr.collection.location; let (lhs, lhs_type) = self.elaborate_expression(index_expr.collection); let (collection, lhs_type) = self.insert_auto_dereferences(lhs, lhs_type); @@ -400,14 +429,16 @@ impl<'context> Elaborator<'context> { Type::Slice(base_type) => *base_type, Type::Error => Type::Error, Type::TypeVariable(_) => { - self.push_err(TypeCheckError::TypeAnnotationsNeededForIndex { span: lhs_span }); + self.push_err(TypeCheckError::TypeAnnotationsNeededForIndex { + location: lhs_location, + }); Type::Error } typ => { self.push_err(TypeCheckError::TypeMismatch { expected_typ: "Array".to_owned(), expr_typ: typ.to_string(), - expr_span: lhs_span, + expr_location: lhs_location, }); Type::Error } @@ -417,7 +448,11 @@ impl<'context> Elaborator<'context> { (expr, typ) } - fn elaborate_call(&mut self, call: CallExpression, span: Span) -> (HirExpression, Type) { + fn elaborate_call( + &mut self, + call: CallExpression, + location: Location, + ) -> (HirExpression, Type) { let (func, func_type) = self.elaborate_expression(*call.func); let func_type = func_type.follow_bindings(); let func_arg_types = @@ -425,7 +460,7 @@ impl<'context> Elaborator<'context> { let mut arguments = Vec::with_capacity(call.arguments.len()); let args = vecmap(call.arguments.into_iter().enumerate(), |(arg_index, arg)| { - let span = arg.span; + let location = arg.location; let expected_type = func_arg_types.and_then(|args| args.get(arg_index)); let (arg, typ) = if call.is_macro_call { @@ -443,7 +478,7 @@ impl<'context> Elaborator<'context> { } arguments.push(arg); - (typ, arg, span) + (typ, arg, location) }); // Avoid cloning arguments unless this is a macro call @@ -452,10 +487,9 @@ impl<'context> Elaborator<'context> { comptime_args = arguments.clone(); } - let location = Location::new(span, self.file); let is_macro_call = call.is_macro_call; let hir_call = HirCallExpression { func, arguments, location, is_macro_call }; - let mut typ = self.type_check_call(&hir_call, func_type, args, span); + let mut typ = self.type_check_call(&hir_call, func_type, args, location); if is_macro_call { if self.in_comptime_context() { @@ -463,7 +497,7 @@ impl<'context> Elaborator<'context> { } else { return self .call_macro(func, comptime_args, location, typ) - .unwrap_or_else(|| (HirExpression::Error, Type::Error)); + .unwrap_or((HirExpression::Error, Type::Error)); } } @@ -473,15 +507,16 @@ impl<'context> Elaborator<'context> { fn elaborate_method_call( &mut self, method_call: MethodCallExpression, - span: Span, + location: Location, ) -> (HirExpression, Type) { - let object_span = method_call.object.span; + let object_location = method_call.object.location; let (mut object, mut object_type) = self.elaborate_expression(method_call.object); object_type = object_type.follow_bindings(); - let method_name_span = method_call.method_name.span(); + let method_name_location = method_call.method_name.location(); let method_name = method_call.method_name.0.contents.as_str(); - match self.lookup_method(&object_type, method_name, span, true) { + let check_self_param = true; + match self.lookup_method(&object_type, method_name, location, check_self_param) { Some(method_ref) => { // Automatically add `&mut` if the method expects a mutable reference and // the object is not already one. @@ -497,13 +532,16 @@ impl<'context> Elaborator<'context> { &mut object, ); - self.resolve_function_turbofish_generics(&func_id, method_call.generics, span) + self.resolve_function_turbofish_generics( + &func_id, + method_call.generics, + location, + ) } else { None }; - let call_span = Span::from(object_span.start()..method_name_span.end()); - let location = Location::new(call_span, self.file); + let location = object_location.merge(method_name_location); let (function_id, function_name) = method_ref.clone().into_function_id_and_name( object_type.clone(), @@ -533,10 +571,10 @@ impl<'context> Elaborator<'context> { let mut function_args = Vec::with_capacity(method_call.arguments.len() + 1); let mut arguments = Vec::with_capacity(method_call.arguments.len()); - function_args.push((object_type.clone(), object, object_span)); + function_args.push((object_type.clone(), object, object_location)); for (arg_index, arg) in method_call.arguments.into_iter().enumerate() { - let span = arg.span; + let location = arg.location; let expected_type = func_arg_types.and_then(|args| args.get(arg_index + 1)); let (arg, typ) = self.elaborate_expression_with_type(arg, expected_type); @@ -547,7 +585,7 @@ impl<'context> Elaborator<'context> { } arguments.push(arg); - function_args.push((typ, arg, span)); + function_args.push((typ, arg, location)); } let method = method_call.method_name; @@ -564,12 +602,12 @@ impl<'context> Elaborator<'context> { let function_call = method_call.into_function_call(function_id, is_macro_call, location); - self.interner - .add_function_reference(func_id, Location::new(method_name_span, self.file)); + self.interner.add_function_reference(func_id, method_name_location); // Type check the new call now that it has been changed from a method call // to a function call. This way we avoid duplicating code. - let mut typ = self.type_check_call(&function_call, func_type, function_args, span); + let mut typ = + self.type_check_call(&function_call, func_type, function_args, location); if is_macro_call { if self.in_comptime_context() { typ = self.interner.next_type_variable(); @@ -577,7 +615,7 @@ impl<'context> Elaborator<'context> { let args = function_call.arguments.clone(); return self .call_macro(function_call.func, args, location, typ) - .unwrap_or_else(|| (HirExpression::Error, Type::Error)); + .unwrap_or((HirExpression::Error, Type::Error)); } } (HirExpression::Call(function_call), typ) @@ -590,7 +628,7 @@ impl<'context> Elaborator<'context> { &mut self, mut expr: ConstrainExpression, ) -> (HirExpression, Type) { - let span = expr.span; + let location = expr.location; let min_args_count = expr.kind.required_arguments_count(); let max_args_count = min_args_count + 1; let actual_args_count = expr.arguments.len(); @@ -599,14 +637,14 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::AssertionParameterCountMismatch { kind: expr.kind, found: actual_args_count, - span, + location, }); // Given that we already produced an error, let's make this an `assert(true)` so // we don't get further errors. let message = None; let kind = ExpressionKind::Literal(crate::ast::Literal::Bool(true)); - let expr = Expression { kind, span }; + let expr = Expression { kind, location }; (message, expr) } else { let message = @@ -616,17 +654,17 @@ impl<'context> Elaborator<'context> { ConstrainKind::AssertEq => { let rhs = expr.arguments.pop().unwrap(); let lhs = expr.arguments.pop().unwrap(); - let span = Span::from(lhs.span.start()..rhs.span.end()); - let operator = Spanned::from(span, BinaryOpKind::Equal); + let location = lhs.location.merge(rhs.location); + let operator = Located::from(location, BinaryOpKind::Equal); let kind = ExpressionKind::Infix(Box::new(InfixExpression { lhs, operator, rhs })); - Expression { kind, span } + Expression { kind, location } } }; (message, expr) }; - let expr_span = expr.span; + let expr_location = expr.location; let (expr_id, expr_type) = self.elaborate_expression(expr); // Must type check the assertion message expression so that we instantiate bindings @@ -635,10 +673,10 @@ impl<'context> Elaborator<'context> { self.unify(&expr_type, &Type::Bool, || TypeCheckError::TypeMismatch { expr_typ: expr_type.to_string(), expected_typ: Type::Bool.to_string(), - expr_span, + expr_location, }); - (HirExpression::Constrain(HirConstrainExpression(expr_id, self.file, msg)), Type::Unit) + (HirExpression::Constrain(HirConstrainExpression(expr_id, location.file, msg)), Type::Unit) } /// Elaborates an expression knowing that it has to match a given type. @@ -651,12 +689,12 @@ impl<'context> Elaborator<'context> { return self.elaborate_expression(arg); }; - let span = arg.span; + let location = arg.location; let type_hint = if let Some(Type::Function(func_args, _, _, _)) = typ { Some(func_args) } else { None }; let (hir_expr, typ) = self.elaborate_lambda_with_parameter_type_hints(*lambda, type_hint); let id = self.interner.push_expr(hir_expr); - self.interner.push_expr_location(id, span, self.file); + self.interner.push_expr_location(id, location); self.interner.push_expr_type(id, typ.clone()); (id, typ) } @@ -679,7 +717,7 @@ impl<'context> Elaborator<'context> { &mut self, constructor: ConstructorExpression, ) -> (HirExpression, Type) { - let span = constructor.typ.span; + let location = constructor.typ.location; // A constructor type can either be a Path or an interned UnresolvedType. // We represent both as UnresolvedType (with Path being a Named UnresolvedType) @@ -692,10 +730,18 @@ impl<'context> Elaborator<'context> { // If this type is already resolved we can skip the rest of this function // which just resolves the type, and go straight to resolving the fields. let resolved = self.interner.get_quoted_type(id).clone(); - return self.elaborate_constructor_with_type(resolved, constructor.fields, span, None); + return self.elaborate_constructor_with_type( + resolved, + constructor.fields, + location, + None, + ); } let UnresolvedTypeData::Named(mut path, generics, _) = typ else { - self.push_err(ResolverError::NonStructUsedInConstructor { typ: typ.to_string(), span }); + self.push_err(ResolverError::NonStructUsedInConstructor { + typ: typ.to_string(), + location, + }); return (HirExpression::Error, Type::Error); }; @@ -706,25 +752,18 @@ impl<'context> Elaborator<'context> { let last_segment = path.last_segment(); - let typ = if let Some(struct_id) = constructor.struct_type { - let typ = self.interner.get_type(struct_id); - let generics = typ.borrow().instantiate(self.interner); - Type::DataType(typ, generics) - } else { - match self.lookup_type_or_error(path) { - Some(typ) => typ, - None => return (HirExpression::Error, Type::Error), - } + let Some(typ) = self.lookup_type_or_error(path) else { + return (HirExpression::Error, Type::Error); }; - self.elaborate_constructor_with_type(typ, constructor.fields, span, Some(last_segment)) + self.elaborate_constructor_with_type(typ, constructor.fields, location, Some(last_segment)) } fn elaborate_constructor_with_type( &mut self, typ: Type, fields: Vec<(Ident, Expression)>, - span: Span, + location: Location, last_segment: Option, ) -> (HirExpression, Type) { let typ = typ.follow_bindings_shallow(); @@ -735,7 +774,7 @@ impl<'context> Elaborator<'context> { typ => { self.push_err(ResolverError::NonStructUsedInConstructor { typ: typ.to_string(), - span, + location, }); return (HirExpression::Error, Type::Error); } @@ -745,18 +784,18 @@ impl<'context> Elaborator<'context> { // `last_segment` is optional if this constructor was resolved from a quoted type let mut generics = generics.clone(); let mut is_self_type = false; - let mut constructor_type_span = span; + let mut constructor_type_location = location; if let Some(last_segment) = last_segment { - let turbofish_span = last_segment.turbofish_span(); + let turbofish_location = last_segment.turbofish_location(); is_self_type = last_segment.ident.is_self_type_name(); - constructor_type_span = last_segment.ident.span(); + constructor_type_location = last_segment.ident.location(); generics = self.resolve_struct_turbofish_generics( &r#type.borrow(), generics, last_segment.generics, - turbofish_span, + turbofish_location, ); } @@ -767,8 +806,12 @@ impl<'context> Elaborator<'context> { .get_fields_with_visibility(&generics) .expect("This type should already be validated to be a struct"); - let fields = - self.resolve_constructor_expr_fields(struct_type.clone(), field_types, fields, span); + let fields = self.resolve_constructor_expr_fields( + struct_type.clone(), + field_types, + fields, + location, + ); let expr = HirExpression::Constructor(HirConstructorExpression { fields, r#type: struct_type.clone(), @@ -776,8 +819,7 @@ impl<'context> Elaborator<'context> { }); let struct_id = struct_type.borrow().id; - let reference_location = Location::new(constructor_type_span, self.file); - self.interner.add_type_reference(struct_id, reference_location, is_self_type); + self.interner.add_type_reference(struct_id, constructor_type_location, is_self_type); (expr, Type::DataType(struct_type, generics)) } @@ -796,7 +838,7 @@ impl<'context> Elaborator<'context> { struct_type: Shared, field_types: Vec<(String, ItemVisibility, Type)>, fields: Vec<(Ident, Expression)>, - span: Span, + location: Location, ) -> Vec<(Ident, ExprId)> { let mut ret = Vec::with_capacity(fields.len()); let mut seen_fields = HashSet::default(); @@ -815,20 +857,24 @@ impl<'context> Elaborator<'context> { let expected_type = expected_field_with_index.map(|(_, (_, _, typ))| typ).unwrap_or(&Type::Error); - let field_span = field.span; + let field_location = field.location; let (resolved, field_type) = self.elaborate_expression(field); if unseen_fields.contains(&field_name) { unseen_fields.remove(&field_name); seen_fields.insert(field_name.clone()); - self.unify_with_coercions(&field_type, expected_type, resolved, field_span, || { - TypeCheckError::TypeMismatch { + self.unify_with_coercions( + &field_type, + expected_type, + resolved, + field_location, + || TypeCheckError::TypeMismatch { expected_typ: expected_type.to_string(), expr_typ: field_type.to_string(), - expr_span: field_span, - } - }); + expr_location: field_location, + }, + ); } else if seen_fields.contains(&field_name) { // duplicate field self.push_err(ResolverError::DuplicateField { field: field_name.clone() }); @@ -842,20 +888,16 @@ impl<'context> Elaborator<'context> { if let Some((index, visibility)) = expected_index_and_visibility { let struct_type = struct_type.borrow(); - let field_span = field_name.span(); + let field_location = field_name.location(); let field_name = &field_name.0.contents; self.check_struct_field_visibility( &struct_type, field_name, *visibility, - field_span, + field_location, ); - self.interner.add_struct_member_reference( - struct_type.id, - index, - Location::new(field_span, self.file), - ); + self.interner.add_struct_member_reference(struct_type.id, index, field_location); } ret.push((field_name, resolved)); @@ -863,7 +905,7 @@ impl<'context> Elaborator<'context> { if !unseen_fields.is_empty() { self.push_err(ResolverError::MissingFields { - span, + location, missing_fields: unseen_fields.into_iter().map(|field| field.to_string()).collect(), struct_definition: struct_type.borrow().name.clone(), }); @@ -875,39 +917,44 @@ impl<'context> Elaborator<'context> { fn elaborate_member_access( &mut self, access: MemberAccessExpression, - span: Span, + location: Location, ) -> (ExprId, Type) { let (lhs, lhs_type) = self.elaborate_expression(access.lhs); let rhs = access.rhs; - let rhs_span = rhs.span(); + let rhs_location = rhs.location(); // `is_offset` is only used when lhs is a reference and we want to return a reference to rhs let access = HirMemberAccess { lhs, rhs, is_offset: false }; - let expr_id = self.intern_expr(HirExpression::MemberAccess(access.clone()), span); - let typ = self.type_check_member_access(access, expr_id, lhs_type, rhs_span); + let expr_id = self.intern_expr(HirExpression::MemberAccess(access.clone()), location); + let typ = self.type_check_member_access(access, expr_id, lhs_type, rhs_location); self.interner.push_expr_type(expr_id, typ.clone()); (expr_id, typ) } - pub fn intern_expr(&mut self, expr: HirExpression, span: Span) -> ExprId { + pub fn intern_expr(&mut self, expr: HirExpression, location: Location) -> ExprId { let id = self.interner.push_expr(expr); - self.interner.push_expr_location(id, span, self.file); + self.interner.push_expr_location(id, location); id } - fn elaborate_cast(&mut self, cast: CastExpression, span: Span) -> (HirExpression, Type) { + fn elaborate_cast( + &mut self, + cast: CastExpression, + location: Location, + ) -> (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, &lhs_type, &r#type, span); + let result = self.check_cast(&lhs, &lhs_type, &r#type, location); let expr = HirExpression::Cast(HirCastExpression { lhs, r#type }); (expr, result) } - fn elaborate_infix(&mut self, infix: InfixExpression, span: Span) -> (ExprId, Type) { + fn elaborate_infix(&mut self, infix: InfixExpression, location: Location) -> (ExprId, Type) { let (lhs, lhs_type) = self.elaborate_expression(infix.lhs); let (rhs, rhs_type) = self.elaborate_expression(infix.rhs); let trait_id = self.interner.get_operator_trait_method(infix.operator.contents); - let operator = HirBinaryOp::new(infix.operator, self.file); + let file = infix.operator.location().file; + let operator = HirBinaryOp::new(infix.operator, file); let expr = HirExpression::Infix(HirInfixExpression { lhs, operator, @@ -916,11 +963,16 @@ impl<'context> Elaborator<'context> { }); let expr_id = self.interner.push_expr(expr); - self.interner.push_expr_location(expr_id, span, self.file); + self.interner.push_expr_location(expr_id, location); - let result = self.infix_operand_type_rules(&lhs_type, &operator, &rhs_type, span); - let typ = - self.handle_operand_type_rules_result(result, &lhs_type, Some(trait_id), expr_id, span); + let result = self.infix_operand_type_rules(&lhs_type, &operator, &rhs_type, location); + let typ = self.handle_operand_type_rules_result( + result, + &lhs_type, + Some(trait_id), + expr_id, + location, + ); self.interner.push_expr_type(expr_id, typ.clone()); (expr_id, typ) @@ -932,7 +984,7 @@ impl<'context> Elaborator<'context> { operand_type: &Type, trait_id: Option, expr_id: ExprId, - span: Span, + location: Location, ) -> Type { match result { Ok((typ, use_impl)) => { @@ -948,14 +1000,14 @@ impl<'context> Elaborator<'context> { trait_bound: ResolvedTraitBound { trait_id: trait_id.trait_id, trait_generics: TraitGenerics::default(), - span, + location, }, }; self.push_trait_constraint( constraint, expr_id, true, // this constraint should lead to choosing a trait impl ); - self.type_check_operator_method(expr_id, trait_id, operand_type, span); + self.type_check_operator_method(expr_id, trait_id, operand_type, location); } typ } @@ -971,8 +1023,8 @@ impl<'context> Elaborator<'context> { if_expr: IfExpression, target_type: Option<&Type>, ) -> (HirExpression, Type) { - let expr_span = if_expr.condition.type_span(); - let consequence_span = if_expr.consequence.type_span(); + let expr_location = if_expr.condition.type_location(); + let consequence_location = if_expr.consequence.type_location(); let (condition, cond_type) = self.elaborate_expression(if_expr.condition); let (consequence, mut ret_type) = self.elaborate_expression_with_target_type(if_expr.consequence, target_type); @@ -980,23 +1032,24 @@ impl<'context> Elaborator<'context> { self.unify(&cond_type, &Type::Bool, || TypeCheckError::TypeMismatch { expected_typ: Type::Bool.to_string(), expr_typ: cond_type.to_string(), - expr_span, + expr_location, }); - let (alternative, else_type, error_span) = if let Some(alternative) = if_expr.alternative { - let alternative_span = alternative.type_span(); - let (else_, else_type) = - self.elaborate_expression_with_target_type(alternative, target_type); - (Some(else_), else_type, alternative_span) - } else { - (None, Type::Unit, consequence_span) - }; + let (alternative, else_type, error_location) = + if let Some(alternative) = if_expr.alternative { + let alternative_location = alternative.type_location(); + let (else_, else_type) = + self.elaborate_expression_with_target_type(alternative, target_type); + (Some(else_), else_type, alternative_location) + } else { + (None, Type::Unit, consequence_location) + }; self.unify(&ret_type, &else_type, || { let err = TypeCheckError::TypeMismatch { expected_typ: ret_type.to_string(), expr_typ: else_type.to_string(), - expr_span: error_span, + expr_location: error_location, }; let context = if ret_type == Type::Unit { @@ -1021,8 +1074,10 @@ impl<'context> Elaborator<'context> { fn elaborate_match( &mut self, match_expr: MatchExpression, - span: Span, + location: Location, ) -> (HirExpression, Type) { + self.use_unstable_feature(super::UnstableFeature::Enums, location); + let (expression, typ) = self.elaborate_expression(match_expr.expression); let (let_, variable) = self.wrap_in_let(expression, typ); @@ -1030,10 +1085,10 @@ impl<'context> Elaborator<'context> { let tree = HirExpression::Match(self.elaborate_match_rows(rows)); let tree = self.interner.push_expr(tree); self.interner.push_expr_type(tree, result_type.clone()); - self.interner.push_expr_location(tree, span, self.file); + self.interner.push_expr_location(tree, location); let tree = self.interner.push_stmt(HirStatement::Expression(tree)); - self.interner.push_stmt_location(tree, span, self.file); + self.interner.push_stmt_location(tree, location); let block = HirExpression::Block(HirBlockExpression { statements: vec![let_, tree] }); (block, result_type) @@ -1049,7 +1104,7 @@ impl<'context> Elaborator<'context> { let pattern = HirPattern::Identifier(HirIdent::non_trait_method(variable, location)); let let_ = HirStatement::Let(HirLetStatement::basic(pattern, typ, expr_id)); let let_ = self.interner.push_stmt(let_); - self.interner.push_stmt_location(let_, location.span, location.file); + self.interner.push_stmt_location(let_, location); (let_, variable) } @@ -1121,7 +1176,7 @@ impl<'context> Elaborator<'context> { }); let return_type = self.resolve_inferred_type(lambda.return_type); - let body_span = lambda.body.span; + let body_location = lambda.body.location; let (body, body_type) = self.elaborate_expression(lambda.body); let lambda_context = self.lambda_stack.pop().unwrap(); @@ -1130,7 +1185,7 @@ impl<'context> Elaborator<'context> { self.unify(&body_type, &return_type, || TypeCheckError::TypeMismatch { expected_typ: return_type.to_string(), expr_typ: body_type.to_string(), - expr_span: body_span, + expr_location: body_location, }); let captured_vars = vecmap(&lambda_context.captures, |capture| { @@ -1145,13 +1200,13 @@ impl<'context> Elaborator<'context> { (expr, Type::Function(arg_types, Box::new(body_type), Box::new(env_type), false)) } - fn elaborate_quote(&mut self, mut tokens: Tokens, span: Span) -> (HirExpression, Type) { + fn elaborate_quote(&mut self, mut tokens: Tokens, location: Location) -> (HirExpression, Type) { tokens = self.find_unquoted_exprs_tokens(tokens); if self.in_comptime_context() { (HirExpression::Quote(tokens), Type::Quoted(QuotedType::Quoted)) } else { - self.push_err(ResolverError::QuoteInRuntimeCode { span }); + self.push_err(ResolverError::QuoteInRuntimeCode { location }); (HirExpression::Error, Type::Quoted(QuotedType::Quoted)) } } @@ -1159,7 +1214,7 @@ impl<'context> Elaborator<'context> { fn elaborate_comptime_block( &mut self, block: BlockExpression, - span: Span, + location: Location, target_type: Option<&Type>, ) -> (ExprId, Type) { let (block, _typ) = self.elaborate_in_comptime_context(|this| { @@ -1168,11 +1223,11 @@ impl<'context> Elaborator<'context> { let mut interpreter = self.setup_interpreter(); let value = interpreter.evaluate_block(block); - let (id, typ) = self.inline_comptime_value(value, span); + let (id, typ) = self.inline_comptime_value(value, location); let location = self.interner.id_location(id); self.debug_comptime(location, |interner| { - interner.expression(&id).to_display_ast(interner, location.span).kind + interner.expression(&id).to_display_ast(interner, location).kind }); (id, typ) @@ -1181,12 +1236,13 @@ impl<'context> Elaborator<'context> { pub fn inline_comptime_value( &mut self, value: Result, - span: Span, + location: Location, ) -> (ExprId, Type) { let make_error = |this: &mut Self, error: InterpreterError| { - this.errors.push(error.into_compilation_error_pair()); + let error: CompilationError = error.into(); + this.push_err(error); let error = this.interner.push_expr(HirExpression::Error); - this.interner.push_expr_location(error, span, this.file); + this.interner.push_expr_location(error, location); (error, Type::Error) }; @@ -1195,7 +1251,6 @@ impl<'context> Elaborator<'context> { Err(error) => return make_error(self, error), }; - let location = Location::new(span, self.file); match value.into_expression(self, location) { Ok(new_expr) => { // At this point the Expression was already elaborated and we got a Value. @@ -1225,17 +1280,17 @@ impl<'context> Elaborator<'context> { if meta.is_comptime { Ok(Some(function)) } else { - Err(ResolverError::MacroIsNotComptime { span: location.span }) + Err(ResolverError::MacroIsNotComptime { location }) } } else { - Err(ResolverError::InvalidSyntaxInMacroCall { span: location.span }) + Err(ResolverError::InvalidSyntaxInMacroCall { location }) } } else { // Assume a name resolution error has already been issued Ok(None) } } - _ => Err(ResolverError::InvalidSyntaxInMacroCall { span: location.span }), + _ => Err(ResolverError::InvalidSyntaxInMacroCall { location }), } } @@ -1249,7 +1304,7 @@ impl<'context> Elaborator<'context> { return_type: Type, ) -> Option<(HirExpression, Type)> { self.unify(&return_type, &Type::Quoted(QuotedType::Quoted), || { - TypeCheckError::MacroReturningNonExpr { typ: return_type.clone(), span: location.span } + TypeCheckError::MacroReturningNonExpr { typ: return_type.clone(), location } }); let function = match self.try_get_comptime_function(func, location) { @@ -1260,7 +1315,6 @@ impl<'context> Elaborator<'context> { } }; - let file = self.file; let mut interpreter = self.setup_interpreter(); let mut comptime_args = Vec::new(); let mut errors = Vec::new(); @@ -1271,7 +1325,7 @@ impl<'context> Elaborator<'context> { let location = interpreter.elaborator.interner.expr_location(&argument); comptime_args.push((arg, location)); } - Err(error) => errors.push((error.into(), file)), + Err(error) => errors.push(error.into()), } } @@ -1283,7 +1337,58 @@ impl<'context> Elaborator<'context> { return None; } - let (expr_id, typ) = self.inline_comptime_value(result, location.span); + let (expr_id, typ) = self.inline_comptime_value(result, location); Some((self.interner.expression(&expr_id), typ)) } + + fn elaborate_as_trait_path(&mut self, path: AsTraitPath) -> (ExprId, Type) { + let location = path.typ.location.merge(path.trait_path.location); + + let constraint = UnresolvedTraitConstraint { + typ: path.typ, + trait_bound: TraitBound { + trait_path: path.trait_path, + trait_id: None, + trait_generics: path.trait_generics, + }, + }; + + let typ = self.resolve_type(constraint.typ.clone()); + let Some(trait_bound) = self.resolve_trait_bound(&constraint.trait_bound) else { + // resolve_trait_bound only returns None if it has already issued an error, so don't + // issue another here. + let error = self.interner.push_expr_full(HirExpression::Error, location, Type::Error); + return (error, Type::Error); + }; + + let constraint = TraitConstraint { typ, trait_bound }; + + let the_trait = self.interner.get_trait(constraint.trait_bound.trait_id); + let Some(method) = the_trait.find_method(&path.impl_item.0.contents) else { + let trait_name = the_trait.name.to_string(); + let method_name = path.impl_item.to_string(); + let location = path.impl_item.location(); + self.push_err(ResolverError::NoSuchMethodInTrait { trait_name, method_name, location }); + let error = self.interner.push_expr_full(HirExpression::Error, location, Type::Error); + return (error, Type::Error); + }; + + let trait_method = + TraitMethod { method_id: method, constraint: constraint.clone(), assumed: true }; + + let definition_id = self.interner.trait_method_id(trait_method.method_id); + + let ident = HirIdent { + location: path.impl_item.location(), + id: definition_id, + impl_kind: ImplKind::TraitMethod(trait_method), + }; + + let id = self.interner.push_expr(HirExpression::Ident(ident.clone(), None)); + self.interner.push_expr_location(id, location); + + let typ = self.type_check_variable(ident, id, None); + self.interner.push_expr_type(id, typ.clone()); + (id, typ) + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/lints.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/lints.rs index 3a5844ab21d2..4ce797c6e077 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/lints.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/lints.rs @@ -1,4 +1,5 @@ use crate::{ + Type, ast::{Ident, NoirFunction, Signedness, UnaryOp, Visibility}, graph::CrateId, hir::{ @@ -13,10 +14,9 @@ use crate::{ node_interner::{ DefinitionId, DefinitionKind, ExprId, FuncId, FunctionModifiers, NodeInterner, }, - Type, }; -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Located, Location}; pub(super) fn deprecated_function(interner: &NodeInterner, expr: ExprId) -> Option { let HirExpression::Ident(HirIdent { location, id, impl_kind: _ }, _) = @@ -34,7 +34,7 @@ pub(super) fn deprecated_function(interner: &NodeInterner, expr: ExprId) -> Opti attributes.get_deprecated_note().map(|note| TypeCheckError::CallDeprecated { name: interner.definition_name(id).to_string(), note, - span: location.span, + location, }) } @@ -67,7 +67,7 @@ pub(super) fn low_level_function_outside_stdlib( crate_id: CrateId, ) -> Option { let is_low_level_function = - modifiers.attributes.function().map_or(false, |func| func.is_low_level()); + modifiers.attributes.function().is_some_and(|func| func.is_low_level()); if !crate_id.is_stdlib() && is_low_level_function { let ident = func_meta_name_ident(func, modifiers); Some(ResolverError::LowLevelFunctionOutsideOfStdlib { ident }) @@ -81,7 +81,7 @@ pub(super) fn oracle_not_marked_unconstrained( func: &FuncMeta, modifiers: &FunctionModifiers, ) -> Option { - let is_oracle_function = modifiers.attributes.function().map_or(false, |func| func.is_oracle()); + let is_oracle_function = modifiers.attributes.function().is_some_and(|func| func.is_oracle()); if is_oracle_function && !modifiers.is_unconstrained { let ident = func_meta_name_ident(func, modifiers); Some(ResolverError::OracleMarkedAsConstrained { ident }) @@ -97,16 +97,16 @@ pub(super) fn oracle_called_from_constrained_function( interner: &NodeInterner, called_func: &FuncId, calling_from_constrained_runtime: bool, - span: Span, + location: Location, ) -> Option { if !calling_from_constrained_runtime { return None; } let function_attributes = interner.function_attributes(called_func); - let is_oracle_call = function_attributes.function().map_or(false, |func| func.is_oracle()); + let is_oracle_call = function_attributes.function().is_some_and(|func| func.is_oracle()); if is_oracle_call { - Some(ResolverError::UnconstrainedOracleReturnToConstrained { span }) + Some(ResolverError::UnconstrainedOracleReturnToConstrained { location }) } else { None } @@ -127,13 +127,13 @@ pub(super) fn missing_pub(func: &FuncMeta, modifiers: &FunctionModifiers) -> Opt /// Check that we are not passing a mutable reference from a constrained runtime to an unconstrained runtime. pub(super) fn unconstrained_function_args( - function_args: &[(Type, ExprId, Span)], + function_args: &[(Type, ExprId, Location)], ) -> Vec { function_args .iter() - .filter_map(|(typ, _, span)| { + .filter_map(|(typ, _, location)| { if !typ.is_valid_for_unconstrained_boundary() { - Some(TypeCheckError::ConstrainedReferenceToUnconstrained { span: *span }) + Some(TypeCheckError::ConstrainedReferenceToUnconstrained { location: *location }) } else { None } @@ -144,12 +144,12 @@ pub(super) fn unconstrained_function_args( /// Check that we are not passing a slice from an unconstrained runtime to a constrained runtime. pub(super) fn unconstrained_function_return( return_type: &Type, - span: Span, + location: Location, ) -> Option { if return_type.contains_slice() { - Some(TypeCheckError::UnconstrainedSliceReturnToConstrained { span }) + Some(TypeCheckError::UnconstrainedSliceReturnToConstrained { location }) } else if !return_type.is_valid_for_unconstrained_boundary() { - Some(TypeCheckError::UnconstrainedReferenceToConstrained { span }) + Some(TypeCheckError::UnconstrainedReferenceToConstrained { location }) } else { None } @@ -197,20 +197,20 @@ pub(crate) fn overflowing_int( annotated_type: &Type, ) -> Vec { let expr = interner.expression(rhs_expr); - let span = interner.expr_span(rhs_expr); + let location = interner.expr_location(rhs_expr); let mut errors = Vec::with_capacity(2); match expr { - HirExpression::Literal(HirLiteral::Integer(value, negative)) => match annotated_type { - Type::Integer(Signedness::Unsigned, bit_count) => { - let bit_count: u32 = (*bit_count).into(); - let max = 2u128.pow(bit_count) - 1; - if value > max.into() || negative { + HirExpression::Literal(HirLiteral::Integer(value)) => match annotated_type { + Type::Integer(Signedness::Unsigned, bit_size) => { + let bit_size: u32 = (*bit_size).into(); + let max = if bit_size == 128 { u128::MAX } else { 2u128.pow(bit_size) - 1 }; + if value.field > max.into() || value.is_negative { errors.push(TypeCheckError::OverflowingAssignment { - expr: if negative { -value } else { value }, + expr: value, ty: annotated_type.clone(), range: format!("0..={}", max), - span, + location, }); } } @@ -218,12 +218,14 @@ pub(crate) fn overflowing_int( let bit_count: u32 = (*bit_count).into(); let min = 2u128.pow(bit_count - 1); let max = 2u128.pow(bit_count - 1) - 1; - if (negative && value > min.into()) || (!negative && value > max.into()) { + if (value.is_negative && value.field > min.into()) + || (!value.is_negative && value.field > max.into()) + { errors.push(TypeCheckError::OverflowingAssignment { - expr: if negative { -value } else { value }, + expr: value, ty: annotated_type.clone(), range: format!("-{}..={}", min, max), - span, + location, }); } } @@ -234,7 +236,7 @@ pub(crate) fn overflowing_int( if expr.operator == UnaryOp::Minus && annotated_type.is_unsigned() { errors.push(TypeCheckError::InvalidUnaryOp { kind: annotated_type.to_string(), - span, + location, }); } } @@ -249,7 +251,7 @@ pub(crate) fn overflowing_int( } fn func_meta_name_ident(func: &FuncMeta, modifiers: &FunctionModifiers) -> Ident { - Ident(Spanned::from(func.name.location.span, modifiers.name.clone())) + Ident(Located::from(func.name.location, modifiers.name.clone())) } /// Check that a recursive function *can* return without endlessly calling itself. @@ -257,13 +259,13 @@ pub(crate) fn unbounded_recursion<'a>( interner: &'a NodeInterner, func_id: FuncId, func_name: impl FnOnce() -> &'a str, - func_span: Span, + func_location: Location, body_id: ExprId, ) -> Option { if !can_return_without_recursing(interner, func_id, body_id) { Some(ResolverError::UnconditionalRecursion { name: func_name().to_string(), - span: func_span, + location: func_location, }) } else { None @@ -297,11 +299,7 @@ fn can_return_without_recursing(interner: &NodeInterner, func_id: FuncId, expr_i return true; } let definition = interner.definition(ident.id); - if let DefinitionKind::Function(id) = definition.kind { - func_id != id - } else { - true - } + if let DefinitionKind::Function(id) = definition.kind { func_id != id } else { true } } HirExpression::Block(b) => check_block(b), HirExpression::Prefix(e) => check(e.rhs), @@ -344,7 +342,7 @@ fn can_return_without_recursing_match( HirMatch::Guard { cond: _, body, otherwise } => check(*body) && check_match(otherwise), HirMatch::Switch(_, cases, otherwise) => { cases.iter().all(|case| check_match(&case.body)) - && otherwise.as_ref().map_or(true, |case| check_match(case)) + && otherwise.as_ref().is_none_or(|case| check_match(case)) } } } 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 d007bee0d8d3..27726744fd2d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs @@ -4,6 +4,15 @@ use std::{ }; use crate::{ + DataType, StructField, TypeBindings, + ast::{ItemVisibility, UnresolvedType}, + graph::CrateGraph, + hir_def::traits::ResolvedTraitBound, + node_interner::GlobalValue, + usage_tracker::UsageTracker, +}; +use crate::{ + EnumVariant, Shared, Type, TypeVariable, ast::{ BlockExpression, FunctionKind, GenericTypeArgs, Ident, NoirFunction, NoirStruct, Param, Path, Pattern, TraitBound, UnresolvedGeneric, UnresolvedGenerics, @@ -11,19 +20,20 @@ use crate::{ }, graph::CrateId, hir::{ + Context, + comptime::ComptimeError, def_collector::{ dc_crate::{ - filter_literal_globals, CollectedItems, CompilationError, ImplMap, UnresolvedEnum, - UnresolvedFunctions, UnresolvedGlobal, UnresolvedStruct, UnresolvedTraitImpl, - UnresolvedTypeAlias, + CollectedItems, CompilationError, ImplMap, UnresolvedEnum, UnresolvedFunctions, + UnresolvedGlobal, UnresolvedStruct, UnresolvedTraitImpl, UnresolvedTypeAlias, + filter_literal_globals, }, errors::DefCollectorErrorKind, }, - def_map::{DefMaps, LocalModuleId, ModuleData, ModuleId, MAIN_FUNCTION}, + def_map::{DefMaps, LocalModuleId, MAIN_FUNCTION, ModuleData, ModuleId}, resolution::errors::ResolverError, scope::ScopeForest as GenericScopeForest, - type_check::{generics::TraitGenerics, TypeCheckError}, - Context, + type_check::{TypeCheckError, generics::TraitGenerics}, }, hir_def::{ expr::{HirCapturedVar, HirIdent}, @@ -35,22 +45,15 @@ use crate::{ DefinitionKind, DependencyId, ExprId, FuncId, FunctionModifiers, GlobalId, NodeInterner, ReferenceId, TraitId, TraitImplId, TypeAliasId, TypeId, }, + parser::{ParserError, ParserErrorReason}, token::SecondaryAttribute, - EnumVariant, Shared, Type, TypeVariable, -}; -use crate::{ - ast::{ItemVisibility, UnresolvedType}, - graph::CrateGraph, - hir_def::traits::ResolvedTraitBound, - node_interner::GlobalValue, - usage_tracker::UsageTracker, - DataType, StructField, TypeBindings, }; mod comptime; mod enums; mod expressions; mod lints; +mod options; mod path_resolution; mod patterns; mod scope; @@ -60,9 +63,10 @@ mod traits; pub mod types; mod unquote; -use fm::FileId; use iter_extended::vecmap; -use noirc_errors::{Location, Span, Spanned}; +use noirc_errors::{Located, Location}; +pub(crate) use options::ElaboratorOptions; +pub use options::{FrontendOptions, UnstableFeature}; pub use path_resolution::Turbofish; use path_resolution::{PathResolution, PathResolutionItem}; use types::bind_ordered_generics; @@ -104,15 +108,13 @@ pub struct Loop { pub struct Elaborator<'context> { scopes: ScopeForest, - pub(crate) errors: Vec<(CompilationError, FileId)>, + pub(crate) errors: Vec, pub(crate) interner: &'context mut NodeInterner, pub(crate) def_maps: &'context mut DefMaps, pub(crate) usage_tracker: &'context mut UsageTracker, pub(crate) crate_graph: &'context CrateGraph, - pub(crate) file: FileId, - unsafe_block_status: UnsafeBlockStatus, current_loop: Option, @@ -178,9 +180,6 @@ pub struct Elaborator<'context> { crate_id: CrateId, - /// The scope of --debug-comptime, or None if unset - debug_comptime_in_file: Option, - /// These are the globals that have yet to be elaborated. /// This map is used to lazily evaluate these globals if they're encountered before /// they are elaborated (e.g. in a function's type or another global's RHS). @@ -194,8 +193,34 @@ pub struct Elaborator<'context> { /// that comptime value and any visibility errors were already reported. silence_field_visibility_errors: usize, - /// Use pedantic ACVM solving - pedantic_solving: bool, + /// Options from the nargo cli + options: ElaboratorOptions<'context>, + + /// Sometimes items are elaborated because a function attribute ran and generated items. + /// The Elaborator keeps track of these reasons so that when an error is produced it will + /// be wrapped in another error that will include this reason. + pub(crate) elaborate_reasons: im::Vector<(ElaborateReason, Location)>, +} + +#[derive(Copy, Clone)] +pub enum ElaborateReason { + /// A function attribute generated an item that's being elaborated. + RunningAttribute, + /// Evaluating `Module::add_item` + AddingItemToModule, +} + +impl ElaborateReason { + fn to_macro_error(self, error: CompilationError, location: Location) -> ComptimeError { + match self { + ElaborateReason::RunningAttribute => { + ComptimeError::ErrorRunningAttribute { error: Box::new(error), location } + } + ElaborateReason::AddingItemToModule => { + ComptimeError::ErrorAddingItemToModule { error: Box::new(error), location } + } + } + } } #[derive(Default)] @@ -224,9 +249,9 @@ impl<'context> Elaborator<'context> { usage_tracker: &'context mut UsageTracker, crate_graph: &'context CrateGraph, crate_id: CrateId, - debug_comptime_in_file: Option, interpreter_call_stack: im::Vector, - pedantic_solving: bool, + options: ElaboratorOptions<'context>, + elaborate_reasons: im::Vector<(ElaborateReason, Location)>, ) -> Self { Self { scopes: ScopeForest::default(), @@ -235,7 +260,6 @@ impl<'context> Elaborator<'context> { def_maps, usage_tracker, crate_graph, - file: FileId::dummy(), unsafe_block_status: UnsafeBlockStatus::NotInUnsafeBlock, current_loop: None, generics: Vec::new(), @@ -248,21 +272,20 @@ impl<'context> Elaborator<'context> { trait_bounds: Vec::new(), function_context: vec![FunctionContext::default()], current_trait_impl: None, - debug_comptime_in_file, unresolved_globals: BTreeMap::new(), current_trait: None, interpreter_call_stack, in_comptime_context: false, silence_field_visibility_errors: 0, - pedantic_solving, + options, + elaborate_reasons, } } pub fn from_context( context: &'context mut Context, crate_id: CrateId, - debug_comptime_in_file: Option, - pedantic_solving: bool, + options: ElaboratorOptions<'context>, ) -> Self { Self::new( &mut context.def_interner, @@ -270,9 +293,9 @@ impl<'context> Elaborator<'context> { &mut context.usage_tracker, &context.crate_graph, crate_id, - debug_comptime_in_file, im::Vector::new(), - pedantic_solving, + options, + im::Vector::new(), ) } @@ -280,28 +303,18 @@ impl<'context> Elaborator<'context> { context: &'context mut Context, crate_id: CrateId, items: CollectedItems, - debug_comptime_in_file: Option, - pedantic_solving: bool, - ) -> Vec<(CompilationError, FileId)> { - Self::elaborate_and_return_self( - context, - crate_id, - items, - debug_comptime_in_file, - pedantic_solving, - ) - .errors + options: ElaboratorOptions<'context>, + ) -> Vec { + Self::elaborate_and_return_self(context, crate_id, items, options).errors } pub fn elaborate_and_return_self( context: &'context mut Context, crate_id: CrateId, items: CollectedItems, - debug_comptime_in_file: Option, - pedantic_solving: bool, + options: ElaboratorOptions<'context>, ) -> Self { - let mut this = - Self::from_context(context, crate_id, debug_comptime_in_file, pedantic_solving); + let mut this = Self::from_context(context, crate_id, options); this.elaborate_items(items); this.check_and_pop_function_context(); this @@ -382,7 +395,12 @@ impl<'context> Elaborator<'context> { self.elaborate_trait_impl(trait_impl); } - self.errors.extend(self.interner.check_for_dependency_cycles()); + self.push_errors(self.interner.check_for_dependency_cycles()); + } + + /// True if we should use pedantic ACVM solving + pub fn pedantic_solving(&self) -> bool { + self.options.pedantic_solving } /// Runs `f` and if it modifies `self.generics`, `self.generics` is truncated @@ -409,7 +427,7 @@ impl<'context> Elaborator<'context> { if let Kind::Numeric(typ) = &generic.kind() { let definition = DefinitionKind::NumericGeneric(generic.type_var.clone(), typ.clone()); - let ident = Ident::new(generic.name.to_string(), generic.span); + let ident = Ident::new(generic.name.to_string(), generic.location); let hir_ident = self.add_variable_decl( ident, false, // mutable false, // allow_shadowing @@ -428,8 +446,8 @@ impl<'context> Elaborator<'context> { let func_meta = func_meta.expect("FuncMetas should be declared before a function is elaborated"); - let (kind, body, body_span) = match func_meta.take_body() { - FunctionBody::Unresolved(kind, body, span) => (kind, body, span), + let (kind, body, body_location) = match func_meta.take_body() { + FunctionBody::Unresolved(kind, body, location) => (kind, body, location), FunctionBody::Resolved => return, // Do not error for the still-resolving case. If there is a dependency cycle, // the dependency cycle check will find it later on. @@ -444,7 +462,6 @@ impl<'context> Elaborator<'context> { ); 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; @@ -460,8 +477,8 @@ impl<'context> Elaborator<'context> { // Check arg and return-value visibility of standalone functions. if self.should_check_function_visibility(&func_meta, &modifiers) { - let name = Ident(Spanned::from( - func_meta.name.location.span, + let name = Ident(Located::from( + func_meta.name.location, self.interner.definition_name(func_meta.name.id).to_string(), )); for (_, typ, _) in func_meta.parameters.iter() { @@ -469,14 +486,14 @@ impl<'context> Elaborator<'context> { &name, modifiers.visibility, typ, - name.span(), + name.location(), ); } self.check_type_is_not_more_private_then_item( &name, modifiers.visibility, func_meta.return_type(), - name.span(), + name.location(), ); } @@ -493,7 +510,7 @@ impl<'context> Elaborator<'context> { self.add_existing_variable_to_scope(name, parameter.clone(), warn_if_unused); } - self.add_trait_constraints_to_scope(&func_meta.trait_constraints, func_meta.location.span); + self.add_trait_constraints_to_scope(&func_meta.trait_constraints, func_meta.location); let (hir_func, body_type) = match kind { FunctionKind::Builtin @@ -503,7 +520,7 @@ impl<'context> Elaborator<'context> { FunctionKind::Normal => { let return_type = func_meta.return_type(); let (block, body_type) = self.elaborate_block(body, Some(return_type)); - let expr_id = self.intern_expr(block, body_span); + let expr_id = self.intern_expr(block, body_location); self.interner.push_expr_type(expr_id, body_type.clone()); (HirFunction::unchecked_from_expr(expr_id), body_type) } @@ -536,7 +553,7 @@ impl<'context> Elaborator<'context> { elaborator.interner, id, || elaborator.interner.definition_name(func_meta.name.id), - func_meta.name.location.span, + func_meta.name.location, hir_func.as_expr(), ) .map(Into::into) @@ -569,7 +586,7 @@ impl<'context> Elaborator<'context> { } for (mut constraint, expr_id, select_impl) in context.trait_constraints { - let span = self.interner.expr_span(&expr_id); + let location = self.interner.expr_location(&expr_id); if matches!(&constraint.typ, Type::MutableReference(_)) { let (_, dereferenced_typ) = @@ -584,7 +601,7 @@ impl<'context> Elaborator<'context> { &constraint.trait_bound.trait_generics.named, expr_id, select_impl, - span, + location, ); } } @@ -635,9 +652,9 @@ impl<'context> Elaborator<'context> { } }; - let span = generic.span(); + let location = generic.location(); let name_owned = name.as_ref().clone(); - let resolved_generic = ResolvedGeneric { name, type_var, span }; + let resolved_generic = ResolvedGeneric { name, type_var, location }; // Check for name collisions of this generic // Checking `is_error` here prevents DuplicateDefinition errors when @@ -647,8 +664,8 @@ impl<'context> Elaborator<'context> { if let Some(generic) = self.find_generic(&name_owned) { self.push_err(ResolverError::DuplicateDefinition { name: name_owned, - first_span: generic.span, - second_span: span, + first_location: generic.location, + second_location: location, }); } else { self.generics.push(resolved_generic.clone()); @@ -675,11 +692,11 @@ impl<'context> Elaborator<'context> { } // An already-resolved generic is only possible if it is the result of a // previous macro call being inserted into a generics list. - UnresolvedGeneric::Resolved(id, span) => { + UnresolvedGeneric::Resolved(id, location) => { match self.interner.get_quoted_type(*id).follow_bindings() { Type::NamedGeneric(type_variable, name) => Ok((type_variable.clone(), name)), other => Err(ResolverError::MacroResultInGenericsListNotAGeneric { - span: *span, + location: *location, typ: other.clone(), }), } @@ -715,8 +732,13 @@ impl<'context> Elaborator<'context> { } } - fn push_err(&mut self, error: impl Into) { - self.errors.push((error.into(), self.file)); + pub(crate) fn push_err(&mut self, error: impl Into) { + let error: CompilationError = error.into(); + self.errors.push(error); + } + + pub(crate) fn push_errors(&mut self, errors: impl IntoIterator) { + self.errors.extend(errors); } fn run_lint(&mut self, lint: impl Fn(&Elaborator) -> Option) { @@ -728,11 +750,7 @@ impl<'context> Elaborator<'context> { pub fn resolve_module_by_path(&mut self, path: Path) -> Option { match self.resolve_path(path.clone()) { Ok(PathResolution { item: PathResolutionItem::Module(module_id), errors }) => { - if errors.is_empty() { - Some(module_id) - } else { - None - } + if errors.is_empty() { Some(module_id) } else { None } } _ => None, } @@ -831,16 +849,16 @@ impl<'context> Elaborator<'context> { let kind = associated_type.type_var.kind(); let type_var = TypeVariable::unbound(new_generic_id, kind); - let span = bound.trait_path.span; + let location = bound.trait_path.location; let name = format!("<{object} as {trait_name}>::{}", associated_type.name); let name = Rc::new(name); let typ = Type::NamedGeneric(type_var.clone(), name.clone()); let typ = self.interner.push_quoted_type(typ); - let typ = UnresolvedTypeData::Resolved(typ).with_span(span); - let ident = Ident::new(associated_type.name.as_ref().clone(), span); + let typ = UnresolvedTypeData::Resolved(typ).with_location(location); + let ident = Ident::new(associated_type.name.as_ref().clone(), location); bound.trait_generics.named_args.push((ident, typ)); - added_generics.push(ResolvedGeneric { name, span, type_var }); + added_generics.push(ResolvedGeneric { name, location, type_var }); } } } @@ -860,7 +878,7 @@ impl<'context> Elaborator<'context> { let trait_bound = self.resolve_trait_bound(&constraint.trait_bound)?; self.add_trait_bound_to_scope( - constraint.trait_bound.trait_path.span, + constraint.trait_bound.trait_path.location, &typ, &trait_bound, trait_bound.trait_id, @@ -872,12 +890,13 @@ impl<'context> Elaborator<'context> { pub fn resolve_trait_bound(&mut self, bound: &TraitBound) -> Option { let the_trait = self.lookup_trait_or_error(bound.trait_path.clone())?; let trait_id = the_trait.id; - let span = bound.trait_path.span; + let location = bound.trait_path.location; - let (ordered, named) = self.resolve_type_args(bound.trait_generics.clone(), trait_id, span); + let (ordered, named) = + self.resolve_type_args(bound.trait_generics.clone(), trait_id, location); let trait_generics = TraitGenerics { ordered, named }; - Some(ResolvedTraitBound { trait_id, trait_generics, span }) + Some(ResolvedTraitBound { trait_id, trait_generics, location }) } /// Extract metadata from a NoirFunction @@ -901,7 +920,7 @@ impl<'context> Elaborator<'context> { self.scopes.start_function(); self.current_item = Some(DependencyId::Function(func_id)); - let location = Location::new(func.name_ident().span(), self.file); + let location = func.name_ident().location(); let id = self.interner.function_definition_id(func_id); let name_ident = HirIdent::non_trait_method(id, location); @@ -926,12 +945,12 @@ impl<'context> Elaborator<'context> { let mut parameter_types = Vec::new(); let mut parameter_idents = Vec::new(); - for Param { visibility, pattern, typ, span: _ } in func.parameters().iter().cloned() { + for Param { visibility, pattern, typ, location: _ } in func.parameters().iter().cloned() { self.run_lint(|_| { lints::unnecessary_pub_argument(func, visibility, is_pub_allowed).map(Into::into) }); - let type_span = typ.span; + let type_location = typ.location; let typ = match typ.typ { UnresolvedTypeData::TraitAsType(path, args) => { self.desugar_impl_trait_arg(path, args, &mut generics, &mut trait_constraints) @@ -944,7 +963,7 @@ impl<'context> Elaborator<'context> { &typ, is_entry_point, has_inline_attribute, - type_span, + type_location, ); if is_entry_point { @@ -1016,9 +1035,9 @@ impl<'context> Elaborator<'context> { has_inline_attribute, source_crate: self.crate_id, source_module: self.local_module, - function_body: FunctionBody::Unresolved(func.kind, body, func.def.span), + function_body: FunctionBody::Unresolved(func.kind, body, func.def.location), self_type: self.self_type.clone(), - source_file: self.file, + source_file: location.file, }; self.interner.push_fn_meta(meta, func_id); @@ -1105,12 +1124,12 @@ impl<'context> Elaborator<'context> { typ: &Type, is_entry_point: bool, has_inline_attribute: bool, - span: Span, + location: Location, ) { if (is_entry_point && !typ.is_valid_for_program_input()) || (has_inline_attribute && !typ.is_valid_non_inlined_function_input()) { - self.push_err(TypeCheckError::InvalidTypeForEntryPoint { span }); + self.push_err(TypeCheckError::InvalidTypeForEntryPoint { location }); } } @@ -1143,10 +1162,14 @@ impl<'context> Elaborator<'context> { } } - fn add_trait_constraints_to_scope(&mut self, constraints: &[TraitConstraint], span: Span) { + fn add_trait_constraints_to_scope( + &mut self, + constraints: &[TraitConstraint], + location: Location, + ) { for constraint in constraints { self.add_trait_bound_to_scope( - span, + location, &constraint.typ, &constraint.trait_bound, constraint.trait_bound.trait_id, @@ -1156,11 +1179,11 @@ impl<'context> Elaborator<'context> { // Also assume `self` implements the current trait if we are inside a trait definition if let Some(trait_id) = self.current_trait { let the_trait = self.interner.get_trait(trait_id); - let constraint = the_trait.as_constraint(the_trait.name.span()); + let constraint = the_trait.as_constraint(the_trait.name.location()); let self_type = self.self_type.clone().expect("Expected a self type if there's a current trait"); self.add_trait_bound_to_scope( - span, + location, &self_type, &constraint.trait_bound, constraint.trait_bound.trait_id, @@ -1182,7 +1205,7 @@ impl<'context> Elaborator<'context> { fn add_trait_bound_to_scope( &mut self, - span: Span, + location: Location, object: &Type, trait_bound: &ResolvedTraitBound, starting_trait_id: TraitId, @@ -1194,7 +1217,11 @@ impl<'context> Elaborator<'context> { if let Some(the_trait) = self.interner.try_get_trait(trait_id) { let trait_name = the_trait.name.to_string(); let typ = object.clone(); - self.push_err(TypeCheckError::UnneededTraitConstraint { trait_name, typ, span }); + self.push_err(TypeCheckError::UnneededTraitConstraint { + trait_name, + typ, + location, + }); } } @@ -1210,20 +1237,23 @@ impl<'context> Elaborator<'context> { let parent_trait_bound = self.instantiate_parent_trait_bound(trait_bound, &parent_trait_bound); - self.add_trait_bound_to_scope(span, object, &parent_trait_bound, starting_trait_id); + self.add_trait_bound_to_scope( + location, + object, + &parent_trait_bound, + starting_trait_id, + ); } } } - fn elaborate_impls(&mut self, impls: Vec<(UnresolvedGenerics, Span, UnresolvedFunctions)>) { + fn elaborate_impls(&mut self, impls: Vec<(UnresolvedGenerics, Location, UnresolvedFunctions)>) { for (_, _, functions) in impls { - self.file = functions.file_id; self.recover_generics(|this| this.elaborate_functions(functions)); } } fn elaborate_trait_impl(&mut self, trait_impl: UnresolvedTraitImpl) { - self.file = trait_impl.file_id; self.local_module = trait_impl.module_id; self.generics = trait_impl.resolved_generics.clone(); @@ -1234,10 +1264,14 @@ impl<'context> Elaborator<'context> { self.check_parent_traits_are_implemented(&trait_impl); self.remove_trait_impl_assumed_trait_implementations(trait_impl.impl_id); - for (module, function, _) in &trait_impl.methods.functions { + for (module, function, noir_function) in &trait_impl.methods.functions { self.local_module = *module; - let errors = check_trait_impl_method_matches_declaration(self.interner, *function); - self.errors.extend(errors.into_iter().map(|error| (error.into(), self.file))); + let errors = check_trait_impl_method_matches_declaration( + self.interner, + *function, + noir_function, + ); + self.push_errors(errors.into_iter().map(|error| error.into())); } self.elaborate_functions(trait_impl.methods); @@ -1293,7 +1327,6 @@ impl<'context> Elaborator<'context> { } let impl_trait = the_trait.name.to_string(); - let the_trait_file = the_trait.location.file; let mut bindings = TypeBindings::new(); bind_ordered_generics( @@ -1329,8 +1362,8 @@ impl<'context> Elaborator<'context> { impl_trait: impl_trait.clone(), missing_trait, type_missing_trait: trait_constraint_type.to_string(), - span: trait_impl.object_type.span, - missing_trait_location: Location::new(trait_bound.span, the_trait_file), + location: trait_impl.object_type.location, + missing_trait_location: trait_bound.location, }); } } @@ -1354,7 +1387,6 @@ impl<'context> Elaborator<'context> { } let impl_trait = the_trait.name.to_string(); - let the_trait_file = the_trait.location.file; let mut bindings = TypeBindings::new(); bind_ordered_generics( @@ -1396,8 +1428,8 @@ impl<'context> Elaborator<'context> { impl_trait: impl_trait.clone(), missing_trait, type_missing_trait: trait_impl.object_type.to_string(), - span: trait_impl.object_type.span, - missing_trait_location: Location::new(parent_trait_bound.span, the_trait_file), + location: trait_impl.object_type.location, + missing_trait_location: parent_trait_bound.location, }); } } @@ -1406,22 +1438,20 @@ impl<'context> Elaborator<'context> { fn collect_impls( &mut self, module: LocalModuleId, - impls: &mut [(UnresolvedGenerics, Span, UnresolvedFunctions)], + impls: &mut [(UnresolvedGenerics, Location, UnresolvedFunctions)], ) { self.local_module = module; - for (generics, span, unresolved) in impls { - self.file = unresolved.file_id; + for (generics, location, unresolved) in impls { let old_generic_count = self.generics.len(); self.add_generics(generics); - self.declare_methods_on_struct(None, unresolved, *span); + self.declare_methods_on_struct(None, unresolved, *location); self.generics.truncate(old_generic_count); } } fn collect_trait_impl(&mut self, trait_impl: &mut UnresolvedTraitImpl) { self.local_module = trait_impl.module_id; - self.file = trait_impl.file_id; self.current_trait_impl = trait_impl.impl_id; let self_type = trait_impl.methods.self_type.clone(); @@ -1429,11 +1459,12 @@ impl<'context> Elaborator<'context> { self_type.expect("Expected struct type to be set before collect_trait_impl"); self.self_type = Some(self_type.clone()); - let self_type_span = trait_impl.object_type.span; + let self_type_location = trait_impl.object_type.location; if matches!(self_type, Type::MutableReference(_)) { - let span = self_type_span; - self.push_err(DefCollectorErrorKind::MutableReferenceInTraitImpl { span }); + self.push_err(DefCollectorErrorKind::MutableReferenceInTraitImpl { + location: self_type_location, + }); } if let Some(trait_id) = trait_impl.trait_id { @@ -1444,8 +1475,8 @@ impl<'context> Elaborator<'context> { self.collect_trait_impl_methods(trait_id, trait_impl, &where_clause); - let span = trait_impl.object_type.span; - self.declare_methods_on_struct(Some(trait_id), &mut trait_impl.methods, span); + let location = trait_impl.object_type.location; + self.declare_methods_on_struct(Some(trait_id), &mut trait_impl.methods, location); let trait_visibility = self.interner.get_trait(trait_id).visibility; @@ -1468,12 +1499,12 @@ impl<'context> Elaborator<'context> { } else { typ.to_string() }; - Ident::new(name, trait_impl.r#trait.span) + Ident::new(name, trait_impl.r#trait.location) } _ => { // We don't error in this case because an error will be produced later on when // solving the trait impl trait type - Ident::new(trait_impl.r#trait.to_string(), trait_impl.r#trait.span) + Ident::new(trait_impl.r#trait.to_string(), trait_impl.r#trait.location) } }; @@ -1489,7 +1520,7 @@ impl<'context> Elaborator<'context> { let generics = vecmap(&self.generics, |generic| generic.type_var.clone()); - if let Err((prev_span, prev_file)) = self.interner.add_trait_implementation( + if let Err(prev_location) = self.interner.add_trait_implementation( self_type.clone(), trait_id, trait_impl.impl_id.expect("impl_id should be set in define_function_metas"), @@ -1498,14 +1529,9 @@ impl<'context> Elaborator<'context> { ) { self.push_err(DefCollectorErrorKind::OverlappingImpl { typ: self_type.clone(), - span: self_type_span, + location: self_type_location, + prev_location, }); - - // The 'previous impl defined here' note must be a separate error currently - // since it may be in a different file and all errors have the same file id. - self.file = prev_file; - self.push_err(DefCollectorErrorKind::OverlappingImplNote { span: prev_span }); - self.file = trait_impl.file_id; } } @@ -1529,7 +1555,7 @@ impl<'context> Elaborator<'context> { &mut self, trait_id: Option, functions: &mut UnresolvedFunctions, - span: Span, + location: Location, ) { let self_type = functions.self_type.as_ref(); let self_type = @@ -1543,7 +1569,7 @@ impl<'context> Elaborator<'context> { // `impl`s are only allowed on types defined within the current crate if trait_id.is_none() && struct_ref.id.krate() != self.crate_id { let type_name = struct_ref.name.to_string(); - self.push_err(DefCollectorErrorKind::ForeignImpl { span, type_name }); + self.push_err(DefCollectorErrorKind::ForeignImpl { location, type_name }); return; } @@ -1589,7 +1615,7 @@ impl<'context> Elaborator<'context> { self.declare_methods(self_type, &function_ids); } } else { - self.push_err(DefCollectorErrorKind::NonStructTypeInImpl { span }); + self.push_err(DefCollectorErrorKind::NonStructTypeInImpl { location }); } } } @@ -1601,10 +1627,12 @@ impl<'context> Elaborator<'context> { if let Some(first_fn) = self.interner.add_method(self_type, method_name.clone(), *method_id, None) { + let first_location = self.interner.function_ident(&first_fn).location(); + let second_location = self.interner.function_ident(method_id).location(); let error = ResolverError::DuplicateDefinition { name: method_name, - first_span: self.interner.function_ident(&first_fn).span(), - second_span: self.interner.function_ident(method_id).span(), + first_location, + second_location, }; self.push_err(error); } @@ -1612,19 +1640,18 @@ impl<'context> Elaborator<'context> { } fn define_type_alias(&mut self, alias_id: TypeAliasId, alias: UnresolvedTypeAlias) { - self.file = alias.file_id; self.local_module = alias.module_id; let name = &alias.type_alias_def.name; let visibility = alias.type_alias_def.visibility; - let span = alias.type_alias_def.typ.span; + let location = alias.type_alias_def.typ.location; let generics = self.add_generics(&alias.type_alias_def.generics); self.current_item = Some(DependencyId::Alias(alias_id)); let typ = self.resolve_type(alias.type_alias_def.typ); if visibility != ItemVisibility::Private { - self.check_type_is_not_more_private_then_item(name, visibility, &typ, span); + self.check_type_is_not_more_private_then_item(name, visibility, &typ, location); } self.interner.set_type_alias(alias_id, typ, generics); @@ -1671,7 +1698,7 @@ impl<'context> Elaborator<'context> { name: &Ident, visibility: ItemVisibility, typ: &Type, - span: Span, + location: Location, ) { match typ { Type::DataType(struct_type, generics) => { @@ -1687,19 +1714,21 @@ impl<'context> Elaborator<'context> { self.push_err(ResolverError::TypeIsMorePrivateThenItem { typ: struct_type.name.to_string(), item: name.to_string(), - span, + location, }); } } } for generic in generics { - self.check_type_is_not_more_private_then_item(name, visibility, generic, span); + self.check_type_is_not_more_private_then_item( + name, visibility, generic, location, + ); } } Type::Tuple(types) => { for typ in types { - self.check_type_is_not_more_private_then_item(name, visibility, typ, span); + self.check_type_is_not_more_private_then_item(name, visibility, typ, location); } } Type::Alias(alias_type, generics) => { @@ -1707,26 +1736,31 @@ impl<'context> Elaborator<'context> { name, visibility, &alias_type.borrow().get_type(generics), - span, + location, ); } Type::CheckedCast { from, to } => { - self.check_type_is_not_more_private_then_item(name, visibility, from, span); - self.check_type_is_not_more_private_then_item(name, visibility, to, span); + self.check_type_is_not_more_private_then_item(name, visibility, from, location); + self.check_type_is_not_more_private_then_item(name, visibility, to, location); } Type::Function(args, return_type, env, _) => { for arg in args { - self.check_type_is_not_more_private_then_item(name, visibility, arg, span); + self.check_type_is_not_more_private_then_item(name, visibility, arg, location); } - self.check_type_is_not_more_private_then_item(name, visibility, return_type, span); - self.check_type_is_not_more_private_then_item(name, visibility, env, span); + self.check_type_is_not_more_private_then_item( + name, + visibility, + return_type, + location, + ); + self.check_type_is_not_more_private_then_item(name, visibility, env, location); } Type::MutableReference(typ) | Type::Array(_, typ) | Type::Slice(typ) => { - self.check_type_is_not_more_private_then_item(name, visibility, typ, span); + self.check_type_is_not_more_private_then_item(name, visibility, typ, location); } Type::InfixExpr(left, _op, right, _) => { - self.check_type_is_not_more_private_then_item(name, visibility, left, span); - self.check_type_is_not_more_private_then_item(name, visibility, right, span); + self.check_type_is_not_more_private_then_item(name, visibility, left, location); + self.check_type_is_not_more_private_then_item(name, visibility, right, location); } Type::FieldElement | Type::Integer(..) @@ -1752,7 +1786,6 @@ impl<'context> Elaborator<'context> { // Resolve each field in each struct. // Each struct should already be present in the NodeInterner after def collection. for (type_id, typ) in structs { - self.file = typ.file_id; self.local_module = typ.module_id; let fields = self.resolve_struct_fields(&typ.struct_def, *type_id); @@ -1766,22 +1799,22 @@ impl<'context> Elaborator<'context> { // Check that the a public struct doesn't have a private type as a public field. if typ.struct_def.visibility != ItemVisibility::Private { for field in &fields { - let ident = Ident(Spanned::from( - field.name.span(), + let ident = Ident::from(Located::from( + field.name.location(), format!("{}::{}", typ.struct_def.name, field.name), )); self.check_type_is_not_more_private_then_item( &ident, field.visibility, &field.typ, - field.name.span(), + field.name.location(), ); } } if self.interner.is_in_lsp_mode() { for (field_index, field) in fields.iter().enumerate() { - let location = Location::new(field.name.span(), self.file); + let location = field.name.location(); let reference_id = ReferenceId::StructMember(*type_id, field_index); self.interner.add_definition_location(reference_id, location, None); } @@ -1805,8 +1838,7 @@ impl<'context> Elaborator<'context> { for (_, field_type) in fields.iter() { if field_type.is_nested_slice() { let location = struct_type.borrow().location; - self.file = location.file; - self.push_err(ResolverError::NestedSlices { span: location.span }); + self.push_err(ResolverError::NestedSlices { location }); } } } @@ -1841,20 +1873,22 @@ impl<'context> Elaborator<'context> { fn collect_enum_definitions(&mut self, enums: &BTreeMap) { for (type_id, typ) in enums { - self.file = typ.file_id; self.local_module = typ.module_id; self.generics.clear(); let datatype = self.interner.get_type(*type_id); - let generics = datatype.borrow().generic_types(); - self.add_existing_generics(&typ.enum_def.generics, &datatype.borrow().generics); + let datatype_ref = datatype.borrow(); + let generics = datatype_ref.generic_types(); + self.add_existing_generics(&typ.enum_def.generics, &datatype_ref.generics); + + self.use_unstable_feature(UnstableFeature::Enums, datatype_ref.name.location()); + drop(datatype_ref); let self_type = Type::DataType(datatype.clone(), generics); let self_type_id = self.interner.push_quoted_type(self_type.clone()); - let unresolved = UnresolvedType { - typ: UnresolvedTypeData::Resolved(self_type_id), - span: typ.enum_def.span, - }; + let location = typ.enum_def.location; + let unresolved = + UnresolvedType { typ: UnresolvedTypeData::Resolved(self_type_id), location }; datatype.borrow_mut().init_variants(); let module_id = ModuleId { krate: self.crate_id, local_id: typ.module_id }; @@ -1881,7 +1915,7 @@ impl<'context> Elaborator<'context> { ); let reference_id = ReferenceId::EnumVariant(*type_id, i); - let location = Location::new(variant.item.name.span(), self.file); + let location = variant.item.name.location(); self.interner.add_definition_location(reference_id, location, Some(module_id)); } } @@ -1889,7 +1923,6 @@ impl<'context> Elaborator<'context> { fn elaborate_global(&mut self, global: UnresolvedGlobal) { let old_module = std::mem::replace(&mut self.local_module, global.module_id); - let old_file = std::mem::replace(&mut self.file, global.file_id); let old_item = self.current_item.take(); let global_id = global.global_id; @@ -1902,16 +1935,16 @@ impl<'context> Elaborator<'context> { None }; - let span = let_stmt.pattern.span(); + let location = let_stmt.pattern.location(); if !self.in_contract() && let_stmt.attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) { - self.push_err(ResolverError::AbiAttributeOutsideContract { span }); + self.push_err(ResolverError::AbiAttributeOutsideContract { location }); } if !let_stmt.comptime && matches!(let_stmt.pattern, Pattern::Mutable(..)) { - self.push_err(ResolverError::MutableGlobal { span }); + self.push_err(ResolverError::MutableGlobal { location }); } let (let_statement, _typ) = self @@ -1923,7 +1956,6 @@ impl<'context> Elaborator<'context> { self.elaborate_comptime_global(global_id); if let Some(name) = name { - let location = Location::new(span, self.file); self.interner.register_global( global_id, name, @@ -1934,7 +1966,6 @@ impl<'context> Elaborator<'context> { } self.local_module = old_module; - self.file = old_file; self.current_item = old_item; } @@ -1950,7 +1981,8 @@ impl<'context> Elaborator<'context> { let mut interpreter = self.setup_interpreter(); if let Err(error) = interpreter.evaluate_let(let_statement) { - self.errors.push(error.into_compilation_error_pair()); + let error: CompilationError = error.into(); + self.push_err(error); } else { let value = interpreter .lookup_id(definition_id, location) @@ -1986,7 +2018,6 @@ impl<'context> Elaborator<'context> { self.local_module = *local_module; for (generics, _, function_set) in function_sets { - self.file = function_set.file_id; self.add_generics(generics); let self_type = self.resolve_type(self_type.clone()); function_set.self_type = Some(self_type.clone()); @@ -1998,20 +2029,19 @@ impl<'context> Elaborator<'context> { } for trait_impl in trait_impls { - self.file = trait_impl.file_id; self.local_module = trait_impl.module_id; - let (trait_id, mut trait_generics, path_span) = match &trait_impl.r#trait.typ { + let (trait_id, mut trait_generics, path_location) = match &trait_impl.r#trait.typ { UnresolvedTypeData::Named(trait_path, trait_generics, _) => { let trait_id = self.resolve_trait_by_path(trait_path.clone()); - (trait_id, trait_generics.clone(), trait_path.span) + (trait_id, trait_generics.clone(), trait_path.location) } UnresolvedTypeData::Resolved(quoted_type_id) => { let typ = self.interner.get_quoted_type(*quoted_type_id); - let span = trait_impl.r#trait.span; + let location = trait_impl.r#trait.location; let Type::TraitAsType(trait_id, _, trait_generics) = typ else { let found = typ.to_string(); - self.push_err(ResolverError::ExpectedTrait { span, found }); + self.push_err(ResolverError::ExpectedTrait { location, found }); continue; }; @@ -2023,24 +2053,24 @@ impl<'context> Elaborator<'context> { ordered_args: vecmap(&trait_generics.ordered, |typ| { let quoted_type_id = self.interner.push_quoted_type(typ.clone()); let typ = UnresolvedTypeData::Resolved(quoted_type_id); - UnresolvedType { typ, span } + UnresolvedType { typ, location } }), named_args: vecmap(&trait_generics.named, |named_type| { let quoted_type_id = self.interner.push_quoted_type(named_type.typ.clone()); let typ = UnresolvedTypeData::Resolved(quoted_type_id); - (named_type.name.clone(), UnresolvedType { typ, span }) + (named_type.name.clone(), UnresolvedType { typ, location }) }), kinds: Vec::new(), }; - (Some(trait_id), trait_generics, span) + (Some(trait_id), trait_generics, location) } _ => { - let span = trait_impl.r#trait.span; + let location = trait_impl.r#trait.location; let found = trait_impl.r#trait.typ.to_string(); - self.push_err(ResolverError::ExpectedTrait { span, found }); - continue; + self.push_err(ResolverError::ExpectedTrait { location, found }); + (None, GenericTypeArgs::default(), location) } }; @@ -2076,7 +2106,7 @@ impl<'context> Elaborator<'context> { .trait_id .map(|trait_id| { // Check for missing generics & associated types for the trait being implemented - self.resolve_trait_args_from_trait_impl(trait_generics, trait_id, path_span) + self.resolve_trait_args_from_trait_impl(trait_generics, trait_id, path_location) }) .unwrap_or_default(); @@ -2096,22 +2126,19 @@ impl<'context> Elaborator<'context> { self.generics.clear(); if let Some(trait_id) = trait_id { - let (span, is_self_type_name) = match &trait_impl.r#trait.typ { + let (location, is_self_type_name) = match &trait_impl.r#trait.typ { UnresolvedTypeData::Named(trait_path, _, _) => { let trait_name = trait_path.last_ident(); - (trait_name.span(), trait_name.is_self_type_name()) + (trait_name.location(), trait_name.is_self_type_name()) } - _ => (trait_impl.r#trait.span, false), + _ => (trait_impl.r#trait.location, false), }; - let location = Location::new(span, trait_impl.file_id); self.interner.add_trait_reference(trait_id, location, is_self_type_name); } } } fn define_function_metas_for_functions(&mut self, function_set: &mut UnresolvedFunctions) { - self.file = function_set.file_id; - for (local_module, id, func) in &mut function_set.functions { self.local_module = *local_module; self.recover_generics(|this| { @@ -2124,11 +2151,20 @@ impl<'context> Elaborator<'context> { /// Defaults to `true` if the current function is unknown. fn in_constrained_function(&self) -> bool { !self.in_comptime_context() - && self.current_item.map_or(true, |id| match id { + && self.current_item.is_none_or(|id| match id { DependencyId::Function(id) => { !self.interner.function_modifiers(&id).is_unconstrained } _ => true, }) } + + /// Register a use of the given unstable feature. Errors if the feature has not + /// been explicitly enabled in this package. + pub fn use_unstable_feature(&mut self, feature: UnstableFeature, location: Location) { + if !self.options.enabled_unstable_features.contains(&feature) { + let reason = ParserErrorReason::ExperimentalFeature(feature); + self.push_err(ParserError::with_reason(reason, location)); + } + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/options.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/options.rs new file mode 100644 index 000000000000..58bb5e73a618 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/options.rs @@ -0,0 +1,62 @@ +use std::str::FromStr; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum UnstableFeature { + Enums, + ArrayOwnership, +} + +impl std::fmt::Display for UnstableFeature { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Enums => write!(f, "enums"), + Self::ArrayOwnership => write!(f, "array-ownership"), + } + } +} + +impl FromStr for UnstableFeature { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "enums" => Ok(Self::Enums), + "array-ownership" => Ok(Self::ArrayOwnership), + other => Err(format!("Unknown unstable feature '{other}'")), + } + } +} + +/// Generic options struct meant to resolve to ElaboratorOptions below when +/// we can resolve a file path to a file id later. This generic struct is used +/// so that FrontendOptions doesn't need to duplicate fields and methods with ElaboratorOptions. +#[derive(Copy, Clone)] +pub struct GenericOptions<'a, T> { + /// The scope of --debug-comptime, or None if unset + pub debug_comptime_in_file: Option, + + /// Use pedantic ACVM solving + pub pedantic_solving: bool, + + /// Unstable compiler features that were explicitly enabled. Any unstable features + /// that are not in this list result in an error when used. + pub enabled_unstable_features: &'a [UnstableFeature], +} + +/// Options from nargo_cli that need to be passed down to the elaborator +pub(crate) type ElaboratorOptions<'a> = GenericOptions<'a, fm::FileId>; + +/// This is the unresolved version of `ElaboratorOptions` +/// CLI options that need to be passed to the compiler frontend (the elaborator). +pub type FrontendOptions<'a> = GenericOptions<'a, &'a str>; + +impl GenericOptions<'_, T> { + /// A sane default of frontend options for running tests + pub fn test_default() -> GenericOptions<'static, T> { + GenericOptions { + debug_comptime_in_file: None, + pedantic_solving: true, + enabled_unstable_features: &[UnstableFeature::Enums], + } + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/path_resolution.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/path_resolution.rs index 67a99da70eb8..c0dfa90d36c9 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/path_resolution.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/path_resolution.rs @@ -1,9 +1,9 @@ use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use crate::ast::{Ident, Path, PathKind, UnresolvedType}; use crate::hir::def_map::{ModuleData, ModuleDefId, ModuleId, PerNs}; -use crate::hir::resolution::import::{resolve_path_kind, PathResolutionError}; +use crate::hir::resolution::import::{PathResolutionError, resolve_path_kind}; use crate::hir::resolution::errors::ResolverError; use crate::hir::resolution::visibility::item_in_module_is_visible; @@ -12,8 +12,8 @@ use crate::locations::ReferencesTracker; use crate::node_interner::{FuncId, GlobalId, TraitId, TypeAliasId, TypeId}; use crate::{Shared, Type, TypeAlias}; -use super::types::SELF_TYPE_NAME; use super::Elaborator; +use super::types::SELF_TYPE_NAME; #[derive(Debug)] pub(crate) struct PathResolution { @@ -73,7 +73,7 @@ impl PathResolutionItem { #[derive(Debug, Clone)] pub struct Turbofish { pub generics: Vec, - pub span: Span, + pub location: Location, } /// Any item that can appear before the last segment in a path. @@ -102,7 +102,7 @@ enum MethodLookupResult { FoundMultipleTraitMethods(Vec), } -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { pub(super) fn resolve_path_or_error( &mut self, path: Path, @@ -149,7 +149,7 @@ impl<'context> Elaborator<'context> { importing_module: ModuleId, ) -> PathResolutionResult { let references_tracker = if self.interner.is_in_lsp_mode() { - Some(ReferencesTracker::new(self.interner, self.file)) + Some(ReferencesTracker::new(self.interner)) } else { None }; @@ -204,7 +204,7 @@ impl<'context> Elaborator<'context> { Some((typ, visibility, _)) => (typ, visibility), }; - let location = Location::new(last_segment.span, self.file); + let location = last_segment.location; self.interner.add_module_def_id_reference( typ, location, @@ -218,7 +218,7 @@ impl<'context> Elaborator<'context> { if last_segment_generics.is_some() { errors.push(PathResolutionError::TurbofishNotAllowedOnItem { item: format!("module `{last_ident}`"), - span: last_segment.turbofish_span(), + location: last_segment.turbofish_location(), }); } @@ -324,7 +324,7 @@ impl<'context> Elaborator<'context> { let name = path.last_ident(); let is_self_type = name.is_self_type_name(); - let location = Location::new(name.span(), self.file); + let location = name.location(); self.interner.add_module_def_id_reference(module_def_id, location, is_self_type); let item = merge_intermediate_path_resolution_item_with_module_def_id( 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 b01c4e0d768b..6ad8e1ec04c9 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -1,11 +1,12 @@ use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use rustc_hash::FxHashSet as HashSet; use crate::{ + DataType, Kind, Shared, Type, TypeAlias, TypeBindings, ast::{ - Expression, ExpressionKind, Ident, ItemVisibility, Path, Pattern, TypePath, UnresolvedType, - ERROR_IDENT, + ERROR_IDENT, Expression, ExpressionKind, Ident, ItemVisibility, Path, Pattern, TypePath, + UnresolvedType, }, hir::{ def_collector::dc_crate::CompilationError, @@ -17,12 +18,11 @@ use crate::{ stmt::HirPattern, }, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, TraitImplKind}, - DataType, Kind, Shared, Type, TypeAlias, TypeBindings, }; -use super::{path_resolution::PathResolutionItem, Elaborator, ResolverMeta}; +use super::{Elaborator, ResolverMeta, path_resolution::PathResolutionItem}; -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { pub(super) fn elaborate_pattern( &mut self, pattern: Pattern, @@ -66,7 +66,7 @@ impl<'context> Elaborator<'context> { pattern: Pattern, expected_type: Type, definition: DefinitionKind, - mutable: Option, + mutable: Option, new_definitions: &mut Vec, warn_if_unused: bool, ) -> HirPattern { @@ -81,7 +81,7 @@ impl<'context> Elaborator<'context> { let ident = if let DefinitionKind::Global(global_id) = definition { // Globals don't need to be added to scope, they're already in the def_maps let id = self.interner.get_global(global_id).definition_id; - let location = Location::new(name.span(), self.file); + let location = name.location(); HirIdent::non_trait_method(id, location) } else { self.add_variable_decl( @@ -96,23 +96,25 @@ impl<'context> Elaborator<'context> { new_definitions.push(ident.clone()); HirPattern::Identifier(ident) } - Pattern::Mutable(pattern, span, _) => { + Pattern::Mutable(pattern, location, _) => { if let Some(first_mut) = mutable { - self.push_err(ResolverError::UnnecessaryMut { first_mut, second_mut: span }); + self.push_err(ResolverError::UnnecessaryMut { + first_mut, + second_mut: location, + }); } let pattern = self.elaborate_pattern_mut( *pattern, expected_type, definition, - Some(span), + Some(location), new_definitions, warn_if_unused, ); - let location = Location::new(span, self.file); HirPattern::Mutable(Box::new(pattern), location) } - Pattern::Tuple(fields, span) => { + Pattern::Tuple(fields, location) => { let field_types = match expected_type.follow_bindings() { Type::Tuple(fields) => fields, Type::Error => Vec::new(), @@ -123,7 +125,7 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::TypeMismatchWithSource { expected: expected_type, actual: tuple, - span, + location, source: Source::Assignment, }); Vec::new() @@ -141,13 +143,12 @@ impl<'context> Elaborator<'context> { warn_if_unused, ) }); - let location = Location::new(span, self.file); HirPattern::Tuple(fields, location) } - Pattern::Struct(name, fields, span) => self.elaborate_struct_pattern( + Pattern::Struct(name, fields, location) => self.elaborate_struct_pattern( name, fields, - span, + location, expected_type, definition, mutable, @@ -172,14 +173,14 @@ impl<'context> Elaborator<'context> { &mut self, name: Path, fields: Vec<(Ident, Pattern)>, - span: Span, + location: Location, expected_type: Type, definition: DefinitionKind, - mutable: Option, + mutable: Option, new_definitions: &mut Vec, ) -> HirPattern { let last_segment = name.last_segment(); - let name_span = last_segment.ident.span(); + let name_location = last_segment.ident.location(); let is_self_type = last_segment.ident.is_self_type_name(); let error_identifier = |this: &mut Self| { @@ -200,27 +201,26 @@ impl<'context> Elaborator<'context> { None => return error_identifier(self), Some(typ) => { let typ = typ.to_string(); - self.push_err(ResolverError::NonStructUsedInConstructor { typ, span }); + self.push_err(ResolverError::NonStructUsedInConstructor { typ, location }); return error_identifier(self); } }; - let turbofish_span = last_segment.turbofish_span(); + let turbofish_location = last_segment.turbofish_location(); let generics = self.resolve_struct_turbofish_generics( &struct_type.borrow(), generics, last_segment.generics, - turbofish_span, + turbofish_location, ); let actual_type = Type::DataType(struct_type.clone(), generics); - let location = Location::new(span, self.file); self.unify(&actual_type, &expected_type, || TypeCheckError::TypeMismatchWithSource { expected: expected_type.clone(), actual: actual_type.clone(), - span: location.span, + location, source: Source::Assignment, }); @@ -228,7 +228,7 @@ impl<'context> Elaborator<'context> { let fields = self.resolve_constructor_pattern_fields( typ, fields, - span, + location, expected_type.clone(), definition, mutable, @@ -237,11 +237,10 @@ impl<'context> Elaborator<'context> { let struct_id = struct_type.borrow().id; - let reference_location = Location::new(name_span, self.file); - self.interner.add_type_reference(struct_id, reference_location, is_self_type); + self.interner.add_type_reference(struct_id, name_location, is_self_type); for (field_index, field) in fields.iter().enumerate() { - let reference_location = Location::new(field.0.span(), self.file); + let reference_location = field.0.location(); self.interner.add_struct_member_reference(struct_id, field_index, reference_location); } @@ -256,10 +255,10 @@ impl<'context> Elaborator<'context> { &mut self, struct_type: Shared, fields: Vec<(Ident, Pattern)>, - span: Span, + location: Location, expected_type: Type, definition: DefinitionKind, - mutable: Option, + mutable: Option, new_definitions: &mut Vec, ) -> Vec<(Ident, HirPattern)> { let mut ret = Vec::with_capacity(fields.len()); @@ -290,7 +289,7 @@ impl<'context> Elaborator<'context> { &struct_type.borrow(), &field.0.contents, visibility, - field.span(), + field.location(), ); } else if seen_fields.contains(&field) { // duplicate field @@ -308,7 +307,7 @@ impl<'context> Elaborator<'context> { if !unseen_fields.is_empty() { self.push_err(ResolverError::MissingFields { - span, + location, missing_fields: unseen_fields.into_iter().map(|field| field.to_string()).collect(), struct_definition: struct_type.borrow().name.clone(), }); @@ -329,7 +328,7 @@ impl<'context> Elaborator<'context> { return self.add_global_variable_decl(name, global_id); } - let location = Location::new(name.span(), self.file); + let location = name.location(); let name = name.0.contents; let comptime = self.in_comptime_context(); let id = @@ -346,8 +345,8 @@ impl<'context> Elaborator<'context> { if let Some(old_value) = old_value { self.push_err(ResolverError::DuplicateDefinition { name, - first_span: old_value.ident.location.span, - second_span: location.span, + first_location: old_value.ident.location, + second_location: location, }); } } @@ -362,14 +361,18 @@ impl<'context> Elaborator<'context> { ident: HirIdent, warn_if_unused: bool, ) { - let second_span = ident.location.span; + let second_location = ident.location; let resolver_meta = ResolverMeta { num_times_used: 0, ident, warn_if_unused }; let old_value = self.scopes.get_mut_scope().add_key_value(name.clone(), resolver_meta); if let Some(old_value) = old_value { - let first_span = old_value.ident.location.span; - self.push_err(ResolverError::DuplicateDefinition { name, first_span, second_span }); + let first_location = old_value.ident.location; + self.push_err(ResolverError::DuplicateDefinition { + name, + first_location, + second_location, + }); } } @@ -383,9 +386,9 @@ impl<'context> Elaborator<'context> { let old_global_value = scope.add_key_value(name.0.contents.clone(), resolver_meta); if let Some(old_global_value) = old_global_value { self.push_err(ResolverError::DuplicateDefinition { - second_span: name.span(), + first_location: old_global_value.ident.location, + second_location: name.location(), name: name.0.contents, - first_span: old_global_value.ident.location.span, }); } ident @@ -402,7 +405,7 @@ impl<'context> Elaborator<'context> { let scope_tree = self.scopes.current_scope_tree(); let variable = scope_tree.find(&name.0.contents); - let location = Location::new(name.span(), self.file); + let location = name.location(); if let Some((variable_found, scope)) = variable { variable_found.num_times_used += 1; let id = variable_found.ident.id; @@ -410,7 +413,7 @@ impl<'context> Elaborator<'context> { } else { Err(ResolverError::VariableNotDeclared { name: name.0.contents.clone(), - span: name.0.span(), + location: name.0.location(), }) } } @@ -420,7 +423,7 @@ impl<'context> Elaborator<'context> { &mut self, func_id: &FuncId, unresolved_turbofish: Option>, - span: Span, + location: Location, ) -> Option> { let direct_generic_kinds = vecmap(&self.interner.function_meta(func_id).direct_generics, |generic| generic.kind()); @@ -430,7 +433,7 @@ impl<'context> Elaborator<'context> { let type_check_err = TypeCheckError::IncorrectTurbofishGenericCount { expected_count: direct_generic_kinds.len(), actual_count: unresolved_turbofish.len(), - span, + location, }; self.push_err(type_check_err); } @@ -444,7 +447,7 @@ impl<'context> Elaborator<'context> { struct_type: &DataType, generics: Vec, unresolved_turbofish: Option>, - span: Span, + location: Location, ) -> Vec { let kinds = vecmap(&struct_type.generics, |generic| generic.kind()); self.resolve_item_turbofish_generics( @@ -453,7 +456,7 @@ impl<'context> Elaborator<'context> { kinds, generics, unresolved_turbofish, - span, + location, ) } @@ -463,7 +466,7 @@ impl<'context> Elaborator<'context> { trait_generic_kinds: Vec, generics: Vec, unresolved_turbofish: Option>, - span: Span, + location: Location, ) -> Vec { self.resolve_item_turbofish_generics( "trait", @@ -471,7 +474,7 @@ impl<'context> Elaborator<'context> { trait_generic_kinds, generics, unresolved_turbofish, - span, + location, ) } @@ -480,7 +483,7 @@ impl<'context> Elaborator<'context> { type_alias: &TypeAlias, generics: Vec, unresolved_turbofish: Option>, - span: Span, + location: Location, ) -> Vec { let kinds = vecmap(&type_alias.generics, |generic| generic.kind()); self.resolve_item_turbofish_generics( @@ -489,7 +492,7 @@ impl<'context> Elaborator<'context> { kinds, generics, unresolved_turbofish, - span, + location, ) } @@ -500,7 +503,7 @@ impl<'context> Elaborator<'context> { item_generic_kinds: Vec, generics: Vec, unresolved_turbofish: Option>, - span: Span, + location: Location, ) -> Vec { let Some(turbofish_generics) = unresolved_turbofish else { return generics; @@ -511,7 +514,7 @@ impl<'context> Elaborator<'context> { item: format!("{item_kind} {item_name}"), expected: generics.len(), found: turbofish_generics.len(), - span, + location, }); return generics; } @@ -533,7 +536,7 @@ impl<'context> Elaborator<'context> { pub(super) fn elaborate_variable(&mut self, variable: Path) -> (ExprId, Type) { let unresolved_turbofish = variable.segments.last().unwrap().generics.clone(); - let span = variable.span; + let location = variable.location; let (expr, item) = self.resolve_variable(variable); let definition_id = expr.id; @@ -547,7 +550,7 @@ impl<'context> Elaborator<'context> { // Resolve any generics if we the variable we have resolved is a function // and if the turbofish operator was used. let generics = if let Some(DefinitionKind::Function(func_id)) = &definition_kind { - self.resolve_function_turbofish_generics(func_id, unresolved_turbofish, span) + self.resolve_function_turbofish_generics(func_id, unresolved_turbofish, location) } else { None }; @@ -567,7 +570,7 @@ impl<'context> Elaborator<'context> { let id = self.interner.push_expr(HirExpression::Ident(expr.clone(), generics.clone())); - self.interner.push_expr_location(id, span, self.file); + self.interner.push_expr_location(id, location); let typ = self.type_check_variable_with_bindings(expr, id, generics, bindings); self.interner.push_expr_type(id, typ.clone()); @@ -589,7 +592,7 @@ impl<'context> Elaborator<'context> { &struct_type, struct_generics, Some(generics.generics), - generics.span, + generics.location, ) } PathResolutionItem::TypeAliasFunction(type_alias_id, generics, _func_id) => { @@ -605,7 +608,7 @@ impl<'context> Elaborator<'context> { &type_alias, alias_generics, Some(generics.generics), - generics.span, + generics.location, ) } else { alias_generics @@ -628,7 +631,7 @@ impl<'context> Elaborator<'context> { kinds, trait_generics, Some(generics.generics), - generics.span, + generics.location, ) } _ => Vec::new(), @@ -643,7 +646,7 @@ impl<'context> Elaborator<'context> { ( HirIdent { - location: Location::new(path.span, self.file), + location: path.location, id: self.interner.trait_method_id(trait_path_resolution.method.method_id), impl_kind: ImplKind::TraitMethod(trait_path_resolution.method), }, @@ -654,7 +657,7 @@ impl<'context> Elaborator<'context> { // Otherwise, then it is referring to an Identifier // This lookup allows support of such statements: let x = foo::bar::SOME_GLOBAL + 10; // If the expression is a singular indent, we search the resolver's current scope as normal. - let span = path.span(); + let location = path.location; let ((hir_ident, var_scope_index), item) = self.get_ident_from_path(path); if hir_ident.id != DefinitionId::dummy_id() { @@ -689,8 +692,7 @@ impl<'context> Elaborator<'context> { // only local variables can be captured by closures. self.resolve_local_variable(hir_ident.clone(), var_scope_index); - let reference_location = Location::new(span, self.file); - self.interner.add_local_reference(hir_ident.id, reference_location); + self.interner.add_local_reference(hir_ident.id, location); } } } @@ -742,14 +744,13 @@ impl<'context> Elaborator<'context> { _ => 0, }); - let span = self.interner.expr_span(&expr_id); let location = self.interner.expr_location(&expr_id); // This instantiates a trait's generics as well which need to be set // when the constraint below is later solved for when the function is // finished. How to link the two? let (typ, bindings) = - self.instantiate(t, bindings, generics, function_generic_count, span, location); + self.instantiate(t, bindings, generics, function_generic_count, location); // Push any trait constraints required by this definition to the context // to be checked later when the type of this variable is further constrained. @@ -796,7 +797,6 @@ impl<'context> Elaborator<'context> { bindings: TypeBindings, turbofish_generics: Option>, function_generic_count: usize, - span: Span, location: Location, ) -> (Type, TypeBindings) { match turbofish_generics { @@ -805,9 +805,9 @@ impl<'context> Elaborator<'context> { let type_check_err = TypeCheckError::IncorrectTurbofishGenericCount { expected_count: function_generic_count, actual_count: turbofish_generics.len(), - span, + location, }; - self.errors.push((CompilationError::TypeError(type_check_err), location.file)); + self.push_err(CompilationError::TypeError(type_check_err)); typ.instantiate_with_bindings(bindings, self.interner) } else { // Fetch the count of any implicit generics on the function, such as @@ -827,20 +827,20 @@ impl<'context> Elaborator<'context> { &mut self, path: Path, ) -> ((HirIdent, usize), Option) { - let location = Location::new(path.last_ident().span(), self.file); + let location = Location::new(path.last_ident().span(), path.location.file); let error = match path.as_ident().map(|ident| self.use_variable(ident)) { Some(Ok(found)) => return (found, None), // Try to look it up as a global, but still issue the first error if we fail Some(Err(error)) => match self.lookup_global(path) { Ok((id, item)) => { - return ((HirIdent::non_trait_method(id, location), 0), Some(item)) + return ((HirIdent::non_trait_method(id, location), 0), Some(item)); } Err(_) => error, }, None => match self.lookup_global(path) { Ok((id, item)) => { - return ((HirIdent::non_trait_method(id, location), 0), Some(item)) + return ((HirIdent::non_trait_method(id, location), 0), Some(item)); } Err(error) => error, }, @@ -851,11 +851,14 @@ impl<'context> Elaborator<'context> { } pub(super) fn elaborate_type_path(&mut self, path: TypePath) -> (ExprId, Type) { - let span = path.item.span(); + let location = path.item.location(); let typ = self.resolve_type(path.typ); + let check_self_param = false; - let Some(method) = self.lookup_method(&typ, &path.item.0.contents, span, false) else { - let error = Expression::new(ExpressionKind::Error, span); + let Some(method) = + self.lookup_method(&typ, &path.item.0.contents, location, check_self_param) + else { + let error = Expression::new(ExpressionKind::Error, location); return self.elaborate_expression(error); }; @@ -864,16 +867,15 @@ impl<'context> Elaborator<'context> { .expect("Expected trait function to be a DefinitionKind::Function"); let generics = - path.turbofish.map(|turbofish| self.resolve_type_args(turbofish, func_id, span).0); + path.turbofish.map(|turbofish| self.resolve_type_args(turbofish, func_id, location).0); - let location = Location::new(span, self.file); let id = self.interner.function_definition_id(func_id); let impl_kind = match method { HirMethodReference::FuncId(_) => ImplKind::NotATraitMethod, HirMethodReference::TraitMethodId(method_id, generics, _) => { let mut constraint = - self.interner.get_trait(method_id.trait_id).as_constraint(span); + self.interner.get_trait(method_id.trait_id).as_constraint(location); constraint.trait_bound.trait_generics = generics; ImplKind::TraitMethod(TraitMethod { method_id, constraint, assumed: false }) } @@ -881,7 +883,7 @@ impl<'context> Elaborator<'context> { let ident = HirIdent { location, id, impl_kind }; let id = self.interner.push_expr(HirExpression::Ident(ident.clone(), generics.clone())); - self.interner.push_expr_location(id, location.span, location.file); + self.interner.push_expr_location(id, location); let typ = self.type_check_variable(ident, id, generics); self.interner.push_expr_type(id, typ.clone()); 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 327ae02b2046..9955d874927e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs @@ -1,17 +1,17 @@ -use noirc_errors::Spanned; +use noirc_errors::Located; -use crate::ast::{Ident, Path, ERROR_IDENT}; +use crate::ast::{ERROR_IDENT, Ident, Path}; use crate::hir::def_map::{LocalModuleId, ModuleId}; use crate::hir::scope::{Scope as GenericScope, ScopeTree as GenericScopeTree}; use crate::{ + DataType, Shared, hir::resolution::errors::ResolverError, hir_def::{ expr::{HirCapturedVar, HirIdent}, traits::Trait, }, node_interner::{DefinitionId, TraitId, TypeId}, - DataType, Shared, }; use crate::{Type, TypeAlias}; @@ -22,7 +22,7 @@ use super::{Elaborator, ResolverMeta}; type Scope = GenericScope; type ScopeTree = GenericScopeTree; -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { pub fn module_id(&self) -> ModuleId { assert_ne!(self.local_module, LocalModuleId::dummy_id(), "local_module is unset"); ModuleId { krate: self.crate_id, local_id: self.local_module } @@ -83,7 +83,7 @@ impl<'context> Elaborator<'context> { &mut self, path: Path, ) -> Result<(DefinitionId, PathResolutionItem), ResolverError> { - let span = path.span(); + let location = path.location; let item = self.resolve_path_or_error(path)?; if let Some(function) = item.function_id() { @@ -97,7 +97,7 @@ impl<'context> Elaborator<'context> { let expected = "global variable"; let got = "local variable"; - Err(ResolverError::Expected { span, expected, got }) + Err(ResolverError::Expected { location, expected, got }) } pub fn push_scope(&mut self) { @@ -121,7 +121,7 @@ impl<'context> Elaborator<'context> { if let Some(definition_info) = self.interner.try_definition(unused_var.id) { let name = &definition_info.name; if name != ERROR_IDENT && !definition_info.is_global() { - let ident = Ident(Spanned::from(unused_var.location.span, name.to_owned())); + let ident = Ident(Located::from(unused_var.location, name.to_owned())); self.push_err(ResolverError::UnusedVariable { ident }); } } @@ -138,7 +138,7 @@ impl<'context> Elaborator<'context> { /// Lookup a given trait by name/path. pub fn lookup_trait_or_error(&mut self, path: Path) -> Option<&mut Trait> { - let span = path.span(); + let location = path.location; match self.resolve_path_or_error(path) { Ok(item) => { if let PathResolutionItem::Trait(trait_id) = item { @@ -147,7 +147,7 @@ impl<'context> Elaborator<'context> { self.push_err(ResolverError::Expected { expected: "trait", got: item.description(), - span, + location, }); None } @@ -161,7 +161,7 @@ impl<'context> Elaborator<'context> { /// Lookup a given struct type by name. pub fn lookup_datatype_or_error(&mut self, path: Path) -> Option> { - let span = path.span(); + let location = path.location; match self.resolve_path_or_error(path) { Ok(item) => { if let PathResolutionItem::Type(struct_id) = item { @@ -170,7 +170,7 @@ impl<'context> Elaborator<'context> { self.push_err(ResolverError::Expected { expected: "type", got: item.description(), - span, + location, }); None } @@ -186,13 +186,13 @@ impl<'context> Elaborator<'context> { /// This will also instantiate any struct types found. pub(super) fn lookup_type_or_error(&mut self, path: Path) -> Option { let ident = path.as_ident(); - if ident.map_or(false, |i| i == SELF_TYPE_NAME) { + if ident.is_some_and(|i| i == SELF_TYPE_NAME) { if let Some(typ) = &self.self_type { return Some(typ.clone()); } } - let span = path.span; + let location = path.location; match self.resolve_path_or_error(path) { Ok(PathResolutionItem::Type(struct_id)) => { let struct_type = self.get_type(struct_id); @@ -208,7 +208,7 @@ impl<'context> Elaborator<'context> { self.push_err(ResolverError::Expected { expected: "type", got: other.description(), - span, + location, }); None } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs index 3379db4aa668..f00b2a87b1ed 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs @@ -1,6 +1,7 @@ -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use crate::{ + DataType, Type, ast::{ AssignStatement, Expression, ForLoopStatement, ForRange, Ident, ItemVisibility, LValue, LetStatement, Path, Statement, StatementKind, WhileStatement, @@ -17,12 +18,11 @@ use crate::{ stmt::{HirAssignStatement, HirForStatement, HirLValue, HirLetStatement, HirStatement}, }, node_interner::{DefinitionId, DefinitionKind, GlobalId, StmtId}, - DataType, Type, }; -use super::{lints, Elaborator, Loop}; +use super::{Elaborator, Loop, lints}; -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { fn elaborate_statement_value(&mut self, statement: Statement) -> (HirStatement, Type) { self.elaborate_statement_value_with_target_type(statement, None) } @@ -36,10 +36,10 @@ impl<'context> Elaborator<'context> { StatementKind::Let(let_stmt) => self.elaborate_local_let(let_stmt), StatementKind::Assign(assign) => self.elaborate_assign(assign), StatementKind::For(for_stmt) => self.elaborate_for(for_stmt), - StatementKind::Loop(block, span) => self.elaborate_loop(block, span), + StatementKind::Loop(block, location) => self.elaborate_loop(block, location), StatementKind::While(while_) => self.elaborate_while(while_), - StatementKind::Break => self.elaborate_jump(true, statement.span), - StatementKind::Continue => self.elaborate_jump(false, statement.span), + StatementKind::Break => self.elaborate_jump(true, statement.location), + StatementKind::Continue => self.elaborate_jump(false, statement.location), StatementKind::Comptime(statement) => self.elaborate_comptime_statement(*statement), StatementKind::Expression(expr) => { let (expr, typ) = self.elaborate_expression_with_target_type(expr, target_type); @@ -51,7 +51,7 @@ impl<'context> Elaborator<'context> { } StatementKind::Interned(id) => { let kind = self.interner.get_statement_kind(id); - let statement = Statement { kind: kind.clone(), span: statement.span }; + let statement = Statement { kind: kind.clone(), location: statement.location }; self.elaborate_statement_value_with_target_type(statement, target_type) } StatementKind::Error => (HirStatement::Error, Type::Error), @@ -67,11 +67,11 @@ impl<'context> Elaborator<'context> { statement: Statement, target_type: Option<&Type>, ) -> (StmtId, Type) { - let span = statement.span; + let location = statement.location; let (hir_statement, typ) = self.elaborate_statement_value_with_target_type(statement, target_type); let id = self.interner.push_stmt(hir_statement); - self.interner.push_stmt_location(id, span, self.file); + self.interner.push_stmt_location(id, location); (id, typ) } @@ -91,15 +91,19 @@ impl<'context> Elaborator<'context> { let type_contains_unspecified = let_stmt.r#type.contains_unspecified(); let annotated_type = self.resolve_inferred_type(let_stmt.r#type); - let expr_span = let_stmt.expression.span; + let pattern_location = let_stmt.pattern.location(); + let expr_location = let_stmt.expression.location; let (expression, expr_type) = self.elaborate_expression_with_target_type(let_stmt.expression, Some(&annotated_type)); // Require the top-level of a global's type to be fully-specified if type_contains_unspecified && global_id.is_some() { - let span = expr_span; let expected_type = annotated_type.clone(); - let error = ResolverError::UnspecifiedGlobalType { span, expected_type }; + let error = ResolverError::UnspecifiedGlobalType { + pattern_location, + expr_location, + expected_type, + }; self.push_err(error); } @@ -110,11 +114,11 @@ impl<'context> Elaborator<'context> { // Now check if LHS is the same type as the RHS // Importantly, we do not coerce any types implicitly - self.unify_with_coercions(&expr_type, &annotated_type, expression, expr_span, || { + self.unify_with_coercions(&expr_type, &annotated_type, expression, expr_location, || { TypeCheckError::TypeMismatch { expected_typ: annotated_type.to_string(), expr_typ: expr_type.to_string(), - expr_span, + expr_location, } }); @@ -146,20 +150,23 @@ impl<'context> Elaborator<'context> { } pub(super) fn elaborate_assign(&mut self, assign: AssignStatement) -> (HirStatement, Type) { - let expr_span = assign.expression.span; + let expr_location = assign.expression.location; let (expression, expr_type) = self.elaborate_expression(assign.expression); let (lvalue, lvalue_type, mutable) = self.elaborate_lvalue(assign.lvalue); if !mutable { - let (name, span) = self.get_lvalue_name_and_span(&lvalue); - self.push_err(TypeCheckError::VariableMustBeMutable { name, span }); + let (_, name, location) = self.get_lvalue_error_info(&lvalue); + self.push_err(TypeCheckError::VariableMustBeMutable { name, location }); + } else { + let (id, name, location) = self.get_lvalue_error_info(&lvalue); + self.check_can_mutate_lambda_capture(id, name, location); } - self.unify_with_coercions(&expr_type, &lvalue_type, expression, expr_span, || { + self.unify_with_coercions(&expr_type, &lvalue_type, expression, expr_location, || { TypeCheckError::TypeMismatchWithSource { actual: expr_type.clone(), expected: lvalue_type.clone(), - span: expr_span, + location: expr_location, source: Source::Assignment, } }); @@ -173,14 +180,14 @@ impl<'context> Elaborator<'context> { ForRange::Range(bounds) => bounds.into_half_open(), ForRange::Array(_) => { let for_stmt = - for_loop.range.into_for(for_loop.identifier, for_loop.block, for_loop.span); + for_loop.range.into_for(for_loop.identifier, for_loop.block, for_loop.location); return self.elaborate_statement_value(for_stmt); } }; - let start_span = start.span; - let end_span = end.span; + let start_location = start.location; + let end_location = end.location; let (start_range, start_range_type) = self.elaborate_expression(start); let (end_range, end_range_type) = self.elaborate_expression(end); @@ -202,11 +209,10 @@ impl<'context> Elaborator<'context> { ); // Check that start range and end range have the same types - let range_span = start_span.merge(end_span); self.unify(&start_range_type, &end_range_type, || TypeCheckError::TypeMismatch { expected_typ: start_range_type.to_string(), expr_typ: end_range_type.to_string(), - expr_span: range_span, + expr_location: end_location, }); let expected_type = self.polymorphic_integer(); @@ -214,18 +220,18 @@ impl<'context> Elaborator<'context> { self.unify(&start_range_type, &expected_type, || TypeCheckError::TypeCannotBeUsed { typ: start_range_type.clone(), place: "for loop", - span: range_span, + location: start_location, }); self.interner.push_definition_type(identifier.id, start_range_type); - let block_span = block.type_span(); + let block_location = block.type_location(); let (block, block_type) = self.elaborate_expression(block); self.unify(&block_type, &Type::Unit, || TypeCheckError::TypeMismatch { expected_typ: Type::Unit.to_string(), expr_typ: block_type.to_string(), - expr_span: block_span, + expr_location: block_location, }); self.pop_scope(); @@ -240,24 +246,24 @@ impl<'context> Elaborator<'context> { pub(super) fn elaborate_loop( &mut self, block: Expression, - span: noirc_errors::Span, + location: Location, ) -> (HirStatement, Type) { let in_constrained_function = self.in_constrained_function(); if in_constrained_function { - self.push_err(ResolverError::LoopInConstrainedFn { span }); + self.push_err(ResolverError::LoopInConstrainedFn { location }); } let old_loop = std::mem::take(&mut self.current_loop); self.current_loop = Some(Loop { is_for: false, has_break: false }); self.push_scope(); - let block_span = block.type_span(); + let block_location = block.type_location(); let (block, block_type) = self.elaborate_expression(block); self.unify(&block_type, &Type::Unit, || TypeCheckError::TypeMismatch { expected_typ: Type::Unit.to_string(), expr_typ: block_type.to_string(), - expr_span: block_span, + expr_location: block_location, }); self.pop_scope(); @@ -265,7 +271,7 @@ impl<'context> Elaborator<'context> { let last_loop = std::mem::replace(&mut self.current_loop, old_loop).expect("Expected a loop"); if !last_loop.has_break { - self.push_err(ResolverError::LoopWithoutBreak { span }); + self.push_err(ResolverError::LoopWithoutBreak { location }); } let statement = HirStatement::Loop(block); @@ -276,29 +282,31 @@ impl<'context> Elaborator<'context> { pub(super) fn elaborate_while(&mut self, while_: WhileStatement) -> (HirStatement, Type) { let in_constrained_function = self.in_constrained_function(); if in_constrained_function { - self.push_err(ResolverError::WhileInConstrainedFn { span: while_.while_keyword_span }); + self.push_err(ResolverError::WhileInConstrainedFn { + location: while_.while_keyword_location, + }); } let old_loop = std::mem::take(&mut self.current_loop); self.current_loop = Some(Loop { is_for: false, has_break: false }); self.push_scope(); - let condition_span = while_.condition.type_span(); + let location = while_.condition.type_location(); let (condition, cond_type) = self.elaborate_expression(while_.condition); self.unify(&cond_type, &Type::Bool, || TypeCheckError::TypeMismatch { expected_typ: Type::Bool.to_string(), expr_typ: cond_type.to_string(), - expr_span: condition_span, + expr_location: location, }); - let block_span = while_.body.type_span(); + let block_location = while_.body.type_location(); let (block, block_type) = self.elaborate_expression(while_.body); self.unify(&block_type, &Type::Unit, || TypeCheckError::TypeMismatch { expected_typ: Type::Unit.to_string(), expr_typ: block_type.to_string(), - expr_span: block_span, + expr_location: block_location, }); self.pop_scope(); @@ -310,11 +318,11 @@ impl<'context> Elaborator<'context> { (statement, Type::Unit) } - fn elaborate_jump(&mut self, is_break: bool, span: noirc_errors::Span) -> (HirStatement, Type) { + fn elaborate_jump(&mut self, is_break: bool, location: Location) -> (HirStatement, Type) { let in_constrained_function = self.in_constrained_function(); if in_constrained_function { - self.push_err(ResolverError::JumpInConstrainedFn { is_break, span }); + self.push_err(ResolverError::JumpInConstrainedFn { is_break, location }); } if let Some(current_loop) = &mut self.current_loop { @@ -322,27 +330,27 @@ impl<'context> Elaborator<'context> { current_loop.has_break = true; } } else { - self.push_err(ResolverError::JumpOutsideLoop { is_break, span }); + self.push_err(ResolverError::JumpOutsideLoop { is_break, location }); } let expr = if is_break { HirStatement::Break } else { HirStatement::Continue }; (expr, self.interner.next_type_variable()) } - fn get_lvalue_name_and_span(&self, lvalue: &HirLValue) -> (String, Span) { + fn get_lvalue_error_info(&self, lvalue: &HirLValue) -> (DefinitionId, String, Location) { match lvalue { HirLValue::Ident(name, _) => { - let span = name.location.span; + let location = name.location; if let Some(definition) = self.interner.try_definition(name.id) { - (definition.name.clone(), span) + (name.id, definition.name.clone(), location) } else { - ("(undeclared variable)".into(), span) + (DefinitionId::dummy_id(), "(undeclared variable)".into(), location) } } - HirLValue::MemberAccess { object, .. } => self.get_lvalue_name_and_span(object), - HirLValue::Index { array, .. } => self.get_lvalue_name_and_span(array), - HirLValue::Dereference { lvalue, .. } => self.get_lvalue_name_and_span(lvalue), + HirLValue::MemberAccess { object, .. } => self.get_lvalue_error_info(object), + HirLValue::Index { array, .. } => self.get_lvalue_error_info(array), + HirLValue::Dereference { lvalue, .. } => self.get_lvalue_error_info(lvalue), } } @@ -350,8 +358,8 @@ impl<'context> Elaborator<'context> { match lvalue { LValue::Ident(ident) => { let mut mutable = true; - let span = ident.span(); - let path = Path::from_single(ident.0.contents, span); + let location = ident.location(); + let path = Path::from_single(ident.0.contents, location); let ((ident, scope_index), _) = self.get_ident_from_path(path); self.resolve_local_variable(ident.clone(), scope_index); @@ -365,7 +373,7 @@ impl<'context> Elaborator<'context> { if definition.comptime && !self.in_comptime_context() { self.push_err(ResolverError::MutatingComptimeInNonComptimeContext { name: definition.name.clone(), - span: ident.location.span, + location: ident.location, }); } } @@ -374,19 +382,17 @@ impl<'context> Elaborator<'context> { typ.follow_bindings() }; - let reference_location = Location::new(span, self.file); - self.interner.add_local_reference(ident.id, reference_location); + self.interner.add_local_reference(ident.id, location); (HirLValue::Ident(ident.clone(), typ.clone()), typ, mutable) } - LValue::MemberAccess { object, field_name, span } => { + LValue::MemberAccess { object, field_name, location } => { let (object, lhs_type, mut mutable) = self.elaborate_lvalue(*object); let mut object = Box::new(object); let field_name = field_name.clone(); let object_ref = &mut object; let mutable_ref = &mut mutable; - let location = Location::new(span, self.file); let dereference_lhs = move |_: &mut Self, _, element_type| { // We must create a temporary value first to move out of object_ref before @@ -403,7 +409,12 @@ impl<'context> Elaborator<'context> { let name = &field_name.0.contents; let (object_type, field_index) = self - .check_field_access(&lhs_type, name, field_name.span(), Some(dereference_lhs)) + .check_field_access( + &lhs_type, + name, + field_name.location(), + Some(dereference_lhs), + ) .unwrap_or((Type::Error, 0)); let field_index = Some(field_index); @@ -412,16 +423,15 @@ impl<'context> Elaborator<'context> { HirLValue::MemberAccess { object, field_name, field_index, typ, location }; (lvalue, object_type, mutable) } - LValue::Index { array, index, span } => { - let expr_span = index.span; + LValue::Index { array, index, location } => { + let expr_location = index.location; let (index, index_type) = self.elaborate_expression(index); - let location = Location::new(span, self.file); let expected = self.polymorphic_integer_or_field(); self.unify(&index_type, &expected, || TypeCheckError::TypeMismatch { expected_typ: "an integer".to_owned(), expr_typ: index_type.to_string(), - expr_span, + expr_location, }); let (mut lvalue, mut lvalue_type, mut mutable) = self.elaborate_lvalue(*array); @@ -442,19 +452,22 @@ impl<'context> Elaborator<'context> { Type::Slice(elem_type) => *elem_type, Type::Error => Type::Error, Type::String(_) => { - let (_lvalue_name, lvalue_span) = self.get_lvalue_name_and_span(&lvalue); - self.push_err(TypeCheckError::StringIndexAssign { span: lvalue_span }); + let (_id, _lvalue_name, lvalue_location) = + self.get_lvalue_error_info(&lvalue); + self.push_err(TypeCheckError::StringIndexAssign { + location: lvalue_location, + }); Type::Error } Type::TypeVariable(_) => { - self.push_err(TypeCheckError::TypeAnnotationsNeededForIndex { span }); + self.push_err(TypeCheckError::TypeAnnotationsNeededForIndex { location }); Type::Error } other => { self.push_err(TypeCheckError::TypeMismatch { expected_typ: "array".to_string(), expr_typ: other.to_string(), - expr_span: span, + expr_location: location, }); Type::Error } @@ -464,10 +477,9 @@ impl<'context> Elaborator<'context> { let array_type = typ.clone(); (HirLValue::Index { array, index, typ, location }, array_type, mutable) } - LValue::Dereference(lvalue, span) => { + LValue::Dereference(lvalue, location) => { let (lvalue, reference_type, _) = self.elaborate_lvalue(*lvalue); let lvalue = Box::new(lvalue); - let location = Location::new(span, self.file); let element_type = Type::type_variable(self.interner.next_type_variable_id()); let expected_type = Type::MutableReference(Box::new(element_type.clone())); @@ -475,7 +487,7 @@ impl<'context> Elaborator<'context> { self.unify(&reference_type, &expected_type, || TypeCheckError::TypeMismatch { expected_typ: expected_type.to_string(), expr_typ: reference_type.to_string(), - expr_span: span, + expr_location: location, }); // Dereferences are always mutable since we already type checked against a &mut T @@ -483,8 +495,8 @@ impl<'context> Elaborator<'context> { let lvalue = HirLValue::Dereference { lvalue, element_type, location }; (lvalue, typ, true) } - LValue::Interned(id, span) => { - let lvalue = self.interner.get_lvalue(id, span).clone(); + LValue::Interned(id, location) => { + let lvalue = self.interner.get_lvalue(id, location).clone(); self.elaborate_lvalue(lvalue) } } @@ -495,7 +507,7 @@ impl<'context> Elaborator<'context> { &mut self, lhs_type: &Type, field_name: &str, - span: Span, + location: Location, dereference_lhs: Option, ) -> Option<(Type, usize)> { let lhs_type = lhs_type.follow_bindings(); @@ -504,10 +516,9 @@ impl<'context> Elaborator<'context> { Type::DataType(s, args) => { let s = s.borrow(); if let Some((field, visibility, index)) = s.get_field(field_name, args) { - let reference_location = Location::new(span, self.file); - self.interner.add_struct_member_reference(s.id, index, reference_location); + self.interner.add_struct_member_reference(s.id, index, location); - self.check_struct_field_visibility(&s, field_name, visibility, span); + self.check_struct_field_visibility(&s, field_name, visibility, location); return Some((field, index)); } @@ -522,7 +533,7 @@ impl<'context> Elaborator<'context> { index, lhs_type, length, - span, + location, }); return None; } @@ -536,12 +547,12 @@ impl<'context> Elaborator<'context> { return self.check_field_access( element, field_name, - span, + location, Some(dereference_lhs), ); } else { let (element, index) = - self.check_field_access(element, field_name, span, dereference_lhs)?; + self.check_field_access(element, field_name, location, dereference_lhs)?; return Some((Type::MutableReference(Box::new(element)), index)); } } @@ -551,12 +562,12 @@ impl<'context> Elaborator<'context> { // If we get here the type has no field named 'access.rhs'. // Now we specialize the error message based on whether we know the object type in question yet. if let Type::TypeVariable(..) = &lhs_type { - self.push_err(TypeCheckError::TypeAnnotationsNeededForFieldAccess { span }); + self.push_err(TypeCheckError::TypeAnnotationsNeededForFieldAccess { location }); } else if lhs_type != Type::Error { self.push_err(TypeCheckError::AccessUnknownMember { lhs_type, field_name: field_name.to_string(), - span, + location, }); } @@ -568,7 +579,7 @@ impl<'context> Elaborator<'context> { struct_type: &DataType, field_name: &str, visibility: ItemVisibility, - span: Span, + location: Location, ) { if self.silence_field_visibility_errors > 0 { return; @@ -576,18 +587,18 @@ impl<'context> Elaborator<'context> { if !struct_member_is_visible(struct_type.id, visibility, self.module_id(), self.def_maps) { self.push_err(ResolverError::PathResolutionError(PathResolutionError::Private( - Ident::new(field_name.to_string(), span), + Ident::new(field_name.to_string(), location), ))); } } fn elaborate_comptime_statement(&mut self, statement: Statement) -> (HirStatement, Type) { - let span = statement.span; + let location = statement.location; let (hir_statement, _typ) = self.elaborate_in_comptime_context(|this| this.elaborate_statement(statement)); let mut interpreter = self.setup_interpreter(); let value = interpreter.evaluate_statement(hir_statement); - let (expr, typ) = self.inline_comptime_value(value, span); + let (expr, typ) = self.inline_comptime_value(value, location); let location = self.interner.id_location(hir_statement); self.debug_comptime(location, |interner| expr.to_display_ast(interner).kind); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/trait_impls.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/trait_impls.rs index aa27ac29fa67..63befb67b093 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/trait_impls.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/trait_impls.rs @@ -1,23 +1,25 @@ use crate::{ + ResolvedGeneric, ast::{Ident, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, graph::CrateId, - hir::def_collector::{dc_crate::UnresolvedTraitImpl, errors::DefCollectorErrorKind}, + hir::def_collector::{ + dc_crate::{CompilationError, UnresolvedTraitImpl}, + errors::DefCollectorErrorKind, + }, node_interner::TraitImplId, - ResolvedGeneric, }; use crate::{ + Type, hir::def_collector::errors::DuplicateType, hir_def::traits::{TraitConstraint, TraitFunction}, node_interner::{FuncId, TraitId}, - Type, }; -use noirc_errors::Location; use rustc_hash::FxHashSet as HashSet; use super::Elaborator; -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { pub(super) fn collect_trait_impl_methods( &mut self, trait_id: TraitId, @@ -25,7 +27,6 @@ impl<'context> Elaborator<'context> { trait_impl_where_clause: &[TraitConstraint], ) { self.local_module = trait_impl.module_id; - self.file = trait_impl.file_id; let impl_id = trait_impl.impl_id.expect("impl_id should be set in define_function_metas"); @@ -59,7 +60,7 @@ impl<'context> Elaborator<'context> { let func_id = self.interner.push_empty_fn(); let module = self.module_id(); - let location = Location::new(default_impl.def.span, trait_impl.file_id); + let location = default_impl.def.location; self.interner.push_function(func_id, &default_impl.def, module, location); self.define_function_meta(&mut default_impl_clone, func_id, None); func_ids_in_trait.insert(func_id); @@ -72,7 +73,7 @@ impl<'context> Elaborator<'context> { self.push_err(DefCollectorErrorKind::TraitMissingMethod { trait_name: self.interner.get_trait(trait_id).name.clone(), method_name: method.name.clone(), - trait_impl_span: trait_impl.object_type.span, + trait_impl_location: trait_impl.object_type.location, }); } } else { @@ -105,14 +106,17 @@ impl<'context> Elaborator<'context> { let the_trait = self.interner.get_trait_mut(trait_id); the_trait.set_methods(methods); + let trait_name = the_trait.name.clone(); + // Emit MethodNotInTrait error for methods in the impl block that // don't have a corresponding method signature defined in the trait for (_, func_id, func) in &trait_impl.methods.functions { if !func_ids_in_trait.contains(func_id) { - let trait_name = the_trait.name.clone(); + let trait_name = trait_name.clone(); let impl_method = func.name_ident().clone(); let error = DefCollectorErrorKind::MethodNotInTrait { trait_name, impl_method }; - self.errors.push((error.into(), self.file)); + let error: CompilationError = error.into(); + self.push_err(error); } } @@ -200,9 +204,9 @@ impl<'context> Elaborator<'context> { constraint_typ: override_trait_constraint.typ, constraint_name: the_trait.name.0.contents.clone(), constraint_generics: override_trait_constraint.trait_bound.trait_generics, - constraint_span: override_trait_constraint.trait_bound.span, + constraint_location: override_trait_constraint.trait_bound.location, trait_method_name: method.name.0.contents.clone(), - trait_method_span: method.location.span, + trait_method_location: method.location, }); } } @@ -214,7 +218,6 @@ impl<'context> Elaborator<'context> { trait_impl: &UnresolvedTraitImpl, ) { self.local_module = trait_impl.module_id; - self.file = trait_impl.file_id; let object_crate = match &trait_impl.resolved_object_type { Some(Type::DataType(struct_type, _)) => struct_type.borrow().id.krate(), @@ -224,7 +227,7 @@ impl<'context> Elaborator<'context> { let the_trait = self.interner.get_trait(trait_id); if self.crate_id != the_trait.crate_id && self.crate_id != object_crate { self.push_err(DefCollectorErrorKind::TraitImplOrphaned { - span: trait_impl.object_type.span, + location: trait_impl.object_type.location, }); } } @@ -235,12 +238,12 @@ impl<'context> Elaborator<'context> { ) -> Vec<(Ident, UnresolvedType)> { let mut associated_types = Vec::new(); for (name, _, expr) in trait_impl.associated_constants.drain(..) { - let span = expr.span; - let typ = match UnresolvedTypeExpression::from_expr(expr, span) { - Ok(expr) => UnresolvedTypeData::Expression(expr).with_span(span), + let location = expr.location; + let typ = match UnresolvedTypeExpression::from_expr(expr, location) { + Ok(expr) => UnresolvedTypeData::Expression(expr).with_location(location), Err(error) => { self.push_err(error); - UnresolvedTypeData::Error.with_span(span) + UnresolvedTypeData::Error.with_location(location) } }; associated_types.push((name, typ)); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs index bbc1214de9ed..a931dde93de5 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs @@ -1,9 +1,10 @@ use std::{collections::BTreeMap, rc::Rc}; use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use crate::{ + ResolvedGeneric, Type, TypeBindings, ast::{ BlockExpression, FunctionDefinition, FunctionKind, FunctionReturnType, Ident, ItemVisibility, NoirFunction, TraitItem, UnresolvedGeneric, UnresolvedGenerics, @@ -15,12 +16,11 @@ use crate::{ traits::{ResolvedTraitBound, TraitFunction}, }, node_interner::{DependencyId, FuncId, NodeInterner, ReferenceId, TraitId}, - ResolvedGeneric, Type, TypeBindings, }; use super::Elaborator; -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { pub fn collect_traits(&mut self, traits: &mut BTreeMap) { for (trait_id, unresolved_trait) in traits { self.local_module = unresolved_trait.module_id; @@ -97,7 +97,6 @@ impl<'context> Elaborator<'context> { unresolved_trait: &UnresolvedTrait, ) -> Vec { self.local_module = unresolved_trait.module_id; - self.file = self.def_maps[&self.crate_id].file_id(unresolved_trait.module_id); let mut functions = vec![]; @@ -117,15 +116,15 @@ impl<'context> Elaborator<'context> { self.recover_generics(|this| { let the_trait = this.interner.get_trait(trait_id); let self_typevar = the_trait.self_type_typevar.clone(); - let name_span = the_trait.name.span(); + let name_location = the_trait.name.location(); this.add_existing_generic( &UnresolvedGeneric::Variable(Ident::from("Self")), - name_span, + name_location, &ResolvedGeneric { name: Rc::new("Self".to_owned()), type_var: self_typevar, - span: name_span, + location: name_location, }, ); @@ -274,6 +273,7 @@ impl<'context> Elaborator<'context> { pub(crate) fn check_trait_impl_method_matches_declaration( interner: &mut NodeInterner, function: FuncId, + noir_function: &NoirFunction, ) -> Vec { let meta = interner.function_meta(&function); let modifiers = interner.function_modifiers(&function); @@ -297,9 +297,9 @@ pub(crate) fn check_trait_impl_method_matches_declaration( if trait_info.generics.len() != impl_.trait_generics.len() { let expected = trait_info.generics.len(); let found = impl_.trait_generics.len(); - let span = impl_.ident.span(); + let location = impl_.ident.location(); let item = trait_info.name.to_string(); - errors.push(TypeCheckError::GenericCountMismatch { item, expected, found, span }); + errors.push(TypeCheckError::GenericCountMismatch { item, expected, found, location }); } // Substitute each generic on the trait with the corresponding generic on the impl @@ -319,17 +319,17 @@ pub(crate) fn check_trait_impl_method_matches_declaration( if modifiers.is_unconstrained != trait_fn_modifiers.is_unconstrained { let expected = trait_fn_modifiers.is_unconstrained; - let span = meta.name.location.span; + let location = meta.name.location; let item = method_name.to_string(); - errors.push(TypeCheckError::UnconstrainedMismatch { item, expected, span }); + errors.push(TypeCheckError::UnconstrainedMismatch { item, expected, location }); } if trait_fn_meta.direct_generics.len() != meta.direct_generics.len() { let expected = trait_fn_meta.direct_generics.len(); let found = meta.direct_generics.len(); - let span = meta.name.location.span; + let location = meta.name.location; let item = method_name.to_string(); - errors.push(TypeCheckError::GenericCountMismatch { item, expected, found, span }); + errors.push(TypeCheckError::GenericCountMismatch { item, expected, found, location }); } // Substitute each generic on the trait function with the corresponding generic on the impl function @@ -350,7 +350,9 @@ pub(crate) fn check_trait_impl_method_matches_declaration( definition_type, method_name, &meta.parameters, - meta.name.location.span, + &meta.return_type, + noir_function, + meta.name.location, &trait_info.name.0.contents, &mut errors, ); @@ -359,12 +361,15 @@ pub(crate) fn check_trait_impl_method_matches_declaration( errors } +#[allow(clippy::too_many_arguments)] fn check_function_type_matches_expected_type( expected: &Type, actual: &Type, method_name: &str, actual_parameters: &Parameters, - span: Span, + actual_return_type: &FunctionReturnType, + noir_function: &NoirFunction, + location: Location, trait_name: &str, errors: &mut Vec, ) { @@ -382,11 +387,16 @@ fn check_function_type_matches_expected_type( if params_a.len() == params_b.len() { for (i, (a, b)) in params_a.iter().zip(params_b.iter()).enumerate() { if a.try_unify(b, &mut bindings).is_err() { + let parameter_location = noir_function.def.parameters.get(i); + let parameter_location = parameter_location.map(|param| param.typ.location); + let parameter_location = + parameter_location.unwrap_or_else(|| actual_parameters.0[i].0.location()); + errors.push(TypeCheckError::TraitMethodParameterTypeMismatch { method_name: method_name.to_string(), expected_typ: a.to_string(), actual_typ: b.to_string(), - parameter_span: actual_parameters.0[i].0.span(), + parameter_location, parameter_index: i + 1, }); } @@ -396,7 +406,7 @@ fn check_function_type_matches_expected_type( errors.push(TypeCheckError::TypeMismatch { expected_typ: ret_a.to_string(), expr_typ: ret_b.to_string(), - expr_span: span, + expr_location: actual_return_type.location(), }); } } else { @@ -405,7 +415,7 @@ fn check_function_type_matches_expected_type( expected_num_parameters: params_a.len(), trait_name: trait_name.to_string(), method_name: method_name.to_string(), - span, + location, }); } } @@ -416,6 +426,10 @@ fn check_function_type_matches_expected_type( if !bindings.is_empty() { let expected_typ = expected.to_string(); let expr_typ = actual.to_string(); - errors.push(TypeCheckError::TypeMismatch { expected_typ, expr_typ, expr_span: span }); + errors.push(TypeCheckError::TypeMismatch { + expected_typ, + expr_typ, + expr_location: location, + }); } } 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 53d0860ebf12..fb6431610269 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs @@ -2,10 +2,11 @@ use std::{borrow::Cow, rc::Rc}; use im::HashSet; use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use rustc_hash::FxHashMap as HashMap; use crate::{ + Generics, Kind, ResolvedGeneric, Type, TypeBinding, TypeBindings, UnificationError, ast::{ AsTraitPath, BinaryOpKind, GenericTypeArgs, Ident, IntegerBitSize, Path, PathKind, Signedness, UnaryOp, UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, @@ -13,11 +14,11 @@ use crate::{ }, hir::{ def_collector::dc_crate::CompilationError, - def_map::{fully_qualified_module_path, ModuleDefId}, + def_map::{ModuleDefId, fully_qualified_module_path}, resolution::{errors::ResolverError, import::PathResolutionError}, type_check::{ - generics::{Generic, TraitGenerics}, NoMatchingImplFoundError, Source, TypeCheckError, + generics::{Generic, TraitGenerics}, }, }, hir_def::{ @@ -30,14 +31,14 @@ use crate::{ traits::{NamedType, ResolvedTraitBound, Trait, TraitConstraint}, }, node_interner::{ - DependencyId, ExprId, FuncId, GlobalValue, ImplSearchErrorKind, NodeInterner, TraitId, - TraitImplKind, TraitMethodId, + DependencyId, ExprId, FuncId, GlobalValue, ImplSearchErrorKind, TraitId, TraitImplKind, + TraitMethodId, }, + signed_field::SignedField, token::SecondaryAttribute, - Generics, Kind, ResolvedGeneric, Type, TypeBinding, TypeBindings, UnificationError, }; -use super::{lints, path_resolution::PathResolutionItem, Elaborator, UnsafeBlockStatus}; +use super::{Elaborator, UnsafeBlockStatus, lints, path_resolution::PathResolutionItem}; pub const SELF_TYPE_NAME: &str = "Self"; @@ -47,13 +48,13 @@ pub(super) struct TraitPathResolution { pub(super) errors: Vec, } -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { /// Translates an UnresolvedType to a Type with a `TypeKind::Normal` pub(crate) fn resolve_type(&mut self, typ: UnresolvedType) -> Type { - let span = typ.span; + let location = typ.location; let resolved_type = self.resolve_type_inner(typ, &Kind::Normal); if resolved_type.is_nested_slice() { - self.push_err(ResolverError::NestedSlices { span }); + self.push_err(ResolverError::NestedSlices { location }); } resolved_type } @@ -63,11 +64,11 @@ impl<'context> Elaborator<'context> { pub fn resolve_type_inner(&mut self, typ: UnresolvedType, kind: &Kind) -> Type { use crate::ast::UnresolvedTypeData::*; - let span = typ.span; - let (named_path_span, is_self_type_name, is_synthetic) = + let location = typ.location; + let (named_path_location, is_self_type_name, is_synthetic) = if let Named(ref named_path, _, synthetic) = typ.typ { ( - Some(named_path.last_ident().span()), + Some(named_path.last_ident().location()), named_path.last_ident().is_self_type_name(), synthetic, ) @@ -79,38 +80,38 @@ impl<'context> Elaborator<'context> { FieldElement => Type::FieldElement, Array(size, elem) => { let elem = Box::new(self.resolve_type_inner(*elem, kind)); - let size = self.convert_expression_type(size, &Kind::u32(), span); + let size = self.convert_expression_type(size, &Kind::u32(), location); Type::Array(Box::new(size), elem) } Slice(elem) => { let elem = Box::new(self.resolve_type_inner(*elem, kind)); Type::Slice(elem) } - Expression(expr) => self.convert_expression_type(expr, kind, span), + Expression(expr) => self.convert_expression_type(expr, kind, location), Integer(sign, bits) => Type::Integer(sign, bits), Bool => Type::Bool, String(size) => { - let resolved_size = self.convert_expression_type(size, &Kind::u32(), span); + let resolved_size = self.convert_expression_type(size, &Kind::u32(), location); Type::String(Box::new(resolved_size)) } FormatString(size, fields) => { - let resolved_size = self.convert_expression_type(size, &Kind::u32(), span); + let resolved_size = self.convert_expression_type(size, &Kind::u32(), location); let fields = self.resolve_type_inner(*fields, kind); Type::FmtString(Box::new(resolved_size), Box::new(fields)) } Quoted(quoted) => { let in_function = matches!(self.current_item, Some(DependencyId::Function(_))); if in_function && !self.in_comptime_context() { - let span = typ.span; + let location = typ.location; let typ = quoted.to_string(); - self.push_err(ResolverError::ComptimeTypeInRuntimeCode { span, typ }); + self.push_err(ResolverError::ComptimeTypeInRuntimeCode { location, typ }); } Type::Quoted(quoted) } Unit => Type::Unit, Unspecified => { - let span = typ.span; - self.push_err(TypeCheckError::UnspecifiedType { span }); + let location = typ.location; + self.push_err(TypeCheckError::UnspecifiedType { location }); Type::Error } Error => Type::Error, @@ -123,7 +124,7 @@ impl<'context> Elaborator<'context> { Function(args, ret, env, unconstrained) => { let args = vecmap(args, |arg| self.resolve_type_inner(arg, kind)); let ret = Box::new(self.resolve_type_inner(*ret, kind)); - let env_span = env.span; + let env_location = env.location; let env = Box::new(self.resolve_type_inner(*env, kind)); @@ -134,7 +135,7 @@ impl<'context> Elaborator<'context> { _ => { self.push_err(ResolverError::InvalidClosureEnvironment { typ: *env, - span: env_span, + location: env_location, }); Type::Error } @@ -148,11 +149,11 @@ impl<'context> Elaborator<'context> { AsTraitPath(path) => self.resolve_as_trait_path(*path), Interned(id) => { let typ = self.interner.get_unresolved_type_data(id).clone(); - return self.resolve_type_inner(UnresolvedType { typ, span }, kind); + return self.resolve_type_inner(UnresolvedType { typ, location }, kind); } }; - let location = Location::new(named_path_span.unwrap_or(typ.span), self.file); + let location = named_path_location.unwrap_or(typ.location); match resolved_type { Type::DataType(ref data_type, _) => { // Record the location of the type reference @@ -173,11 +174,11 @@ impl<'context> Elaborator<'context> { if !kind.unifies(&resolved_type.kind()) { let expected_typ_err = CompilationError::TypeError(TypeCheckError::TypeKindMismatch { - expected_kind: kind.to_string(), - expr_kind: resolved_type.kind().to_string(), - expr_span: span, + expected_kind: kind.clone(), + expr_kind: resolved_type.kind(), + expr_location: location, }); - self.errors.push((expected_typ_err, self.file)); + self.push_err(expected_typ_err); return Type::Error; } @@ -223,7 +224,9 @@ impl<'context> Elaborator<'context> { if name == SELF_TYPE_NAME { if let Some(self_type) = self.self_type.clone() { if !args.is_empty() { - self.push_err(ResolverError::GenericsOnSelfType { span: path.span() }); + self.push_err(ResolverError::GenericsOnSelfType { + location: path.location, + }); } return self_type; } @@ -232,16 +235,16 @@ impl<'context> Elaborator<'context> { } } else if let Some(typ) = self.lookup_associated_type_on_self(&path) { if !args.is_empty() { - self.push_err(ResolverError::GenericsOnAssociatedType { span: path.span() }); + self.push_err(ResolverError::GenericsOnAssociatedType { location: path.location }); } return typ; } - let span = path.span(); + let location = path.location; if let Some(type_alias) = self.lookup_type_alias(path.clone()) { let id = type_alias.borrow().id; - let (args, _) = self.resolve_type_args(args, id, path.span()); + let (args, _) = self.resolve_type_args(args, id, location); if let Some(item) = self.current_item { self.interner.add_type_alias_dependency(item, id); @@ -249,7 +252,7 @@ impl<'context> Elaborator<'context> { // Collecting Type Alias references [Location]s to be used by LSP in order // to resolve the definition of the type alias - self.interner.add_type_alias_ref(id, Location::new(span, self.file)); + self.interner.add_type_alias_ref(id, location); // Because there is no ordering to when type aliases (and other globals) are resolved, // it is possible for one to refer to an Error type and issue no error if it is set @@ -263,7 +266,7 @@ impl<'context> Elaborator<'context> { Some(data_type) => { if self.resolving_ids.contains(&data_type.borrow().id) { self.push_err(ResolverError::SelfReferentialType { - span: data_type.borrow().name.span(), + location: data_type.borrow().name.location(), }); return Type::Error; @@ -277,11 +280,11 @@ impl<'context> Elaborator<'context> { .any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) { self.push_err(ResolverError::AbiAttributeOutsideContract { - span: data_type.borrow().name.span(), + location: data_type.borrow().name.location(), }); } - let (args, _) = self.resolve_type_args(args, data_type.borrow(), span); + let (args, _) = self.resolve_type_args(args, data_type.borrow(), location); if let Some(current_item) = self.current_item { let dependency_id = data_type.borrow().id; @@ -297,11 +300,11 @@ impl<'context> Elaborator<'context> { fn resolve_trait_as_type(&mut self, path: Path, args: GenericTypeArgs) -> Type { // Fetch information needed from the trait as the closure for resolving all the `args` // requires exclusive access to `self` - let span = path.span; + let location = path.location; let trait_as_type_info = self.lookup_trait_or_error(path).map(|t| t.id); if let Some(id) = trait_as_type_info { - let (ordered, named) = self.resolve_type_args(args, id, span); + let (ordered, named) = self.resolve_type_args(args, id, location); let name = self.interner.get_trait(id).name.to_string(); let generics = TraitGenerics { ordered, named }; Type::TraitAsType(id, Rc::new(name), generics) @@ -316,25 +319,25 @@ impl<'context> Elaborator<'context> { &mut self, args: GenericTypeArgs, item: TraitId, - span: Span, + location: Location, ) -> (Vec, Vec) { - self.resolve_type_args_inner(args, item, span, false) + self.resolve_type_args_inner(args, item, location, false) } pub(super) fn resolve_type_args( &mut self, args: GenericTypeArgs, item: impl Generic, - span: Span, + location: Location, ) -> (Vec, Vec) { - self.resolve_type_args_inner(args, item, span, true) + self.resolve_type_args_inner(args, item, location, true) } pub(super) fn resolve_type_args_inner( &mut self, mut args: GenericTypeArgs, item: impl Generic, - span: Span, + location: Location, allow_implicit_named_args: bool, ) -> (Vec, Vec) { let expected_kinds = item.generics(self.interner); @@ -344,9 +347,9 @@ impl<'context> Elaborator<'context> { item: item.item_name(self.interner), expected: expected_kinds.len(), found: args.ordered_args.len(), - span, + location, }); - let error_type = UnresolvedTypeData::Error.with_span(span); + let error_type = UnresolvedTypeData::Error.with_location(location); args.ordered_args.resize(expected_kinds.len(), error_type); } @@ -360,12 +363,12 @@ impl<'context> Elaborator<'context> { associated = self.resolve_associated_type_args( args.named_args, item, - span, + location, allow_implicit_named_args, ); } else if !args.named_args.is_empty() { let item_kind = item.item_kind(); - self.push_err(ResolverError::NamedTypeArgs { span, item_kind }); + self.push_err(ResolverError::NamedTypeArgs { location, item_kind }); } (ordered, associated) @@ -375,7 +378,7 @@ impl<'context> Elaborator<'context> { &mut self, args: Vec<(Ident, UnresolvedType)>, item: impl Generic, - span: Span, + location: Location, allow_implicit_named_args: bool, ) -> Vec { let mut seen_args = HashMap::default(); @@ -389,8 +392,8 @@ impl<'context> Elaborator<'context> { required_args.iter().position(|item| item.name.as_ref() == &name.0.contents); let Some(index) = index else { - if let Some(prev_span) = seen_args.get(&name.0.contents).copied() { - self.push_err(TypeCheckError::DuplicateNamedTypeArg { name, prev_span }); + if let Some(prev_location) = seen_args.get(&name.0.contents).copied() { + self.push_err(TypeCheckError::DuplicateNamedTypeArg { name, prev_location }); } else { let item = item.item_name(self.interner); self.push_err(TypeCheckError::NoSuchNamedTypeArg { name, item }); @@ -400,7 +403,7 @@ impl<'context> Elaborator<'context> { // Remove the argument from the required list so we remember that we already have it let expected = required_args.remove(index); - seen_args.insert(name.0.contents.clone(), name.span()); + seen_args.insert(name.0.contents.clone(), name.location()); let typ = self.resolve_type_inner(typ, &expected.kind()); resolved.push(NamedType { name, typ }); @@ -412,12 +415,12 @@ impl<'context> Elaborator<'context> { let name = generic.name.clone(); if allow_implicit_named_args { - let name = Ident::new(name.as_ref().clone(), span); + let name = Ident::new(name.as_ref().clone(), location); let typ = self.interner.next_type_variable(); resolved.push(NamedType { name, typ }); } else { let item = item.item_name(self.interner); - self.push_err(TypeCheckError::MissingNamedTypeArg { item, span, name }); + self.push_err(TypeCheckError::MissingNamedTypeArg { item, location, name }); } } @@ -442,7 +445,7 @@ impl<'context> Elaborator<'context> { self.interner.add_global_dependency(current_item, id); } - let reference_location = Location::new(path.span(), self.file); + let reference_location = path.location; self.interner.add_global_reference(id, reference_location); let kind = self .interner @@ -461,26 +464,33 @@ impl<'context> Elaborator<'context> { }; let rhs = stmt.expression; - let span = self.interner.expr_span(&rhs); + let location = self.interner.expr_location(&rhs); let GlobalValue::Resolved(global_value) = &self.interner.get_global(id).value else { - self.push_err(ResolverError::UnevaluatedGlobalType { span }); + self.push_err(ResolverError::UnevaluatedGlobalType { location }); return None; }; let Some(global_value) = global_value.to_field_element() else { let global_value = global_value.clone(); if global_value.is_integral() { - self.push_err(ResolverError::NegativeGlobalType { span, global_value }); + self.push_err(ResolverError::NegativeGlobalType { location, global_value }); } else { - self.push_err(ResolverError::NonIntegralGlobalType { span, global_value }); + self.push_err(ResolverError::NonIntegralGlobalType { + location, + global_value, + }); } return None; }; - let Ok(global_value) = kind.ensure_value_fits(global_value, span) else { - self.push_err(ResolverError::GlobalLargerThanKind { span, global_value, kind }); + let Ok(global_value) = kind.ensure_value_fits(global_value, location) else { + self.push_err(ResolverError::GlobalLargerThanKind { + location, + global_value, + kind, + }); return None; }; @@ -494,42 +504,38 @@ impl<'context> Elaborator<'context> { &mut self, length: UnresolvedTypeExpression, expected_kind: &Kind, - span: Span, + location: Location, ) -> Type { match length { UnresolvedTypeExpression::Variable(path) => { let typ = self.resolve_named_type(path, GenericTypeArgs::default()); - self.check_kind(typ, expected_kind, span) + self.check_kind(typ, expected_kind, location) } UnresolvedTypeExpression::Constant(int, _span) => { Type::Constant(int, expected_kind.clone()) } - UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, span) => { - let (lhs_span, rhs_span) = (lhs.span(), rhs.span()); - let lhs = self.convert_expression_type(*lhs, expected_kind, lhs_span); - let rhs = self.convert_expression_type(*rhs, expected_kind, rhs_span); + UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, location) => { + let (lhs_location, rhs_location) = (lhs.location(), rhs.location()); + let lhs = self.convert_expression_type(*lhs, expected_kind, lhs_location); + let rhs = self.convert_expression_type(*rhs, expected_kind, rhs_location); match (lhs, rhs) { (Type::Constant(lhs, lhs_kind), Type::Constant(rhs, rhs_kind)) => { if !lhs_kind.unifies(&rhs_kind) { self.push_err(TypeCheckError::TypeKindMismatch { - expected_kind: lhs_kind.to_string(), - expr_kind: rhs_kind.to_string(), - expr_span: span, + expected_kind: lhs_kind, + expr_kind: rhs_kind, + expr_location: location, }); return Type::Error; } - match op.function(lhs, rhs, &lhs_kind, span) { + match op.function(lhs, rhs, &lhs_kind, location) { Ok(result) => Type::Constant(result, lhs_kind), Err(err) => { let err = Box::new(err); - self.push_err(ResolverError::BinaryOpError { - lhs, - op, - rhs, - err, - span, - }); + let error = + ResolverError::BinaryOpError { lhs, op, rhs, err, location }; + self.push_err(error); Type::Error } } @@ -543,17 +549,17 @@ impl<'context> Elaborator<'context> { } UnresolvedTypeExpression::AsTraitPath(path) => { let typ = self.resolve_as_trait_path(*path); - self.check_kind(typ, expected_kind, span) + self.check_kind(typ, expected_kind, location) } } } - fn check_kind(&mut self, typ: Type, expected_kind: &Kind, span: Span) -> Type { + fn check_kind(&mut self, typ: Type, expected_kind: &Kind, location: Location) -> Type { if !typ.kind().unifies(expected_kind) { self.push_err(TypeCheckError::TypeKindMismatch { - expected_kind: expected_kind.to_string(), - expr_kind: typ.kind().to_string(), - expr_span: span, + expected_kind: expected_kind.clone(), + expr_kind: typ.kind(), + expr_location: location, }); return Type::Error; } @@ -561,19 +567,20 @@ impl<'context> Elaborator<'context> { } fn resolve_as_trait_path(&mut self, path: AsTraitPath) -> Type { - let span = path.trait_path.span; + let location = path.trait_path.location; let Some(trait_id) = self.resolve_trait_by_path(path.trait_path.clone()) else { // Error should already be pushed in the None case return Type::Error; }; - let (ordered, named) = self.resolve_type_args(path.trait_generics.clone(), trait_id, span); + let (ordered, named) = + self.resolve_type_args(path.trait_generics.clone(), trait_id, location); let object_type = self.resolve_type(path.typ.clone()); match self.interner.lookup_trait_implementation(&object_type, trait_id, &ordered, &named) { Ok(impl_kind) => self.get_associated_type_from_trait_impl(path, impl_kind), Err(constraints) => { - self.push_trait_constraint_error(&object_type, constraints, span); + self.push_trait_constraint_error(&object_type, constraints, location); Type::Error } } @@ -622,7 +629,7 @@ impl<'context> Elaborator<'context> { if name == SELF_TYPE_NAME { let the_trait = self.interner.get_trait(trait_id); let method = the_trait.find_method(method.0.contents.as_str())?; - let constraint = the_trait.as_constraint(path.span); + let constraint = the_trait.as_constraint(path.location); return Some(TraitPathResolution { method: TraitMethod { method_id: method, constraint, assumed: true }, item: None, @@ -643,7 +650,7 @@ impl<'context> Elaborator<'context> { let meta = self.interner.try_function_meta(&func_id)?; let the_trait = self.interner.get_trait(meta.trait_id?); let method = the_trait.find_method(path.last_name())?; - let constraint = the_trait.as_constraint(path.span); + let constraint = the_trait.as_constraint(path.location); Some(TraitPathResolution { method: TraitMethod { method_id: method, constraint, assumed: false }, item: Some(path_resolution.item), @@ -691,7 +698,7 @@ impl<'context> Elaborator<'context> { } let mut path = path.clone(); - let span = path.span(); + let location = path.location; let last_segment = path.pop(); let before_last_segment = path.last_segment(); @@ -716,7 +723,7 @@ impl<'context> Elaborator<'context> { } let (hir_method_reference, error) = - self.get_trait_method_in_scope(&trait_methods, method_name, last_segment.span); + self.get_trait_method_in_scope(&trait_methods, method_name, last_segment.location); let hir_method_reference = hir_method_reference?; let func_id = hir_method_reference.func_id(self.interner)?; let HirMethodReference::TraitMethodId(trait_method_id, _, _) = hir_method_reference else { @@ -725,7 +732,7 @@ impl<'context> Elaborator<'context> { let trait_id = trait_method_id.trait_id; let trait_ = self.interner.get_trait(trait_id); - let mut constraint = trait_.as_constraint(span); + let mut constraint = trait_.as_constraint(location); constraint.typ = typ; let method = TraitMethod { method_id: trait_method_id, constraint, assumed: false }; @@ -759,7 +766,8 @@ impl<'context> Elaborator<'context> { make_error: impl FnOnce() -> TypeCheckError, ) { if let Err(UnificationError) = actual.unify(expected) { - self.errors.push((make_error().into(), self.file)); + let error: CompilationError = make_error().into(); + self.push_err(error); } } @@ -770,12 +778,12 @@ impl<'context> Elaborator<'context> { &mut self, actual: &Type, expected: &Type, - file: fm::FileId, make_error: impl FnOnce() -> TypeCheckError, ) { let mut bindings = TypeBindings::new(); if actual.try_unify(expected, &mut bindings).is_err() { - self.errors.push((make_error().into(), file)); + let error: CompilationError = make_error().into(); + self.push_err(error); } } @@ -785,19 +793,19 @@ impl<'context> Elaborator<'context> { actual: &Type, expected: &Type, expression: ExprId, - span: Span, + location: Location, make_error: impl FnOnce() -> TypeCheckError, ) { let mut errors = Vec::new(); actual.unify_with_coercions( expected, expression, - span, + location, self.interner, &mut errors, make_error, ); - self.errors.extend(errors.into_iter().map(|error| (error.into(), self.file))); + self.push_errors(errors.into_iter().map(|error| error.into())); } /// Return a fresh integer or field type variable and log it @@ -847,7 +855,7 @@ impl<'context> Elaborator<'context> { trait_method_id: None, })); self.interner.push_expr_type(object, element.as_ref().clone()); - self.interner.push_expr_location(object, location.span, location.file); + self.interner.push_expr_location(object, location); // Recursively dereference to allow for converting &mut &mut T to T self.insert_auto_dereferences(object, *element) @@ -885,24 +893,24 @@ impl<'context> Elaborator<'context> { &mut self, fn_params: &[Type], fn_ret: &Type, - callsite_args: &[(Type, ExprId, Span)], - span: Span, + callsite_args: &[(Type, ExprId, Location)], + location: Location, ) -> Type { if fn_params.len() != callsite_args.len() { self.push_err(TypeCheckError::ParameterCountMismatch { expected: fn_params.len(), found: callsite_args.len(), - span, + location, }); return Type::Error; } - for (param, (arg, arg_expr_id, arg_span)) in fn_params.iter().zip(callsite_args) { - self.unify_with_coercions(arg, param, *arg_expr_id, *arg_span, || { + for (param, (arg, arg_expr_id, arg_location)) in fn_params.iter().zip(callsite_args) { + self.unify_with_coercions(arg, param, *arg_expr_id, *arg_location, || { TypeCheckError::TypeMismatch { expected_typ: param.to_string(), expr_typ: arg.to_string(), - expr_span: *arg_span, + expr_location: *arg_location, } }); } @@ -913,15 +921,15 @@ impl<'context> Elaborator<'context> { pub(super) fn bind_function_type( &mut self, function: Type, - args: Vec<(Type, ExprId, Span)>, - span: Span, + args: Vec<(Type, ExprId, Location)>, + location: Location, ) -> Type { // Could do a single unification for the entire function type, but matching beforehand // lets us issue a more precise error on the individual argument that fails to type check. match function { Type::TypeVariable(binding) if binding.kind() == Kind::Normal => { if let TypeBinding::Bound(typ) = &*binding.borrow() { - return self.bind_function_type(typ.clone(), args, span); + return self.bind_function_type(typ.clone(), args, location); } let ret = self.interner.next_type_variable(); @@ -931,7 +939,7 @@ impl<'context> Elaborator<'context> { Type::Function(args, Box::new(ret.clone()), Box::new(env_type), false); let expected_kind = expected.kind(); - if let Err(error) = binding.try_bind(expected, &expected_kind, span) { + if let Err(error) = binding.try_bind(expected, &expected_kind, location) { self.push_err(error); } ret @@ -939,11 +947,11 @@ impl<'context> Elaborator<'context> { // The closure env is ignored on purpose: call arguments never place // constraints on closure environments. Type::Function(parameters, ret, _env, _unconstrained) => { - self.bind_function_type_impl(¶meters, &ret, &args, span) + self.bind_function_type_impl(¶meters, &ret, &args, location) } Type::Error => Type::Error, found => { - self.push_err(TypeCheckError::ExpectedFunction { found, span }); + self.push_err(TypeCheckError::ExpectedFunction { found, location }); Type::Error } } @@ -954,12 +962,13 @@ impl<'context> Elaborator<'context> { from_expr_id: &ExprId, from: &Type, to: &Type, - span: Span, + location: Location, ) -> Type { let from_follow_bindings = from.follow_bindings(); + use HirExpression::Literal; let from_value_opt = match self.interner.expression(from_expr_id) { - HirExpression::Literal(HirLiteral::Integer(int, false)) => Some(int), + Literal(HirLiteral::Integer(SignedField { field, is_negative: false })) => Some(field), // TODO(https://github.com/noir-lang/noir/issues/6247): // handle negative literals @@ -976,7 +985,7 @@ impl<'context> Elaborator<'context> { let expected = self.polymorphic_integer_or_field(); self.unify(from, &expected, || TypeCheckError::InvalidCast { from: from.clone(), - span, + location, reason: "casting from a non-integral type is unsupported".into(), }); true @@ -984,7 +993,7 @@ impl<'context> Elaborator<'context> { Type::Error => return Type::Error, from => { let reason = "casting from this type is unsupported".into(); - self.push_err(TypeCheckError::InvalidCast { from, span, reason }); + self.push_err(TypeCheckError::InvalidCast { from, location, reason }); return Type::Error; } }; @@ -999,9 +1008,11 @@ impl<'context> Elaborator<'context> { if from_is_polymorphic && from_value > to_maximum_size { let from = from.clone(); let to = to.clone(); - let reason = format!("casting untyped value ({from_value}) to a type with a maximum size ({to_maximum_size}) that's smaller than it"); + let reason = format!( + "casting untyped value ({from_value}) to a type with a maximum size ({to_maximum_size}) that's smaller than it" + ); // we warn that the 'to' type is too small for the value - self.push_err(TypeCheckError::DownsizingCast { from, to, span, reason }); + self.push_err(TypeCheckError::DownsizingCast { from, to, location, reason }); } } @@ -1011,7 +1022,7 @@ impl<'context> Elaborator<'context> { Type::Bool => Type::Bool, Type::Error => Type::Error, _ => { - self.push_err(TypeCheckError::UnsupportedCast { span }); + self.push_err(TypeCheckError::UnsupportedCast { location }); Type::Error } } @@ -1026,7 +1037,7 @@ impl<'context> Elaborator<'context> { lhs_type: &Type, rhs_type: &Type, op: &HirBinaryOp, - span: Span, + location: Location, ) -> Result<(Type, bool), TypeCheckError> { use Type::*; @@ -1035,17 +1046,17 @@ impl<'context> Elaborator<'context> { (Error, _) | (_, Error) => Ok((Bool, false)), (Alias(alias, args), other) | (other, Alias(alias, args)) => { let alias = alias.borrow().get_type(args); - self.comparator_operand_type_rules(&alias, other, op, span) + self.comparator_operand_type_rules(&alias, other, op, location) } // Matches on TypeVariable must be first to follow any type // bindings. (TypeVariable(var), other) | (other, TypeVariable(var)) => { - if let TypeBinding::Bound(ref binding) = &*var.borrow() { - return self.comparator_operand_type_rules(other, binding, op, span); + if let TypeBinding::Bound(binding) = &*var.borrow() { + return self.comparator_operand_type_rules(other, binding, op, location); } - let use_impl = self.bind_type_variables_for_infix(lhs_type, op, rhs_type, span); + let use_impl = self.bind_type_variables_for_infix(lhs_type, op, rhs_type, location); Ok((Bool, use_impl)) } (Integer(sign_x, bit_width_x), Integer(sign_y, bit_width_y)) => { @@ -1053,14 +1064,14 @@ impl<'context> Elaborator<'context> { return Err(TypeCheckError::IntegerSignedness { sign_x: *sign_x, sign_y: *sign_y, - span, + location, }); } if bit_width_x != bit_width_y { return Err(TypeCheckError::IntegerBitWidth { bit_width_x: *bit_width_x, bit_width_y: *bit_width_y, - span, + location, }); } Ok((Bool, false)) @@ -1069,7 +1080,7 @@ impl<'context> Elaborator<'context> { if op.kind.is_valid_for_field_type() { Ok((Bool, false)) } else { - Err(TypeCheckError::FieldComparison { span }) + Err(TypeCheckError::FieldComparison { location }) } } @@ -1080,7 +1091,7 @@ impl<'context> Elaborator<'context> { self.unify(lhs, rhs, || TypeCheckError::TypeMismatchWithSource { expected: lhs.clone(), actual: rhs.clone(), - span: op.location.span, + location: op.location, source: Source::Binary, }); Ok((Bool, true)) @@ -1096,13 +1107,13 @@ impl<'context> Elaborator<'context> { lhs_type: &Type, op: &HirBinaryOp, rhs_type: &Type, - span: Span, + location: Location, ) -> bool { self.unify(lhs_type, rhs_type, || TypeCheckError::TypeMismatchWithSource { expected: lhs_type.clone(), actual: rhs_type.clone(), source: Source::Binary, - span, + location, }); let use_impl = !lhs_type.is_numeric_value(); @@ -1117,9 +1128,9 @@ impl<'context> Elaborator<'context> { use crate::ast::BinaryOpKind::*; use TypeCheckError::*; self.unify(lhs_type, &target, || match op.kind { - Less | LessEqual | Greater | GreaterEqual => FieldComparison { span }, - And | Or | Xor | ShiftRight | ShiftLeft => FieldBitwiseOp { span }, - Modulo => FieldModulo { span }, + Less | LessEqual | Greater | GreaterEqual => FieldComparison { location }, + And | Or | Xor | ShiftRight | ShiftLeft => FieldBitwiseOp { location }, + Modulo => FieldModulo { location }, other => unreachable!("Operator {other:?} should be valid for Field"), }); } @@ -1136,10 +1147,10 @@ impl<'context> Elaborator<'context> { lhs_type: &Type, op: &HirBinaryOp, rhs_type: &Type, - span: Span, + location: Location, ) -> Result<(Type, bool), TypeCheckError> { if op.kind.is_comparator() { - return self.comparator_operand_type_rules(lhs_type, rhs_type, op, span); + return self.comparator_operand_type_rules(lhs_type, rhs_type, op, location); } use Type::*; @@ -1148,7 +1159,7 @@ impl<'context> Elaborator<'context> { (Error, _) | (_, Error) => Ok((Error, false)), (Alias(alias, args), other) | (other, Alias(alias, args)) => { let alias = alias.borrow().get_type(args); - self.infix_operand_type_rules(&alias, op, other, span) + self.infix_operand_type_rules(&alias, op, other, location) } // Matches on TypeVariable must be first so that we follow any type @@ -1158,26 +1169,26 @@ impl<'context> Elaborator<'context> { self.unify( rhs_type, &Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight), - || TypeCheckError::InvalidShiftSize { span }, + || TypeCheckError::InvalidShiftSize { location }, ); let use_impl = if lhs_type.is_numeric_value() { let integer_type = self.polymorphic_integer(); - self.bind_type_variables_for_infix(lhs_type, op, &integer_type, span) + self.bind_type_variables_for_infix(lhs_type, op, &integer_type, location) } else { true }; return Ok((lhs_type.clone(), use_impl)); } - if let TypeBinding::Bound(ref binding) = &*int.borrow() { - return self.infix_operand_type_rules(binding, op, other, span); + if let TypeBinding::Bound(binding) = &*int.borrow() { + return self.infix_operand_type_rules(binding, op, other, location); } - let use_impl = self.bind_type_variables_for_infix(lhs_type, op, rhs_type, span); + let use_impl = self.bind_type_variables_for_infix(lhs_type, op, rhs_type, location); Ok((other.clone(), use_impl)) } (Integer(sign_x, bit_width_x), Integer(sign_y, bit_width_y)) => { if op.kind == BinaryOpKind::ShiftLeft || op.kind == BinaryOpKind::ShiftRight { if *sign_y != Signedness::Unsigned || *bit_width_y != IntegerBitSize::Eight { - return Err(TypeCheckError::InvalidShiftSize { span }); + return Err(TypeCheckError::InvalidShiftSize { location }); } return Ok((Integer(*sign_x, *bit_width_x), false)); } @@ -1185,14 +1196,14 @@ impl<'context> Elaborator<'context> { return Err(TypeCheckError::IntegerSignedness { sign_x: *sign_x, sign_y: *sign_y, - span, + location, }); } if bit_width_x != bit_width_y { return Err(TypeCheckError::IntegerBitWidth { bit_width_x: *bit_width_x, bit_width_y: *bit_width_y, - span, + location, }); } Ok((Integer(*sign_x, *bit_width_x), false)) @@ -1201,9 +1212,9 @@ impl<'context> Elaborator<'context> { (FieldElement, FieldElement) => { if !op.kind.is_valid_for_field_type() { if op.kind == BinaryOpKind::Modulo { - return Err(TypeCheckError::FieldModulo { span }); + return Err(TypeCheckError::FieldModulo { location }); } else { - return Err(TypeCheckError::FieldBitwiseOp { span }); + return Err(TypeCheckError::FieldBitwiseOp { location }); } } Ok((FieldElement, false)) @@ -1216,12 +1227,12 @@ impl<'context> Elaborator<'context> { if rhs == &Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight) { return Ok((lhs.clone(), true)); } - return Err(TypeCheckError::InvalidShiftSize { span }); + return Err(TypeCheckError::InvalidShiftSize { location }); } self.unify(lhs, rhs, || TypeCheckError::TypeMismatchWithSource { expected: lhs.clone(), actual: rhs.clone(), - span: op.location.span, + location: op.location, source: Source::Binary, }); Ok((lhs.clone(), true)) @@ -1237,7 +1248,7 @@ impl<'context> Elaborator<'context> { &mut self, op: &UnaryOp, rhs_type: &Type, - span: Span, + location: Location, ) -> Result<(Type, bool), TypeCheckError> { use Type::*; @@ -1248,14 +1259,14 @@ impl<'context> Elaborator<'context> { Error => Ok((Error, false)), Alias(alias, args) => { let alias = alias.borrow().get_type(args); - self.prefix_operand_type_rules(op, &alias, span) + self.prefix_operand_type_rules(op, &alias, location) } // Matches on TypeVariable must be first so that we follow any type // bindings. TypeVariable(int) => { - if let TypeBinding::Bound(ref binding) = &*int.borrow() { - return self.prefix_operand_type_rules(op, binding, span); + if let TypeBinding::Bound(binding) = &*int.borrow() { + return self.prefix_operand_type_rules(op, binding, location); } // The `!` prefix operator is not valid for Field, so if this is a numeric @@ -1263,7 +1274,10 @@ impl<'context> Elaborator<'context> { if matches!(op, crate::ast::UnaryOp::Not) && rhs_type.is_numeric_value() { let integer_type = Type::polymorphic_integer(self.interner); self.unify(rhs_type, &integer_type, || { - TypeCheckError::InvalidUnaryOp { kind: rhs_type.to_string(), span } + TypeCheckError::InvalidUnaryOp { + kind: rhs_type.to_string(), + location, + } }); } @@ -1273,7 +1287,7 @@ impl<'context> Elaborator<'context> { if *op == UnaryOp::Minus && *sign_x == Signedness::Unsigned { return Err(TypeCheckError::InvalidUnaryOp { kind: rhs_type.to_string(), - span, + location, }); } Ok((Integer(*sign_x, *bit_width_x), false)) @@ -1281,7 +1295,7 @@ impl<'context> Elaborator<'context> { // The result of a Field is always a witness FieldElement => { if *op == UnaryOp::Not { - return Err(TypeCheckError::FieldNot { span }); + return Err(TypeCheckError::FieldNot { location }); } Ok((FieldElement, false)) } @@ -1300,7 +1314,7 @@ impl<'context> Elaborator<'context> { self.unify(rhs_type, &expected, || TypeCheckError::TypeMismatch { expr_typ: rhs_type.to_string(), expected_typ: expected.to_string(), - expr_span: span, + expr_location: location, }); Ok((element_type, false)) } @@ -1318,7 +1332,7 @@ impl<'context> Elaborator<'context> { expr_id: ExprId, trait_method_id: TraitMethodId, object_type: &Type, - span: Span, + location: Location, ) { let the_trait = self.interner.get_trait(trait_method_id.trait_id); @@ -1333,7 +1347,7 @@ impl<'context> Elaborator<'context> { self.unify(object_type, expected_object_type, || TypeCheckError::TypeMismatch { expected_typ: expected_object_type.to_string(), expr_typ: object_type.to_string(), - expr_span: span, + expr_location: location, }); } other => { @@ -1369,7 +1383,7 @@ impl<'context> Elaborator<'context> { mut access: HirMemberAccess, expr_id: ExprId, lhs_type: Type, - span: Span, + location: Location, ) -> Type { let access_lhs = &mut access.lhs; @@ -1384,13 +1398,15 @@ impl<'context> Elaborator<'context> { this.interner.push_expr_type(*access_lhs, element); let old_location = this.interner.id_location(old_lhs); - this.interner.push_expr_location(*access_lhs, span, old_location.file); + let location = Location::new(location.span, old_location.file); + this.interner.push_expr_location(*access_lhs, location); }; // If this access is just a field offset, we want to avoid dereferencing let dereference_lhs = (!access.is_offset).then_some(dereference_lhs); - match self.check_field_access(&lhs_type, &access.rhs.0.contents, span, dereference_lhs) { + match self.check_field_access(&lhs_type, &access.rhs.0.contents, location, dereference_lhs) + { Some((element_type, index)) => { self.interner.set_field_index(expr_id, index); // We must update `access` in case we added any dereferences to it @@ -1405,8 +1421,8 @@ impl<'context> Elaborator<'context> { &mut self, object_type: &Type, method_name: &str, - span: Span, - has_self_arg: bool, + location: Location, + check_self_param: bool, ) -> Option { match object_type.follow_bindings() { // TODO: We should allow method calls on `impl Trait`s eventually. @@ -1415,17 +1431,17 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::UnresolvedMethodCall { method_name: method_name.to_string(), object_type: object_type.clone(), - span, + location, }); None } Type::NamedGeneric(_, _) => { - self.lookup_method_in_trait_constraints(object_type, method_name, span) + self.lookup_method_in_trait_constraints(object_type, method_name, location) } // Mutable references to another type should resolve to methods of their element type. // This may be a data type or a primitive type. Type::MutableReference(element) => { - self.lookup_method(&element, method_name, span, has_self_arg) + self.lookup_method(&element, method_name, location, check_self_param) } // If we fail to resolve the object to a data type, we have no way of type @@ -1434,11 +1450,16 @@ impl<'context> Elaborator<'context> { // The type variable must be unbound at this point since follow_bindings was called Type::TypeVariable(var) if var.kind() == Kind::Normal => { - self.push_err(TypeCheckError::TypeAnnotationsNeededForMethodCall { span }); + self.push_err(TypeCheckError::TypeAnnotationsNeededForMethodCall { location }); None } - other => self.lookup_type_or_primitive_method(&other, method_name, span, has_self_arg), + other => self.lookup_type_or_primitive_method( + &other, + method_name, + location, + check_self_param, + ), } } @@ -1446,31 +1467,31 @@ impl<'context> Elaborator<'context> { &mut self, object_type: &Type, method_name: &str, - span: Span, - has_self_arg: bool, + location: Location, + check_self_param: bool, ) -> Option { // First search in the type methods. If there is one, that's the one. if let Some(method_id) = - self.interner.lookup_direct_method(object_type, method_name, has_self_arg) + self.interner.lookup_direct_method(object_type, method_name, check_self_param) { return Some(HirMethodReference::FuncId(method_id)); } // Next lookup all matching trait methods. let trait_methods = - self.interner.lookup_trait_methods(object_type, method_name, has_self_arg); + self.interner.lookup_trait_methods(object_type, method_name, check_self_param); // If there's at least one matching trait method we need to see if only one is in scope. if !trait_methods.is_empty() { - return self.return_trait_method_in_scope(&trait_methods, method_name, span); + return self.return_trait_method_in_scope(&trait_methods, method_name, location); } // If we couldn't find any trait methods, search in // impls for all types `T`, e.g. `impl Foo for T` let generic_methods = - self.interner.lookup_generic_methods(object_type, method_name, has_self_arg); + self.interner.lookup_generic_methods(object_type, method_name, check_self_param); if !generic_methods.is_empty() { - return self.return_trait_method_in_scope(&generic_methods, method_name, span); + return self.return_trait_method_in_scope(&generic_methods, method_name, location); } if let Type::DataType(datatype, _) = object_type { @@ -1487,13 +1508,13 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::CannotInvokeStructFieldFunctionType { method_name: method_name.to_string(), object_type: object_type.clone(), - span, + location, }); } else { self.push_err(TypeCheckError::UnresolvedMethodCall { method_name: method_name.to_string(), object_type: object_type.clone(), - span, + location, }); } None @@ -1501,7 +1522,7 @@ impl<'context> Elaborator<'context> { // 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) + self.lookup_method_in_trait_constraints(object_type, method_name, location) } } @@ -1511,9 +1532,9 @@ impl<'context> Elaborator<'context> { &mut self, trait_methods: &[(FuncId, TraitId)], method_name: &str, - span: Span, + location: Location, ) -> Option { - let (method, error) = self.get_trait_method_in_scope(trait_methods, method_name, span); + let (method, error) = self.get_trait_method_in_scope(trait_methods, method_name, location); if let Some(error) = error { self.push_err(error); } @@ -1524,7 +1545,7 @@ impl<'context> Elaborator<'context> { &mut self, trait_methods: &[(FuncId, TraitId)], method_name: &str, - span: Span, + location: Location, ) -> (Option, Option) { let module_id = self.module_id(); let module_data = self.get_module(module_id); @@ -1560,9 +1581,9 @@ impl<'context> Elaborator<'context> { let trait_ = self.interner.get_trait(trait_id); let trait_name = self.fully_qualified_trait_path(trait_); let method = - self.trait_hir_method_reference(trait_id, trait_methods, method_name, span); + self.trait_hir_method_reference(trait_id, trait_methods, method_name, location); let error = PathResolutionError::TraitMethodNotInScope { - ident: Ident::new(method_name.into(), span), + ident: Ident::new(method_name.into(), location), trait_name, }; return (Some(method), Some(error)); @@ -1573,7 +1594,7 @@ impl<'context> Elaborator<'context> { }); let method = None; let error = PathResolutionError::UnresolvedWithPossibleTraitsToImport { - ident: Ident::new(method_name.into(), span), + ident: Ident::new(method_name.into(), location), traits, }; return (method, Some(error)); @@ -1587,14 +1608,15 @@ impl<'context> Elaborator<'context> { }); let method = None; let error = PathResolutionError::MultipleTraitsInScope { - ident: Ident::new(method_name.into(), span), + ident: Ident::new(method_name.into(), location), traits, }; return (method, Some(error)); } let trait_id = traits_in_scope[0].0; - let method = self.trait_hir_method_reference(trait_id, trait_methods, method_name, span); + let method = + self.trait_hir_method_reference(trait_id, trait_methods, method_name, location); let error = None; (Some(method), error) } @@ -1604,7 +1626,7 @@ impl<'context> Elaborator<'context> { trait_id: TraitId, trait_methods: &[(FuncId, TraitId)], method_name: &str, - span: Span, + location: Location, ) -> HirMethodReference { // If we find a single trait impl method, return it so we don't have to later determine the impl if trait_methods.len() == 1 { @@ -1614,7 +1636,7 @@ impl<'context> Elaborator<'context> { // Return a TraitMethodId with unbound generics. These will later be bound by the type-checker. let trait_ = self.interner.get_trait(trait_id); - let generics = trait_.get_trait_generics(span); + let generics = trait_.get_trait_generics(location); let trait_method_id = trait_.find_method(method_name).unwrap(); HirMethodReference::TraitMethodId(trait_method_id, generics, false) } @@ -1623,7 +1645,7 @@ impl<'context> Elaborator<'context> { &mut self, object_type: &Type, method_name: &str, - span: Span, + location: Location, ) -> Option { let func_id = match self.current_item { Some(DependencyId::Function(id)) => id, @@ -1635,7 +1657,7 @@ impl<'context> Elaborator<'context> { if let Some(trait_id) = func_meta.trait_id { if Some(object_type) == self.self_type.as_ref() { let the_trait = self.interner.get_trait(trait_id); - let constraint = the_trait.as_constraint(the_trait.name.span()); + let constraint = the_trait.as_constraint(the_trait.name.location()); if let Some(HirMethodReference::TraitMethodId(method_id, generics, _)) = self .lookup_method_in_trait( the_trait, @@ -1670,7 +1692,7 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::UnresolvedMethodCall { method_name: method_name.to_string(), object_type: object_type.clone(), - span, + location, }); None @@ -1719,8 +1741,8 @@ impl<'context> Elaborator<'context> { &mut self, call: &HirCallExpression, func_type: Type, - args: Vec<(Type, ExprId, Span)>, - span: Span, + args: Vec<(Type, ExprId, Location)>, + location: Location, ) -> Type { self.run_lint(|elaborator| { lints::deprecated_function(elaborator.interner, call.func).map(Into::into) @@ -1741,7 +1763,7 @@ impl<'context> Elaborator<'context> { if crossing_runtime_boundary { match self.unsafe_block_status { UnsafeBlockStatus::NotInUnsafeBlock => { - self.push_err(TypeCheckError::Unsafe { span }); + self.push_err(TypeCheckError::Unsafe { location }); } UnsafeBlockStatus::InUnsafeBlockWithoutUnconstrainedCalls => { self.unsafe_block_status = UnsafeBlockStatus::InUnsafeBlockWithConstrainedCalls; @@ -1755,7 +1777,7 @@ impl<'context> Elaborator<'context> { elaborator.interner, &called_func_id, is_current_func_constrained, - span, + location, ) .map(Into::into) }); @@ -1767,11 +1789,11 @@ impl<'context> Elaborator<'context> { } } - let return_type = self.bind_function_type(func_type, args, span); + let return_type = self.bind_function_type(func_type, args, location); if crossing_runtime_boundary { self.run_lint(|_| { - lints::unconstrained_function_return(&return_type, span).map(Into::into) + lints::unconstrained_function_return(&return_type, location).map(Into::into) }); } @@ -1817,9 +1839,8 @@ impl<'context> Elaborator<'context> { if matches!(expected_object_type.follow_bindings(), Type::MutableReference(_)) { if !matches!(actual_type, Type::MutableReference(_)) { - if let Err(error) = verify_mutable_reference(self.interner, *object) { - self.push_err(TypeCheckError::ResolverError(error)); - } + let location = self.interner.id_location(*object); + self.check_can_mutate(*object, location); let new_type = Type::MutableReference(Box::new(actual_type)); *object_type = new_type.clone(); @@ -1830,8 +1851,6 @@ impl<'context> Elaborator<'context> { // If that didn't work, then wrap the whole expression in an `&mut` *object = new_object.unwrap_or_else(|| { - let location = self.interner.id_location(*object); - let new_object = self.interner.push_expr(HirExpression::Prefix(HirPrefixExpression { operator: UnaryOp::MutableReference, @@ -1839,7 +1858,7 @@ impl<'context> Elaborator<'context> { trait_method_id: None, })); self.interner.push_expr_type(new_object, new_type); - self.interner.push_expr_location(new_object, location.span, location.file); + self.interner.push_expr_location(new_object, location); new_object }); } @@ -1854,10 +1873,10 @@ impl<'context> Elaborator<'context> { } pub fn type_check_function_body(&mut self, body_type: Type, meta: &FuncMeta, body_id: ExprId) { - let (expr_span, empty_function) = self.function_info(body_id); + let (expr_location, empty_function) = self.function_info(body_id); let declared_return_type = meta.return_type(); - let func_span = self.interner.expr_span(&body_id); // XXX: We could be more specific and return the span of the last stmt, however stmts do not have spans yet + let func_location = self.interner.expr_location(&body_id); // XXX: We could be more specific and return the span of the last stmt, however stmts do not have spans yet if let Type::TraitAsType(trait_id, _, generics) = declared_return_type { if self .interner @@ -1872,46 +1891,52 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::TypeMismatchWithSource { expected: declared_return_type.clone(), actual: body_type, - span: func_span, - source: Source::Return(meta.return_type.clone(), expr_span), + location: func_location, + source: Source::Return(meta.return_type.clone(), expr_location), }); } } else { - self.unify_with_coercions(&body_type, declared_return_type, body_id, func_span, || { - let mut error = TypeCheckError::TypeMismatchWithSource { - expected: declared_return_type.clone(), - actual: body_type.clone(), - span: func_span, - source: Source::Return(meta.return_type.clone(), expr_span), - }; + self.unify_with_coercions( + &body_type, + declared_return_type, + body_id, + func_location, + || { + let mut error = TypeCheckError::TypeMismatchWithSource { + expected: declared_return_type.clone(), + actual: body_type.clone(), + location: func_location, + source: Source::Return(meta.return_type.clone(), expr_location), + }; - if empty_function { - error = error.add_context( + if empty_function { + error = error.add_context( "implicitly returns `()` as its body has no tail or `return` expression", ); - } - error - }); + } + error + }, + ); } } - fn function_info(&self, function_body_id: ExprId) -> (noirc_errors::Span, bool) { - let (expr_span, empty_function) = + fn function_info(&self, function_body_id: ExprId) -> (noirc_errors::Location, bool) { + let (expr_location, empty_function) = if let HirExpression::Block(block) = self.interner.expression(&function_body_id) { let last_stmt = block.statements().last(); - let mut span = self.interner.expr_span(&function_body_id); + let mut location = self.interner.expr_location(&function_body_id); if let Some(last_stmt) = last_stmt { if let HirStatement::Expression(expr) = self.interner.statement(last_stmt) { - span = self.interner.expr_span(&expr); + location = self.interner.expr_location(&expr); } } - (span, last_stmt.is_none()) + (location, last_stmt.is_none()) } else { - (self.interner.expr_span(&function_body_id), false) + (self.interner.expr_location(&function_body_id), false) }; - (expr_span, empty_function) + (expr_location, empty_function) } #[allow(clippy::too_many_arguments)] @@ -1923,7 +1948,7 @@ impl<'context> Elaborator<'context> { associated_types: &[NamedType], function_ident_id: ExprId, select_impl: bool, - span: Span, + location: Location, ) { match self.interner.lookup_trait_implementation( object_type, @@ -1936,7 +1961,7 @@ impl<'context> Elaborator<'context> { self.interner.select_impl_for_expression(function_ident_id, impl_kind); } } - Err(error) => self.push_trait_constraint_error(object_type, error, span), + Err(error) => self.push_trait_constraint_error(object_type, error, location), } } @@ -1944,25 +1969,24 @@ impl<'context> Elaborator<'context> { &mut self, object_type: &Type, error: ImplSearchErrorKind, - span: Span, + location: Location, ) { match error { ImplSearchErrorKind::TypeAnnotationsNeededOnObjectType => { - self.push_err(TypeCheckError::TypeAnnotationsNeededForMethodCall { span }); + self.push_err(TypeCheckError::TypeAnnotationsNeededForMethodCall { location }); } ImplSearchErrorKind::Nested(constraints) => { - if let Some(error) = NoMatchingImplFoundError::new(self.interner, constraints, span) + if let Some(error) = + NoMatchingImplFoundError::new(self.interner, constraints, location) { self.push_err(TypeCheckError::NoMatchingImplFound(error)); } } ImplSearchErrorKind::MultipleMatching(candidates) => { let object_type = object_type.clone(); - self.push_err(TypeCheckError::MultipleMatchingImpls { - object_type, - span, - candidates, - }); + let err = + TypeCheckError::MultipleMatchingImpls { object_type, location, candidates }; + self.push_err(err); } } } @@ -1975,14 +1999,14 @@ impl<'context> Elaborator<'context> { assert_eq!(unresolved_generics.len(), generics.len()); for (unresolved_generic, generic) in unresolved_generics.iter().zip(generics) { - self.add_existing_generic(unresolved_generic, unresolved_generic.span(), generic); + self.add_existing_generic(unresolved_generic, unresolved_generic.location(), generic); } } pub fn add_existing_generic( &mut self, unresolved_generic: &UnresolvedGeneric, - span: Span, + location: Location, resolved_generic: &ResolvedGeneric, ) { let name = &unresolved_generic.ident().0.contents; @@ -1990,8 +2014,8 @@ impl<'context> Elaborator<'context> { if let Some(generic) = self.find_generic(name) { self.push_err(ResolverError::DuplicateDefinition { name: name.clone(), - first_span: generic.span, - second_span: span, + first_location: generic.location, + second_location: location, }); } else { self.generics.push(resolved_generic.clone()); @@ -2109,29 +2133,3 @@ fn bind_generic(param: &ResolvedGeneric, arg: &Type, bindings: &mut TypeBindings bindings.insert(param.type_var.id(), (param.type_var.clone(), param.kind(), arg.clone())); } } - -/// Gives an error if a user tries to create a mutable reference -/// to an immutable variable. -fn verify_mutable_reference(interner: &NodeInterner, rhs: ExprId) -> Result<(), ResolverError> { - match interner.expression(&rhs) { - HirExpression::MemberAccess(member_access) => { - verify_mutable_reference(interner, member_access.lhs) - } - HirExpression::Index(_) => { - let span = interner.expr_span(&rhs); - Err(ResolverError::MutableReferenceToArrayElement { span }) - } - HirExpression::Ident(ident, _) => { - if let Some(definition) = interner.try_definition(ident.id) { - if !definition.mutable { - return Err(ResolverError::MutableReferenceToImmutableVariable { - span: interner.expr_span(&rhs), - variable: definition.name.clone(), - }); - } - } - Ok(()) - } - _ => Ok(()), - } -} 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 982ad3d2e1f8..56f9a694c9bf 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/unquote.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/unquote.rs @@ -1,11 +1,11 @@ use crate::{ ast::Path, - token::{SpannedToken, Token, Tokens}, + token::{LocatedToken, Token, Tokens}, }; use super::Elaborator; -impl<'a> Elaborator<'a> { +impl Elaborator<'_> { /// Go through the given tokens looking for a '$' token followed by a variable to unquote. /// Each time these two tokens are found, they are replaced by a new UnquoteMarker token /// containing the ExprId of the resolved variable to unquote. @@ -20,17 +20,18 @@ impl<'a> Elaborator<'a> { if is_unquote { if let Some(next) = tokens.next() { - let span = next.to_span(); + let location = next.location(); match next.into_token() { Token::Ident(name) => { // Don't want the leading `$` anymore new_tokens.pop(); - let path = Path::from_single(name, span); + let path = Path::from_single(name, location); let (expr_id, _) = self.elaborate_variable(path); - new_tokens.push(SpannedToken::new(Token::UnquoteMarker(expr_id), span)); + new_tokens + .push(LocatedToken::new(Token::UnquoteMarker(expr_id), location)); } - other_next => new_tokens.push(SpannedToken::new(other_next, span)), + other_next => new_tokens.push(LocatedToken::new(other_next, location)), } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/graph/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/graph/mod.rs index c007d6792bd1..0ce744aded5a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/graph/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/graph/mod.rs @@ -87,7 +87,7 @@ impl FromStr for CrateName { #[cfg(test)] mod crate_name { - use super::{CrateName, CHARACTER_BLACK_LIST}; + use super::{CHARACTER_BLACK_LIST, CrateName}; #[test] fn it_rejects_empty_string() { @@ -379,7 +379,7 @@ mod tests { use super::{CrateGraph, FileId}; fn dummy_file_ids(n: usize) -> Vec { - use fm::{FileMap, FILE_EXTENSION}; + use fm::{FILE_EXTENSION, FileMap}; let mut fm = FileMap::default(); let mut vec_ids = Vec::with_capacity(n); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/display.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/display.rs index aa2f7d99fab9..c0283d9701b4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/display.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/display.rs @@ -1,30 +1,30 @@ -use std::{fmt::Display, rc::Rc}; +use std::fmt::Display; use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ + Type, ast::{ ArrayLiteral, AsTraitPath, AssignStatement, BlockExpression, CallExpression, CastExpression, ConstrainExpression, ConstructorExpression, Expression, ExpressionKind, ForBounds, ForLoopStatement, ForRange, GenericTypeArgs, IfExpression, IndexExpression, InfixExpression, LValue, Lambda, LetStatement, Literal, MatchExpression, MemberAccessExpression, MethodCallExpression, Pattern, PrefixExpression, Statement, - StatementKind, UnresolvedType, UnresolvedTypeData, WhileStatement, + StatementKind, UnresolvedType, UnresolvedTypeData, UnsafeExpression, WhileStatement, }, hir_def::traits::TraitConstraint, node_interner::{InternedStatementKind, NodeInterner}, - token::{Keyword, Token}, - Type, + token::{Keyword, LocatedToken, Token}, }; use super::{ - value::{ExprValue, TypedExpr}, Value, + value::{ExprValue, TypedExpr}, }; pub(super) fn display_quoted( - tokens: &[Token], + tokens: &[LocatedToken], indent: usize, interner: &NodeInterner, f: &mut std::fmt::Formatter<'_>, @@ -44,16 +44,16 @@ pub(super) fn display_quoted( } struct TokensPrettyPrinter<'tokens, 'interner> { - tokens: &'tokens [Token], + tokens: &'tokens [LocatedToken], interner: &'interner NodeInterner, indent: usize, } -impl<'tokens, 'interner> Display for TokensPrettyPrinter<'tokens, 'interner> { +impl Display for TokensPrettyPrinter<'_, '_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut token_printer = TokenPrettyPrinter::new(self.interner, self.indent); for token in self.tokens { - token_printer.print(token, f)?; + token_printer.print(token.token(), f)?; } // If the printer refrained from printing a token right away, this will make it do it @@ -63,9 +63,8 @@ impl<'tokens, 'interner> Display for TokensPrettyPrinter<'tokens, 'interner> { } } -pub(super) fn tokens_to_string(tokens: Rc>, interner: &NodeInterner) -> String { - let tokens: Vec = tokens.iter().cloned().collect(); - TokensPrettyPrinter { tokens: &tokens, interner, indent: 0 }.to_string() +pub(super) fn tokens_to_string(tokens: &[LocatedToken], interner: &NodeInterner) -> String { + TokensPrettyPrinter { tokens, interner, indent: 0 }.to_string() } /// Tries to print tokens in a way that it'll be easier for the user to understand a @@ -180,7 +179,7 @@ impl<'interner> TokenPrettyPrinter<'interner> { self.print_value(&value, f) } Token::InternedLValue(id) => { - let value = Value::lvalue(LValue::Interned(*id, Span::default())); + let value = Value::lvalue(LValue::Interned(*id, Location::dummy())); self.print_value(&value, f) } Token::InternedUnresolvedTypeData(id) => { @@ -188,7 +187,7 @@ impl<'interner> TokenPrettyPrinter<'interner> { self.print_value(&value, f) } Token::InternedPattern(id) => { - let value = Value::pattern(Pattern::Interned(*id, Span::default())); + let value = Value::pattern(Pattern::Interned(*id, Location::dummy())); self.print_value(&value, f) } Token::UnquoteMarker(id) => { @@ -229,8 +228,7 @@ impl<'interner> TokenPrettyPrinter<'interner> { if last_was_alphanumeric { write!(f, " ")?; } - let tokens = vecmap(&tokens.0, |spanned_token| spanned_token.clone().into_token()); - display_quoted(&tokens, self.indent, self.interner, f) + display_quoted(&tokens.0, self.indent, self.interner, f) } Token::Colon => { write!(f, "{token} ") @@ -329,7 +327,7 @@ pub struct ValuePrinter<'value, 'interner> { interner: &'interner NodeInterner, } -impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { +impl Display for ValuePrinter<'_, '_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self.value { Value::Unit => write!(f, "()"), @@ -347,6 +345,7 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { Value::U16(value) => write!(f, "{value}"), Value::U32(value) => write!(f, "{value}"), Value::U64(value) => write!(f, "{value}"), + Value::U128(value) => write!(f, "{value}"), Value::String(value) => write!(f, "{value}"), Value::CtString(value) => write!(f, "{value}"), Value::FormatString(value, _) => write!(f, "{value}"), @@ -466,12 +465,12 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { }, Value::TypedExpr(TypedExpr::ExprId(id)) => { let hir_expr = self.interner.expression(id); - let expr = hir_expr.to_display_ast(self.interner, Span::default()); + let expr = hir_expr.to_display_ast(self.interner, Location::dummy()); write!(f, "{}", expr.kind) } Value::TypedExpr(TypedExpr::StmtId(id)) => { let hir_statement = self.interner.statement(id); - let stmt = hir_statement.to_display_ast(self.interner, Span::default()); + let stmt = hir_statement.to_display_ast(self.interner, Location::dummy()); write!(f, "{}", stmt.kind) } Value::UnresolvedType(typ) => { @@ -495,7 +494,7 @@ pub struct TokenPrinter<'token, 'interner> { interner: &'interner NodeInterner, } -impl<'token, 'interner> Display for TokenPrinter<'token, 'interner> { +impl Display for TokenPrinter<'_, '_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self.token { Token::QuotedType(id) => { @@ -510,7 +509,7 @@ impl<'token, 'interner> Display for TokenPrinter<'token, 'interner> { value.display(self.interner).fmt(f) } Token::InternedLValue(id) => { - let value = Value::lvalue(LValue::Interned(*id, Span::default())); + let value = Value::lvalue(LValue::Interned(*id, Location::dummy())); value.display(self.interner).fmt(f) } Token::InternedUnresolvedTypeData(id) => { @@ -518,7 +517,7 @@ impl<'token, 'interner> Display for TokenPrinter<'token, 'interner> { value.display(self.interner).fmt(f) } Token::InternedPattern(id) => { - let value = Value::pattern(Pattern::Interned(*id, Span::default())); + let value = Value::pattern(Pattern::Interned(*id, Location::dummy())); value.display(self.interner).fmt(f) } Token::UnquoteMarker(id) => { @@ -540,7 +539,10 @@ fn display_trait_constraint(interner: &NodeInterner, trait_constraint: &TraitCon // Returns a new Expression where all Interned and Resolved expressions have been turned into non-interned ExpressionKind. fn remove_interned_in_expression(interner: &NodeInterner, expr: Expression) -> Expression { - Expression { kind: remove_interned_in_expression_kind(interner, expr.kind), span: expr.span } + Expression { + kind: remove_interned_in_expression_kind(interner, expr.kind), + location: expr.location, + } } // Returns a new ExpressionKind where all Interned and Resolved expressions have been turned into non-interned ExpressionKind. @@ -643,10 +645,13 @@ fn remove_interned_in_expression_kind( vecmap(block.statements, |stmt| remove_interned_in_statement(interner, stmt)); ExpressionKind::Comptime(BlockExpression { statements }, span) } - ExpressionKind::Unsafe(block, span) => { + ExpressionKind::Unsafe(UnsafeExpression { block, unsafe_keyword_location }) => { let statements = vecmap(block.statements, |stmt| remove_interned_in_statement(interner, stmt)); - ExpressionKind::Unsafe(BlockExpression { statements }, span) + ExpressionKind::Unsafe(UnsafeExpression { + block: BlockExpression { statements }, + unsafe_keyword_location, + }) } ExpressionKind::AsTraitPath(mut path) => { path.typ = remove_interned_in_unresolved_type(interner, path.typ); @@ -663,7 +668,7 @@ fn remove_interned_in_expression_kind( } ExpressionKind::Resolved(id) => { let expr = interner.expression(&id); - expr.to_display_ast(interner, Span::default()).kind + expr.to_display_ast(interner, Location::dummy()).kind } ExpressionKind::Interned(id) => { let expr = interner.get_expression_kind(id).clone(); @@ -695,7 +700,7 @@ fn remove_interned_in_literal(interner: &NodeInterner, literal: Literal) -> Lite Literal::Array(remove_interned_in_array_literal(interner, array_literal)) } Literal::Bool(_) - | Literal::Integer(_, _) + | Literal::Integer(_) | Literal::Str(_) | Literal::RawStr(_, _) | Literal::FmtStr(_, _) @@ -724,7 +729,7 @@ fn remove_interned_in_array_literal( fn remove_interned_in_statement(interner: &NodeInterner, statement: Statement) -> Statement { Statement { kind: remove_interned_in_statement_kind(interner, statement.kind), - span: statement.span, + location: statement.location, } } @@ -769,7 +774,7 @@ fn remove_interned_in_statement_kind( StatementKind::While(while_) => StatementKind::While(WhileStatement { condition: remove_interned_in_expression(interner, while_.condition), body: remove_interned_in_expression(interner, while_.body), - while_keyword_span: while_.while_keyword_span, + while_keyword_location: while_.while_keyword_location, }), StatementKind::Comptime(statement) => { StatementKind::Comptime(Box::new(remove_interned_in_statement(interner, *statement))) @@ -789,15 +794,15 @@ fn remove_interned_in_statement_kind( fn remove_interned_in_lvalue(interner: &NodeInterner, lvalue: LValue) -> LValue { match lvalue { LValue::Ident(_) => lvalue, - LValue::MemberAccess { object, field_name, span } => LValue::MemberAccess { + LValue::MemberAccess { object, field_name, location: span } => LValue::MemberAccess { object: Box::new(remove_interned_in_lvalue(interner, *object)), field_name, - span, + location: span, }, - LValue::Index { array, index, span } => LValue::Index { + LValue::Index { array, index, location: span } => LValue::Index { array: Box::new(remove_interned_in_lvalue(interner, *array)), index: remove_interned_in_expression(interner, index), - span, + location: span, }, LValue::Dereference(lvalue, span) => { LValue::Dereference(Box::new(remove_interned_in_lvalue(interner, *lvalue)), span) @@ -815,7 +820,7 @@ fn remove_interned_in_unresolved_type( ) -> UnresolvedType { UnresolvedType { typ: remove_interned_in_unresolved_type_data(interner, typ.typ), - span: typ.span, + location: typ.location, } } 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 64297a240621..27440069c023 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,16 +2,16 @@ use std::fmt::Display; use std::rc::Rc; use crate::{ + Type, ast::TraitBound, hir::{ def_collector::dc_crate::CompilationError, type_check::{NoMatchingImplFoundError, TypeCheckError}, }, parser::ParserError, - Type, + signed_field::SignedField, }; -use acvm::{acir::AcirField, BlackBoxResolutionError, FieldElement}; -use fm::FileId; +use acvm::BlackBoxResolutionError; use noirc_errors::{CustomDiagnostic, Location}; /// The possible errors that can halt the interpreter. @@ -35,7 +35,7 @@ pub enum InterpreterError { location: Location, }, IntegerOutOfRangeForType { - value: FieldElement, + value: SignedField, typ: Type, location: Location, }, @@ -157,7 +157,7 @@ pub enum InterpreterError { error: Box, tokens: String, rule: &'static str, - file: FileId, + location: Location, }, UnsupportedTopLevelItemUnquote { item: String, @@ -172,7 +172,6 @@ pub enum InterpreterError { }, NoMatchingImplFound { error: NoMatchingImplFoundError, - file: FileId, }, ImplMethodTypeMismatch { expected: Type, @@ -274,12 +273,7 @@ impl From for CompilationError { } impl InterpreterError { - pub fn into_compilation_error_pair(self) -> (CompilationError, fm::FileId) { - let location = self.get_location(); - (CompilationError::InterpreterError(self), location.file) - } - - pub fn get_location(&self) -> Location { + pub fn location(&self) -> Location { match self { InterpreterError::ArgumentCountMismatch { location, .. } | InterpreterError::TypeMismatch { location, .. } @@ -339,12 +333,8 @@ impl InterpreterError { | InterpreterError::GlobalsDependencyCycle { location } | InterpreterError::LoopHaltedForUiResponsiveness { location } => *location, - InterpreterError::FailedToParseMacro { error, file, .. } => { - Location::new(error.span(), *file) - } - InterpreterError::NoMatchingImplFound { error, file } => { - Location::new(error.span, *file) - } + InterpreterError::FailedToParseMacro { error, .. } => error.location(), + InterpreterError::NoMatchingImplFound { error } => error.location, InterpreterError::Break | InterpreterError::Continue => { panic!("Tried to get the location of Break/Continue error!") } @@ -360,7 +350,7 @@ impl InterpreterError { let diagnostic = CustomDiagnostic::simple_info( "`comptime` expression ran:".to_string(), format!("After evaluation: {}", formatted_result), - location.span, + location, ); InterpreterError::DebugEvaluateComptime { diagnostic, location } } @@ -379,66 +369,62 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let few_many = if actual < expected { "few" } else { "many" }; let secondary = format!("Too {few_many} arguments"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::TypeMismatch { expected, actual, location } => { let msg = format!("Expected `{expected}` but a value of type `{actual}` was given"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NonComptimeVarReferenced { name, location } => { let msg = format!("Non-comptime variable `{name}` referenced in comptime code"); let secondary = "Non-comptime variables can't be used in comptime code".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::VariableNotInScope { location } => { let msg = "Variable not in scope".to_string(); let secondary = "Could not find variable".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::IntegerOutOfRangeForType { value, typ, location } => { - let int = match value.try_into_u128() { - Some(int) => int.to_string(), - None => value.to_string(), - }; - let msg = format!("{int} is outside the range of the {typ} type"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + let msg = format!("{value} is outside the range of the {typ} type"); + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::ErrorNodeEncountered { location } => { let msg = "Internal Compiler Error: Error node encountered".to_string(); let secondary = "This is a bug, please report this if found!".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonFunctionCalled { typ, location } => { let msg = "Only functions may be called".to_string(); let secondary = format!("Expression has type {typ}"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonBoolUsedInIf { typ, location } => { let msg = format!("Expected a `bool` but found `{typ}`"); let secondary = "If conditions must be a boolean value".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonBoolUsedInWhile { typ, location } => { let msg = format!("Expected a `bool` but found `{typ}`"); let secondary = "While conditions must be a boolean value".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonBoolUsedInConstrain { typ, location } => { let msg = format!("Expected a `bool` but found `{typ}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::FailingConstraint { message, location, call_stack } => { let (primary, secondary) = match message { Some(msg) => (msg.clone(), "Assertion failed".into()), None => ("Assertion failed".into(), String::new()), }; - let diagnostic = CustomDiagnostic::simple_error(primary, secondary, location.span); + let diagnostic = CustomDiagnostic::simple_error(primary, secondary, *location); diagnostic.with_call_stack(call_stack.into_iter().copied().collect()) } InterpreterError::NoMethodFound { name, typ, location } => { let msg = format!("No method named `{name}` found for type `{typ}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NonIntegerUsedInLoop { typ, location } => { let msg = format!("Non-integer type `{typ}` used in for loop"); @@ -447,97 +433,97 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { } else { String::new() }; - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonPointerDereferenced { typ, location } => { let msg = format!("Only references may be dereferenced, but found `{typ}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NonTupleOrStructInMemberAccess { typ, location } => { let msg = format!("The type `{typ}` has no fields to access"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NonArrayIndexed { typ, location } => { let msg = format!("Expected an array or slice but found a(n) {typ}"); let secondary = "Only arrays or slices may be indexed".into(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonIntegerUsedAsIndex { typ, location } => { let msg = format!("Expected an integer but found a(n) {typ}"); let secondary = "Only integers may be indexed. Note that this excludes `field`s".into(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonIntegerIntegerLiteral { typ, location } => { let msg = format!("This integer literal somehow has the type `{typ}`"); let secondary = "This is likely a bug".into(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonIntegerArrayLength { typ, err, location } => { let msg = format!("Non-integer array length: `{typ}`"); let secondary = if let Some(err) = err { - format!("Array lengths must be integers, but evaluating `{typ}` resulted in `{err}`") + format!( + "Array lengths must be integers, but evaluating `{typ}` resulted in `{err}`" + ) } else { "Array lengths must be integers".to_string() }; - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonNumericCasted { typ, location } => { let msg = "Only numeric types may be casted".into(); let secondary = format!("`{typ}` is non-numeric"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::IndexOutOfBounds { index, length, location } => { let msg = format!("{index} is out of bounds for the array of length {length}"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::ExpectedStructToHaveField { typ, field_name, location } => { let msg = format!("The type `{typ}` has no field named `{field_name}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::TypeUnsupported { typ, location } => { let msg = format!("The type `{typ}` is currently unsupported in comptime expressions"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::InvalidValueForUnary { typ, operator, location } => { let msg = format!("`{typ}` cannot be used with unary {operator}"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::InvalidValuesForBinary { lhs, rhs, operator, location } => { let msg = format!("No implementation for `{lhs}` {operator} `{rhs}`",); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::CastToNonNumericType { typ, location } => { let msg = format!("Cannot cast to non-numeric type `{typ}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::QuoteInRuntimeCode { location } => { let msg = "`quote` may only be used in comptime code".into(); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NonStructInConstructor { typ, location } => { let msg = format!("`{typ}` is not a struct type"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NonEnumInConstructor { typ, location } => { let msg = format!("`{typ}` is not an enum type"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::CannotInlineMacro { value, typ, location } => { let msg = format!("Cannot inline values of type `{typ}` into this position"); let secondary = format!("Cannot inline value `{value}`"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::UnquoteFoundDuringEvaluation { location } => { let msg = "Unquote found during comptime evaluation".into(); let secondary = "This is a bug".into(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::DebugEvaluateComptime { diagnostic, .. } => diagnostic.clone(), - InterpreterError::FailedToParseMacro { error, tokens, rule, file: _ } => { - let message = format!("Failed to parse macro's token stream into {rule}"); - + InterpreterError::FailedToParseMacro { error, tokens, rule, location } => { // If it's less than 48 chars, the error message fits in a single line (less than 80 chars total) let token_stream = if tokens.len() <= 48 && !tokens.contains('\n') { format!("The resulting token stream was: {tokens}") @@ -549,12 +535,13 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let push_the_problem_on_the_library_author = "To avoid this error in the future, try adding input validation to your macro. Erroring out early with an `assert` can be a good way to provide a user-friendly error message".into(); - let mut diagnostic = CustomDiagnostic::from(error.as_ref()); - // Swap the parser's primary note to become the secondary note so that it is - // more clear this error originates from failing to parse a macro. - let secondary = std::mem::take(&mut diagnostic.message); - diagnostic.add_secondary(secondary, error.span()); - diagnostic.message = message; + let mut diagnostic = CustomDiagnostic::from(&**error); + + // Given more prominence to where the parser error happened, but still show that it's + // because of a failure to parse a macro's token stream, and where that happens. + let message = format!("Failed to parse macro's token stream into {rule}"); + diagnostic.add_secondary(message, *location); + diagnostic.add_note(token_stream); diagnostic.add_note(push_the_problem_on_the_library_author); diagnostic @@ -563,7 +550,7 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let msg = "Unsupported statement type to unquote".into(); let secondary = "Only functions, structs, globals, and impls can be unquoted here".into(); - let mut error = CustomDiagnostic::simple_error(msg, secondary, location.span); + let mut error = CustomDiagnostic::simple_error(msg, secondary, *location); error.add_note(format!("Unquoted item was:\n{item}")); error } @@ -571,63 +558,63 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let msg = format!("Comptime dependency cycle while resolving `{function}`"); let secondary = "This function uses comptime code internally which calls into itself".into(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::Unimplemented { item, location } => { let msg = format!("{item} is currently unimplemented"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::InvalidInComptimeContext { item, location, explanation } => { let msg = format!("{item} is invalid in comptime context"); - CustomDiagnostic::simple_error(msg, explanation.clone(), location.span) + CustomDiagnostic::simple_error(msg, explanation.clone(), *location) } InterpreterError::BreakNotInLoop { location } => { let msg = "There is no loop to break out of!".into(); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::ContinueNotInLoop { location } => { let msg = "There is no loop to continue!".into(); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NoImpl { location } => { let msg = "No impl found due to prior type error".into(); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::ImplMethodTypeMismatch { expected, actual, location } => { let msg = format!( "Impl method type {actual} does not unify with trait method type {expected}" ); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::BlackBoxError(error, location) => { - CustomDiagnostic::simple_error(error.to_string(), String::new(), location.span) + CustomDiagnostic::simple_error(error.to_string(), String::new(), *location) } InterpreterError::FailedToResolveTraitBound { trait_bound, location } => { let msg = format!("Failed to resolve trait bound `{trait_bound}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NoMatchingImplFound { error, .. } => error.into(), InterpreterError::Break => unreachable!("Uncaught InterpreterError::Break"), InterpreterError::Continue => unreachable!("Uncaught InterpreterError::Continue"), InterpreterError::TraitDefinitionMustBeAPath { location } => { let msg = "Trait definition arguments must be a variable or path".to_string(); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::FailedToResolveTraitDefinition { location } => { let msg = "Failed to resolve to a trait definition".to_string(); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::FunctionAlreadyResolved { location } => { let msg = "Function already resolved".to_string(); let secondary = "The function was previously called at compile-time or is in another crate" .to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::MultipleMatchingImpls { object_type, candidates, location } => { let message = format!("Multiple trait impls match the object type `{object_type}`"); let secondary = "Ambiguous impl".to_string(); - let mut error = CustomDiagnostic::simple_error(message, secondary, location.span); + let mut error = CustomDiagnostic::simple_error(message, secondary, *location); for (i, candidate) in candidates.iter().enumerate() { error.add_note(format!("Candidate {}: `{candidate}`", i + 1)); } @@ -637,7 +624,7 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let mut error = CustomDiagnostic::simple_error( "Object type is unknown in method call".to_string(), "Type must be known by this point to know which method to call".to_string(), - location.span, + *location, ); let message = "Try adding a type annotation for the object type before this method call"; @@ -649,19 +636,19 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { "Quoted value in index {index} of this slice is not a valid field name" ); let secondary = format!("`{value}` is not a valid field name for `set_fields`"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::InvalidAttribute { attribute, location } => { let msg = format!("`{attribute}` is not a valid attribute"); let secondary = "Note that this method expects attribute contents, without the leading `#[` or trailing `]`".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::GenericNameShouldBeAnIdent { name, location } => { let msg = "Generic name needs to be a valid identifier (one word beginning with a letter)" .to_string(); let secondary = format!("`{name}` is not a valid identifier"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::DuplicateGeneric { name, @@ -671,47 +658,76 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { } => { let msg = format!("`{struct_name}` already has a generic named `{name}`"); let secondary = format!("`{name}` added here a second time"); - let mut error = - CustomDiagnostic::simple_error(msg, secondary, duplicate_location.span); + let mut error = CustomDiagnostic::simple_error(msg, secondary, *duplicate_location); let existing_msg = format!("`{name}` was previously defined here"); - error.add_secondary_with_file( - existing_msg, - existing_location.span, - existing_location.file, - ); + error.add_secondary(existing_msg, *existing_location); error } InterpreterError::CannotResolveExpression { location, expression } => { let msg = format!("Cannot resolve expression `{expression}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::CannotSetFunctionBody { location, expression } => { let msg = format!("`{expression}` is not a valid function body"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::UnknownArrayLength { length, err, location } => { let msg = format!("Could not determine array length `{length}`"); let secondary = format!("Evaluating the length failed with: `{err}`"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::CannotInterpretFormatStringWithErrors { location } => { let msg = "Cannot interpret format string with errors".to_string(); let secondary = "Some of the variables to interpolate could not be evaluated".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::GlobalsDependencyCycle { location } => { let msg = "This global recursively depends on itself".to_string(); let secondary = String::new(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::LoopHaltedForUiResponsiveness { location } => { let msg = "This loop took too much time to execute so it was halted for UI responsiveness" .to_string(); let secondary = "This error doesn't happen in normal executions of `nargo`".to_string(); - CustomDiagnostic::simple_warning(msg, secondary, location.span) + CustomDiagnostic::simple_warning(msg, secondary, *location) + } + } + } +} + +/// Comptime errors always wrap another error to show it together with a +/// comptime call or macro "something" that eventually led to that error. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ComptimeError { + ErrorRunningAttribute { error: Box, location: Location }, + ErrorAddingItemToModule { error: Box, location: Location }, +} + +impl ComptimeError { + pub fn location(&self) -> Location { + match self { + ComptimeError::ErrorRunningAttribute { location, .. } + | ComptimeError::ErrorAddingItemToModule { location, .. } => *location, + } + } +} + +impl<'a> From<&'a ComptimeError> for CustomDiagnostic { + fn from(error: &'a ComptimeError) -> Self { + match error { + ComptimeError::ErrorRunningAttribute { error, location } => { + let mut diagnostic = CustomDiagnostic::from(&**error); + diagnostic.add_secondary("While running this function attribute".into(), *location); + diagnostic + } + ComptimeError::ErrorAddingItemToModule { error, location } => { + let mut diagnostic = CustomDiagnostic::from(&**error); + diagnostic.add_secondary("While interpreting `Module::add_item`".into(), *location); + diagnostic } } } 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 05a0435b4503..dcc938faf2ac 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 @@ -1,12 +1,13 @@ +use fm::FileId; use iter_extended::vecmap; -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Located, Location, Span}; use crate::ast::{ ArrayLiteral, AssignStatement, BlockExpression, CallExpression, CastExpression, ConstrainKind, ConstructorExpression, ExpressionKind, ForLoopStatement, ForRange, GenericTypeArgs, Ident, IfExpression, IndexExpression, InfixExpression, LValue, Lambda, Literal, MatchExpression, - MemberAccessExpression, MethodCallExpression, Path, PathKind, PathSegment, Pattern, - PrefixExpression, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, WhileStatement, + MemberAccessExpression, MethodCallExpression, Path, PathSegment, Pattern, PrefixExpression, + UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, UnsafeExpression, WhileStatement, }; use crate::ast::{ConstrainExpression, Expression, Statement, StatementKind}; use crate::hir_def::expr::{ @@ -15,6 +16,7 @@ use crate::hir_def::expr::{ use crate::hir_def::stmt::{HirLValue, HirPattern, HirStatement}; use crate::hir_def::types::{Type, TypeBinding}; use crate::node_interner::{DefinitionId, ExprId, NodeInterner, StmtId}; +use crate::signed_field::SignedField; // TODO: // - Full path for idents & types @@ -24,7 +26,7 @@ use crate::node_interner::{DefinitionId, ExprId, NodeInterner, StmtId}; // - Type::TypeVariable has no equivalent in the Ast impl HirStatement { - pub fn to_display_ast(&self, interner: &NodeInterner, span: Span) -> Statement { + pub fn to_display_ast(&self, interner: &NodeInterner, location: Location) -> Statement { let kind = match self { HirStatement::Let(let_stmt) => { let pattern = let_stmt.pattern.to_display_ast(interner); @@ -43,13 +45,15 @@ impl HirStatement { for_stmt.end_range.to_display_ast(interner), ), block: for_stmt.block.to_display_ast(interner), - span, + location, }), - HirStatement::Loop(block) => StatementKind::Loop(block.to_display_ast(interner), span), + HirStatement::Loop(block) => { + StatementKind::Loop(block.to_display_ast(interner), location) + } HirStatement::While(condition, block) => StatementKind::While(WhileStatement { condition: condition.to_display_ast(interner), body: block.to_display_ast(interner), - while_keyword_span: span, + while_keyword_location: location, }), HirStatement::Break => StatementKind::Break, HirStatement::Continue => StatementKind::Continue, @@ -63,7 +67,7 @@ impl HirStatement { } }; - Statement { kind, span } + Statement { kind, location } } } @@ -71,32 +75,32 @@ impl StmtId { /// Convert to AST for display (some details lost) pub fn to_display_ast(self, interner: &NodeInterner) -> Statement { let statement = interner.statement(&self); - let span = interner.statement_span(self); + let location = interner.statement_location(self); - statement.to_display_ast(interner, span) + statement.to_display_ast(interner, location) } } impl HirExpression { /// Convert to AST for display (some details lost) - pub fn to_display_ast(&self, interner: &NodeInterner, span: Span) -> Expression { + pub fn to_display_ast(&self, interner: &NodeInterner, location: Location) -> Expression { let kind = match self { HirExpression::Ident(ident, generics) => { - ident.to_display_expr(interner, generics, span) + ident.to_display_expr(interner, generics, location) } HirExpression::Literal(HirLiteral::Array(array)) => { - let array = array.to_display_ast(interner, span); + let array = array.to_display_ast(interner, location); ExpressionKind::Literal(Literal::Array(array)) } HirExpression::Literal(HirLiteral::Slice(array)) => { - let array = array.to_display_ast(interner, span); + let array = array.to_display_ast(interner, location); ExpressionKind::Literal(Literal::Slice(array)) } HirExpression::Literal(HirLiteral::Bool(value)) => { ExpressionKind::Literal(Literal::Bool(*value)) } - HirExpression::Literal(HirLiteral::Integer(value, sign)) => { - ExpressionKind::Literal(Literal::Integer(*value, *sign)) + HirExpression::Literal(HirLiteral::Integer(value)) => { + ExpressionKind::Literal(Literal::Integer(*value)) } HirExpression::Literal(HirLiteral::Str(string)) => { ExpressionKind::Literal(Literal::Str(string.clone())) @@ -113,7 +117,7 @@ impl HirExpression { })), HirExpression::Infix(infix) => ExpressionKind::Infix(Box::new(InfixExpression { lhs: infix.lhs.to_display_ast(interner), - operator: Spanned::from(infix.operator.location.span, infix.operator.kind), + operator: Located::from(infix.operator.location, infix.operator.kind), rhs: infix.rhs.to_display_ast(interner), })), HirExpression::Index(index) => ExpressionKind::Index(Box::new(IndexExpression { @@ -122,16 +126,14 @@ impl HirExpression { })), HirExpression::Constructor(constructor) => { let type_name = constructor.r#type.borrow().name.to_string(); - let type_name = Path::from_single(type_name, span); + let type_name = Path::from_single(type_name, location); let fields = vecmap(constructor.fields.clone(), |(name, expr): (Ident, ExprId)| { (name, expr.to_display_ast(interner)) }); - let struct_type = None; ExpressionKind::Constructor(Box::new(ConstructorExpression { typ: UnresolvedType::from_path(type_name), fields, - struct_type, })) } HirExpression::MemberAccess(access) => { @@ -170,7 +172,7 @@ impl HirExpression { ExpressionKind::Constrain(ConstrainExpression { kind: ConstrainKind::Assert, arguments, - span, + location, }) } HirExpression::Cast(cast) => { @@ -183,7 +185,7 @@ impl HirExpression { consequence: if_expr.consequence.to_display_ast(interner), alternative: if_expr.alternative.map(|expr| expr.to_display_ast(interner)), })), - HirExpression::Match(match_expr) => match_expr.to_display_ast(interner, span), + HirExpression::Match(match_expr) => match_expr.to_display_ast(interner, location), HirExpression::Tuple(fields) => { ExpressionKind::Tuple(vecmap(fields, |field| field.to_display_ast(interner))) } @@ -197,11 +199,12 @@ impl HirExpression { } HirExpression::Error => ExpressionKind::Error, HirExpression::Comptime(block) => { - ExpressionKind::Comptime(block.to_display_ast(interner), span) - } - HirExpression::Unsafe(block) => { - ExpressionKind::Unsafe(block.to_display_ast(interner), span) + ExpressionKind::Comptime(block.to_display_ast(interner), location) } + HirExpression::Unsafe(block) => ExpressionKind::Unsafe(UnsafeExpression { + block: block.to_display_ast(interner), + unsafe_keyword_location: location, + }), HirExpression::Quote(block) => ExpressionKind::Quote(block.clone()), // A macro was evaluated here: return the quoted result @@ -211,22 +214,23 @@ impl HirExpression { HirExpression::EnumConstructor(constructor) => { let typ = constructor.r#type.borrow(); let variant = &typ.variant_at(constructor.variant_index); - let segment1 = PathSegment { ident: typ.name.clone(), span, generics: None }; - let segment2 = PathSegment { ident: variant.name.clone(), span, generics: None }; - let path = Path { segments: vec![segment1, segment2], kind: PathKind::Plain, span }; - let func = Box::new(Expression::new(ExpressionKind::Variable(path), span)); + let segment1 = PathSegment { ident: typ.name.clone(), location, generics: None }; + let segment2 = + PathSegment { ident: variant.name.clone(), location, generics: None }; + let path = Path::plain(vec![segment1, segment2], location); + let func = Box::new(Expression::new(ExpressionKind::Variable(path), location)); let arguments = vecmap(&constructor.arguments, |arg| arg.to_display_ast(interner)); let call = CallExpression { func, arguments, is_macro_call: false }; ExpressionKind::Call(Box::new(call)) } }; - Expression::new(kind, span) + Expression::new(kind, location) } } impl HirMatch { - fn to_display_ast(&self, interner: &NodeInterner, span: Span) -> ExpressionKind { + fn to_display_ast(&self, interner: &NodeInterner, location: Location) -> ExpressionKind { match self { HirMatch::Success(expr) => expr.to_display_ast(interner).kind, HirMatch::Failure => ExpressionKind::Error, @@ -234,28 +238,29 @@ impl HirMatch { let condition = cond.to_display_ast(interner); let consequence = body.to_display_ast(interner); let alternative = - Some(Expression::new(otherwise.to_display_ast(interner, span), span)); + Some(Expression::new(otherwise.to_display_ast(interner, location), location)); ExpressionKind::If(Box::new(IfExpression { condition, consequence, alternative })) } HirMatch::Switch(variable, cases, default) => { let location = interner.definition(*variable).location; let ident = HirIdent::non_trait_method(*variable, location); - let expression = ident.to_display_expr(interner, &None, location.span); - let expression = Expression::new(expression, location.span); + let expression = ident.to_display_expr(interner, &None, location); + let expression = Expression::new(expression, location); let mut rules = vecmap(cases, |case| { let args = vecmap(&case.arguments, |arg| arg.to_display_ast(interner)); let constructor = case.constructor.to_display_ast(args); - let constructor = Expression::new(constructor, span); - let branch = case.body.to_display_ast(interner, span); - (constructor, Expression::new(branch, span)) + let constructor = Expression::new(constructor, location); + let branch = case.body.to_display_ast(interner, location); + (constructor, Expression::new(branch, location)) }); if let Some(case) = default { - let kind = ExpressionKind::Variable(Path::from_single("_".to_string(), span)); - let pattern = Expression::new(kind, span); - let branch = Expression::new(case.to_display_ast(interner, span), span); + let kind = + ExpressionKind::Variable(Path::from_single("_".to_string(), location)); + let pattern = Expression::new(kind, location); + let branch = Expression::new(case.to_display_ast(interner, location), location); rules.push((pattern, branch)); } @@ -268,12 +273,9 @@ impl HirMatch { impl DefinitionId { fn to_display_ast(self, interner: &NodeInterner) -> Expression { let location = interner.definition(self).location; - let kind = HirIdent::non_trait_method(self, location).to_display_expr( - interner, - &None, - location.span, - ); - Expression::new(kind, location.span) + let kind = + HirIdent::non_trait_method(self, location).to_display_expr(interner, &None, location); + Expression::new(kind, location) } } @@ -283,9 +285,7 @@ impl Constructor { Constructor::True => ExpressionKind::Literal(Literal::Bool(true)), Constructor::False => ExpressionKind::Literal(Literal::Bool(false)), Constructor::Unit => ExpressionKind::Literal(Literal::Unit), - Constructor::Int(value) => { - ExpressionKind::Literal(Literal::Integer(value.field, value.is_negative)) - } + Constructor::Int(value) => ExpressionKind::Literal(Literal::Integer(*value)), Constructor::Tuple(_) => ExpressionKind::Tuple(arguments), Constructor::Variant(typ, index) => { let typ = typ.follow_bindings_shallow(); @@ -301,9 +301,9 @@ impl Constructor { return ExpressionKind::Error; }; - let span = name.span(); + let location = name.location(); let name = ExpressionKind::Variable(Path::from_ident(name)); - let func = Box::new(Expression::new(name, span)); + let func = Box::new(Expression::new(name, location)); let is_macro_call = false; ExpressionKind::Call(Box::new(CallExpression { func, arguments, is_macro_call })) } @@ -319,8 +319,8 @@ impl ExprId { pub fn to_display_ast(self, interner: &NodeInterner) -> Expression { let expression = interner.expression(&self); // TODO: empty 0 span - let span = interner.try_expr_span(&self).unwrap_or_else(|| Span::empty(0)); - expression.to_display_ast(interner, span) + let location = interner.try_id_location(self).unwrap_or_else(Location::dummy); + expression.to_display_ast(interner, location) } } @@ -331,11 +331,11 @@ impl HirPattern { HirPattern::Identifier(ident) => Pattern::Identifier(ident.to_display_ast(interner)), HirPattern::Mutable(pattern, location) => { let pattern = Box::new(pattern.to_display_ast(interner)); - Pattern::Mutable(pattern, location.span, false) + Pattern::Mutable(pattern, *location, false) } HirPattern::Tuple(patterns, location) => { let patterns = vecmap(patterns, |pattern| pattern.to_display_ast(interner)); - Pattern::Tuple(patterns, location.span) + Pattern::Tuple(patterns, *location) } HirPattern::Struct(typ, patterns, location) => { let patterns = vecmap(patterns, |(name, pattern)| { @@ -352,8 +352,8 @@ impl HirPattern { other => other.to_string(), }; // The name span is lost here - let path = Path::from_single(name, location.span); - Pattern::Struct(path, patterns, location.span) + let path = Path::from_single(name, *location); + Pattern::Struct(path, patterns, *location) } } } @@ -363,14 +363,14 @@ impl HirIdent { /// Convert to AST for display (some details lost) fn to_display_ast(&self, interner: &NodeInterner) -> Ident { let name = interner.definition_name(self.id).to_owned(); - Ident(Spanned::from(self.location.span, name)) + Ident(Located::from(self.location, name)) } fn to_display_expr( &self, interner: &NodeInterner, generics: &Option>, - span: Span, + location: Location, ) -> ExpressionKind { let ident = self.to_display_ast(interner); let segment = PathSegment { @@ -378,10 +378,10 @@ impl HirIdent { generics: generics .as_ref() .map(|option| option.iter().map(|generic| generic.to_display_ast()).collect()), - span, + location, }; - let path = Path { segments: vec![segment], kind: crate::ast::PathKind::Plain, span }; + let path = Path::plain(vec![segment], location); ExpressionKind::Variable(path) } @@ -439,7 +439,8 @@ impl Type { TypeBinding::Bound(typ) => return typ.to_display_ast(), TypeBinding::Unbound(id, type_var_kind) => { let name = format!("var_{:?}_{}", type_var_kind, id); - let path = Path::from_single(name, Span::empty(0)); + let path = + Path::from_single(name, Location::new(Span::empty(0), FileId::dummy())); let expression = UnresolvedTypeExpression::Variable(path); UnresolvedTypeData::Expression(expression) } @@ -450,11 +451,11 @@ impl Type { (named_type.name.clone(), named_type.typ.to_display_ast()) }); let generics = GenericTypeArgs { ordered_args, named_args, kinds: Vec::new() }; - let name = Path::from_single(name.as_ref().clone(), Span::default()); + let name = Path::from_single(name.as_ref().clone(), Location::dummy()); UnresolvedTypeData::TraitAsType(name, generics) } Type::NamedGeneric(_var, name) => { - let name = Path::from_single(name.as_ref().clone(), Span::default()); + let name = Path::from_single(name.as_ref().clone(), Location::dummy()); UnresolvedTypeData::Named(name, GenericTypeArgs::default(), true) } Type::CheckedCast { to, .. } => to.to_display_ast().typ, @@ -479,23 +480,23 @@ impl Type { Type::InfixExpr(lhs, op, rhs, _) => { let lhs = Box::new(lhs.to_type_expression()); let rhs = Box::new(rhs.to_type_expression()); - let span = Span::default(); - let expr = UnresolvedTypeExpression::BinaryOperation(lhs, *op, rhs, span); + let location = Location::dummy(); + let expr = UnresolvedTypeExpression::BinaryOperation(lhs, *op, rhs, location); UnresolvedTypeData::Expression(expr) } }; - UnresolvedType { typ, span: Span::default() } + UnresolvedType { typ, location: Location::dummy() } } /// Convert to AST for display (some details lost) fn to_type_expression(&self) -> UnresolvedTypeExpression { - let span = Span::default(); + let location = Location::dummy(); match self.follow_bindings() { - Type::Constant(length, _kind) => UnresolvedTypeExpression::Constant(length, span), + Type::Constant(length, _kind) => UnresolvedTypeExpression::Constant(length, location), Type::NamedGeneric(_var, name) => { - let path = Path::from_single(name.as_ref().clone(), span); + let path = Path::from_single(name.as_ref().clone(), location); UnresolvedTypeExpression::Variable(path) } // TODO: This should be turned into a proper error. @@ -511,16 +512,16 @@ impl HirLValue { HirLValue::Ident(ident, _) => LValue::Ident(ident.to_display_ast(interner)), HirLValue::MemberAccess { object, field_name, field_index: _, typ: _, location } => { let object = Box::new(object.to_display_ast(interner)); - LValue::MemberAccess { object, field_name: field_name.clone(), span: location.span } + LValue::MemberAccess { object, field_name: field_name.clone(), location: *location } } HirLValue::Index { array, index, typ: _, location } => { let array = Box::new(array.to_display_ast(interner)); let index = index.to_display_ast(interner); - LValue::Index { array, index, span: location.span } + LValue::Index { array, index, location: *location } } HirLValue::Dereference { lvalue, element_type: _, location } => { let lvalue = Box::new(lvalue.to_display_ast(interner)); - LValue::Dereference(lvalue, location.span) + LValue::Dereference(lvalue, *location) } } } @@ -528,7 +529,7 @@ impl HirLValue { impl HirArrayLiteral { /// Convert to AST for display (some details lost) - fn to_display_ast(&self, interner: &NodeInterner, span: Span) -> ArrayLiteral { + fn to_display_ast(&self, interner: &NodeInterner, location: Location) -> ArrayLiteral { match self { HirArrayLiteral::Standard(elements) => { ArrayLiteral::Standard(vecmap(elements, |element| element.to_display_ast(interner))) @@ -537,11 +538,13 @@ impl HirArrayLiteral { let repeated_element = Box::new(repeated_element.to_display_ast(interner)); let length = match length { Type::Constant(length, _kind) => { - let literal = Literal::Integer(*length, false); + let literal = Literal::Integer(SignedField::positive(*length)); let expr_kind = ExpressionKind::Literal(literal); - Box::new(Expression::new(expr_kind, span)) + Box::new(Expression::new(expr_kind, location)) } - other => panic!("Cannot convert non-constant type for repeated array literal from Hir -> Ast: {other:?}"), + other => panic!( + "Cannot convert non-constant type for repeated array literal from Hir -> Ast: {other:?}" + ), }; ArrayLiteral::Repeated { repeated_element, length } } 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 279d176ad33b..5c87e70949a1 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -2,13 +2,13 @@ use std::collections::VecDeque; use std::{collections::hash_map::Entry, rc::Rc}; use acvm::blackbox_solver::BigIntSolverWithId; -use acvm::{acir::AcirField, FieldElement}; -use fm::FileId; +use acvm::{FieldElement, acir::AcirField}; use im::Vector; use iter_extended::try_vecmap; use noirc_errors::Location; use rustc_hash::FxHashMap as HashMap; +use crate::TypeVariable; use crate::ast::{BinaryOpKind, FunctionKind, IntegerBitSize, Signedness, UnaryOp}; use crate::elaborator::Elaborator; use crate::graph::CrateId; @@ -21,9 +21,10 @@ use crate::monomorphization::{ undo_instantiation_bindings, }; use crate::node_interner::GlobalValue; +use crate::signed_field::SignedField; use crate::token::{FmtStrFragment, Tokens}; -use crate::TypeVariable; use crate::{ + Shared, Type, TypeBinding, TypeBindings, hir_def::{ expr::{ HirArrayLiteral, HirBlockExpression, HirCallExpression, HirCastExpression, @@ -38,11 +39,10 @@ use crate::{ types::Kind, }, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, NodeInterner, StmtId}, - Shared, Type, TypeBinding, TypeBindings, }; use super::errors::{IResult, InterpreterError}; -use super::value::{unwrap_rc, Closure, Value}; +use super::value::{Closure, Value, unwrap_rc}; mod builtin; mod foreign; @@ -67,9 +67,6 @@ pub struct Interpreter<'local, 'interner> { /// Stateful bigint calculator. bigint_solver: BigIntSolverWithId, - - /// Use pedantic ACVM solving - pedantic_solving: bool, } #[allow(unused)] @@ -78,8 +75,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { elaborator: &'local mut Elaborator<'interner>, crate_id: CrateId, current_function: Option, - pedantic_solving: bool, ) -> Self { + let pedantic_solving = elaborator.pedantic_solving(); let bigint_solver = BigIntSolverWithId::with_pedantic_solving(pedantic_solving); Self { elaborator, @@ -88,7 +85,6 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { bound_generics: Vec::new(), in_loop: false, bigint_solver, - pedantic_solving, } } @@ -222,11 +218,10 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { fn elaborate_in_module( &mut self, module: ModuleId, - file: FileId, f: impl FnOnce(&mut Elaborator) -> T, ) -> T { self.unbind_generics_from_previous_function(); - let result = self.elaborator.elaborate_item_from_comptime_in_module(module, file, f); + let result = self.elaborator.elaborate_item_from_comptime_in_module(module, f); self.rebind_generics_from_previous_function(); result } @@ -623,9 +618,12 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Err(InterpreterError::NonIntegerArrayLength { typ, err: None, location }) } TypeBinding::Bound(binding) => { - let span = self.elaborator.interner.id_location(id).span; + let location = self.elaborator.interner.id_location(id); binding - .evaluate_to_field_element(&Kind::Numeric(numeric_typ.clone()), span) + .evaluate_to_field_element( + &Kind::Numeric(numeric_typ.clone()), + location, + ) .map_err(|err| { let typ = Type::TypeVariable(type_variable.clone()); let err = Some(Box::new(err)); @@ -635,7 +633,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } }?; - self.evaluate_integer(value, false, id) + self.evaluate_integer(value.into(), id) } } } @@ -644,9 +642,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { match literal { HirLiteral::Unit => Ok(Value::Unit), HirLiteral::Bool(value) => Ok(Value::Bool(value)), - HirLiteral::Integer(value, is_negative) => { - self.evaluate_integer(value, is_negative, id) - } + HirLiteral::Integer(value) => self.evaluate_integer(value, id), HirLiteral::Str(string) => Ok(Value::String(Rc::new(string))), HirLiteral::FmtStr(fragments, captures, _length) => { self.evaluate_format_string(fragments, captures, id) @@ -674,7 +670,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { FmtStrFragment::String(string) => { result.push_str(&string); } - FmtStrFragment::Interpolation(_, span) => { + FmtStrFragment::Interpolation(..) => { if let Some(value) = values.pop_front() { // When interpolating a quoted value inside a format string, we don't include the // surrounding `quote {` ... `}` as if we are unquoting the quoted value inside the string. @@ -683,8 +679,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { if index > 0 { result.push(' '); } - result - .push_str(&token.display(self.elaborator.interner).to_string()); + result.push_str( + &token.token().display(self.elaborator.interner).to_string(), + ); } } else { result.push_str(&value.display(self.elaborator.interner).to_string()); @@ -705,103 +702,85 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Ok(Value::FormatString(Rc::new(result), typ)) } - fn evaluate_integer( - &self, - value: FieldElement, - is_negative: bool, - id: ExprId, - ) -> IResult { + fn evaluate_integer(&self, value: SignedField, id: ExprId) -> IResult { let typ = self.elaborator.interner.id_type(id).follow_bindings(); let location = self.elaborator.interner.expr_location(&id); if let Type::FieldElement = &typ { - let value = if is_negative { -value } else { value }; - Ok(Value::Field(value)) + Ok(Value::Field(value.into())) } else if let Type::Integer(sign, bit_size) = &typ { match (sign, bit_size) { (Signedness::Unsigned, IntegerBitSize::One) => { return Err(InterpreterError::TypeUnsupported { typ, location }); } (Signedness::Unsigned, IntegerBitSize::Eight) => { - let value: u8 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( - InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; - let value = if is_negative { 0u8.wrapping_sub(value) } else { value }; + let value = value.try_to_unsigned().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::U8(value)) } (Signedness::Unsigned, IntegerBitSize::Sixteen) => { - let value: u16 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( - InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; - let value = if is_negative { 0u16.wrapping_sub(value) } else { value }; + let value = value.try_to_unsigned().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::U16(value)) } (Signedness::Unsigned, IntegerBitSize::ThirtyTwo) => { - let value: u32 = - value.try_to_u32().ok_or(InterpreterError::IntegerOutOfRangeForType { - value, - typ, - location, - })?; - let value = if is_negative { 0u32.wrapping_sub(value) } else { value }; + let value = value.try_to_unsigned().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::U32(value)) } (Signedness::Unsigned, IntegerBitSize::SixtyFour) => { - let value: u64 = - value.try_to_u64().ok_or(InterpreterError::IntegerOutOfRangeForType { - value, - typ, - location, - })?; - let value = if is_negative { 0u64.wrapping_sub(value) } else { value }; + let value = value.try_to_unsigned().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::U64(value)) } + (Signedness::Unsigned, IntegerBitSize::HundredTwentyEight) => { + let value: u128 = value.try_to_unsigned().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; + Ok(Value::U128(value)) + } (Signedness::Signed, IntegerBitSize::One) => { return Err(InterpreterError::TypeUnsupported { typ, location }); } (Signedness::Signed, IntegerBitSize::Eight) => { - let value: i8 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( - InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; - let value = if is_negative { -value } else { value }; + let value = value.try_to_signed().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::I8(value)) } (Signedness::Signed, IntegerBitSize::Sixteen) => { - let value: i16 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( - InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; - let value = if is_negative { -value } else { value }; + let value = value.try_to_signed().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::I16(value)) } (Signedness::Signed, IntegerBitSize::ThirtyTwo) => { - let value: i32 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( - InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; - let value = if is_negative { -value } else { value }; + let value = value.try_to_signed().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::I32(value)) } (Signedness::Signed, IntegerBitSize::SixtyFour) => { - let value: i64 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( - InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; - let value = if is_negative { -value } else { value }; + let value = value.try_to_signed().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::I64(value)) } + (Signedness::Signed, IntegerBitSize::HundredTwentyEight) => { + return Err(InterpreterError::TypeUnsupported { typ, location }); + } } } else if let Type::TypeVariable(variable) = &typ { if variable.is_integer_or_field() { - Ok(Value::Field(value)) + Ok(Value::Field(value.into())) } else if variable.is_integer() { - let value: u64 = value - .try_to_u64() + let value = value + .try_to_unsigned() .ok_or(InterpreterError::IntegerOutOfRangeForType { value, typ, location })?; - let value = if is_negative { 0u64.wrapping_sub(value) } else { value }; Ok(Value::U64(value)) } else { Err(InterpreterError::NonIntegerIntegerLiteral { typ, location }) @@ -844,8 +823,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { HirArrayLiteral::Repeated { repeated_element, length } => { let element = self.evaluate(repeated_element)?; - let span = self.elaborator.interner.id_location(id).span; - match length.evaluate_to_u32(span) { + let location = self.elaborator.interner.id_location(id); + match length.evaluate_to_u32(location) { Ok(length) => { let elements = (0..length).map(|_| element.clone()).collect(); Ok(Value::Array(elements, typ)) @@ -1248,6 +1227,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Value::Field(value) => { value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else(|| { let typ = Type::default_int_type(); + let value = SignedField::positive(value); InterpreterError::IntegerOutOfRangeForType { value, typ, location } })? } @@ -1377,11 +1357,11 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } fn unify_without_binding(&mut self, actual: &Type, expected: &Type, location: Location) { - self.elaborator.unify_without_applying_bindings(actual, expected, location.file, || { + self.elaborator.unify_without_applying_bindings(actual, expected, || { TypeCheckError::TypeMismatch { expected_typ: expected.to_string(), expr_typ: actual.to_string(), - expr_span: location.span, + expr_location: location, } }); } @@ -1399,10 +1379,11 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let typ = object.get_type().follow_bindings(); let method_name = &call.method.0.contents; + let check_self_param = true; let method = self .elaborator - .lookup_method(&typ, method_name, location.span, true) + .lookup_method(&typ, method_name, location, check_self_param) .and_then(|method| method.func_id(self.elaborator.interner)); if let Some(method) = method { @@ -1495,6 +1476,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Signedness::Unsigned, IntegerBitSize::SixtyFour) => { cast_to_int!(lhs, to_u128, u64, U64) } + (Signedness::Unsigned, IntegerBitSize::HundredTwentyEight) => { + cast_to_int!(lhs, to_u128, u128, U128) + } (Signedness::Signed, IntegerBitSize::One) => { Err(InterpreterError::TypeUnsupported { typ: typ.clone(), location }) } @@ -1508,6 +1492,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Signedness::Signed, IntegerBitSize::SixtyFour) => { cast_to_int!(lhs, to_i128, i64, I64) } + (Signedness::Signed, IntegerBitSize::HundredTwentyEight) => { + todo!() + } }, Type::Bool => Ok(Value::Bool(!lhs.is_zero() || lhs_is_negative)), typ => Err(InterpreterError::CastToNonNumericType { typ, location }), 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 ff46592f9ed1..34a5535f63c2 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 @@ -12,23 +12,24 @@ use builtin_helpers::{ }; use im::Vector; use iter_extended::{try_vecmap, vecmap}; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use num_bigint::BigUint; use rustc_hash::FxHashMap as HashMap; use crate::{ + Kind, QuotedType, ResolvedGeneric, Shared, Type, TypeVariable, ast::{ ArrayLiteral, BlockExpression, ConstrainKind, Expression, ExpressionKind, ForRange, FunctionKind, FunctionReturnType, Ident, IntegerBitSize, ItemVisibility, LValue, Literal, Pattern, Signedness, Statement, StatementKind, UnaryOp, UnresolvedType, UnresolvedTypeData, - Visibility, + UnsafeExpression, Visibility, }, - elaborator::Elaborator, + elaborator::{ElaborateReason, Elaborator}, hir::{ comptime::{ + InterpreterError, Value, errors::IResult, value::{ExprValue, TypedExpr}, - InterpreterError, Value, }, def_collector::dc_crate::CollectedItems, def_map::ModuleDefId, @@ -40,8 +41,7 @@ use crate::{ }, node_interner::{DefinitionKind, NodeInterner, TraitImplKind}, parser::{Parser, StatementOrExpressionOrLValue}, - token::{Attribute, Token}, - Kind, QuotedType, ResolvedGeneric, Shared, Type, TypeVariable, + token::{Attribute, LocatedToken, Token}, }; use self::builtin_helpers::{eq_item, get_array, get_ctstring, get_str, get_u8, hash_item, lex}; @@ -49,7 +49,7 @@ use super::Interpreter; pub(crate) mod builtin_helpers; -impl<'local, 'context> Interpreter<'local, 'context> { +impl Interpreter<'_, '_> { pub(super) fn call_builtin( &mut self, name: &str, @@ -247,7 +247,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "unresolved_type_is_bool" => unresolved_type_is_bool(interner, arguments, location), "unresolved_type_is_field" => unresolved_type_is_field(interner, arguments, location), "unresolved_type_is_unit" => unresolved_type_is_unit(interner, arguments, location), - "zeroed" => Ok(zeroed(return_type, location.span)), + "zeroed" => Ok(zeroed(return_type, location)), _ => { let item = format!("Comptime evaluation for builtin function '{name}'"); Err(InterpreterError::Unimplemented { item, location }) @@ -385,7 +385,7 @@ fn struct_def_add_attribute( let attribute_location = attribute.1; let attribute = get_str(interner, attribute)?; let attribute = format!("#[{}]", attribute); - let mut parser = Parser::for_str(&attribute); + let mut parser = Parser::for_str(&attribute, attribute_location.file); let Some((Attribute::Secondary(attribute), _span)) = parser.parse_attribute() else { return Err(InterpreterError::InvalidAttribute { attribute: attribute.to_string(), @@ -411,7 +411,7 @@ fn struct_def_add_generic( let generic_location = generic.1; let generic = get_str(interner, generic)?; - let mut tokens = lex(&generic); + let mut tokens = lex(&generic, location); if tokens.len() != 1 { return Err(InterpreterError::GenericNameShouldBeAnIdent { name: generic, @@ -419,7 +419,7 @@ fn struct_def_add_generic( }); } - let Token::Ident(generic_name) = tokens.remove(0) else { + let Token::Ident(generic_name) = tokens.remove(0).into_token() else { return Err(InterpreterError::GenericNameShouldBeAnIdent { name: generic, location: generic_location, @@ -436,7 +436,7 @@ fn struct_def_add_generic( return Err(InterpreterError::DuplicateGeneric { name, struct_name: the_struct.name.to_string(), - existing_location: Location::new(generic.span, the_struct.location.file), + existing_location: generic.location, duplicate_location: generic_location, }); } @@ -444,9 +444,8 @@ fn struct_def_add_generic( let type_var_kind = Kind::Normal; let type_var = TypeVariable::unbound(interner.next_type_variable_id(), type_var_kind); - let span = generic_location.span; let typ = Type::NamedGeneric(type_var.clone(), name.clone()); - let new_generic = ResolvedGeneric { name, type_var, span }; + let new_generic = ResolvedGeneric { name, type_var, location: generic_location }; the_struct.generics.push(new_generic); Ok(Value::Type(typ)) @@ -509,7 +508,7 @@ fn struct_def_generics( Kind::Numeric(numeric_type) => Some(Value::Type(*numeric_type)), _ => None, }; - let numeric_type = option(option_typ.clone(), numeric_type, location.span); + let numeric_type = option(option_typ.clone(), numeric_type, location); Value::Tuple(vec![Value::Type(generic_as_named), numeric_type]) }) .collect(); @@ -562,7 +561,10 @@ fn struct_def_fields( if actual != expected { let s = if expected == 1 { "" } else { "s" }; let was_were = if actual == 1 { "was" } else { "were" }; - let message = Some(format!("`StructDefinition::fields` expected {expected} generic{s} for `{}` but {actual} {was_were} given", struct_def.name)); + let message = Some(format!( + "`StructDefinition::fields` expected {expected} generic{s} for `{}` but {actual} {was_were} given", + struct_def.name + )); let location = args_location; let call_stack = call_stack.clone(); return Err(InterpreterError::FailingConstraint { message, location, call_stack }); @@ -572,7 +574,8 @@ fn struct_def_fields( if let Some(struct_fields) = struct_def.get_fields(&generic_args) { for (field_name, field_type) in struct_fields { - let name = Value::Quoted(Rc::new(vec![Token::Ident(field_name)])); + let token = LocatedToken::new(Token::Ident(field_name), location); + let name = Value::Quoted(Rc::new(vec![token])); fields.push_back(Value::Tuple(vec![name, Value::Type(field_type)])); } } @@ -602,7 +605,8 @@ fn struct_def_fields_as_written( if let Some(struct_fields) = struct_def.get_fields_as_written() { for field in struct_fields { - let name = Value::Quoted(Rc::new(vec![Token::Ident(field.name.to_string())])); + let token = LocatedToken::new(Token::Ident(field.name.to_string()), location); + let name = Value::Quoted(Rc::new(vec![token])); let typ = Value::Type(field.typ); fields.push_back(Value::Tuple(vec![name, typ])); } @@ -638,7 +642,8 @@ fn struct_def_name( let the_struct = interner.get_type(struct_id); let name = Token::Ident(the_struct.borrow().name.to_string()); - Ok(Value::Quoted(Rc::new(vec![name]))) + let token = LocatedToken::new(name, location); + Ok(Value::Quoted(Rc::new(vec![token]))) } /// fn set_fields(self, new_fields: [(Quoted, Type)]) {} @@ -669,11 +674,11 @@ fn struct_def_set_fields( let name_tokens = get_quoted((name_value.clone(), field_location))?; let typ = get_type((typ, field_location))?; - match name_tokens.first() { + match name_tokens.first().map(|t| t.token()) { Some(Token::Ident(name)) if name_tokens.len() == 1 => { Ok(hir_def::types::StructField { visibility: ItemVisibility::Public, - name: Ident::new(name.clone(), field_location.span), + name: Ident::new(name.clone(), field_location), typ, }) } @@ -812,7 +817,7 @@ fn quoted_as_expr( }, ); - Ok(option(return_type, value, location.span)) + Ok(option(return_type, value, location)) } // fn as_module(quoted: Quoted) -> Option @@ -835,7 +840,7 @@ fn quoted_as_module( module.map(Value::ModuleDefinition) }); - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn as_trait_constraint(quoted: Quoted) -> TraitConstraint @@ -953,11 +958,7 @@ fn to_le_radix( None => 0, }; // The only built-ins that use these either return `[u1; N]` or `[u8; N]` - if return_type_is_bits { - Value::U1(digit != 0) - } else { - Value::U8(digit) - } + if return_type_is_bits { Value::U1(digit != 0) } else { Value::U8(digit) } }); let result_type = Type::Array( @@ -1003,7 +1004,7 @@ fn type_as_constant( // Prefer to use `evaluate_to_u32` over matching on `Type::Constant` // since arithmetic generics may be `Type::InfixExpr`s which evaluate to // constants but are not actually the `Type::Constant` variant. - match typ.evaluate_to_u32(location.span) { + match typ.evaluate_to_u32(location) { Ok(constant) => Ok(Some(Value::U32(constant))), Err(err) => { // Evaluating to a non-constant returns 'None' in user code @@ -1040,11 +1041,7 @@ fn type_as_mutable_reference( location: Location, ) -> IResult { type_as(arguments, return_type, location, |typ| { - if let Type::MutableReference(typ) = typ { - Some(Value::Type(*typ)) - } else { - None - } + if let Type::MutableReference(typ) = typ { Some(Value::Type(*typ)) } else { None } }) } @@ -1055,11 +1052,7 @@ fn type_as_slice( location: Location, ) -> IResult { type_as(arguments, return_type, location, |typ| { - if let Type::Slice(slice_type) = typ { - Some(Value::Type(*slice_type)) - } else { - None - } + if let Type::Slice(slice_type) = typ { Some(Value::Type(*slice_type)) } else { None } }) } @@ -1070,11 +1063,7 @@ fn type_as_str( location: Location, ) -> IResult { type_as(arguments, return_type, location, |typ| { - if let Type::String(n) = typ { - Some(Value::Type(*n)) - } else { - None - } + if let Type::String(n) = typ { Some(Value::Type(*n)) } else { None } }) } @@ -1147,7 +1136,7 @@ where let option_value = f(typ)?; - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn type_eq(_first: Type, _second: Type) -> bool @@ -1182,7 +1171,7 @@ fn type_get_trait_impl( _ => None, }; - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn implements(self, constraint: TraitConstraint) -> bool @@ -1303,7 +1292,7 @@ fn typed_expr_as_function_definition( } else { None }; - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn get_type(self) -> Option @@ -1317,15 +1306,11 @@ fn typed_expr_get_type( let typed_expr = get_typed_expr(self_argument)?; let option_value = if let TypedExpr::ExprId(expr_id) = typed_expr { let typ = interner.id_type(expr_id); - if typ == Type::Error { - None - } else { - Some(Value::Type(typ)) - } + if typ == Type::Error { None } else { Some(Value::Type(typ)) } } else { None }; - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn as_mutable_reference(self) -> Option @@ -1408,16 +1393,16 @@ where let typ = get_unresolved_type(interner, value)?; let option_value = f(typ); - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn zeroed() -> T -fn zeroed(return_type: Type, span: Span) -> Value { +fn zeroed(return_type: Type, location: Location) -> Value { match return_type { Type::FieldElement => Value::Field(0u128.into()), Type::Array(length_type, elem) => { - if let Ok(length) = length_type.evaluate_to_u32(span) { - let element = zeroed(elem.as_ref().clone(), span); + if let Ok(length) = length_type.evaluate_to_u32(location) { + let element = zeroed(elem.as_ref().clone(), location); let array = std::iter::repeat(element).take(length as usize).collect(); Value::Array(array, Type::Array(length_type, elem)) } else { @@ -1432,15 +1417,17 @@ fn zeroed(return_type: Type, span: Span) -> Value { (Signedness::Unsigned, IntegerBitSize::Sixteen) => Value::U16(0), (Signedness::Unsigned, IntegerBitSize::ThirtyTwo) => Value::U32(0), (Signedness::Unsigned, IntegerBitSize::SixtyFour) => Value::U64(0), + (Signedness::Unsigned, IntegerBitSize::HundredTwentyEight) => Value::U128(0), (Signedness::Signed, IntegerBitSize::One) => Value::I8(0), (Signedness::Signed, IntegerBitSize::Eight) => Value::I8(0), (Signedness::Signed, IntegerBitSize::Sixteen) => Value::I16(0), (Signedness::Signed, IntegerBitSize::ThirtyTwo) => Value::I32(0), (Signedness::Signed, IntegerBitSize::SixtyFour) => Value::I64(0), + (Signedness::Signed, IntegerBitSize::HundredTwentyEight) => todo!(), }, Type::Bool => Value::Bool(false), Type::String(length_type) => { - if let Ok(length) = length_type.evaluate_to_u32(span) { + if let Ok(length) = length_type.evaluate_to_u32(location) { Value::String(Rc::new("\0".repeat(length as usize))) } else { // Assume we can resolve the length later @@ -1448,7 +1435,7 @@ fn zeroed(return_type: Type, span: Span) -> Value { } } Type::FmtString(length_type, captures) => { - let length = length_type.evaluate_to_u32(span); + let length = length_type.evaluate_to_u32(location); let typ = Type::FmtString(length_type, captures); if let Ok(length) = length { Value::FormatString(Rc::new("\0".repeat(length as usize)), typ) @@ -1458,7 +1445,7 @@ fn zeroed(return_type: Type, span: Span) -> Value { } } Type::Unit => Value::Unit, - Type::Tuple(fields) => Value::Tuple(vecmap(fields, |field| zeroed(field, span))), + Type::Tuple(fields) => Value::Tuple(vecmap(fields, |field| zeroed(field, location))), Type::DataType(data_type, generics) => { let typ = data_type.borrow(); @@ -1466,7 +1453,7 @@ fn zeroed(return_type: Type, span: Span) -> Value { let mut values = HashMap::default(); for (field_name, field_type) in fields { - let field_value = zeroed(field_type, span); + let field_value = zeroed(field_type, location); values.insert(Rc::new(field_name), field_value); } @@ -1480,7 +1467,7 @@ fn zeroed(return_type: Type, span: Span) -> Value { if !variants.is_empty() { // is_empty & swap_remove let us avoid a .clone() we'd need if we did .get(0) let (_name, params) = variants.swap_remove(0); - args = vecmap(params, |param| zeroed(param, span)); + args = vecmap(params, |param| zeroed(param, location)); } drop(typ); @@ -1490,14 +1477,14 @@ fn zeroed(return_type: Type, span: Span) -> Value { Value::Zeroed(Type::DataType(data_type, generics)) } } - Type::Alias(alias, generics) => zeroed(alias.borrow().get_type(&generics), span), - Type::CheckedCast { to, .. } => zeroed(*to, span), + Type::Alias(alias, generics) => zeroed(alias.borrow().get_type(&generics), location), + Type::CheckedCast { to, .. } => zeroed(*to, location), typ @ Type::Function(..) => { // Using Value::Zeroed here is probably safer than using FuncId::dummy_id() or similar Value::Zeroed(typ) } Type::MutableReference(element) => { - let element = zeroed(*element, span); + let element = zeroed(*element, location); Value::Pointer(Shared::new(element), false) } // Optimistically assume we can resolve this type later or that the value is unused @@ -1561,7 +1548,7 @@ fn expr_as_assert( let option_type = tuple_types.pop().unwrap(); let message = message.map(|msg| Value::expression(msg.kind)); - let message = option(option_type, message, location.span); + let message = option(option_type, message, location); Some(Value::Tuple(vec![predicate, message])) } else { @@ -1607,7 +1594,7 @@ fn expr_as_assert_eq( let option_type = tuple_types.pop().unwrap(); let message = message.map(|message| Value::expression(message.kind)); - let message = option(option_type, message, location.span); + let message = option(option_type, message, location); Some(Value::Tuple(vec![lhs, rhs, message])) } else { @@ -1770,7 +1757,7 @@ fn expr_as_constructor( let typ = Value::UnresolvedType(constructor.typ.typ); let fields = constructor.fields.into_iter(); let fields = fields.map(|(name, value)| { - Value::Tuple(vec![quote_ident(&name), Value::expression(value.kind)]) + Value::Tuple(vec![quote_ident(&name, location), Value::expression(value.kind)]) }); let fields = fields.collect(); let fields_type = Type::Slice(Box::new(Type::Tuple(vec![ @@ -1783,7 +1770,7 @@ fn expr_as_constructor( None }; - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn as_for(self) -> Option<(Quoted, Expr, Expr)> @@ -1796,8 +1783,9 @@ fn expr_as_for( expr_as(interner, arguments, return_type, location, |expr| { if let ExprValue::Statement(StatementKind::For(for_statement)) = expr { if let ForRange::Array(array) = for_statement.range { - let identifier = - Value::Quoted(Rc::new(vec![Token::Ident(for_statement.identifier.0.contents)])); + let token = Token::Ident(for_statement.identifier.0.contents); + let token = LocatedToken::new(token, location); + let identifier = Value::Quoted(Rc::new(vec![token])); let array = Value::expression(array.kind); let body = Value::expression(for_statement.block.kind); Some(Value::Tuple(vec![identifier, array, body])) @@ -1821,8 +1809,9 @@ fn expr_as_for_range( if let ExprValue::Statement(StatementKind::For(for_statement)) = expr { if let ForRange::Range(bounds) = for_statement.range { let (from, to) = bounds.into_half_open(); - let identifier = - Value::Quoted(Rc::new(vec![Token::Ident(for_statement.identifier.0.contents)])); + let token = Token::Ident(for_statement.identifier.0.contents); + let token = LocatedToken::new(token, location); + let identifier = Value::Quoted(Rc::new(vec![token])); let from = Value::expression(from.kind); let to = Value::expression(to.kind); let body = Value::expression(for_statement.block.kind); @@ -1877,7 +1866,7 @@ fn expr_as_if( let alternative = option( alternative_option_type, if_expr.alternative.map(|e| Value::expression(e.kind)), - location.span, + location, ); Some(Value::Tuple(vec![ @@ -1918,14 +1907,12 @@ fn expr_as_integer( location: Location, ) -> IResult { expr_as(interner, arguments, return_type.clone(), location, |expr| match expr { - ExprValue::Expression(ExpressionKind::Literal(Literal::Integer(field, sign))) => { - Some(Value::Tuple(vec![Value::Field(field), Value::Bool(sign)])) + ExprValue::Expression(ExpressionKind::Literal(Literal::Integer(field))) => { + Some(Value::Tuple(vec![Value::Field(field.field), Value::Bool(field.is_negative)])) } ExprValue::Expression(ExpressionKind::Resolved(id)) => { - if let HirExpression::Literal(HirLiteral::Integer(field, sign)) = - interner.expression(&id) - { - Some(Value::Tuple(vec![Value::Field(field), Value::Bool(sign)])) + if let HirExpression::Literal(HirLiteral::Integer(field)) = interner.expression(&id) { + Some(Value::Tuple(vec![Value::Field(field.field), Value::Bool(field.is_negative)])) } else { None } @@ -1966,7 +1953,7 @@ fn expr_as_lambda( } else { Some(Value::UnresolvedType(typ.typ)) }; - let typ = option(option_unresolved_type.clone(), typ, location.span); + let typ = option(option_unresolved_type.clone(), typ, location); Value::Tuple(vec![pattern, typ]) }) .collect(); @@ -1985,7 +1972,7 @@ fn expr_as_lambda( Some(return_type) }; let return_type = return_type.map(Value::UnresolvedType); - let return_type = option(option_unresolved_type, return_type, location.span); + let return_type = option(option_unresolved_type, return_type, location); let body = Value::expression(lambda.body.kind); @@ -2019,7 +2006,7 @@ fn expr_as_let( Some(Value::UnresolvedType(let_statement.r#type.typ)) }; - let typ = option(option_type, typ, location.span); + let typ = option(option_type, typ, location); Some(Value::Tuple(vec![ Value::pattern(let_statement.pattern), @@ -2042,11 +2029,11 @@ fn expr_as_member_access( ExprValue::Expression(ExpressionKind::MemberAccess(member_access)) => { Some(Value::Tuple(vec![ Value::expression(member_access.lhs.kind), - quote_ident(&member_access.rhs), + quote_ident(&member_access.rhs, location), ])) } - ExprValue::LValue(crate::ast::LValue::MemberAccess { object, field_name, span: _ }) => { - Some(Value::Tuple(vec![Value::lvalue(*object), quote_ident(&field_name)])) + ExprValue::LValue(crate::ast::LValue::MemberAccess { object, field_name, location: _ }) => { + Some(Value::Tuple(vec![Value::lvalue(*object), quote_ident(&field_name, location)])) } _ => None, }) @@ -2063,7 +2050,7 @@ fn expr_as_method_call( if let ExprValue::Expression(ExpressionKind::MethodCall(method_call)) = expr { let object = Value::expression(method_call.object.kind); - let name = quote_ident(&method_call.method_name); + let name = quote_ident(&method_call.method_name, location); let generics = method_call.generics.unwrap_or_default().into_iter(); let generics = generics.map(|generic| Value::UnresolvedType(generic.typ)).collect(); @@ -2214,8 +2201,9 @@ fn expr_as_unsafe( location: Location, ) -> IResult { expr_as(interner, arguments, return_type, location, |expr| { - if let ExprValue::Expression(ExpressionKind::Unsafe(block_expr, _)) = expr { - Some(block_expression_to_value(block_expr)) + if let ExprValue::Expression(ExpressionKind::Unsafe(UnsafeExpression { block, .. })) = expr + { + Some(block_expression_to_value(block)) } else { None } @@ -2271,7 +2259,7 @@ where let expr_value = unwrap_expr_value(interner, expr_value); let option_value = f(expr_value); - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn resolve(self, in_function: Option) -> TypedExpr @@ -2306,12 +2294,12 @@ fn expr_resolve( interpreter.elaborate_in_function(function_to_resolve_in, |elaborator| match expr_value { ExprValue::Expression(expression_kind) => { - let expr = Expression { kind: expression_kind, span: self_argument_location.span }; + let expr = Expression { kind: expression_kind, location: self_argument_location }; let (expr_id, _) = elaborator.elaborate_expression(expr); Ok(Value::TypedExpr(TypedExpr::ExprId(expr_id))) } ExprValue::Statement(statement_kind) => { - let statement = Statement { kind: statement_kind, span: self_argument_location.span }; + let statement = Statement { kind: statement_kind, location: self_argument_location }; let (stmt_id, _) = elaborator.elaborate_statement(statement); Ok(Value::TypedExpr(TypedExpr::StmtId(stmt_id))) } @@ -2380,7 +2368,7 @@ fn fmtstr_quoted_contents( ) -> IResult { let self_argument = check_one_argument(arguments, location)?; let (string, _) = get_format_string(interner, self_argument)?; - let tokens = lex(&string); + let tokens = lex(&string, location); Ok(Value::Quoted(Rc::new(tokens))) } @@ -2399,7 +2387,7 @@ fn function_def_add_attribute( let attribute_location = attribute.1; let attribute = get_str(interpreter.elaborator.interner, attribute)?; let attribute = format!("#[{}]", attribute); - let mut parser = Parser::for_str(&attribute); + let mut parser = Parser::for_str(&attribute, attribute_location.file); let Some((attribute, _span)) = parser.parse_attribute() else { return Err(InterpreterError::InvalidAttribute { attribute: attribute.to_string(), @@ -2437,7 +2425,7 @@ fn function_def_as_typed_expr( let generics = None; let hir_expr = HirExpression::Ident(hir_ident.clone(), generics.clone()); let expr_id = interpreter.elaborator.interner.push_expr(hir_expr); - interpreter.elaborator.interner.push_expr_location(expr_id, location.span, location.file); + interpreter.elaborator.interner.push_expr_location(expr_id, location); let typ = interpreter.elaborator.type_check_variable(hir_ident, expr_id, generics); interpreter.elaborator.interner.push_expr_type(expr_id, typ); Ok(Value::TypedExpr(TypedExpr::ExprId(expr_id))) @@ -2521,7 +2509,9 @@ fn function_def_name( let self_argument = check_one_argument(arguments, location)?; let func_id = get_function_def(self_argument)?; let name = interner.function_name(&func_id).to_string(); - let tokens = Rc::new(vec![Token::Ident(name)]); + let token = Token::Ident(name); + let token = LocatedToken::new(token, location); + let tokens = Rc::new(vec![token]); Ok(Value::Quoted(tokens)) } @@ -2539,7 +2529,9 @@ fn function_def_parameters( .parameters .iter() .map(|(hir_pattern, typ, _visibility)| { - let name = Value::Quoted(Rc::new(hir_pattern_to_tokens(interner, hir_pattern))); + let tokens = hir_pattern_to_tokens(interner, hir_pattern); + let tokens = vecmap(tokens, |token| LocatedToken::new(token, location)); + let name = Value::Quoted(Rc::new(tokens)); let typ = Value::Type(typ.clone()); Value::Tuple(vec![name, typ]) }) @@ -2580,10 +2572,9 @@ fn function_def_set_body( let body_argument = get_expr(interpreter.elaborator.interner, body_argument)?; let statement_kind = match body_argument { - ExprValue::Expression(expression_kind) => StatementKind::Expression(Expression { - kind: expression_kind, - span: body_location.span, - }), + ExprValue::Expression(expression_kind) => { + StatementKind::Expression(Expression { kind: expression_kind, location: body_location }) + } ExprValue::Statement(statement_kind) => statement_kind, ExprValue::LValue(lvalue) => StatementKind::Expression(lvalue.as_expression()), ExprValue::Pattern(pattern) => { @@ -2598,12 +2589,12 @@ fn function_def_set_body( } }; - let statement = Statement { kind: statement_kind, span: body_location.span }; + let statement = Statement { kind: statement_kind, location: body_location }; let body = BlockExpression { statements: vec![statement] }; let func_meta = interpreter.elaborator.interner.function_meta_mut(&func_id); func_meta.has_body = true; - func_meta.function_body = FunctionBody::Unresolved(FunctionKind::Normal, body, location.span); + func_meta.function_body = FunctionBody::Unresolved(FunctionKind::Normal, body, location); Ok(Value::Unit) } @@ -2681,7 +2672,7 @@ fn function_def_set_return_type( mutate_func_meta_type(interpreter.elaborator.interner, func_id, |func_meta| { func_meta.return_type = FunctionReturnType::Ty(UnresolvedType { typ: UnresolvedTypeData::Resolved(quoted_type_id), - span: location.span, + location, }); replace_func_meta_return_type(&mut func_meta.typ, return_type); }); @@ -2756,8 +2747,10 @@ fn module_add_item( let parser = Parser::parse_top_level_items; let top_level_statements = parse(interpreter.elaborator, item, parser, "a top-level item")?; - let module_data = interpreter.elaborator.get_module(module_id); - interpreter.elaborate_in_module(module_id, module_data.location.file, |elaborator| { + interpreter.elaborate_in_module(module_id, |elaborator| { + let previous_errors = elaborator + .push_elaborate_reason_and_take_errors(ElaborateReason::AddingItemToModule, location); + let mut generated_items = CollectedItems::default(); for top_level_statement in top_level_statements { @@ -2767,6 +2760,8 @@ fn module_add_item( if !generated_items.is_empty() { elaborator.elaborate_items(generated_items); } + + elaborator.pop_elaborate_reason(previous_errors); }); Ok(Value::Unit) @@ -2867,7 +2862,9 @@ fn module_name( let self_argument = check_one_argument(arguments, location)?; let module_id = get_module(self_argument)?; let name = &interner.module_attributes(&module_id).name; - let tokens = Rc::new(vec![Token::Ident(name.clone())]); + let token = Token::Ident(name.clone()); + let token = LocatedToken::new(token, location); + let tokens = Rc::new(vec![token]); Ok(Value::Quoted(tokens)) } @@ -2932,19 +2929,19 @@ fn trait_def_as_trait_constraint( let argument = check_one_argument(arguments, location)?; let trait_id = get_trait_def(argument)?; - let constraint = interner.get_trait(trait_id).as_constraint(location.span); + let constraint = interner.get_trait(trait_id).as_constraint(location); Ok(Value::TraitConstraint(trait_id, constraint.trait_bound.trait_generics)) } /// Creates a value that holds an `Option`. /// `option_type` must be a Type referencing the `Option` type. -pub(crate) fn option(option_type: Type, value: Option, span: Span) -> Value { +pub(crate) fn option(option_type: Type, value: Option, location: Location) -> Value { let t = extract_option_generic_type(option_type.clone()); let (is_some, value) = match value { Some(value) => (Value::Bool(true), value), - None => (Value::Bool(false), zeroed(t, span)), + None => (Value::Bool(false), zeroed(t, location)), }; let mut fields = HashMap::default(); @@ -2993,7 +2990,7 @@ fn derive_generators( _ => panic!("ICE: Should only have an array return type"), }; - let num_generators = size.evaluate_to_u32(location.span).map_err(|err| { + let num_generators = size.evaluate_to_u32(location).map_err(|err| { let err = Box::new(err); InterpreterError::UnknownArrayLength { length: *size, err, location } })?; @@ -3009,10 +3006,10 @@ fn derive_generators( let y_field_name: Rc = Rc::new("y".to_owned()); let is_infinite_field_name: Rc = Rc::new("is_infinite".to_owned()); let mut results = Vector::new(); - for gen in generators { - let x_big: BigUint = gen.x.into(); + for generator in generators { + let x_big: BigUint = generator.x.into(); let x = FieldElement::from_be_bytes_reduce(&x_big.to_bytes_be()); - let y_big: BigUint = gen.y.into(); + let y_big: BigUint = generator.y.into(); let y = FieldElement::from_be_bytes_reduce(&y_big.to_bytes_be()); let mut embedded_curve_point_fields = HashMap::default(); embedded_curve_point_fields.insert(x_field_name.clone(), Value::Field(x)); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs index 095f20b3f4c7..e552cf0c5a22 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs @@ -7,19 +7,23 @@ use noirc_errors::Location; use crate::elaborator::Elaborator; use crate::hir::comptime::display::tokens_to_string; -use crate::hir::comptime::value::add_token_spans; +use crate::hir::comptime::value::unwrap_rc; +use crate::hir::def_collector::dc_crate::CompilationError; use crate::lexer::Lexer; use crate::parser::{Parser, ParserError}; +use crate::token::LocatedToken; +use crate::{DataType, Kind, Shared}; use crate::{ + QuotedType, Type, ast::{ BlockExpression, ExpressionKind, Ident, IntegerBitSize, LValue, Pattern, Signedness, StatementKind, UnresolvedTypeData, }, hir::{ comptime::{ + Interpreter, InterpreterError, Value, errors::IResult, value::{ExprValue, TypedExpr}, - Interpreter, InterpreterError, Value, }, def_map::ModuleId, type_check::generics::TraitGenerics, @@ -30,9 +34,7 @@ use crate::{ }, node_interner::{FuncId, NodeInterner, TraitId, TraitImplId, TypeId}, token::{SecondaryAttribute, Token, Tokens}, - QuotedType, Type, }; -use crate::{DataType, Kind, Shared}; use rustc_hash::FxHashMap as HashMap; pub(crate) fn check_argument_count( @@ -110,7 +112,7 @@ pub(crate) fn get_struct_fields( _ => { let expected = DataType::new( TypeId::dummy_id(), - Ident::new(name.to_string(), location.span), + Ident::new(name.to_string(), location), location, Vec::new(), ); @@ -287,7 +289,7 @@ pub(crate) fn get_expr( Ok(ExprValue::Statement(interner.get_statement_kind(id).clone())) } ExprValue::LValue(LValue::Interned(id, _)) => { - Ok(ExprValue::LValue(interner.get_lvalue(id, location.span).clone())) + Ok(ExprValue::LValue(interner.get_lvalue(id, location).clone())) } ExprValue::Pattern(Pattern::Interned(id, _)) => { Ok(ExprValue::Pattern(interner.get_pattern(id).clone())) @@ -370,7 +372,7 @@ pub(crate) fn get_typed_expr((value, location): (Value, Location)) -> IResult IResult>> { +pub(crate) fn get_quoted((value, location): (Value, Location)) -> IResult>> { match value { Value::Quoted(tokens) => Ok(tokens), value => type_mismatch(value, Type::Quoted(QuotedType::Quoted), location), @@ -483,10 +485,11 @@ pub(super) fn check_function_not_yet_resolved( } } -pub(super) fn lex(input: &str) -> Vec { - let (tokens, _) = Lexer::lex(input); - let mut tokens: Vec<_> = tokens.0.into_iter().map(|token| token.into_token()).collect(); - if let Some(Token::EOF) = tokens.last() { +pub(super) fn lex(input: &str, location: Location) -> Vec { + let (tokens, _) = Lexer::lex(input, location.file); + let mut tokens: Vec<_> = + tokens.0.into_iter().map(|token| LocatedToken::new(token.into_token(), location)).collect(); + if let Some(Token::EOF) = tokens.last().map(|t| t.token()) { tokens.pop(); } tokens @@ -502,17 +505,18 @@ where F: FnOnce(&mut Parser<'a>) -> T, { let tokens = get_quoted((value, location))?; - let quoted = add_token_spans(tokens.clone(), location.span); + let quoted = Tokens(unwrap_rc(tokens.clone())); let (result, warnings) = parse_tokens(tokens, quoted, elaborator.interner, location, parser, rule)?; for warning in warnings { - elaborator.errors.push((warning.into(), location.file)); + let warning: CompilationError = warning.into(); + elaborator.push_err(warning); } Ok(result) } pub(super) fn parse_tokens<'a, T, F>( - tokens: Rc>, + tokens: Rc>, quoted: Tokens, interner: &NodeInterner, location: Location, @@ -524,8 +528,8 @@ where { Parser::for_tokens(quoted).parse_result(parsing_function).map_err(|mut errors| { let error = Box::new(errors.swap_remove(0)); - let tokens = tokens_to_string(tokens, interner); - InterpreterError::FailedToParseMacro { error, tokens, rule, file: location.file } + let tokens = tokens_to_string(&tokens, interner); + InterpreterError::FailedToParseMacro { error, tokens, rule, location } }) } @@ -582,12 +586,14 @@ pub(super) fn has_named_attribute(name: &str, attributes: &[SecondaryAttribute]) false } -pub(super) fn quote_ident(ident: &Ident) -> Value { - Value::Quoted(ident_to_tokens(ident)) +pub(super) fn quote_ident(ident: &Ident, location: Location) -> Value { + Value::Quoted(ident_to_tokens(ident, location)) } -pub(super) fn ident_to_tokens(ident: &Ident) -> Rc> { - Rc::new(vec![Token::Ident(ident.0.contents.clone())]) +fn ident_to_tokens(ident: &Ident, location: Location) -> Rc> { + let token = Token::Ident(ident.0.contents.clone()); + let token = LocatedToken::new(token, location); + Rc::new(vec![token]) } pub(super) fn hash_item( diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs index 0221280ae1b3..823e0297755c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs @@ -1,31 +1,31 @@ use acvm::{ + AcirField, BlackBoxResolutionError, FieldElement, acir::BlackBoxFunc, blackbox_solver::{BigIntSolverWithId, BlackBoxFunctionSolver}, - AcirField, BlackBoxResolutionError, FieldElement, }; use bn254_blackbox_solver::Bn254BlackBoxSolver; // Currently locked to only bn254! use im::Vector; use noirc_errors::Location; use crate::{ + Type, hir::comptime::{ - errors::IResult, interpreter::builtin::builtin_helpers::to_byte_array, InterpreterError, - Value, + InterpreterError, Value, errors::IResult, + interpreter::builtin::builtin_helpers::to_byte_array, }, node_interner::NodeInterner, - Type, }; use super::{ + Interpreter, builtin::builtin_helpers::{ check_arguments, check_one_argument, check_three_arguments, check_two_arguments, get_array_map, get_bool, get_field, get_fixed_array_map, get_slice_map, get_struct_field, - get_struct_fields, get_u32, get_u64, get_u8, to_byte_slice, to_field_array, to_struct, + get_struct_fields, get_u8, get_u32, get_u64, to_byte_slice, to_field_array, to_struct, }, - Interpreter, }; -impl<'local, 'context> Interpreter<'local, 'context> { +impl Interpreter<'_, '_> { pub(super) fn call_foreign( &mut self, name: &str, @@ -40,7 +40,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { arguments, return_type, location, - self.pedantic_solving, + self.elaborator.pedantic_solving(), ) } } @@ -234,7 +234,7 @@ fn blake_hash( /// signature: [u8; 64], /// message_hash: [u8; N], /// ) -> bool - +/// /// pub fn verify_signature_slice( /// public_key_x: [u8; 32], /// public_key_y: [u8; 32], @@ -422,11 +422,11 @@ mod tests { use noirc_errors::Location; use strum::IntoEnumIterator; - use crate::hir::comptime::tests::with_interpreter; + use crate::Type; use crate::hir::comptime::InterpreterError::{ ArgumentCountMismatch, InvalidInComptimeContext, Unimplemented, }; - use crate::Type; + use crate::hir::comptime::tests::with_interpreter; use super::call_foreign; 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 c7b1532c9b7e..fc4daa22edb7 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 @@ -2,12 +2,12 @@ use noirc_errors::Location; use crate::{ hir::comptime::errors::IResult, - token::{Token, Tokens}, + token::{LocatedToken, Token, Tokens}, }; use super::Interpreter; -impl<'local, 'interner> Interpreter<'local, 'interner> { +impl Interpreter<'_, '_> { /// Evaluates any expressions within UnquoteMarkers in the given token list /// and replaces the expression held by the marker with the evaluated value /// in expression form. @@ -15,17 +15,17 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { &mut self, tokens: Tokens, location: Location, - ) -> IResult> { + ) -> IResult> { let mut new_tokens = Vec::with_capacity(tokens.0.len()); for token in tokens.0 { - match token.into_token() { + match token.token() { Token::UnquoteMarker(id) => { - let value = self.evaluate(id)?; + let value = self.evaluate(*id)?; let tokens = value.into_tokens(self.elaborator.interner, location)?; new_tokens.extend(tokens); } - token => new_tokens.push(token), + _ => new_tokens.push(token), } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs index 2e2753001b48..c4a987e54195 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs @@ -5,6 +5,6 @@ mod interpreter; mod tests; mod value; -pub use errors::InterpreterError; +pub use errors::{ComptimeError, InterpreterError}; pub use interpreter::Interpreter; pub use value::Value; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs index 342d0a616a05..88a2bc8b52a8 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs @@ -7,10 +7,10 @@ use fm::{FileId, FileManager}; use noirc_arena::Index; use noirc_errors::Location; +use super::Interpreter; use super::errors::InterpreterError; use super::value::Value; -use super::Interpreter; -use crate::elaborator::Elaborator; +use crate::elaborator::{Elaborator, ElaboratorOptions}; use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector}; use crate::hir::def_collector::dc_mod::collect_defs; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleData}; @@ -23,7 +23,7 @@ use crate::parse_program; /// The stdlib is not made available as a dependency. pub(crate) fn with_interpreter( src: &str, - f: impl FnOnce(&mut Interpreter, FuncId, &[(CompilationError, FileId)]) -> T, + f: impl FnOnce(&mut Interpreter, FuncId, &[CompilationError]) -> T, ) -> T { let file = FileId::default(); @@ -48,7 +48,7 @@ pub(crate) fn with_interpreter( let krate = context.crate_graph.add_crate_root(FileId::dummy()); - let (module, errors) = parse_program(src); + let (module, errors) = parse_program(src, file); assert_eq!(errors.len(), 0); let ast = module.into_sorted(); @@ -60,13 +60,11 @@ pub(crate) fn with_interpreter( let main = context.get_main_function(&krate).expect("Expected 'main' function"); - let pedantic_solving = true; let mut elaborator = Elaborator::elaborate_and_return_self( &mut context, krate, collector.items, - None, - pedantic_solving, + ElaboratorOptions::test_default(), ); let errors = elaborator.errors.clone(); 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 a6668eae1b0a..8d07669497f6 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 @@ -3,25 +3,29 @@ use std::{borrow::Cow, rc::Rc, vec}; use acvm::FieldElement; use im::Vector; use iter_extended::{try_vecmap, vecmap}; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use strum_macros::Display; use crate::{ + Kind, QuotedType, Shared, Type, TypeBindings, ast::{ ArrayLiteral, BlockExpression, ConstructorExpression, Expression, ExpressionKind, Ident, - IntegerBitSize, LValue, Literal, Path, Pattern, Signedness, Statement, StatementKind, + IntegerBitSize, LValue, Literal, Pattern, Signedness, Statement, StatementKind, UnresolvedType, UnresolvedTypeData, }, elaborator::Elaborator, - hir::{def_map::ModuleId, type_check::generics::TraitGenerics}, + hir::{ + def_collector::dc_crate::CompilationError, def_map::ModuleId, + type_check::generics::TraitGenerics, + }, hir_def::expr::{ HirArrayLiteral, HirConstructorExpression, HirEnumConstructorExpression, HirExpression, HirIdent, HirLambda, HirLiteral, ImplKind, }, node_interner::{ExprId, FuncId, NodeInterner, StmtId, TraitId, TraitImplId, TypeId}, parser::{Item, Parser}, - token::{SpannedToken, Token, Tokens}, - Kind, QuotedType, Shared, Type, TypeBindings, + signed_field::SignedField, + token::{LocatedToken, Token, Tokens}, }; use rustc_hash::FxHashMap as HashMap; @@ -44,6 +48,7 @@ pub enum Value { U16(u16), U32(u32), U64(u64), + U128(u128), String(Rc), FormatString(Rc, Type), CtString(Rc), @@ -59,10 +64,7 @@ pub enum Value { Pointer(Shared, /* auto_deref */ bool), Array(Vector, Type), Slice(Vector, Type), - /// Quoted tokens don't have spans because otherwise inserting them in the middle of other - /// tokens can cause larger spans to be before lesser spans, causing an assert. They may also - /// be inserted into separate files entirely. - Quoted(Rc>), + Quoted(Rc>), StructDefinition(TypeId), TraitConstraint(TraitId, TraitGenerics), TraitDefinition(TraitId), @@ -130,6 +132,9 @@ impl Value { Value::U16(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::Sixteen), Value::U32(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo), Value::U64(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour), + Value::U128(_) => { + Type::Integer(Signedness::Unsigned, IntegerBitSize::HundredTwentyEight) + } Value::String(value) => { let length = Type::Constant(value.len().into(), Kind::u32()); Type::String(Box::new(length)) @@ -176,45 +181,38 @@ impl Value { let kind = match self { Value::Unit => ExpressionKind::Literal(Literal::Unit), Value::Bool(value) => ExpressionKind::Literal(Literal::Bool(value)), - Value::Field(value) => ExpressionKind::Literal(Literal::Integer(value, false)), + Value::Field(value) => { + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value))) + } Value::I8(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - ExpressionKind::Literal(Literal::Integer(value, negative)) + ExpressionKind::Literal(Literal::Integer(SignedField::from_signed(value))) } Value::I16(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - ExpressionKind::Literal(Literal::Integer(value, negative)) + ExpressionKind::Literal(Literal::Integer(SignedField::from_signed(value))) } Value::I32(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - ExpressionKind::Literal(Literal::Integer(value, negative)) + ExpressionKind::Literal(Literal::Integer(SignedField::from_signed(value))) } Value::I64(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - ExpressionKind::Literal(Literal::Integer(value, negative)) + ExpressionKind::Literal(Literal::Integer(SignedField::from_signed(value))) } Value::U1(value) => { - ExpressionKind::Literal(Literal::Integer((value as u128).into(), false)) + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value))) } Value::U8(value) => { - ExpressionKind::Literal(Literal::Integer((value as u128).into(), false)) + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value as u128))) } Value::U16(value) => { - ExpressionKind::Literal(Literal::Integer((value as u128).into(), false)) + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value as u128))) } Value::U32(value) => { - ExpressionKind::Literal(Literal::Integer((value as u128).into(), false)) + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value))) } Value::U64(value) => { - ExpressionKind::Literal(Literal::Integer((value as u128).into(), false)) + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value))) + } + Value::U128(value) => { + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value))) } Value::String(value) | Value::CtString(value) => { ExpressionKind::Literal(Literal::Str(unwrap_rc(value))) @@ -228,7 +226,7 @@ impl Value { let impl_kind = ImplKind::NotATraitMethod; let ident = HirIdent { location, id, impl_kind }; let expr_id = elaborator.interner.push_expr(HirExpression::Ident(ident, None)); - elaborator.interner.push_expr_location(expr_id, location.span, location.file); + elaborator.interner.push_expr_location(expr_id, location); elaborator.interner.push_expr_type(expr_id, typ); elaborator.interner.store_instantiation_bindings(expr_id, unwrap_rc(bindings)); ExpressionKind::Resolved(expr_id) @@ -241,21 +239,21 @@ impl Value { Value::Struct(fields, typ) => { let fields = try_vecmap(fields, |(name, field)| { let field = field.into_expression(elaborator, location)?; - Ok((Ident::new(unwrap_rc(name), location.span), field)) + Ok((Ident::new(unwrap_rc(name), location), field)) })?; - let struct_type = match typ.follow_bindings_shallow().as_ref() { - Type::DataType(def, _) => Some(def.borrow().id), + let typ = match typ.follow_bindings_shallow().as_ref() { + Type::DataType(data_type, generics) => { + Type::DataType(data_type.clone(), generics.clone()) + } _ => return Err(InterpreterError::NonStructInConstructor { typ, location }), }; - // Since we've provided the struct_type, the path should be ignored. - let type_name = Path::from_single(String::new(), location.span); - ExpressionKind::Constructor(Box::new(ConstructorExpression { - typ: UnresolvedType::from_path(type_name), - fields, - struct_type, - })) + let quoted_type_id = elaborator.interner.push_quoted_type(typ); + + let typ = UnresolvedTypeData::Resolved(quoted_type_id); + let typ = UnresolvedType { typ, location }; + ExpressionKind::Constructor(Box::new(ConstructorExpression { typ, fields })) } value @ Value::Enum(..) => { let hir = value.into_hir_expression(elaborator.interner, location)?; @@ -273,29 +271,30 @@ impl Value { } Value::Quoted(tokens) => { // Wrap the tokens in '{' and '}' so that we can parse statements as well. - let mut tokens_to_parse = add_token_spans(tokens.clone(), location.span); - tokens_to_parse.0.insert(0, SpannedToken::new(Token::LeftBrace, location.span)); - tokens_to_parse.0.push(SpannedToken::new(Token::RightBrace, location.span)); + let mut tokens_to_parse = unwrap_rc(tokens.clone()); + tokens_to_parse.insert(0, LocatedToken::new(Token::LeftBrace, location)); + tokens_to_parse.push(LocatedToken::new(Token::RightBrace, location)); + + let tokens_to_parse = Tokens(tokens_to_parse); let parser = Parser::for_tokens(tokens_to_parse); return match parser.parse_result(Parser::parse_expression_or_error) { Ok((expr, warnings)) => { for warning in warnings { - elaborator.errors.push((warning.into(), location.file)); + let warning: CompilationError = warning.into(); + elaborator.push_err(warning); } Ok(expr) } Err(mut errors) => { let error = Box::new(errors.swap_remove(0)); - let file = location.file; let rule = "an expression"; - let tokens = tokens_to_string(tokens, elaborator.interner); - Err(InterpreterError::FailedToParseMacro { error, file, tokens, rule }) + let tokens = tokens_to_string(&tokens, elaborator.interner); + Err(InterpreterError::FailedToParseMacro { error, tokens, rule, location }) } }; } - Value::Expr(ref expr) => { // We need to do some shenanigans to get around the borrow checker here due to using a boxed value. @@ -314,7 +313,7 @@ impl Value { match *expr { ExprValue::Expression(expr) => expr, ExprValue::Statement(statement) => ExpressionKind::Block(BlockExpression { - statements: vec![Statement { kind: statement, span: location.span }], + statements: vec![Statement { kind: statement, location }], }), ExprValue::LValue(lvalue) => lvalue.as_expression().kind, ExprValue::Pattern(_) => unreachable!("this case is handled above"), @@ -338,7 +337,7 @@ impl Value { } }; - Ok(Expression::new(kind, location.span)) + Ok(Expression::new(kind, location)) } pub(crate) fn into_hir_expression( @@ -351,45 +350,36 @@ impl Value { let expression = match self { Value::Unit => HirExpression::Literal(HirLiteral::Unit), Value::Bool(value) => HirExpression::Literal(HirLiteral::Bool(value)), - Value::Field(value) => HirExpression::Literal(HirLiteral::Integer(value, false)), + Value::Field(value) => HirExpression::Literal(HirLiteral::Integer(value.into())), Value::I8(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - HirExpression::Literal(HirLiteral::Integer(value, negative)) + HirExpression::Literal(HirLiteral::Integer(SignedField::from_signed(value))) } Value::I16(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - HirExpression::Literal(HirLiteral::Integer(value, negative)) + HirExpression::Literal(HirLiteral::Integer(SignedField::from_signed(value))) } Value::I32(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - HirExpression::Literal(HirLiteral::Integer(value, negative)) + HirExpression::Literal(HirLiteral::Integer(SignedField::from_signed(value))) } Value::I64(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - HirExpression::Literal(HirLiteral::Integer(value, negative)) + HirExpression::Literal(HirLiteral::Integer(SignedField::from_signed(value))) } Value::U1(value) => { - HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) + HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value))) } Value::U8(value) => { - HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) + HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value as u128))) } Value::U16(value) => { - HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) + HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value as u128))) } Value::U32(value) => { - HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) + HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value))) } Value::U64(value) => { - HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) + HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value))) + } + Value::U128(value) => { + HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value))) } Value::String(value) | Value::CtString(value) => { HirExpression::Literal(HirLiteral::Str(unwrap_rc(value))) @@ -403,7 +393,7 @@ impl Value { let impl_kind = ImplKind::NotATraitMethod; let ident = HirIdent { location, id, impl_kind }; let expr_id = interner.push_expr(HirExpression::Ident(ident, None)); - interner.push_expr_location(expr_id, location.span, location.file); + interner.push_expr_location(expr_id, location); interner.push_expr_type(expr_id, typ); interner.store_instantiation_bindings(expr_id, unwrap_rc(bindings)); return Ok(expr_id); @@ -416,7 +406,7 @@ impl Value { Value::Struct(fields, typ) => { let fields = try_vecmap(fields, |(name, field)| { let field = field.into_hir_expression(interner, location)?; - Ok((Ident::new(unwrap_rc(name), location.span), field)) + Ok((Ident::new(unwrap_rc(name), location), field)) })?; let (r#type, struct_generics) = match typ.follow_bindings() { @@ -458,7 +448,7 @@ impl Value { })?; HirExpression::Literal(HirLiteral::Slice(HirArrayLiteral::Standard(elements))) } - Value::Quoted(tokens) => HirExpression::Unquote(add_token_spans(tokens, location.span)), + Value::Quoted(tokens) => HirExpression::Unquote(Tokens(unwrap_rc(tokens))), Value::TypedExpr(TypedExpr::ExprId(expr_id)) => interner.expression(&expr_id), // Only convert pointers with auto_deref = true. These are mutable variables // and we don't need to wrap them in `&mut`. @@ -485,7 +475,7 @@ impl Value { }; let id = interner.push_expr(expression); - interner.push_expr_location(id, location.span, location.file); + interner.push_expr_location(id, location); interner.push_expr_type(id, typ); Ok(id) } @@ -494,74 +484,77 @@ impl Value { self, interner: &mut NodeInterner, location: Location, - ) -> IResult> { - let token = match self { + ) -> IResult> { + let tokens: Vec = match self { Value::Unit => { - return Ok(vec![Token::LeftParen, Token::RightParen]); + vec![Token::LeftParen, Token::RightParen] } Value::Quoted(tokens) => return Ok(unwrap_rc(tokens)), - Value::Type(typ) => Token::QuotedType(interner.push_quoted_type(typ)), + Value::Type(typ) => vec![Token::QuotedType(interner.push_quoted_type(typ))], Value::Expr(expr) => match *expr { ExprValue::Expression(expr) => { - Token::InternedExpr(interner.push_expression_kind(expr)) + vec![Token::InternedExpr(interner.push_expression_kind(expr))] } ExprValue::Statement(StatementKind::Expression(expr)) => { - Token::InternedExpr(interner.push_expression_kind(expr.kind)) + vec![Token::InternedExpr(interner.push_expression_kind(expr.kind))] } ExprValue::Statement(statement) => { - Token::InternedStatement(interner.push_statement_kind(statement)) + vec![Token::InternedStatement(interner.push_statement_kind(statement))] + } + ExprValue::LValue(lvalue) => { + vec![Token::InternedLValue(interner.push_lvalue(lvalue))] } - ExprValue::LValue(lvalue) => Token::InternedLValue(interner.push_lvalue(lvalue)), ExprValue::Pattern(pattern) => { - Token::InternedPattern(interner.push_pattern(pattern)) + vec![Token::InternedPattern(interner.push_pattern(pattern))] } }, Value::UnresolvedType(typ) => { - Token::InternedUnresolvedTypeData(interner.push_unresolved_type_data(typ)) + vec![Token::InternedUnresolvedTypeData(interner.push_unresolved_type_data(typ))] } Value::TraitConstraint(trait_id, generics) => { let name = Rc::new(interner.get_trait(trait_id).name.0.contents.clone()); let typ = Type::TraitAsType(trait_id, name, generics); - Token::QuotedType(interner.push_quoted_type(typ)) - } - Value::TypedExpr(TypedExpr::ExprId(expr_id)) => Token::UnquoteMarker(expr_id), - Value::U1(bool) => Token::Bool(bool), - Value::U8(value) => Token::Int((value as u128).into()), - Value::U16(value) => Token::Int((value as u128).into()), - Value::U32(value) => Token::Int((value as u128).into()), - Value::U64(value) => Token::Int((value as u128).into()), + vec![Token::QuotedType(interner.push_quoted_type(typ))] + } + Value::TypedExpr(TypedExpr::ExprId(expr_id)) => vec![Token::UnquoteMarker(expr_id)], + Value::U1(bool) => vec![Token::Bool(bool)], + Value::U8(value) => vec![Token::Int((value as u128).into())], + Value::U16(value) => vec![Token::Int((value as u128).into())], + Value::U32(value) => vec![Token::Int((value as u128).into())], + Value::U64(value) => vec![Token::Int((value as u128).into())], Value::I8(value) => { if value < 0 { - return Ok(vec![Token::Minus, Token::Int((-value as u128).into())]); + vec![Token::Minus, Token::Int((-value as u128).into())] } else { - Token::Int((value as u128).into()) + vec![Token::Int((value as u128).into())] } } Value::I16(value) => { if value < 0 { - return Ok(vec![Token::Minus, Token::Int((-value as u128).into())]); + vec![Token::Minus, Token::Int((-value as u128).into())] } else { - Token::Int((value as u128).into()) + vec![Token::Int((value as u128).into())] } } Value::I32(value) => { if value < 0 { - return Ok(vec![Token::Minus, Token::Int((-value as u128).into())]); + vec![Token::Minus, Token::Int((-value as u128).into())] } else { - Token::Int((value as u128).into()) + vec![Token::Int((value as u128).into())] } } Value::I64(value) => { if value < 0 { - return Ok(vec![Token::Minus, Token::Int((-value as u128).into())]); + vec![Token::Minus, Token::Int((-value as u128).into())] } else { - Token::Int((value as u128).into()) + vec![Token::Int((value as u128).into())] } } - Value::Field(value) => Token::Int(value), - other => Token::UnquoteMarker(other.into_hir_expression(interner, location)?), + Value::Field(value) => vec![Token::Int(value)], + other => vec![Token::UnquoteMarker(other.into_hir_expression(interner, location)?)], }; - Ok(vec![token]) + let tokens = vecmap(tokens, |token| LocatedToken::new(token, location)); + Ok(tokens) } /// Returns false for non-integral `Value`s. @@ -619,7 +612,7 @@ pub(crate) fn unwrap_rc(rc: Rc) -> T { } fn parse_tokens<'a, T, F>( - tokens: Rc>, + tokens: Rc>, elaborator: &mut Elaborator, parsing_function: F, location: Location, @@ -628,24 +621,19 @@ fn parse_tokens<'a, T, F>( where F: FnOnce(&mut Parser<'a>) -> T, { - let parser = Parser::for_tokens(add_token_spans(tokens.clone(), location.span)); + let parser = Parser::for_tokens(Tokens(unwrap_rc(tokens.clone()))); match parser.parse_result(parsing_function) { Ok((expr, warnings)) => { for warning in warnings { - elaborator.errors.push((warning.into(), location.file)); + let warning: CompilationError = warning.into(); + elaborator.push_err(warning); } Ok(expr) } Err(mut errors) => { let error = Box::new(errors.swap_remove(0)); - let file = location.file; - let tokens = tokens_to_string(tokens, elaborator.interner); - Err(InterpreterError::FailedToParseMacro { error, file, tokens, rule }) + let tokens = tokens_to_string(&tokens, elaborator.interner); + Err(InterpreterError::FailedToParseMacro { error, tokens, rule, location }) } } } - -pub(crate) fn add_token_spans(tokens: Rc>, span: Span) -> Tokens { - let tokens = unwrap_rc(tokens); - Tokens(vecmap(tokens, |token| SpannedToken::new(token, span))) -} 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 9d8c32fbc12d..b0c71f1ebe67 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 @@ -2,7 +2,7 @@ use super::dc_mod::collect_defs; use super::errors::{DefCollectorErrorKind, DuplicateType}; use crate::elaborator::Elaborator; use crate::graph::CrateId; -use crate::hir::comptime::InterpreterError; +use crate::hir::comptime::{ComptimeError, InterpreterError}; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}; use crate::hir::resolution::errors::ResolverError; use crate::hir::type_check::TypeCheckError; @@ -11,8 +11,8 @@ use crate::token::SecondaryAttribute; use crate::usage_tracker::UnusedItem; use crate::{Generics, Type}; -use crate::hir::resolution::import::{resolve_import, ImportDirective}; use crate::hir::Context; +use crate::hir::resolution::import::{ImportDirective, resolve_import}; use crate::ast::{Expression, NoirEnumeration}; use crate::node_interner::{ @@ -22,10 +22,11 @@ use crate::node_interner::{ use crate::ast::{ ExpressionKind, Ident, ItemVisibility, LetStatement, Literal, NoirFunction, NoirStruct, - NoirTrait, NoirTypeAlias, Path, PathKind, PathSegment, UnresolvedGenerics, - UnresolvedTraitConstraint, UnresolvedType, UnsupportedNumericGenericType, + NoirTrait, NoirTypeAlias, Path, PathSegment, UnresolvedGenerics, UnresolvedTraitConstraint, + UnresolvedType, UnsupportedNumericGenericType, }; +use crate::elaborator::FrontendOptions; use crate::parser::{ParserError, SortedModule}; use noirc_errors::{CustomDiagnostic, Location, Span}; @@ -176,17 +177,35 @@ impl CollectedItems { /// Note that because these are keyed by unresolved types, the impl map is one of the few instances /// of HashMap rather than BTreeMap. For this reason, we should be careful not to iterate over it /// since it would be non-deterministic. -pub(crate) type ImplMap = - HashMap<(UnresolvedType, LocalModuleId), Vec<(UnresolvedGenerics, Span, UnresolvedFunctions)>>; +pub(crate) type ImplMap = HashMap< + (UnresolvedType, LocalModuleId), + Vec<(UnresolvedGenerics, Location, UnresolvedFunctions)>, +>; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum CompilationError { ParseError(ParserError), DefinitionError(DefCollectorErrorKind), ResolverError(ResolverError), TypeError(TypeCheckError), InterpreterError(InterpreterError), - DebugComptimeScopeNotFound(Vec), + ComptimeError(ComptimeError), + DebugComptimeScopeNotFound(Vec, Location), +} + +impl CompilationError { + /// Returns the primary location where this error happened. + pub fn location(&self) -> Location { + match self { + CompilationError::ParseError(error) => error.location(), + CompilationError::DefinitionError(error) => error.location(), + CompilationError::ResolverError(error) => error.location(), + CompilationError::TypeError(error) => error.location(), + CompilationError::InterpreterError(error) => error.location(), + CompilationError::ComptimeError(error) => error.location(), + CompilationError::DebugComptimeScopeNotFound(_, location) => *location, + } + } } impl std::fmt::Display for CompilationError { @@ -197,7 +216,8 @@ impl std::fmt::Display for CompilationError { CompilationError::ResolverError(error) => write!(f, "{}", error), CompilationError::TypeError(error) => write!(f, "{}", error), CompilationError::InterpreterError(error) => write!(f, "{:?}", error), - CompilationError::DebugComptimeScopeNotFound(error) => write!(f, "{:?}", error), + CompilationError::DebugComptimeScopeNotFound(error, _) => write!(f, "{:?}", error), + CompilationError::ComptimeError(error) => write!(f, "{:?}", error), } } } @@ -210,15 +230,16 @@ impl<'a> From<&'a CompilationError> for CustomDiagnostic { CompilationError::ResolverError(error) => error.into(), CompilationError::TypeError(error) => error.into(), CompilationError::InterpreterError(error) => error.into(), - CompilationError::DebugComptimeScopeNotFound(error) => { + CompilationError::ComptimeError(error) => error.into(), + CompilationError::DebugComptimeScopeNotFound(error, _) => { let msg = "multiple files found matching --debug-comptime path".into(); let secondary = error.iter().fold(String::new(), |mut output, path| { let _ = writeln!(output, " {}", path.display()); output }); - // NOTE: this span is empty as it is not expected to be displayed - let dummy_span = Span::default(); - CustomDiagnostic::simple_error(msg, secondary, dummy_span) + // NOTE: this location is empty as it is not expected to be displayed + let dummy_location = Location::dummy(); + CustomDiagnostic::simple_error(msg, secondary, dummy_location) } } } @@ -282,10 +303,9 @@ impl DefCollector { context: &mut Context, ast: SortedModule, root_file_id: FileId, - debug_comptime_in_file: Option<&str>, - pedantic_solving: bool, - ) -> Vec<(CompilationError, FileId)> { - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + options: FrontendOptions, + ) -> Vec { + let mut errors: Vec = vec![]; let crate_id = def_map.krate; // Recursively resolve the dependencies @@ -296,12 +316,7 @@ impl DefCollector { let crate_graph = &context.crate_graph[crate_id]; for dep in crate_graph.dependencies.clone() { - errors.extend(CrateDefMap::collect_defs( - dep.crate_id, - context, - debug_comptime_in_file, - pedantic_solving, - )); + errors.extend(CrateDefMap::collect_defs(dep.crate_id, context, options)); let dep_def_map = context.def_map(&dep.crate_id).expect("ice: def map was just created"); @@ -358,15 +373,13 @@ impl DefCollector { for collected_import in std::mem::take(&mut def_collector.imports) { let local_module_id = collected_import.module_id; let module_id = ModuleId { krate: crate_id, local_id: local_module_id }; - let current_def_map = context.def_maps.get(&crate_id).unwrap(); - let file_id = current_def_map.file_id(local_module_id); let resolved_import = resolve_import( collected_import.path.clone(), module_id, &context.def_maps, &mut context.usage_tracker, - Some(ReferencesTracker::new(&mut context.def_interner, file_id)), + Some(ReferencesTracker::new(&mut context.def_interner)), ); match resolved_import { @@ -376,10 +389,7 @@ impl DefCollector { let has_path_resolution_error = !resolved_import.errors.is_empty(); for error in resolved_import.errors { - errors.push(( - DefCollectorErrorKind::PathResolutionError(error).into(), - file_id, - )); + errors.push(DefCollectorErrorKind::PathResolutionError(error).into()); } // Populate module namespaces according to the imports used @@ -390,14 +400,13 @@ impl DefCollector { resolved_import.namespace.iter_items() { if item_visibility < visibility { - errors.push(( + errors.push( DefCollectorErrorKind::CannotReexportItemWithLessVisibility { item_name: name.clone(), desired_visibility: visibility, } .into(), - file_id, - )); + ); } let visibility = visibility.min(item_visibility); @@ -454,34 +463,34 @@ impl DefCollector { first_def, second_def, }; - errors.push((err.into(), root_file_id)); + errors.push(err.into()); } } } Err(error) => { - let current_def_map = context.def_maps.get(&crate_id).unwrap(); - let file_id = current_def_map.file_id(collected_import.module_id); let error = DefCollectorErrorKind::PathResolutionError(error); - errors.push((error.into(), file_id)); + errors.push(error.into()); } } } - let debug_comptime_in_file = debug_comptime_in_file.and_then(|debug_comptime_in_file| { - let file = context.file_manager.find_by_path_suffix(debug_comptime_in_file); + let debug_comptime_in_file = options.debug_comptime_in_file.and_then(|file_suffix| { + let file = context.file_manager.find_by_path_suffix(file_suffix); file.unwrap_or_else(|error| { - errors.push((CompilationError::DebugComptimeScopeNotFound(error), root_file_id)); + let location = Location::new(Span::empty(0), root_file_id); + errors.push(CompilationError::DebugComptimeScopeNotFound(error, location)); None }) }); - let mut more_errors = Elaborator::elaborate( - context, - crate_id, - def_collector.items, + let cli_options = crate::elaborator::ElaboratorOptions { debug_comptime_in_file, - pedantic_solving, - ); + pedantic_solving: options.pedantic_solving, + enabled_unstable_features: options.enabled_unstable_features, + }; + + let mut more_errors = + Elaborator::elaborate(context, crate_id, def_collector.items, cli_options); errors.append(&mut more_errors); @@ -493,20 +502,18 @@ impl DefCollector { fn check_unused_items( context: &Context, crate_id: CrateId, - errors: &mut Vec<(CompilationError, FileId)>, + errors: &mut Vec, ) { let unused_imports = context.usage_tracker.unused_items().iter(); let unused_imports = unused_imports.filter(|(module_id, _)| module_id.krate == crate_id); - errors.extend(unused_imports.flat_map(|(module_id, usage_tracker)| { - let module = &context.def_maps[&crate_id].modules()[module_id.local_id.0]; + errors.extend(unused_imports.flat_map(|(_, usage_tracker)| { usage_tracker.iter().map(|(ident, unused_item)| { let ident = ident.clone(); - let error = CompilationError::ResolverError(ResolverError::UnusedItem { + CompilationError::ResolverError(ResolverError::UnusedItem { ident, item: *unused_item, - }); - (error, module.location.file) + }) }) })); } @@ -539,16 +546,12 @@ fn inject_prelude( .map(|segment| { crate::ast::PathSegment::from(crate::ast::Ident::new( segment.into(), - Span::default(), + Location::dummy(), )) }) .collect(); - let path = Path { - segments: segments.clone(), - kind: crate::ast::PathKind::Plain, - span: Span::default(), - }; + let path = Path::plain(segments.clone(), Location::dummy()); if let Ok(resolved_import) = resolve_import( path, @@ -566,14 +569,14 @@ fn inject_prelude( for path in prelude { let mut segments = segments.clone(); - segments.push(PathSegment::from(Ident::new(path.to_string(), Span::default()))); + segments.push(PathSegment::from(Ident::new(path.to_string(), Location::dummy()))); collected_imports.insert( 0, ImportDirective { visibility: ItemVisibility::Private, module_id: crate_root, - path: Path { segments, kind: PathKind::Plain, span: Span::default() }, + path: Path::plain(segments, Location::dummy()), alias: None, is_prelude: true, }, 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 59e1f2f6e329..8ba6be4fc4bd 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 @@ -4,7 +4,7 @@ use std::rc::Rc; use std::vec; use acvm::{AcirField, FieldElement}; -use fm::{FileId, FileManager, FILE_EXTENSION}; +use fm::{FILE_EXTENSION, FileId, FileManager}; use noirc_errors::{Location, Span}; use num_bigint::BigUint; use num_traits::Num; @@ -20,13 +20,13 @@ use crate::hir::resolution::errors::ResolverError; use crate::node_interner::{ModuleAttributes, NodeInterner, ReferenceId, TypeId}; use crate::token::SecondaryAttribute; use crate::usage_tracker::{UnusedItem, UsageTracker}; +use crate::{Generics, Kind, ResolvedGeneric, Type, TypeVariable}; use crate::{ graph::CrateId, hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait}, node_interner::{FunctionModifiers, TraitId, TypeAliasId}, parser::{SortedModule, SortedSubModule}, }; -use crate::{Generics, Kind, ResolvedGeneric, Type, TypeVariable}; use super::dc_crate::ModuleAttribute; use super::dc_crate::{CollectedItems, UnresolvedEnum}; @@ -37,9 +37,9 @@ use super::{ }, errors::{DefCollectorErrorKind, DuplicateType}, }; -use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleData, ModuleId, MAIN_FUNCTION}; -use crate::hir::resolution::import::ImportDirective; use crate::hir::Context; +use crate::hir::def_map::{CrateDefMap, LocalModuleId, MAIN_FUNCTION, ModuleData, ModuleId}; +use crate::hir::resolution::import::ImportDirective; /// Given a module collect all definitions into ModuleData struct ModCollector<'a> { @@ -58,9 +58,9 @@ pub fn collect_defs( module_id: LocalModuleId, crate_id: CrateId, context: &mut Context, -) -> Vec<(CompilationError, FileId)> { +) -> Vec { let mut collector = ModCollector { def_collector, file_id, module_id }; - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + let mut errors: Vec = vec![]; // First resolve the module declarations for decl in ast.module_decls { @@ -116,7 +116,7 @@ pub fn collect_defs( errors } -impl<'a> ModCollector<'a> { +impl ModCollector<'_> { fn collect_attributes( &mut self, attributes: Vec, @@ -143,7 +143,7 @@ impl<'a> ModCollector<'a> { context: &mut Context, globals: Vec<(Documented, ItemVisibility)>, crate_id: CrateId, - ) -> Vec<(CompilationError, fm::FileId)> { + ) -> Vec { let mut errors = vec![]; for (global, visibility) in globals { let (global, error) = collect_global( @@ -171,7 +171,7 @@ impl<'a> ModCollector<'a> { context: &mut Context, impls: Vec, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { + ) -> Vec { let mut errors = Vec::new(); let module_id = ModuleId { krate, local_id: self.module_id }; @@ -194,7 +194,7 @@ impl<'a> ModCollector<'a> { context: &mut Context, impls: Vec, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { + ) -> Vec { let mut errors = Vec::new(); for mut trait_impl in impls { @@ -212,19 +212,19 @@ impl<'a> ModCollector<'a> { for (_, func_id, noir_function) in &mut unresolved_functions.functions { if noir_function.def.attributes.is_test_function() { let error = DefCollectorErrorKind::TestOnAssociatedFunction { - span: noir_function.name_ident().span(), + location: noir_function.name_ident().location(), }; - errors.push((error.into(), self.file_id)); + errors.push(error.into()); } if noir_function.def.attributes.has_export() { let error = DefCollectorErrorKind::ExportOnAssociatedFunction { - span: noir_function.name_ident().span(), + location: noir_function.name_ident().location(), }; - errors.push((error.into(), self.file_id)); + errors.push(error.into()); } - let location = Location::new(noir_function.def.span, self.file_id); + let location = noir_function.def.location; context.def_interner.push_function(*func_id, &noir_function.def, module, location); } @@ -258,7 +258,7 @@ impl<'a> ModCollector<'a> { context: &mut Context, functions: Vec>, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { + ) -> Vec { let mut unresolved_functions = UnresolvedFunctions { file_id: self.file_id, functions: Vec::new(), @@ -276,7 +276,6 @@ impl<'a> ModCollector<'a> { &mut context.usage_tracker, &function.item, module, - self.file_id, function.doc_comments, &mut errors, ) else { @@ -304,7 +303,7 @@ impl<'a> ModCollector<'a> { context: &mut Context, types: Vec>, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { + ) -> Vec { let mut definition_errors = vec![]; for struct_definition in types { if let Some((id, the_struct)) = collect_struct( @@ -312,7 +311,6 @@ impl<'a> ModCollector<'a> { &mut self.def_collector.def_map, &mut context.usage_tracker, struct_definition, - self.file_id, self.module_id, krate, &mut definition_errors, @@ -331,7 +329,7 @@ impl<'a> ModCollector<'a> { context: &mut Context, types: Vec>, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { + ) -> Vec { let mut definition_errors = vec![]; for enum_definition in types { if let Some((id, the_enum)) = collect_enum( @@ -357,8 +355,8 @@ impl<'a> ModCollector<'a> { context: &mut Context, type_aliases: Vec>, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + ) -> Vec { + let mut errors: Vec = vec![]; for type_alias in type_aliases { let doc_comments = type_alias.doc_comments; let type_alias = type_alias.item; @@ -377,7 +375,6 @@ impl<'a> ModCollector<'a> { &context.def_interner, &unresolved.type_alias_def.generics, &mut errors, - self.file_id, ); let type_alias_id = @@ -406,7 +403,7 @@ impl<'a> ModCollector<'a> { first_def, second_def, }; - errors.push((err.into(), self.file_id)); + errors.push(err.into()); } self.def_collector.items.type_aliases.insert(type_alias_id, unresolved); @@ -433,20 +430,20 @@ impl<'a> ModCollector<'a> { context: &mut Context, traits: Vec>, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + ) -> Vec { + let mut errors: Vec = vec![]; for trait_definition in traits { let doc_comments = trait_definition.doc_comments; let trait_definition = trait_definition.item; let name = trait_definition.name.clone(); - let location = Location::new(trait_definition.name.span(), self.file_id); + let location = trait_definition.location; // Create the corresponding module for the trait namespace let trait_id = match self.push_child_module( context, &name, ItemVisibility::Public, - Location::new(name.span(), self.file_id), + name.location(), Vec::new(), Vec::new(), false, @@ -455,7 +452,7 @@ impl<'a> ModCollector<'a> { ) { Ok(module_id) => TraitId(ModuleId { krate, local_id: module_id.local_id }), Err(error) => { - errors.push((error.into(), self.file_id)); + errors.push(error.into()); continue; } }; @@ -484,7 +481,7 @@ impl<'a> ModCollector<'a> { first_def, second_def, }; - errors.push((error.into(), self.file_id)); + errors.push(error.into()); } // Add all functions that have a default implementation in the trait @@ -573,7 +570,7 @@ impl<'a> ModCollector<'a> { first_def, second_def, }; - errors.push((error.into(), self.file_id)); + errors.push(error.into()); } } } @@ -597,7 +594,7 @@ impl<'a> ModCollector<'a> { first_def, second_def, }; - errors.push((error.into(), self.file_id)); + errors.push(error.into()); } else { let type_variable_id = context.def_interner.next_type_variable_id(); let typ = self.resolve_associated_constant_type(typ, &mut errors); @@ -608,7 +605,7 @@ impl<'a> ModCollector<'a> { type_variable_id, Kind::numeric(typ), ), - span: name.span(), + location: name.location(), }); } } @@ -626,13 +623,13 @@ impl<'a> ModCollector<'a> { first_def, second_def, }; - errors.push((error.into(), self.file_id)); + errors.push(error.into()); } else { let type_variable_id = context.def_interner.next_type_variable_id(); associated_types.push(ResolvedGeneric { name: Rc::new(name.to_string()), type_var: TypeVariable::unbound(type_variable_id, Kind::Normal), - span: name.span(), + location: name.location(), }); } } @@ -643,7 +640,6 @@ impl<'a> ModCollector<'a> { &context.def_interner, &trait_definition.generics, &mut errors, - self.file_id, ); let unresolved = UnresolvedTrait { @@ -684,8 +680,8 @@ impl<'a> ModCollector<'a> { parent_module_id: LocalModuleId, submodules: Vec>, file_id: FileId, - ) -> Vec<(CompilationError, FileId)> { - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + ) -> Vec { + let mut errors: Vec = vec![]; for submodule in submodules { let mut doc_comments = submodule.doc_comments; let submodule = submodule.item; @@ -731,7 +727,7 @@ impl<'a> ModCollector<'a> { )); } Err(error) => { - errors.push((error.into(), file_id)); + errors.push(error.into()); } }; } @@ -749,16 +745,16 @@ impl<'a> ModCollector<'a> { crate_id: CrateId, parent_file_id: FileId, parent_module_id: LocalModuleId, - ) -> Vec<(CompilationError, FileId)> { + ) -> Vec { let mut doc_comments = mod_decl.doc_comments; let mod_decl = mod_decl.item; - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + let mut errors: Vec = vec![]; let child_file_id = match find_module(&context.file_manager, self.file_id, &mod_decl.ident) { Ok(child_file_id) => child_file_id, Err(err) => { - errors.push((err.into(), self.file_id)); + errors.push(err.into()); return errors; } }; @@ -768,15 +764,15 @@ impl<'a> ModCollector<'a> { if let Some(old_location) = context.visited_files.get(&child_file_id) { let error = DefCollectorErrorKind::ModuleAlreadyPartOfCrate { mod_name: mod_decl.ident.clone(), - span: location.span, + location, }; - errors.push((error.into(), location.file)); + errors.push(error.into()); let error = DefCollectorErrorKind::ModuleOriginallyDefined { mod_name: mod_decl.ident.clone(), - span: old_location.span, + location: *old_location, }; - errors.push((error.into(), old_location.file)); + errors.push(error.into()); return errors; } @@ -786,9 +782,7 @@ impl<'a> ModCollector<'a> { let (ast, parsing_errors) = context.parsed_file_results(child_file_id); let ast = ast.into_sorted(); - errors.extend( - parsing_errors.iter().map(|e| (e.clone().into(), child_file_id)).collect::>(), - ); + errors.extend(parsing_errors.iter().map(|e| e.clone().into()).collect::>()); // Add module into def collector and get a ModuleId match self.push_child_module( @@ -833,7 +827,7 @@ impl<'a> ModCollector<'a> { )); } Err(error) => { - errors.push((error.into(), child_file_id)); + errors.push(error.into()); } } errors @@ -872,15 +866,15 @@ impl<'a> ModCollector<'a> { fn resolve_associated_constant_type( &self, typ: &UnresolvedType, - errors: &mut Vec<(CompilationError, FileId)>, + errors: &mut Vec, ) -> Type { match &typ.typ { UnresolvedTypeData::FieldElement => Type::FieldElement, UnresolvedTypeData::Integer(sign, bits) => Type::Integer(*sign, *bits), _ => { - let span = typ.span; - let error = ResolverError::AssociatedConstantsMustBeNumeric { span }; - errors.push((error.into(), self.file_id)); + let error = + ResolverError::AssociatedConstantsMustBeNumeric { location: typ.location }; + errors.push(error.into()); Type::Error } } @@ -979,9 +973,8 @@ pub fn collect_function( usage_tracker: &mut UsageTracker, function: &NoirFunction, module: ModuleId, - file: FileId, doc_comments: Vec, - errors: &mut Vec<(CompilationError, FileId)>, + errors: &mut Vec, ) -> Option { if let Some(field) = function.attributes().get_field_attribute() { if !is_native_field(&field) { @@ -1002,7 +995,7 @@ pub fn collect_function( let name = function.name_ident().clone(); let func_id = interner.push_empty_fn(); let visibility = function.def.visibility; - let location = Location::new(function.span(), file); + let location = function.location(); interner.push_function(func_id, &function.def, module, location); if interner.is_in_lsp_mode() && !function.def.is_test() { interner.register_function(func_id, &function.def); @@ -1023,7 +1016,7 @@ pub fn collect_function( first_def, second_def, }; - errors.push((error.into(), file)); + errors.push(error.into()); } Some(func_id) } @@ -1034,26 +1027,22 @@ pub fn collect_struct( def_map: &mut CrateDefMap, usage_tracker: &mut UsageTracker, struct_definition: Documented, - file_id: FileId, module_id: LocalModuleId, krate: CrateId, - definition_errors: &mut Vec<(CompilationError, FileId)>, + definition_errors: &mut Vec, ) -> Option<(TypeId, UnresolvedStruct)> { let doc_comments = struct_definition.doc_comments; let struct_definition = struct_definition.item; + let file_id = struct_definition.location.file; - check_duplicate_field_names(&struct_definition, file_id, definition_errors); + check_duplicate_field_names(&struct_definition, definition_errors); let name = struct_definition.name.clone(); let unresolved = UnresolvedStruct { file_id, module_id, struct_def: struct_definition }; - let resolved_generics = Context::resolve_generics( - interner, - &unresolved.struct_def.generics, - definition_errors, - file_id, - ); + let resolved_generics = + Context::resolve_generics(interner, &unresolved.struct_def.generics, definition_errors); // Create the corresponding module for the struct namespace let location = Location::new(name.span(), file_id); @@ -1072,13 +1061,13 @@ pub fn collect_struct( ) { Ok(module_id) => { let name = unresolved.struct_def.name.clone(); - let span = unresolved.struct_def.span; + let span = unresolved.struct_def.location.span; let attributes = unresolved.struct_def.attributes.clone(); let local_id = module_id.local_id; interner.new_type(name, span, attributes, resolved_generics, krate, local_id, file_id) } Err(error) => { - definition_errors.push((error.into(), file_id)); + definition_errors.push(error.into()); return None; } }; @@ -1113,7 +1102,7 @@ pub fn collect_struct( first_def, second_def, }; - definition_errors.push((error.into(), file_id)); + definition_errors.push(error.into()); } if interner.is_in_lsp_mode() { @@ -1132,23 +1121,19 @@ pub fn collect_enum( file_id: FileId, module_id: LocalModuleId, krate: CrateId, - definition_errors: &mut Vec<(CompilationError, FileId)>, + definition_errors: &mut Vec, ) -> Option<(TypeId, UnresolvedEnum)> { let doc_comments = enum_def.doc_comments; let enum_def = enum_def.item; - check_duplicate_variant_names(&enum_def, file_id, definition_errors); + check_duplicate_variant_names(&enum_def, definition_errors); let name = enum_def.name.clone(); let unresolved = UnresolvedEnum { file_id, module_id, enum_def }; - let resolved_generics = Context::resolve_generics( - interner, - &unresolved.enum_def.generics, - definition_errors, - file_id, - ); + let resolved_generics = + Context::resolve_generics(interner, &unresolved.enum_def.generics, definition_errors); // Create the corresponding module for the enum namespace let location = Location::new(name.span(), file_id); @@ -1167,13 +1152,13 @@ pub fn collect_enum( ) { Ok(module_id) => { let name = unresolved.enum_def.name.clone(); - let span = unresolved.enum_def.span; + let span = unresolved.enum_def.location.span; let attributes = unresolved.enum_def.attributes.clone(); let local_id = module_id.local_id; interner.new_type(name, span, attributes, resolved_generics, krate, local_id, file_id) } Err(error) => { - definition_errors.push((error.into(), file_id)); + definition_errors.push(error.into()); return None; } }; @@ -1208,7 +1193,7 @@ pub fn collect_enum( first_def, second_def, }; - definition_errors.push((error.into(), file_id)); + definition_errors.push(error.into()); } if interner.is_in_lsp_mode() { @@ -1224,7 +1209,7 @@ pub fn collect_impl( r#impl: TypeImpl, file_id: FileId, module_id: ModuleId, - errors: &mut Vec<(CompilationError, FileId)>, + errors: &mut Vec, ) { let mut unresolved_functions = UnresolvedFunctions { file_id, functions: Vec::new(), trait_id: None, self_type: None }; @@ -1235,16 +1220,16 @@ pub fn collect_impl( if method.def.attributes.is_test_function() { let error = DefCollectorErrorKind::TestOnAssociatedFunction { - span: method.name_ident().span(), + location: method.name_ident().location(), }; - errors.push((error.into(), file_id)); + errors.push(error.into()); continue; } if method.def.attributes.has_export() { let error = DefCollectorErrorKind::ExportOnAssociatedFunction { - span: method.name_ident().span(), + location: method.name_ident().location(), }; - errors.push((error.into(), file_id)); + errors.push(error.into()); } let func_id = interner.push_empty_fn(); @@ -1257,7 +1242,7 @@ pub fn collect_impl( let key = (r#impl.object_type, module_id.local_id); let methods = items.impls.entry(key).or_default(); - methods.push((r#impl.generics, r#impl.type_span, unresolved_functions)); + methods.push((r#impl.generics, r#impl.type_location, unresolved_functions)); } fn find_module( @@ -1339,11 +1324,7 @@ fn is_native_field(str: &str) -> bool { } else { BigUint::from_str_radix(str, 10) }; - if let Ok(big_num) = big_num { - big_num == FieldElement::modulus() - } else { - CHOSEN_FIELD == str - } + if let Ok(big_num) = big_num { big_num == FieldElement::modulus() } else { CHOSEN_FIELD == str } } type AssociatedTypes = Vec<(Ident, UnresolvedType)>; @@ -1401,7 +1382,7 @@ pub(crate) fn collect_global( file_id: FileId, module_id: LocalModuleId, crate_id: CrateId, -) -> (UnresolvedGlobal, Option<(CompilationError, FileId)>) { +) -> (UnresolvedGlobal, Option) { let doc_comments = global.doc_comments; let global = global.item; @@ -1435,7 +1416,7 @@ pub(crate) fn collect_global( let error = result.err().map(|(first_def, second_def)| { let err = DefCollectorErrorKind::Duplicate { typ: DuplicateType::Global, first_def, second_def }; - (err.into(), file_id) + err.into() }); interner.set_doc_comments(ReferenceId::Global(global_id), doc_comments); @@ -1446,8 +1427,7 @@ pub(crate) fn collect_global( fn check_duplicate_field_names( struct_definition: &NoirStruct, - file: FileId, - definition_errors: &mut Vec<(CompilationError, FileId)>, + definition_errors: &mut Vec, ) { let mut seen_field_names = std::collections::HashSet::new(); for field in &struct_definition.fields { @@ -1463,14 +1443,13 @@ fn check_duplicate_field_names( first_def: previous_field_name.clone(), second_def: field_name.clone(), }; - definition_errors.push((error.into(), file)); + definition_errors.push(error.into()); } } fn check_duplicate_variant_names( enum_def: &NoirEnumeration, - file: FileId, - definition_errors: &mut Vec<(CompilationError, FileId)>, + definition_errors: &mut Vec, ) { let mut seen_variant_names = std::collections::HashSet::new(); for variant in &enum_def.variants { @@ -1486,7 +1465,7 @@ fn check_duplicate_variant_names( first_def: previous_variant_name.clone(), second_def: variant_name.clone(), }; - definition_errors.push((error.into(), file)); + definition_errors.push(error.into()); } } @@ -1494,7 +1473,6 @@ fn check_duplicate_variant_names( mod find_module_tests { use super::*; - use noirc_errors::Spanned; use std::path::{Path, PathBuf}; fn add_file(file_manager: &mut FileManager, dir: &Path, file_name: &str) -> FileId { @@ -1513,7 +1491,9 @@ mod find_module_tests { anchor: FileId, mod_name: &str, ) -> Result { - let mod_name = Ident(Spanned::from_position(0, 1, mod_name.to_string())); + let span = Span::from(0..1); + let location = Location::new(span, FileId::dummy()); + let mod_name = Ident::new(mod_name.to_string(), location); super::find_module(file_manager, anchor, &mod_name) } 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 1ca62acd29b1..7f17b1e30430 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 @@ -2,9 +2,7 @@ use crate::ast::{Ident, ItemVisibility, Path, UnsupportedNumericGenericType}; use crate::hir::resolution::import::PathResolutionError; use crate::hir::type_check::generics::TraitGenerics; -use noirc_errors::CustomDiagnostic as Diagnostic; -use noirc_errors::FileDiagnostic; -use noirc_errors::Span; +use noirc_errors::{CustomDiagnostic as Diagnostic, Location}; use thiserror::Error; use std::fmt; @@ -25,7 +23,7 @@ pub enum DuplicateType { EnumVariant, } -#[derive(Error, Debug, Clone)] +#[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum DefCollectorErrorKind { #[error("Duplicate {typ}")] Duplicate { typ: DuplicateType, first_def: Ident, second_def: Ident }, @@ -38,23 +36,13 @@ pub enum DefCollectorErrorKind { #[error("Cannot re-export {item_name} because it has less visibility than this use statement")] CannotReexportItemWithLessVisibility { item_name: Ident, desired_visibility: ItemVisibility }, #[error("Non-struct type used in impl")] - NonStructTypeInImpl { span: Span }, + NonStructTypeInImpl { location: Location }, #[error("Cannot implement trait on a mutable reference type")] - MutableReferenceInTraitImpl { span: Span }, + MutableReferenceInTraitImpl { location: Location }, #[error("Impl for type `{typ}` overlaps with existing impl")] - OverlappingImpl { span: Span, typ: crate::Type }, - #[error("Previous impl defined here")] - OverlappingImplNote { span: Span }, + OverlappingImpl { typ: crate::Type, location: Location, prev_location: Location }, #[error("Cannot `impl` a type defined outside the current crate")] - ForeignImpl { span: Span, type_name: String }, - #[error("Mismatched number of generics in {location}")] - MismatchGenericCount { - actual_generic_count: usize, - expected_generic_count: usize, - location: &'static str, - origin: String, - span: Span, - }, + ForeignImpl { location: Location, type_name: String }, #[error("Method is not defined in trait")] MethodNotInTrait { trait_name: Ident, impl_method: Ident }, #[error("Only traits can be implemented")] @@ -62,35 +50,66 @@ pub enum DefCollectorErrorKind { #[error("Trait not found")] TraitNotFound { trait_path: Path }, #[error("Missing Trait method implementation")] - TraitMissingMethod { trait_name: Ident, method_name: Ident, trait_impl_span: Span }, + TraitMissingMethod { trait_name: Ident, method_name: Ident, trait_impl_location: Location }, #[error("Module is already part of the crate")] - ModuleAlreadyPartOfCrate { mod_name: Ident, span: Span }, + ModuleAlreadyPartOfCrate { mod_name: Ident, location: Location }, #[error("Module was originally declared here")] - ModuleOriginallyDefined { mod_name: Ident, span: Span }, - #[error( - "Either the type or the trait must be from the same crate as the trait implementation" - )] - TraitImplOrphaned { span: Span }, + ModuleOriginallyDefined { mod_name: Ident, location: Location }, + #[error("Either the type or the trait must be from the same crate as the trait implementation")] + TraitImplOrphaned { location: Location }, #[error("impl has stricter requirements than trait")] ImplIsStricterThanTrait { constraint_typ: crate::Type, constraint_name: String, constraint_generics: TraitGenerics, - constraint_span: Span, + constraint_location: Location, trait_method_name: String, - trait_method_span: Span, + trait_method_location: Location, }, #[error("{0}")] UnsupportedNumericGenericType(#[from] UnsupportedNumericGenericType), #[error("The `#[test]` attribute may only be used on a non-associated function")] - TestOnAssociatedFunction { span: Span }, + TestOnAssociatedFunction { location: Location }, #[error("The `#[export]` attribute may only be used on a non-associated function")] - ExportOnAssociatedFunction { span: Span }, + ExportOnAssociatedFunction { location: Location }, } impl DefCollectorErrorKind { - pub fn into_file_diagnostic(&self, file: fm::FileId) -> FileDiagnostic { - Diagnostic::from(self).in_file(file) + pub fn location(&self) -> Location { + match self { + DefCollectorErrorKind::Duplicate { second_def: ident, .. } + | DefCollectorErrorKind::UnresolvedModuleDecl { mod_name: ident, .. } + | DefCollectorErrorKind::CannotReexportItemWithLessVisibility { + item_name: ident, + .. + } + | DefCollectorErrorKind::MethodNotInTrait { impl_method: ident, .. } + | DefCollectorErrorKind::OverlappingModuleDecls { mod_name: ident, .. } => { + ident.location() + } + DefCollectorErrorKind::PathResolutionError(path_resolution_error) => { + path_resolution_error.location() + } + DefCollectorErrorKind::ImplIsStricterThanTrait { + trait_method_location: location, + .. + } + | DefCollectorErrorKind::TestOnAssociatedFunction { location } + | DefCollectorErrorKind::ExportOnAssociatedFunction { location } + | DefCollectorErrorKind::NonStructTypeInImpl { location } + | DefCollectorErrorKind::MutableReferenceInTraitImpl { location } + | DefCollectorErrorKind::OverlappingImpl { location, .. } + | DefCollectorErrorKind::ModuleAlreadyPartOfCrate { location, .. } + | DefCollectorErrorKind::ModuleOriginallyDefined { location, .. } + | DefCollectorErrorKind::TraitImplOrphaned { location } + | DefCollectorErrorKind::TraitMissingMethod { trait_impl_location: location, .. } + | DefCollectorErrorKind::ForeignImpl { location, .. } => *location, + DefCollectorErrorKind::NotATrait { not_a_trait_name: path } + | DefCollectorErrorKind::TraitNotFound { trait_path: path } => path.location, + DefCollectorErrorKind::UnsupportedNumericGenericType( + unsupported_numeric_generic_type, + ) => unsupported_numeric_generic_type.ident.location(), + } } } @@ -100,9 +119,11 @@ impl<'a> From<&'a UnsupportedNumericGenericType> for Diagnostic { let typ = &error.typ; Diagnostic::simple_error( - format!("{name} has a type of {typ}. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`."), + format!( + "{name} has a type of {typ}. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`." + ), "Unsupported numeric generic type".to_string(), - error.ident.0.span(), + error.ident.0.location(), ) } } @@ -135,35 +156,35 @@ impl<'a> From<&'a DefCollectorErrorKind> for Diagnostic { &typ, &first_def.0.contents ); { - let first_span = first_def.0.span(); - let second_span = second_def.0.span(); + let first_location = first_def.0.location(); + let second_location = second_def.0.location(); let mut diag = Diagnostic::simple_error( primary_message, - format!("First {} found here", &typ), - first_span, + format!("Second {} found here", &typ), + second_location, ); - diag.add_secondary(format!("Second {} found here", &typ), second_span); + diag.add_secondary(format!("First {} found here", &typ), first_location); diag } } DefCollectorErrorKind::UnresolvedModuleDecl { mod_name, expected_path, alternative_path } => { - let span = mod_name.0.span(); + let location = mod_name.0.location(); let mod_name = &mod_name.0.contents; Diagnostic::simple_error( format!("No module `{mod_name}` at path `{expected_path}` or `{alternative_path}`"), String::new(), - span, + location, ) } DefCollectorErrorKind::OverlappingModuleDecls { mod_name, expected_path, alternative_path } => { - let span = mod_name.0.span(); + let location = mod_name.0.location(); let mod_name = &mod_name.0.contents; Diagnostic::simple_error( format!("Overlapping modules `{mod_name}` at path `{expected_path}` and `{alternative_path}`"), String::new(), - span, + location, ) } DefCollectorErrorKind::PathResolutionError(error) => error.into(), @@ -171,68 +192,48 @@ impl<'a> From<&'a DefCollectorErrorKind> for Diagnostic { Diagnostic::simple_error( format!("cannot re-export {item_name} because it has less visibility than this use statement"), format!("consider marking {item_name} as {desired_visibility}"), - item_name.span()) + item_name.location()) } - DefCollectorErrorKind::NonStructTypeInImpl { span } => Diagnostic::simple_error( + DefCollectorErrorKind::NonStructTypeInImpl { location } => Diagnostic::simple_error( "Non-struct type used in impl".into(), "Only struct types may have implementation methods".into(), - *span, + *location, ), - DefCollectorErrorKind::MutableReferenceInTraitImpl { span } => Diagnostic::simple_error( + DefCollectorErrorKind::MutableReferenceInTraitImpl { location } => Diagnostic::simple_error( "Trait impls are not allowed on mutable reference types".into(), "Try using a struct type here instead".into(), - *span, + *location, ), - DefCollectorErrorKind::OverlappingImpl { span, typ } => { - Diagnostic::simple_error( + DefCollectorErrorKind::OverlappingImpl { location, typ, prev_location } => { + let mut diagnostic = Diagnostic::simple_error( format!("Impl for type `{typ}` overlaps with existing impl"), "Overlapping impl".into(), - *span, - ) - } - DefCollectorErrorKind::OverlappingImplNote { span } => { - // This should be a note or part of the previous error eventually. - // This must be an error to appear next to the previous OverlappingImpl - // error since we sort warnings first. - Diagnostic::simple_error( - "Previous impl defined here".into(), - "Previous impl defined here".into(), - *span, - ) + *location, + ); + diagnostic.add_secondary("Previous impl defined here".into(), *prev_location); + diagnostic } - DefCollectorErrorKind::ForeignImpl { span, type_name } => Diagnostic::simple_error( + DefCollectorErrorKind::ForeignImpl { location, type_name } => Diagnostic::simple_error( "Cannot `impl` a type that was defined outside the current crate".into(), format!("{type_name} was defined outside the current crate"), - *span, + *location, ), DefCollectorErrorKind::TraitNotFound { trait_path } => Diagnostic::simple_error( format!("Trait {trait_path} not found"), "".to_string(), - trait_path.span(), + trait_path.location, ), - DefCollectorErrorKind::MismatchGenericCount { - actual_generic_count, - expected_generic_count, - location, - origin, - span, - } => { - let plural = if *expected_generic_count == 1 { "" } else { "s" }; - let primary_message = format!( - "`{origin}` expects {expected_generic_count} generic{plural}, but {location} has {actual_generic_count}"); - Diagnostic::simple_error(primary_message, "".to_string(), *span) - } DefCollectorErrorKind::MethodNotInTrait { trait_name, impl_method } => { let trait_name = &trait_name.0.contents; - let impl_method_span = impl_method.span(); + let impl_method_location = impl_method.location(); let impl_method_name = &impl_method.0.contents; let primary_message = format!("Method with name `{impl_method_name}` is not part of trait `{trait_name}`, therefore it can't be implemented"); - Diagnostic::simple_error(primary_message, "".to_owned(), impl_method_span) + Diagnostic::simple_error(primary_message, "".to_owned(), impl_method_location) } DefCollectorErrorKind::TraitMissingMethod { trait_name, method_name, - trait_impl_span, + trait_impl_location, } => { let trait_name = &trait_name.0.contents; let impl_method_name = &method_name.0.contents; @@ -242,53 +243,53 @@ impl<'a> From<&'a DefCollectorErrorKind> for Diagnostic { Diagnostic::simple_error( primary_message, format!("Please implement {impl_method_name} here"), - *trait_impl_span, + *trait_impl_location, ) } DefCollectorErrorKind::NotATrait { not_a_trait_name } => { - let span = not_a_trait_name.span(); + let location = not_a_trait_name.location; Diagnostic::simple_error( format!("{not_a_trait_name} is not a trait, therefore it can't be implemented"), String::new(), - span, + location, ) } - DefCollectorErrorKind::ModuleAlreadyPartOfCrate { mod_name, span } => { + DefCollectorErrorKind::ModuleAlreadyPartOfCrate { mod_name, location } => { let message = format!("Module '{mod_name}' is already part of the crate"); let secondary = String::new(); - Diagnostic::simple_error(message, secondary, *span) + Diagnostic::simple_error(message, secondary, *location) } - DefCollectorErrorKind::ModuleOriginallyDefined { mod_name, span } => { + DefCollectorErrorKind::ModuleOriginallyDefined { mod_name, location } => { let message = format!("Note: {mod_name} was originally declared here"); let secondary = String::new(); - Diagnostic::simple_error(message, secondary, *span) + Diagnostic::simple_error(message, secondary, *location) } - DefCollectorErrorKind::TraitImplOrphaned { span } => Diagnostic::simple_error( + DefCollectorErrorKind::TraitImplOrphaned { location } => Diagnostic::simple_error( "Orphaned trait implementation".into(), "Either the type or the trait must be from the same crate as the trait implementation".into(), - *span, + *location, ), - DefCollectorErrorKind::ImplIsStricterThanTrait { constraint_typ, constraint_name, constraint_generics, constraint_span, trait_method_name, trait_method_span } => { + DefCollectorErrorKind::ImplIsStricterThanTrait { constraint_typ, constraint_name, constraint_generics, constraint_location, trait_method_name, trait_method_location } => { let constraint = format!("{}{}", constraint_name, constraint_generics); let mut diag = Diagnostic::simple_error( "impl has stricter requirements than trait".to_string(), format!("impl has extra requirement `{constraint_typ}: {constraint}`"), - *constraint_span, + *constraint_location, ); - diag.add_secondary(format!("definition of `{trait_method_name}` from trait"), *trait_method_span); + diag.add_secondary(format!("definition of `{trait_method_name}` from trait"), *trait_method_location); diag } DefCollectorErrorKind::UnsupportedNumericGenericType(err) => err.into(), - DefCollectorErrorKind::TestOnAssociatedFunction { span } => Diagnostic::simple_error( + DefCollectorErrorKind::TestOnAssociatedFunction { location } => Diagnostic::simple_error( "The `#[test]` attribute is disallowed on `impl` methods".into(), String::new(), - *span, + *location, ), - DefCollectorErrorKind::ExportOnAssociatedFunction { span } => Diagnostic::simple_error( + DefCollectorErrorKind::ExportOnAssociatedFunction { location } => Diagnostic::simple_error( "The `#[export]` attribute is disallowed on `impl` methods".into(), String::new(), - *span, + *location, ), } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/item_scope.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/item_scope.rs index 3ca89e56bbc4..bbc2f59c655b 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/item_scope.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/item_scope.rs @@ -1,8 +1,8 @@ -use super::{namespace::PerNs, ModuleDefId, ModuleId}; +use super::{ModuleDefId, ModuleId, namespace::PerNs}; use crate::ast::{Ident, ItemVisibility}; use crate::node_interner::{FuncId, TraitId}; -use std::collections::{hash_map::Entry, HashMap}; +use std::collections::{HashMap, hash_map::Entry}; type Scope = HashMap, (ModuleDefId, ItemVisibility, bool /*is_prelude*/)>; @@ -50,11 +50,7 @@ impl ItemScope { let is_prelude = std::mem::replace(&mut n.get_mut().2, is_prelude); let old_ident = o.key(); - if is_prelude { - Ok(()) - } else { - Err((old_ident.clone(), name)) - } + if is_prelude { Ok(()) } else { Err((old_ident.clone(), name)) } } else { trait_hashmap.insert(trait_id, (mod_def, visibility, is_prelude)); Ok(()) 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 fae891a16475..5377ee7d42a6 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 @@ -1,6 +1,7 @@ +use crate::elaborator::FrontendOptions; use crate::graph::{CrateGraph, CrateId}; -use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector}; use crate::hir::Context; +use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector}; use crate::node_interner::{FuncId, GlobalId, NodeInterner, TypeId}; use crate::parse_program; use crate::parser::{ParsedModule, ParserError}; @@ -77,15 +78,14 @@ impl CrateDefMap { pub fn collect_defs( crate_id: CrateId, context: &mut Context, - debug_comptime_in_file: Option<&str>, - pedantic_solving: bool, - ) -> Vec<(CompilationError, FileId)> { + options: FrontendOptions, + ) -> Vec { // Check if this Crate has already been compiled // XXX: There is probably a better alternative for this. // Without this check, the compiler will panic as it does not // expect the same crate to be processed twice. It would not // make the implementation wrong, if the same crate was processed twice, it just makes it slow. - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + let mut errors: Vec = vec![]; if context.def_map(&crate_id).is_some() { return errors; } @@ -120,13 +120,10 @@ impl CrateDefMap { context, ast, root_file_id, - debug_comptime_in_file, - pedantic_solving, + options, )); - errors.extend( - parsing_errors.iter().map(|e| (e.clone().into(), root_file_id)).collect::>(), - ); + errors.extend(parsing_errors.iter().map(|e| e.clone().into()).collect::>()); errors } @@ -373,7 +370,7 @@ pub struct Contract { /// Given a FileId, fetch the File, from the FileManager and parse it's content pub fn parse_file(fm: &FileManager, file_id: FileId) -> (ParsedModule, Vec) { let file_source = fm.fetch_file(file_id).expect("File does not exist"); - parse_program(file_source) + parse_program(file_source, file_id) } impl std::ops::Index for CrateDefMap { 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 e85fa629d561..8f2c3c5f8675 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/mod.rs @@ -14,7 +14,7 @@ use crate::parser::ParserError; use crate::usage_tracker::UsageTracker; use crate::{Generics, Kind, ParsedModule, ResolvedGeneric, TypeVariable}; use def_collector::dc_crate::CompilationError; -use def_map::{fully_qualified_module_path, Contract, CrateDefMap}; +use def_map::{Contract, CrateDefMap, fully_qualified_module_path}; use fm::{FileId, FileManager}; use iter_extended::vecmap; use noirc_errors::Location; @@ -139,11 +139,7 @@ impl Context<'_, '_> { let parent = def_map.get_module_path_with_separator(module_id.local_id.0, module.parent, "::"); - if parent.is_empty() { - name.into() - } else { - format!("{parent}::{name}") - } + if parent.is_empty() { name.into() } else { format!("{parent}::{name}") } } /// Returns a fully-qualified path to the given [StructId] from the given [CrateId]. This function also @@ -232,26 +228,25 @@ impl Context<'_, '_> { pub(crate) fn resolve_generics( interner: &NodeInterner, generics: &UnresolvedGenerics, - errors: &mut Vec<(CompilationError, FileId)>, - file_id: FileId, + errors: &mut Vec, ) -> Generics { vecmap(generics, |generic| { // Map the generic to a fresh type variable let id = interner.next_type_variable_id(); let type_var_kind = generic.kind().unwrap_or_else(|err| { - errors.push((err.into(), file_id)); + errors.push(err.into()); // When there's an error, unify with any other kinds Kind::Any }); let type_var = TypeVariable::unbound(id, type_var_kind); let ident = generic.ident(); - let span = ident.0.span(); + let location = ident.0.location(); // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); - ResolvedGeneric { name, type_var, span } + ResolvedGeneric { name, type_var, location } }) } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs index 5af0cf41a627..bc1c519ed5d8 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -1,9 +1,10 @@ use acvm::FieldElement; pub use noirc_errors::Span; -use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic, Location}; +use noirc_errors::{CustomDiagnostic as Diagnostic, Location}; use thiserror::Error; use crate::{ + Kind, Type, ast::{Ident, UnsupportedNumericGenericType}, hir::{ comptime::{InterpreterError, Value}, @@ -11,7 +12,6 @@ use crate::{ }, parser::ParserError, usage_tracker::UnusedItem, - Kind, Type, }; use super::import::PathResolutionError; @@ -27,166 +27,257 @@ pub enum PubPosition { #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum ResolverError { #[error("Duplicate definition")] - DuplicateDefinition { name: String, first_span: Span, second_span: Span }, + DuplicateDefinition { name: String, first_location: Location, second_location: Location }, #[error("Unused variable")] UnusedVariable { ident: Ident }, #[error("Unused {}", item.item_type())] UnusedItem { ident: Ident, item: UnusedItem }, #[error("Unconditional recursion")] - UnconditionalRecursion { name: String, span: Span }, + UnconditionalRecursion { name: String, location: Location }, #[error("Could not find variable in this scope")] - VariableNotDeclared { name: String, span: Span }, + VariableNotDeclared { name: String, location: Location }, #[error("path is not an identifier")] - PathIsNotIdent { span: Span }, + PathIsNotIdent { location: Location }, #[error("could not resolve path")] PathResolutionError(#[from] PathResolutionError), #[error("Expected")] - Expected { span: Span, expected: &'static str, got: &'static str }, + Expected { location: Location, expected: &'static str, got: &'static str }, #[error("Duplicate field in constructor")] DuplicateField { field: Ident }, #[error("No such field in struct")] NoSuchField { field: Ident, struct_definition: Ident }, #[error("Missing fields from struct")] - MissingFields { span: Span, missing_fields: Vec, struct_definition: Ident }, + MissingFields { location: Location, missing_fields: Vec, struct_definition: Ident }, #[error("Unneeded 'mut', pattern is already marked as mutable")] - UnnecessaryMut { first_mut: Span, second_mut: Span }, + UnnecessaryMut { first_mut: Location, second_mut: Location }, #[error("Unneeded 'pub', function is not the main method")] UnnecessaryPub { ident: Ident, position: PubPosition }, #[error("Required 'pub', main function must return public value")] NecessaryPub { ident: Ident }, #[error("Missing expression for declared constant")] - MissingRhsExpr { name: String, span: Span }, + MissingRhsExpr { name: String, location: Location }, #[error("Expression invalid in an array length context")] - InvalidArrayLengthExpr { span: Span }, + InvalidArrayLengthExpr { location: Location }, #[error("Integer too large to be evaluated in an array length context")] - IntegerTooLarge { span: Span }, + IntegerTooLarge { location: Location }, #[error("No global or generic type parameter found with the given name")] NoSuchNumericTypeVariable { path: crate::ast::Path }, #[error("Closures cannot capture mutable variables")] - CapturedMutableVariable { span: Span }, + CapturedMutableVariable { location: Location }, #[error("Test functions are not allowed to have any parameters")] - TestFunctionHasParameters { span: Span }, + TestFunctionHasParameters { location: Location }, #[error("Only struct types can be used in constructor expressions")] - NonStructUsedInConstructor { typ: String, span: Span }, + NonStructUsedInConstructor { typ: String, location: Location }, #[error("Only struct types can have generics")] - NonStructWithGenerics { span: Span }, + NonStructWithGenerics { location: Location }, #[error("Cannot apply generics on Self type")] - GenericsOnSelfType { span: Span }, + GenericsOnSelfType { location: Location }, #[error("Cannot apply generics on an associated type")] - GenericsOnAssociatedType { span: Span }, + GenericsOnAssociatedType { location: Location }, #[error("{0}")] ParserError(Box), - #[error("Cannot create a mutable reference to {variable}, it was declared to be immutable")] - MutableReferenceToImmutableVariable { variable: String, span: Span }, - #[error("Mutable references to array indices are unsupported")] - MutableReferenceToArrayElement { span: Span }, #[error("Closure environment must be a tuple or unit type")] - InvalidClosureEnvironment { typ: Type, span: Span }, + InvalidClosureEnvironment { typ: Type, location: Location }, #[error("Nested slices, i.e. slices within an array or slice, are not supported")] - NestedSlices { span: Span }, + NestedSlices { location: Location }, #[error("#[abi(tag)] attribute is only allowed in contracts")] - AbiAttributeOutsideContract { span: Span }, - #[error("Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library")] - LowLevelFunctionOutsideOfStdlib { ident: Ident }, + AbiAttributeOutsideContract { location: Location }, #[error( - "Usage of the `#[oracle]` function attribute is only valid on unconstrained functions" + "Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library" )] + LowLevelFunctionOutsideOfStdlib { ident: Ident }, + #[error("Usage of the `#[oracle]` function attribute is only valid on unconstrained functions")] OracleMarkedAsConstrained { ident: Ident }, #[error("Oracle functions cannot be called directly from constrained functions")] - UnconstrainedOracleReturnToConstrained { span: Span }, + UnconstrainedOracleReturnToConstrained { location: Location }, #[error("Dependency cycle found, '{item}' recursively depends on itself: {cycle} ")] - DependencyCycle { span: Span, item: String, cycle: String }, + DependencyCycle { location: Location, item: String, cycle: String }, #[error("break/continue are only allowed in unconstrained functions")] - JumpInConstrainedFn { is_break: bool, span: Span }, + JumpInConstrainedFn { is_break: bool, location: Location }, #[error("`loop` is only allowed in unconstrained functions")] - LoopInConstrainedFn { span: Span }, + LoopInConstrainedFn { location: Location }, #[error("`loop` must have at least one `break` in it")] - LoopWithoutBreak { span: Span }, + LoopWithoutBreak { location: Location }, #[error("`while` is only allowed in unconstrained functions")] - WhileInConstrainedFn { span: Span }, + WhileInConstrainedFn { location: Location }, #[error("break/continue are only allowed within loops")] - JumpOutsideLoop { is_break: bool, span: Span }, + JumpOutsideLoop { is_break: bool, location: Location }, #[error("Only `comptime` globals can be mutable")] - MutableGlobal { span: Span }, + MutableGlobal { location: Location }, #[error("Globals must have a specified type")] - UnspecifiedGlobalType { span: Span, expected_type: Type }, + UnspecifiedGlobalType { + pattern_location: Location, + expr_location: Location, + expected_type: Type, + }, #[error("Global failed to evaluate")] - UnevaluatedGlobalType { span: Span }, + UnevaluatedGlobalType { location: Location }, #[error("Globals used in a type position must be non-negative")] - NegativeGlobalType { span: Span, global_value: Value }, + NegativeGlobalType { location: Location, global_value: Value }, #[error("Globals used in a type position must be integers")] - NonIntegralGlobalType { span: Span, global_value: Value }, + NonIntegralGlobalType { location: Location, global_value: Value }, #[error("Global value `{global_value}` is larger than its kind's maximum value")] - GlobalLargerThanKind { span: Span, global_value: FieldElement, kind: Kind }, + GlobalLargerThanKind { location: Location, global_value: FieldElement, kind: Kind }, #[error("Self-referential types are not supported")] - SelfReferentialType { span: Span }, + SelfReferentialType { location: Location }, #[error("#[no_predicates] attribute is only allowed on constrained functions")] NoPredicatesAttributeOnUnconstrained { ident: Ident }, #[error("#[fold] attribute is only allowed on constrained functions")] FoldAttributeOnUnconstrained { ident: Ident }, #[error("expected type, found numeric generic parameter")] - NumericGenericUsedForType { name: String, span: Span }, + NumericGenericUsedForType { name: String, location: Location }, #[error("Invalid array length construction")] ArrayLengthInterpreter { error: InterpreterError }, #[error("The unquote operator '$' can only be used within a quote expression")] - UnquoteUsedOutsideQuote { span: Span }, - #[error("\"as trait path\" not yet implemented")] - AsTraitPathNotYetImplemented { span: Span }, + UnquoteUsedOutsideQuote { location: Location }, #[error("Invalid syntax in macro call")] - InvalidSyntaxInMacroCall { span: Span }, + InvalidSyntaxInMacroCall { location: Location }, #[error("Macros must be comptime functions")] - MacroIsNotComptime { span: Span }, + MacroIsNotComptime { location: Location }, #[error("Annotation name must refer to a comptime function")] - NonFunctionInAnnotation { span: Span }, + NonFunctionInAnnotation { location: Location }, #[error("Type `{typ}` was inserted into the generics list from a macro, but is not a generic")] - MacroResultInGenericsListNotAGeneric { span: Span, typ: Type }, + MacroResultInGenericsListNotAGeneric { location: Location, typ: Type }, #[error("Named type arguments aren't allowed in a {item_kind}")] - NamedTypeArgs { span: Span, item_kind: &'static str }, + NamedTypeArgs { location: Location, item_kind: &'static str }, #[error("Associated constants may only be a field or integer type")] - AssociatedConstantsMustBeNumeric { span: Span }, + AssociatedConstantsMustBeNumeric { location: Location }, #[error("Computing `{lhs} {op} {rhs}` failed with error {err}")] BinaryOpError { lhs: FieldElement, op: crate::BinaryTypeOperator, rhs: FieldElement, err: Box, - span: Span, + location: Location, }, #[error("`quote` cannot be used in runtime code")] - QuoteInRuntimeCode { span: Span }, + QuoteInRuntimeCode { location: Location }, #[error("Comptime-only type `{typ}` cannot be used in runtime code")] - ComptimeTypeInRuntimeCode { typ: String, span: Span }, + ComptimeTypeInRuntimeCode { typ: String, location: Location }, #[error("Comptime variable `{name}` cannot be mutated in a non-comptime context")] - MutatingComptimeInNonComptimeContext { name: String, span: Span }, + MutatingComptimeInNonComptimeContext { name: String, location: Location }, #[error("Failed to parse `{statement}` as an expression")] - InvalidInternedStatementInExpr { statement: String, span: Span }, + InvalidInternedStatementInExpr { statement: String, location: Location }, #[error("{0}")] UnsupportedNumericGenericType(#[from] UnsupportedNumericGenericType), #[error("Type `{typ}` is more private than item `{item}`")] - TypeIsMorePrivateThenItem { typ: String, item: String, span: Span }, + TypeIsMorePrivateThenItem { typ: String, item: String, location: Location }, #[error("Unable to parse attribute `{attribute}`")] - UnableToParseAttribute { attribute: String, span: Span }, + UnableToParseAttribute { attribute: String, location: Location }, #[error("Attribute function `{function}` is not a path")] - AttributeFunctionIsNotAPath { function: String, span: Span }, + AttributeFunctionIsNotAPath { function: String, location: Location }, #[error("Attribute function `{name}` is not in scope")] - AttributeFunctionNotInScope { name: String, span: Span }, - #[error("The trait `{missing_trait}` is not implemented for `{type_missing_trait}")] + AttributeFunctionNotInScope { name: String, location: Location }, + #[error("The trait `{missing_trait}` is not implemented for `{type_missing_trait}`")] TraitNotImplemented { impl_trait: String, missing_trait: String, type_missing_trait: String, - span: Span, + location: Location, missing_trait_location: Location, }, #[error("`loop` statements are not yet implemented")] - LoopNotYetSupported { span: Span }, + LoopNotYetSupported { location: Location }, #[error("Expected a trait but found {found}")] - ExpectedTrait { found: String, span: Span }, + ExpectedTrait { found: String, location: Location }, + #[error("Invalid syntax in match pattern")] + InvalidSyntaxInPattern { location: Location }, + #[error("Variable '{existing}' was already defined in the same match pattern")] + VariableAlreadyDefinedInPattern { existing: Ident, new_location: Location }, + #[error("Only integer globals can be used in match patterns")] + NonIntegerGlobalUsedInPattern { location: Location }, + #[error("Cannot match on values of type `{typ}`")] + TypeUnsupportedInMatch { typ: Type, location: Location }, + #[error("Expected a struct, enum, or literal value in pattern, but found a {item}")] + UnexpectedItemInPattern { location: Location, item: &'static str }, + #[error("Trait `{trait_name}` doesn't have a method named `{method_name}`")] + NoSuchMethodInTrait { trait_name: String, method_name: String, location: Location }, } impl ResolverError { - pub fn into_file_diagnostic(&self, file: fm::FileId) -> FileDiagnostic { - Diagnostic::from(self).in_file(file) + pub fn location(&self) -> Location { + match self { + ResolverError::DuplicateDefinition { second_location: location, .. } + | ResolverError::UnconditionalRecursion { location, .. } + | ResolverError::PathIsNotIdent { location } + | ResolverError::Expected { location, .. } + | ResolverError::VariableNotDeclared { location, .. } + | ResolverError::MissingFields { location, .. } + | ResolverError::UnnecessaryMut { second_mut: location, .. } + | ResolverError::TypeIsMorePrivateThenItem { location, .. } + | ResolverError::UnableToParseAttribute { location, .. } + | ResolverError::AttributeFunctionIsNotAPath { location, .. } + | ResolverError::AttributeFunctionNotInScope { location, .. } + | ResolverError::TraitNotImplemented { location, .. } + | ResolverError::LoopNotYetSupported { location } + | ResolverError::ExpectedTrait { location, .. } + | ResolverError::MissingRhsExpr { location, .. } + | ResolverError::InvalidArrayLengthExpr { location } + | ResolverError::IntegerTooLarge { location } + | ResolverError::CapturedMutableVariable { location } + | ResolverError::TestFunctionHasParameters { location } + | ResolverError::NonStructUsedInConstructor { location, .. } + | ResolverError::NonStructWithGenerics { location } + | ResolverError::GenericsOnSelfType { location } + | ResolverError::GenericsOnAssociatedType { location } + | ResolverError::InvalidClosureEnvironment { location, .. } + | ResolverError::NestedSlices { location } + | ResolverError::AbiAttributeOutsideContract { location } + | ResolverError::UnconstrainedOracleReturnToConstrained { location } + | ResolverError::DependencyCycle { location, .. } + | ResolverError::JumpInConstrainedFn { location, .. } + | ResolverError::LoopInConstrainedFn { location } + | ResolverError::LoopWithoutBreak { location } + | ResolverError::WhileInConstrainedFn { location } + | ResolverError::JumpOutsideLoop { location, .. } + | ResolverError::MutableGlobal { location } + | ResolverError::UnspecifiedGlobalType { pattern_location: location, .. } + | ResolverError::UnevaluatedGlobalType { location } + | ResolverError::NegativeGlobalType { location, .. } + | ResolverError::NonIntegralGlobalType { location, .. } + | ResolverError::GlobalLargerThanKind { location, .. } + | ResolverError::SelfReferentialType { location } + | ResolverError::NumericGenericUsedForType { location, .. } + | ResolverError::UnquoteUsedOutsideQuote { location } + | ResolverError::InvalidSyntaxInMacroCall { location } + | ResolverError::MacroIsNotComptime { location } + | ResolverError::NonFunctionInAnnotation { location } + | ResolverError::MacroResultInGenericsListNotAGeneric { location, .. } + | ResolverError::NamedTypeArgs { location, .. } + | ResolverError::AssociatedConstantsMustBeNumeric { location } + | ResolverError::BinaryOpError { location, .. } + | ResolverError::QuoteInRuntimeCode { location } + | ResolverError::ComptimeTypeInRuntimeCode { location, .. } + | ResolverError::MutatingComptimeInNonComptimeContext { location, .. } + | ResolverError::InvalidInternedStatementInExpr { location, .. } + | ResolverError::InvalidSyntaxInPattern { location } + | ResolverError::NonIntegerGlobalUsedInPattern { location, .. } + | ResolverError::TypeUnsupportedInMatch { location, .. } + | ResolverError::UnexpectedItemInPattern { location, .. } + | ResolverError::NoSuchMethodInTrait { location, .. } + | ResolverError::VariableAlreadyDefinedInPattern { new_location: location, .. } => { + *location + } + ResolverError::UnusedVariable { ident } + | ResolverError::UnusedItem { ident, .. } + | ResolverError::DuplicateField { field: ident } + | ResolverError::NoSuchField { field: ident, .. } + | ResolverError::UnnecessaryPub { ident, .. } + | ResolverError::NecessaryPub { ident } + | ResolverError::LowLevelFunctionOutsideOfStdlib { ident } + | ResolverError::OracleMarkedAsConstrained { ident } + | ResolverError::NoPredicatesAttributeOnUnconstrained { ident } + | ResolverError::FoldAttributeOnUnconstrained { ident } => ident.location(), + ResolverError::ArrayLengthInterpreter { error } => error.location(), + ResolverError::PathResolutionError(path_resolution_error) => { + path_resolution_error.location() + } + ResolverError::NoSuchNumericTypeVariable { path } => path.location, + ResolverError::ParserError(parser_error) => parser_error.location(), + ResolverError::UnsupportedNumericGenericType(unsupported_numeric_generic_type) => { + unsupported_numeric_generic_type.ident.location() + } + } } } @@ -196,13 +287,13 @@ impl<'a> From<&'a ResolverError> for Diagnostic { /// soundness of the generated program fn from(error: &'a ResolverError) -> Diagnostic { match error { - ResolverError::DuplicateDefinition { name, first_span, second_span } => { + ResolverError::DuplicateDefinition { name, first_location, second_location} => { let mut diag = Diagnostic::simple_error( format!("duplicate definitions of {name} found"), - "first definition found here".to_string(), - *first_span, + "second definition found here".to_string(), + *second_location, ); - diag.add_secondary("second definition found here".to_string(), *second_span); + diag.add_secondary("first definition found here".to_string(), *first_location); diag } ResolverError::UnusedVariable { ident } => { @@ -210,8 +301,8 @@ impl<'a> From<&'a ResolverError> for Diagnostic { let mut diagnostic = Diagnostic::simple_warning( format!("unused variable {name}"), - "unused variable ".to_string(), - ident.span(), + "unused variable".to_string(), + ident.location(), ); diagnostic.unnecessary = true; diagnostic @@ -225,64 +316,64 @@ impl<'a> From<&'a ResolverError> for Diagnostic { Diagnostic::simple_warning( format!("{item_type} `{name}` is never constructed"), format!("{item_type} is never constructed"), - ident.span(), + ident.location(), ) } else { Diagnostic::simple_warning( format!("unused {item_type} {name}"), format!("unused {item_type}"), - ident.span(), + ident.location(), ) }; diagnostic.unnecessary = true; diagnostic } - ResolverError::UnconditionalRecursion { name, span} => { + ResolverError::UnconditionalRecursion { name, location} => { Diagnostic::simple_warning( format!("function `{name}` cannot return without recursing"), "function cannot return without recursing".to_string(), - *span, + *location, ) } - ResolverError::VariableNotDeclared { name, span } => { + ResolverError::VariableNotDeclared { name, location } => { if name == "_" { Diagnostic::simple_error( "in expressions, `_` can only be used on the left-hand side of an assignment".to_string(), "`_` not allowed here".to_string(), - *span, + *location, ) } else { Diagnostic::simple_error( format!("cannot find `{name}` in this scope"), "not found in this scope".to_string(), - *span, + *location, ) } }, - ResolverError::PathIsNotIdent { span } => Diagnostic::simple_error( + ResolverError::PathIsNotIdent { location } => Diagnostic::simple_error( "cannot use path as an identifier".to_string(), String::new(), - *span, + *location, ), ResolverError::PathResolutionError(error) => error.into(), - ResolverError::Expected { span, expected, got } => Diagnostic::simple_error( + ResolverError::Expected { location, expected, got } => Diagnostic::simple_error( format!("expected {expected} got {got}"), String::new(), - *span, + *location, ), ResolverError::DuplicateField { field } => Diagnostic::simple_error( format!("duplicate field {field}"), String::new(), - field.span(), + field.location(), ), ResolverError::NoSuchField { field, struct_definition } => { Diagnostic::simple_error( format!("no such field {field} defined in struct {struct_definition}"), String::new(), - field.span(), + field.location(), ) } - ResolverError::MissingFields { span, missing_fields, struct_definition } => { + ResolverError::MissingFields { location, missing_fields, struct_definition } => { let plural = if missing_fields.len() != 1 { "s" } else { "" }; let remaining_fields_names = match &missing_fields[..] { [field1] => field1.clone(), @@ -301,7 +392,7 @@ impl<'a> From<&'a ResolverError> for Diagnostic { Diagnostic::simple_error( format!("missing field{plural} {remaining_fields_names} in struct {struct_definition}"), String::new(), - *span, + *location, ) } ResolverError::UnnecessaryMut { first_mut, second_mut } => { @@ -322,7 +413,7 @@ impl<'a> From<&'a ResolverError> for Diagnostic { let mut diag = Diagnostic::simple_warning( format!("unnecessary pub keyword on {position} for function {name}"), format!("unnecessary pub {position}"), - ident.0.span(), + ident.0.location(), ); diag.add_note("The `pub` keyword only has effects on arguments to the entry-point function of a program. Thus, adding it to other function parameters can be deceiving and should be removed".to_owned()); @@ -334,192 +425,188 @@ impl<'a> From<&'a ResolverError> for Diagnostic { let mut diag = Diagnostic::simple_error( format!("missing pub keyword on return type of function {name}"), "missing pub on return type".to_string(), - ident.0.span(), + ident.0.location(), ); diag.add_note("The `pub` keyword is mandatory for the entry-point function return type because the verifier cannot retrieve private witness and thus the function will not be able to return a 'priv' value".to_owned()); diag } - ResolverError::MissingRhsExpr { name, span } => Diagnostic::simple_error( + ResolverError::MissingRhsExpr { name, location } => Diagnostic::simple_error( format!( "no expression specifying the value stored by the constant variable {name}" ), "expected expression to be stored for let statement".to_string(), - *span, + *location, ), - ResolverError::InvalidArrayLengthExpr { span } => Diagnostic::simple_error( + ResolverError::InvalidArrayLengthExpr { location } => Diagnostic::simple_error( "Expression invalid in an array-length context".into(), "Array-length expressions can only have simple integer operations and any variables used must be global constants".into(), - *span, + *location, ), - ResolverError::IntegerTooLarge { span } => Diagnostic::simple_error( + ResolverError::IntegerTooLarge { location } => Diagnostic::simple_error( "Integer too large to be evaluated to an array-length".into(), "Array-lengths may be a maximum size of usize::MAX, including intermediate calculations".into(), - *span, + *location, ), ResolverError::NoSuchNumericTypeVariable { path } => Diagnostic::simple_error( format!("Cannot find a global or generic type parameter named `{path}`"), "Only globals or generic type parameters are allowed to be used as an array type's length".to_string(), - path.span(), + path.location, ), - ResolverError::CapturedMutableVariable { span } => Diagnostic::simple_error( + ResolverError::CapturedMutableVariable { location } => Diagnostic::simple_error( "Closures cannot capture mutable variables".into(), "Mutable variable".into(), - *span, + *location, ), - ResolverError::TestFunctionHasParameters { span } => Diagnostic::simple_error( + ResolverError::TestFunctionHasParameters { location } => Diagnostic::simple_error( "Test functions cannot have any parameters".into(), "Try removing the parameters or moving the test into a wrapper function".into(), - *span, + *location, ), - ResolverError::NonStructUsedInConstructor { typ, span } => Diagnostic::simple_error( + ResolverError::NonStructUsedInConstructor { typ, location } => Diagnostic::simple_error( "Only struct types can be used in constructor expressions".into(), format!("{typ} has no fields to construct it with"), - *span, + *location, ), - ResolverError::NonStructWithGenerics { span } => Diagnostic::simple_error( + ResolverError::NonStructWithGenerics { location } => Diagnostic::simple_error( "Only struct types can have generic arguments".into(), "Try removing the generic arguments".into(), - *span, + *location, ), - ResolverError::GenericsOnSelfType { span } => Diagnostic::simple_error( + ResolverError::GenericsOnSelfType { location } => Diagnostic::simple_error( "Cannot apply generics to Self type".into(), "Use an explicit type name or apply the generics at the start of the impl instead".into(), - *span, + *location, ), - ResolverError::GenericsOnAssociatedType { span } => Diagnostic::simple_error( + ResolverError::GenericsOnAssociatedType { location } => Diagnostic::simple_error( "Generic Associated Types (GATs) are currently unsupported in Noir".into(), "Cannot apply generics to an associated type".into(), - *span, + *location, ), ResolverError::ParserError(error) => error.as_ref().into(), - ResolverError::MutableReferenceToImmutableVariable { variable, span } => { - Diagnostic::simple_error(format!("Cannot mutably reference the immutable variable {variable}"), format!("{variable} is immutable"), *span) - }, - ResolverError::MutableReferenceToArrayElement { span } => { - Diagnostic::simple_error("Mutable references to array elements are currently unsupported".into(), "Try storing the element in a fresh variable first".into(), *span) - }, - ResolverError::InvalidClosureEnvironment { span, typ } => Diagnostic::simple_error( + ResolverError::InvalidClosureEnvironment { location, typ } => Diagnostic::simple_error( format!("{typ} is not a valid closure environment type"), - "Closure environment must be a tuple or unit type".to_string(), *span), - ResolverError::NestedSlices { span } => Diagnostic::simple_error( + "Closure environment must be a tuple or unit type".to_string(), *location), + ResolverError::NestedSlices { location } => Diagnostic::simple_error( "Nested slices, i.e. slices within an array or slice, are not supported".into(), "Try to use a constant sized array or BoundedVec instead".into(), - *span, + *location, ), - ResolverError::AbiAttributeOutsideContract { span } => { + ResolverError::AbiAttributeOutsideContract { location } => { Diagnostic::simple_error( "#[abi(tag)] attributes can only be used in contracts".to_string(), "misplaced #[abi(tag)] attribute".to_string(), - *span, + *location, ) }, ResolverError::LowLevelFunctionOutsideOfStdlib { ident } => Diagnostic::simple_error( "Definition of low-level function outside of standard library".into(), "Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library".into(), - ident.span(), + ident.location(), ), ResolverError::OracleMarkedAsConstrained { ident } => Diagnostic::simple_error( error.to_string(), "Oracle functions must have the `unconstrained` keyword applied".into(), - ident.span(), + ident.location(), ), - ResolverError::UnconstrainedOracleReturnToConstrained { span } => Diagnostic::simple_error( + ResolverError::UnconstrainedOracleReturnToConstrained { location } => Diagnostic::simple_error( error.to_string(), "This oracle call must be wrapped in a call to another unconstrained function before being returned to a constrained runtime".into(), - *span, + *location, ), - ResolverError::DependencyCycle { span, item, cycle } => { + ResolverError::DependencyCycle { location, item, cycle } => { Diagnostic::simple_error( "Dependency cycle found".into(), format!("'{item}' recursively depends on itself: {cycle}"), - *span, + *location, ) }, - ResolverError::JumpInConstrainedFn { is_break, span } => { + ResolverError::JumpInConstrainedFn { is_break, location } => { let item = if *is_break { "break" } else { "continue" }; Diagnostic::simple_error( format!("{item} is only allowed in unconstrained functions"), "Constrained code must always have a known number of loop iterations".into(), - *span, + *location, ) }, - ResolverError::LoopInConstrainedFn { span } => { + ResolverError::LoopInConstrainedFn { location } => { Diagnostic::simple_error( "`loop` is only allowed in unconstrained functions".into(), "Constrained code must always have a known number of loop iterations".into(), - *span, + *location, ) }, - ResolverError::LoopWithoutBreak { span } => { + ResolverError::LoopWithoutBreak { location } => { Diagnostic::simple_error( "`loop` must have at least one `break` in it".into(), "Infinite loops are disallowed".into(), - *span, + *location, ) }, - ResolverError::WhileInConstrainedFn { span } => { + ResolverError::WhileInConstrainedFn { location } => { Diagnostic::simple_error( "`while` is only allowed in unconstrained functions".into(), "Constrained code must always have a known number of loop iterations".into(), - *span, + *location, ) }, - ResolverError::JumpOutsideLoop { is_break, span } => { + ResolverError::JumpOutsideLoop { is_break, location } => { let item = if *is_break { "break" } else { "continue" }; Diagnostic::simple_error( format!("{item} is only allowed within loops"), "".into(), - *span, + *location, ) }, - ResolverError::MutableGlobal { span } => { + ResolverError::MutableGlobal { location } => { Diagnostic::simple_error( "Only `comptime` globals may be mutable".into(), String::new(), - *span, + *location, ) }, - ResolverError::UnspecifiedGlobalType { span, expected_type } => { - Diagnostic::simple_error( + ResolverError::UnspecifiedGlobalType { pattern_location, expr_location, expected_type } => { + let mut diagnostic = Diagnostic::simple_error( "Globals must have a specified type".to_string(), - format!("Inferred type is `{expected_type}`"), - *span, - ) + String::new(), + *pattern_location, + ); + diagnostic.add_secondary(format!("Inferred type is `{expected_type}`"), *expr_location); + diagnostic }, - ResolverError::UnevaluatedGlobalType { span } => { + ResolverError::UnevaluatedGlobalType { location } => { Diagnostic::simple_error( "Global failed to evaluate".to_string(), String::new(), - *span, + *location, ) } - ResolverError::NegativeGlobalType { span, global_value } => { + ResolverError::NegativeGlobalType { location, global_value } => { Diagnostic::simple_error( "Globals used in a type position must be non-negative".to_string(), format!("But found value `{global_value:?}`"), - *span, + *location, ) } - ResolverError::NonIntegralGlobalType { span, global_value } => { + ResolverError::NonIntegralGlobalType { location, global_value } => { Diagnostic::simple_error( "Globals used in a type position must be integers".to_string(), format!("But found value `{global_value:?}`"), - *span, + *location, ) } - ResolverError::GlobalLargerThanKind { span, global_value, kind } => { + ResolverError::GlobalLargerThanKind { location, global_value, kind } => { Diagnostic::simple_error( format!("Global value `{global_value}` is larger than its kind's maximum value"), format!("Global's kind inferred to be `{kind}`"), - *span, + *location, ) } - ResolverError::SelfReferentialType { span } => { + ResolverError::SelfReferentialType { location } => { Diagnostic::simple_error( "Self-referential types are not supported".into(), "".into(), - *span, + *location, ) }, ResolverError::NoPredicatesAttributeOnUnconstrained { ident } => { @@ -528,7 +615,7 @@ impl<'a> From<&'a ResolverError> for Diagnostic { let mut diag = Diagnostic::simple_error( format!("misplaced #[no_predicates] attribute on unconstrained function {name}. Only allowed on constrained functions"), "misplaced #[no_predicates] attribute".to_string(), - ident.0.span(), + ident.0.location(), ); diag.add_note("The `#[no_predicates]` attribute specifies to the compiler whether it should diverge from auto-inlining constrained functions".to_owned()); @@ -540,159 +627,191 @@ impl<'a> From<&'a ResolverError> for Diagnostic { let mut diag = Diagnostic::simple_error( format!("misplaced #[fold] attribute on unconstrained function {name}. Only allowed on constrained functions"), "misplaced #[fold] attribute".to_string(), - ident.0.span(), + ident.0.location(), ); diag.add_note("The `#[fold]` attribute specifies whether a constrained function should be treated as a separate circuit rather than inlined into the program entry point".to_owned()); diag } - ResolverError::NumericGenericUsedForType { name, span } => { + ResolverError::NumericGenericUsedForType { name, location } => { Diagnostic::simple_error( format!("expected type, found numeric generic parameter {name}"), String::from("not a type"), - *span, + *location, ) } ResolverError::ArrayLengthInterpreter { error } => Diagnostic::from(error), - ResolverError::UnquoteUsedOutsideQuote { span } => { + ResolverError::UnquoteUsedOutsideQuote { location } => { Diagnostic::simple_error( "The unquote operator '$' can only be used within a quote expression".into(), "".into(), - *span, - ) - }, - ResolverError::AsTraitPathNotYetImplemented { span } => { - Diagnostic::simple_error( - "\"as trait path\" not yet implemented".into(), - "".into(), - *span, + *location, ) }, - ResolverError::InvalidSyntaxInMacroCall { span } => { + ResolverError::InvalidSyntaxInMacroCall { location } => { Diagnostic::simple_error( "Invalid syntax in macro call".into(), "Macro calls must call a comptime function directly, they cannot use higher-order functions".into(), - *span, + *location, ) }, - ResolverError::MacroIsNotComptime { span } => { + ResolverError::MacroIsNotComptime { location } => { Diagnostic::simple_error( "This macro call is to a non-comptime function".into(), "Macro calls must be to comptime functions".into(), - *span, + *location, ) }, - ResolverError::NonFunctionInAnnotation { span } => { + ResolverError::NonFunctionInAnnotation { location } => { Diagnostic::simple_error( "Unknown annotation".into(), "The name of an annotation must refer to a comptime function".into(), - *span, + *location, ) }, - ResolverError::MacroResultInGenericsListNotAGeneric { span, typ } => { + ResolverError::MacroResultInGenericsListNotAGeneric { location, typ } => { Diagnostic::simple_error( format!("Type `{typ}` was inserted into a generics list from a macro, but it is not a generic"), format!("Type `{typ}` is not a generic"), - *span, + *location, ) } - ResolverError::NamedTypeArgs { span, item_kind } => { + ResolverError::NamedTypeArgs { location, item_kind } => { Diagnostic::simple_error( format!("Named type arguments aren't allowed on a {item_kind}"), "Named type arguments are only allowed for associated types on traits".to_string(), - *span, + *location, ) } - ResolverError::AssociatedConstantsMustBeNumeric { span } => { + ResolverError::AssociatedConstantsMustBeNumeric { location } => { Diagnostic::simple_error( "Associated constants may only be a field or integer type".to_string(), "Only numeric constants are allowed".to_string(), - *span, + *location, ) } - ResolverError::BinaryOpError { lhs, op, rhs, err, span } => { + ResolverError::BinaryOpError { lhs, op, rhs, err, location } => { Diagnostic::simple_error( format!("Computing `{lhs} {op} {rhs}` failed with error {err}"), String::new(), - *span, + *location, ) } - ResolverError::QuoteInRuntimeCode { span } => { + ResolverError::QuoteInRuntimeCode { location } => { Diagnostic::simple_error( "`quote` cannot be used in runtime code".to_string(), "Wrap this in a `comptime` block or function to use it".to_string(), - *span, + *location, ) }, - ResolverError::ComptimeTypeInRuntimeCode { typ, span } => { + ResolverError::ComptimeTypeInRuntimeCode { typ, location } => { Diagnostic::simple_error( format!("Comptime-only type `{typ}` cannot be used in runtime code"), "Comptime-only type used here".to_string(), - *span, + *location, ) }, - ResolverError::MutatingComptimeInNonComptimeContext { name, span } => { + ResolverError::MutatingComptimeInNonComptimeContext { name, location } => { Diagnostic::simple_error( format!("Comptime variable `{name}` cannot be mutated in a non-comptime context"), format!("`{name}` mutated here"), - *span, + *location, ) }, - ResolverError::InvalidInternedStatementInExpr { statement, span } => { + ResolverError::InvalidInternedStatementInExpr { statement, location } => { Diagnostic::simple_error( format!("Failed to parse `{statement}` as an expression"), "The statement was used from a macro here".to_string(), - *span, + *location, ) }, ResolverError::UnsupportedNumericGenericType(err) => err.into(), - ResolverError::TypeIsMorePrivateThenItem { typ, item, span } => { + ResolverError::TypeIsMorePrivateThenItem { typ, item, location } => { Diagnostic::simple_error( format!("Type `{typ}` is more private than item `{item}`"), String::new(), - *span, + *location, ) }, - ResolverError::UnableToParseAttribute { attribute, span } => { + ResolverError::UnableToParseAttribute { attribute, location } => { Diagnostic::simple_error( format!("Unable to parse attribute `{attribute}`"), "Attribute should be a function or function call".into(), - *span, + *location, ) }, - ResolverError::AttributeFunctionIsNotAPath { function, span } => { + ResolverError::AttributeFunctionIsNotAPath { function, location } => { Diagnostic::simple_error( format!("Attribute function `{function}` is not a path"), "An attribute's function should be a single identifier or a path".into(), - *span, + *location, ) }, - ResolverError::AttributeFunctionNotInScope { name, span } => { + ResolverError::AttributeFunctionNotInScope { name, location } => { Diagnostic::simple_error( format!("Attribute function `{name}` is not in scope"), String::new(), - *span, + *location, ) }, - ResolverError::TraitNotImplemented { impl_trait, missing_trait: the_trait, type_missing_trait: typ, span, missing_trait_location} => { + ResolverError::TraitNotImplemented { impl_trait, missing_trait: the_trait, type_missing_trait: typ, location, missing_trait_location} => { let mut diagnostic = Diagnostic::simple_error( format!("The trait bound `{typ}: {the_trait}` is not satisfied"), - format!("The trait `{the_trait}` is not implemented for `{typ}") - , *span); - diagnostic.add_secondary_with_file(format!("required by this bound in `{impl_trait}"), missing_trait_location.span, missing_trait_location.file); + format!("The trait `{the_trait}` is not implemented for `{typ}`") + , *location); + diagnostic.add_secondary(format!("required by this bound in `{impl_trait}`"), *missing_trait_location); diagnostic }, - ResolverError::LoopNotYetSupported { span } => { + ResolverError::LoopNotYetSupported { location } => { let msg = "`loop` statements are not yet implemented".to_string(); - Diagnostic::simple_error(msg, String::new(), *span) + Diagnostic::simple_error(msg, String::new(), *location) } - ResolverError::ExpectedTrait { found, span } => { + ResolverError::ExpectedTrait { found, location } => { Diagnostic::simple_error( format!("Expected a trait, found {found}"), String::new(), - *span) + *location) } + ResolverError::InvalidSyntaxInPattern { location } => { + Diagnostic::simple_error( + "Invalid syntax in match pattern".into(), + "Only literal, constructor, and variable patterns are allowed".into(), + *location) + }, + ResolverError::VariableAlreadyDefinedInPattern { existing, new_location } => { + let message = format!("Variable `{existing}` was already defined in the same match pattern"); + let secondary = format!("`{existing}` redefined here"); + let mut error = Diagnostic::simple_error(message, secondary, *new_location); + error.add_secondary(format!("`{existing}` was previously defined here"), existing.location()); + error + }, + ResolverError::NonIntegerGlobalUsedInPattern { location } => { + let message = "Only integer or boolean globals can be used in match patterns".to_string(); + let secondary = "This global is not an integer or boolean".to_string(); + Diagnostic::simple_error(message, secondary, *location) + }, + ResolverError::TypeUnsupportedInMatch { typ, location } => { + Diagnostic::simple_error( + format!("Cannot match on values of type `{typ}`"), + String::new(), + *location, + ) + }, + ResolverError::UnexpectedItemInPattern { item, location } => { + Diagnostic::simple_error( + format!("Expected a struct, enum, or literal pattern, but found a {item}"), + String::new(), + *location, + ) + }, + ResolverError::NoSuchMethodInTrait { trait_name, method_name, location } => { + Diagnostic::simple_error( + format!("Trait `{trait_name}` has no method named `{method_name}`"), + String::new(), + *location, + ) + }, } } } 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 11b694aa61be..16e25b804651 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 @@ -1,5 +1,5 @@ use iter_extended::vecmap; -use noirc_errors::{CustomDiagnostic, Span}; +use noirc_errors::{CustomDiagnostic, Location}; use thiserror::Error; use crate::graph::CrateId; @@ -44,12 +44,14 @@ pub enum PathResolutionError { #[error("{0} is private and not visible from the current module")] Private(Ident), #[error("There is no super module")] - NoSuper(Span), + NoSuper(Location), #[error("turbofish (`::<_>`) not allowed on {item}")] - TurbofishNotAllowedOnItem { item: String, span: Span }, + TurbofishNotAllowedOnItem { item: String, location: Location }, #[error("{ident} is a {kind}, not a module")] NotAModule { ident: Ident, kind: &'static str }, - #[error("trait `{trait_name}` which provides `{ident}` is implemented but not in scope, please import it")] + #[error( + "trait `{trait_name}` which provides `{ident}` is implemented but not in scope, please import it" + )] TraitMethodNotInScope { ident: Ident, trait_name: String }, #[error("Could not resolve '{ident}' in path")] UnresolvedWithPossibleTraitsToImport { ident: Ident, traits: Vec }, @@ -57,6 +59,23 @@ pub enum PathResolutionError { MultipleTraitsInScope { ident: Ident, traits: Vec }, } +impl PathResolutionError { + pub fn location(&self) -> Location { + match self { + PathResolutionError::NoSuper(location) + | PathResolutionError::TurbofishNotAllowedOnItem { location, .. } => *location, + PathResolutionError::Unresolved(ident) + | PathResolutionError::Private(ident) + | PathResolutionError::NotAModule { ident, .. } + | PathResolutionError::TraitMethodNotInScope { ident, .. } + | PathResolutionError::MultipleTraitsInScope { ident, .. } + | PathResolutionError::UnresolvedWithPossibleTraitsToImport { ident, .. } => { + ident.location() + } + } + } +} + #[derive(Debug)] pub struct ResolvedImport { // The symbol which we have resolved to @@ -75,43 +94,48 @@ impl<'a> From<&'a PathResolutionError> for CustomDiagnostic { fn from(error: &'a PathResolutionError) -> Self { match &error { PathResolutionError::Unresolved(ident) => { - CustomDiagnostic::simple_error(error.to_string(), String::new(), ident.span()) + CustomDiagnostic::simple_error(error.to_string(), String::new(), ident.location()) } // This will be upgraded to an error in future versions PathResolutionError::Private(ident) => CustomDiagnostic::simple_warning( error.to_string(), format!("{ident} is private"), - ident.span(), + ident.location(), ), - PathResolutionError::NoSuper(span) => { - CustomDiagnostic::simple_error(error.to_string(), String::new(), *span) + PathResolutionError::NoSuper(location) => { + CustomDiagnostic::simple_error(error.to_string(), String::new(), *location) } - PathResolutionError::TurbofishNotAllowedOnItem { item: _, span } => { - CustomDiagnostic::simple_error(error.to_string(), String::new(), *span) + PathResolutionError::TurbofishNotAllowedOnItem { item: _, location } => { + CustomDiagnostic::simple_error(error.to_string(), String::new(), *location) } PathResolutionError::NotAModule { ident, kind: _ } => { - CustomDiagnostic::simple_error(error.to_string(), String::new(), ident.span()) + CustomDiagnostic::simple_error(error.to_string(), String::new(), ident.location()) } PathResolutionError::TraitMethodNotInScope { ident, .. } => { - CustomDiagnostic::simple_warning(error.to_string(), String::new(), ident.span()) + CustomDiagnostic::simple_warning(error.to_string(), String::new(), ident.location()) } PathResolutionError::UnresolvedWithPossibleTraitsToImport { ident, traits } => { - let traits = vecmap(traits, |trait_name| format!("`{}`", trait_name)); + let mut traits = vecmap(traits, |trait_name| format!("`{}`", trait_name)); + traits.sort(); CustomDiagnostic::simple_error( error.to_string(), - format!("The following traits which provide `{ident}` are implemented but not in scope: {}", traits.join(", ")), - ident.span(), + format!( + "The following traits which provide `{ident}` are implemented but not in scope: {}", + traits.join(", ") + ), + ident.location(), ) } PathResolutionError::MultipleTraitsInScope { ident, traits } => { - let traits = vecmap(traits, |trait_name| format!("`{}`", trait_name)); + let mut traits = vecmap(traits, |trait_name| format!("`{}`", trait_name)); + traits.sort(); CustomDiagnostic::simple_error( error.to_string(), format!( "All these trait which provide `{ident}` are implemented and in scope: {}", traits.join(", ") ), - ident.span(), + ident.location(), ) } } @@ -162,7 +186,7 @@ struct PathResolutionTargetResolver<'def_maps, 'references_tracker> { references_tracker: Option>, } -impl<'def_maps, 'references_tracker> PathResolutionTargetResolver<'def_maps, 'references_tracker> { +impl PathResolutionTargetResolver<'_, '_> { fn resolve(&mut self, path: Path) -> Result<(Path, ModuleId), PathResolutionError> { match path.kind { PathKind::Crate => self.resolve_crate_path(path), @@ -214,8 +238,8 @@ impl<'def_maps, 'references_tracker> PathResolutionTargetResolver<'def_maps, 're .ok_or_else(|| PathResolutionError::Unresolved(crate_name.to_owned()))?; if let Some(references_tracker) = &mut self.references_tracker { - let span = crate_name.span(); - references_tracker.add_reference(ModuleDefId::ModuleId(*dep_module), span, false); + let location = crate_name.location(); + references_tracker.add_reference(ModuleDefId::ModuleId(*dep_module), location, false); } // Now the path can be solved starting from the second segment as a plain path @@ -227,9 +251,7 @@ impl<'def_maps, 'references_tracker> PathResolutionTargetResolver<'def_maps, 're fn resolve_super_path(&mut self, path: Path) -> Result<(Path, ModuleId), PathResolutionError> { let Some(parent_module_id) = get_module(self.def_maps, self.importing_module).parent else { - let span_start = path.span.start(); - let span = Span::from(span_start..span_start + 5); // 5 == "super".len() - return Err(PathResolutionError::NoSuper(span)); + return Err(PathResolutionError::NoSuper(path.kind_location)); }; let current_module = @@ -297,7 +319,7 @@ impl<'def_maps, 'usage_tracker, 'references_tracker> Some((typ, visibility, _)) => (typ, visibility), }; - self.add_reference(typ, last_segment.span, last_segment.ident.is_self_type_name()); + self.add_reference(typ, last_segment.location, last_segment.ident.is_self_type_name()); // In the type namespace, only Mod can be used in a path. current_module_id = match typ { @@ -339,7 +361,7 @@ impl<'def_maps, 'usage_tracker, 'references_tracker> let (module_def_id, visibility, _) = current_ns.values.or(current_ns.types).expect("Found empty namespace"); - self.add_reference(module_def_id, path.segments.last().unwrap().ident.span(), false); + self.add_reference(module_def_id, path.segments.last().unwrap().ident.location(), false); if !self.item_in_module_is_visible(current_module_id, visibility) { errors.push(PathResolutionError::Private(path.last_ident())); @@ -348,9 +370,14 @@ impl<'def_maps, 'usage_tracker, 'references_tracker> Ok(ResolvedImport { namespace: current_ns, errors }) } - fn add_reference(&mut self, reference_id: ModuleDefId, span: Span, is_self_type_name: bool) { + fn add_reference( + &mut self, + reference_id: ModuleDefId, + location: Location, + is_self_type_name: bool, + ) { if let Some(references_tracker) = &mut self.references_tracker { - references_tracker.add_reference(reference_id, span, is_self_type_name); + references_tracker.add_reference(reference_id, location, is_self_type_name); } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/visibility.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/visibility.rs index c592175ffcb6..84badde9d35a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/visibility.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/visibility.rs @@ -1,6 +1,6 @@ +use crate::Type; use crate::graph::CrateId; use crate::node_interner::{FuncId, NodeInterner, TraitId, TypeId}; -use crate::Type; use std::collections::BTreeMap; @@ -61,7 +61,7 @@ pub(crate) fn module_descendent_of_target( def_map.modules[current.0] .parent - .map_or(false, |parent| module_descendent_of_target(def_map, target, parent)) + .is_some_and(|parent| module_descendent_of_target(def_map, target, parent)) } /// Returns true if `target` is a struct and its parent is `current`. 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 d29e1aa43397..9198182312df 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 @@ -2,7 +2,7 @@ use std::rc::Rc; use acvm::FieldElement; use noirc_errors::CustomDiagnostic as Diagnostic; -use noirc_errors::Span; +use noirc_errors::Location; use thiserror::Error; use crate::ast::{ @@ -13,6 +13,7 @@ use crate::hir_def::expr::HirBinaryOp; use crate::hir_def::traits::TraitConstraint; use crate::hir_def::types::{BinaryTypeOperator, Kind, Type}; use crate::node_interner::NodeInterner; +use crate::signed_field::SignedField; #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum Source { @@ -31,190 +32,219 @@ pub enum Source { #[error("{0}")] BinOp(BinaryOpKind), #[error("Return")] - Return(FunctionReturnType, Span), + Return(FunctionReturnType, Location), } #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum TypeCheckError { #[error("Operator {op:?} cannot be used in a {place:?}")] - OpCannotBeUsed { op: HirBinaryOp, place: &'static str, span: Span }, + OpCannotBeUsed { op: HirBinaryOp, place: &'static str, location: Location }, #[error("Division by zero: {lhs} / {rhs}")] - DivisionByZero { lhs: FieldElement, rhs: FieldElement, span: Span }, + DivisionByZero { lhs: FieldElement, rhs: FieldElement, location: Location }, #[error("Modulo on Field elements: {lhs} % {rhs}")] - ModuloOnFields { lhs: FieldElement, rhs: FieldElement, span: Span }, - #[error("The value `{expr:?}` cannot fit into `{ty}` which has range `{range}`")] - OverflowingAssignment { expr: FieldElement, ty: Type, range: String, span: Span }, + ModuloOnFields { lhs: FieldElement, rhs: FieldElement, location: Location }, + #[error("The value `{expr}` cannot fit into `{ty}` which has range `{range}`")] + OverflowingAssignment { expr: SignedField, ty: Type, range: String, location: Location }, #[error( "The value `{value}` cannot fit into `{kind}` which has a maximum size of `{maximum_size}`" )] - OverflowingConstant { value: FieldElement, kind: Kind, maximum_size: FieldElement, span: Span }, + OverflowingConstant { + value: FieldElement, + kind: Kind, + maximum_size: FieldElement, + location: Location, + }, #[error("Evaluating `{op}` on `{lhs}`, `{rhs}` failed")] - FailingBinaryOp { op: BinaryTypeOperator, lhs: i128, rhs: i128, span: Span }, + FailingBinaryOp { op: BinaryTypeOperator, lhs: i128, rhs: i128, location: Location }, #[error("Type {typ:?} cannot be used in a {place:?}")] - TypeCannotBeUsed { typ: Type, place: &'static str, span: Span }, + TypeCannotBeUsed { typ: Type, place: &'static str, location: Location }, #[error("Expected type {expected_typ:?} is not the same as {expr_typ:?}")] - TypeMismatch { expected_typ: String, expr_typ: String, expr_span: Span }, + TypeMismatch { expected_typ: String, expr_typ: String, expr_location: Location }, #[error("Expected type {expected} is not the same as {actual}")] - TypeMismatchWithSource { expected: Type, actual: Type, span: Span, source: Source }, + TypeMismatchWithSource { expected: Type, actual: Type, location: Location, source: Source }, #[error("Expected type {expected_kind:?} is not the same as {expr_kind:?}")] - TypeKindMismatch { expected_kind: String, expr_kind: String, expr_span: Span }, + TypeKindMismatch { expected_kind: Kind, expr_kind: Kind, expr_location: Location }, #[error("Evaluating {to} resulted in {to_value}, but {from_value} was expected")] TypeCanonicalizationMismatch { to: Type, from: Type, to_value: FieldElement, from_value: FieldElement, - span: Span, + location: Location, }, #[error("Expected {expected:?} found {found:?}")] - ArityMisMatch { expected: usize, found: usize, span: Span }, + ArityMisMatch { expected: usize, found: usize, location: Location }, #[error("Return type in a function cannot be public")] - PublicReturnType { typ: Type, span: Span }, + PublicReturnType { typ: Type, location: Location }, #[error("Cannot cast type {from}, 'as' is only for primitive field or integer types")] - InvalidCast { from: Type, span: Span, reason: String }, + InvalidCast { from: Type, location: Location, reason: String }, #[error("Casting value of type {from} to a smaller type ({to})")] - DownsizingCast { from: Type, to: Type, span: Span, reason: String }, + DownsizingCast { from: Type, to: Type, location: Location, reason: String }, #[error("Expected a function, but found a(n) {found}")] - ExpectedFunction { found: Type, span: Span }, + ExpectedFunction { found: Type, location: Location }, #[error("Type {lhs_type} has no member named {field_name}")] - AccessUnknownMember { lhs_type: Type, field_name: String, span: Span }, + AccessUnknownMember { lhs_type: Type, field_name: String, location: Location }, #[error("Function expects {expected} parameters but {found} were given")] - ParameterCountMismatch { expected: usize, found: usize, span: Span }, + ParameterCountMismatch { expected: usize, found: usize, location: Location }, #[error("{} expects {} or {} parameters but {found} were given", kind, kind.required_arguments_count(), kind.required_arguments_count() + 1)] - AssertionParameterCountMismatch { kind: ConstrainKind, found: usize, span: Span }, + AssertionParameterCountMismatch { kind: ConstrainKind, found: usize, location: Location }, #[error("{item} expects {expected} generics but {found} were given")] - GenericCountMismatch { item: String, expected: usize, found: usize, span: Span }, + GenericCountMismatch { item: String, expected: usize, found: usize, location: Location }, #[error("{item} has incompatible `unconstrained`")] - UnconstrainedMismatch { item: String, expected: bool, span: Span }, + UnconstrainedMismatch { item: String, expected: bool, location: Location }, #[error("Only integer and Field types may be casted to")] - UnsupportedCast { span: Span }, + UnsupportedCast { location: Location }, #[error("Index {index} is out of bounds for this tuple {lhs_type} of length {length}")] - TupleIndexOutOfBounds { index: usize, lhs_type: Type, length: usize, span: Span }, + TupleIndexOutOfBounds { index: usize, lhs_type: Type, length: usize, location: Location }, #[error("Variable `{name}` must be mutable to be assigned to")] - VariableMustBeMutable { name: String, span: Span }, + VariableMustBeMutable { name: String, location: Location }, #[error("Cannot mutate immutable variable `{name}`")] - CannotMutateImmutableVariable { name: String, span: Span }, + CannotMutateImmutableVariable { name: String, location: Location }, + #[error("Variable {name} captured in lambda must be a mutable reference")] + MutableCaptureWithoutRef { name: String, location: Location }, + #[error("Mutable references to array indices are unsupported")] + MutableReferenceToArrayElement { location: Location }, #[error("No method named '{method_name}' found for type '{object_type}'")] - UnresolvedMethodCall { method_name: String, object_type: Type, span: Span }, + UnresolvedMethodCall { method_name: String, object_type: Type, location: Location }, #[error("Cannot invoke function field '{method_name}' on type '{object_type}' as a method")] - CannotInvokeStructFieldFunctionType { method_name: String, object_type: Type, span: Span }, + CannotInvokeStructFieldFunctionType { + method_name: String, + object_type: Type, + location: Location, + }, #[error("Integers must have the same signedness LHS is {sign_x:?}, RHS is {sign_y:?}")] - IntegerSignedness { sign_x: Signedness, sign_y: Signedness, span: Span }, + IntegerSignedness { sign_x: Signedness, sign_y: Signedness, location: Location }, #[error("Integers must have the same bit width LHS is {bit_width_x}, RHS is {bit_width_y}")] - IntegerBitWidth { bit_width_x: IntegerBitSize, bit_width_y: IntegerBitSize, span: Span }, + IntegerBitWidth { bit_width_x: IntegerBitSize, bit_width_y: IntegerBitSize, location: Location }, #[error("{kind} cannot be used in an infix operation")] - InvalidInfixOp { kind: &'static str, span: Span }, + InvalidInfixOp { kind: &'static str, location: Location }, #[error("{kind} cannot be used in a unary operation")] - InvalidUnaryOp { kind: String, span: Span }, - #[error("Bitwise operations are invalid on Field types. Try casting the operands to a sized integer type first.")] - FieldBitwiseOp { span: Span }, + InvalidUnaryOp { kind: String, location: Location }, + #[error( + "Bitwise operations are invalid on Field types. Try casting the operands to a sized integer type first." + )] + FieldBitwiseOp { location: Location }, #[error("Integer cannot be used with type {typ}")] - IntegerTypeMismatch { typ: Type, span: Span }, - #[error("Cannot use an integer and a Field in a binary operation, try converting the Field into an integer first")] - IntegerAndFieldBinaryOperation { span: Span }, + IntegerTypeMismatch { typ: Type, location: Location }, + #[error( + "Cannot use an integer and a Field in a binary operation, try converting the Field into an integer first" + )] + IntegerAndFieldBinaryOperation { location: Location }, #[error("Cannot do modulo on Fields, try casting to an integer first")] - FieldModulo { span: Span }, + FieldModulo { location: Location }, #[error("Cannot do not (`!`) on Fields, try casting to an integer first")] - FieldNot { span: Span }, + FieldNot { location: Location }, #[error("Fields cannot be compared, try casting to an integer first")] - FieldComparison { span: Span }, - #[error("The bit count in a bit-shift operation must fit in a u8, try casting the right hand side into a u8 first")] - InvalidShiftSize { span: Span }, - #[error("The number of bits to use for this bitwise operation is ambiguous. Either the operand's type or return type should be specified")] - AmbiguousBitWidth { span: Span }, + FieldComparison { location: Location }, + #[error( + "The bit count in a bit-shift operation must fit in a u8, try casting the right hand side into a u8 first" + )] + InvalidShiftSize { location: Location }, + #[error( + "The number of bits to use for this bitwise operation is ambiguous. Either the operand's type or return type should be specified" + )] + AmbiguousBitWidth { location: Location }, #[error("Error with additional context")] Context { err: Box, ctx: &'static str }, #[error("Array is not homogeneous")] NonHomogeneousArray { - first_span: Span, + first_location: Location, first_type: String, first_index: usize, - second_span: Span, + second_location: Location, second_type: String, second_index: usize, }, #[error("Object type is unknown in method call")] - TypeAnnotationsNeededForMethodCall { span: Span }, + TypeAnnotationsNeededForMethodCall { location: Location }, #[error("Object type is unknown in field access")] - TypeAnnotationsNeededForFieldAccess { span: Span }, + TypeAnnotationsNeededForFieldAccess { location: Location }, #[error("Multiple trait impls may apply to this object type")] - MultipleMatchingImpls { object_type: Type, candidates: Vec, span: Span }, + MultipleMatchingImpls { object_type: Type, candidates: Vec, location: Location }, #[error("use of deprecated function {name}")] - CallDeprecated { name: String, note: Option, span: Span }, + CallDeprecated { name: String, note: Option, location: Location }, #[error("{0}")] ResolverError(ResolverError), #[error("Unused expression result of type {expr_type}")] - UnusedResultError { expr_type: Type, expr_span: Span }, + UnusedResultError { expr_type: Type, expr_location: Location }, #[error("Expected type {expected_typ:?} is not the same as {actual_typ:?}")] TraitMethodParameterTypeMismatch { method_name: String, expected_typ: String, actual_typ: String, - parameter_span: Span, + parameter_location: Location, parameter_index: usize, }, #[error("No matching impl found")] NoMatchingImplFound(NoMatchingImplFoundError), - #[error("Constraint for `{typ}: {trait_name}` is not needed, another matching impl is already in scope")] - UnneededTraitConstraint { trait_name: String, typ: Type, span: Span }, + #[error( + "Constraint for `{typ}: {trait_name}` is not needed, another matching impl is already in scope" + )] + UnneededTraitConstraint { trait_name: String, typ: Type, location: Location }, #[error( "Expected {expected_count} generic(s) from this function, but {actual_count} were provided" )] - IncorrectTurbofishGenericCount { expected_count: usize, actual_count: usize, span: Span }, + IncorrectTurbofishGenericCount { + expected_count: usize, + actual_count: usize, + location: Location, + }, #[error( "Cannot pass a mutable reference from a constrained runtime to an unconstrained runtime" )] - ConstrainedReferenceToUnconstrained { span: Span }, + ConstrainedReferenceToUnconstrained { location: Location }, #[error( "Cannot pass a mutable reference from a unconstrained runtime to an constrained runtime" )] - UnconstrainedReferenceToConstrained { span: Span }, + UnconstrainedReferenceToConstrained { location: Location }, #[error("Slices cannot be returned from an unconstrained runtime to a constrained runtime")] - UnconstrainedSliceReturnToConstrained { span: Span }, - #[error("Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block")] - Unsafe { span: Span }, + UnconstrainedSliceReturnToConstrained { location: Location }, + #[error( + "Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block" + )] + Unsafe { location: Location }, #[error("Converting an unconstrained fn to a non-unconstrained fn is unsafe")] - UnsafeFn { span: Span }, + UnsafeFn { location: Location }, #[error("Expected a constant, but found `{typ}`")] - NonConstantEvaluated { typ: Type, span: Span }, + NonConstantEvaluated { typ: Type, location: Location }, #[error("Slices must have constant length")] - NonConstantSliceLength { span: Span }, + NonConstantSliceLength { location: Location }, #[error("Only sized types may be used in the entry point to a program")] - InvalidTypeForEntryPoint { span: Span }, + InvalidTypeForEntryPoint { location: Location }, #[error("Mismatched number of parameters in trait implementation")] MismatchTraitImplNumParameters { actual_num_parameters: usize, expected_num_parameters: usize, trait_name: String, method_name: String, - span: Span, + location: Location, }, #[error("Strings do not support indexed assignment")] - StringIndexAssign { span: Span }, + StringIndexAssign { location: Location }, #[error("Macro calls may only return `Quoted` values")] - MacroReturningNonExpr { typ: Type, span: Span }, + MacroReturningNonExpr { typ: Type, location: Location }, #[error("`{name}` has already been specified")] - DuplicateNamedTypeArg { name: Ident, prev_span: Span }, + DuplicateNamedTypeArg { name: Ident, prev_location: Location }, #[error("`{item}` has no associated type named `{name}`")] NoSuchNamedTypeArg { name: Ident, item: String }, #[error("`{item}` is missing the associated type `{name}`")] - MissingNamedTypeArg { name: Rc, item: String, span: Span }, + MissingNamedTypeArg { name: Rc, item: String, location: Location }, #[error("Internal compiler error: type unspecified for value")] - UnspecifiedType { span: Span }, + UnspecifiedType { location: Location }, #[error("Binding `{typ}` here to the `_` inside would create a cyclic type")] - CyclicType { typ: Type, span: Span }, + CyclicType { typ: Type, location: Location }, #[error("Type annotations required before indexing this array or slice")] - TypeAnnotationsNeededForIndex { span: Span }, + TypeAnnotationsNeededForIndex { location: Location }, #[error("Unnecessary `unsafe` block")] - UnnecessaryUnsafeBlock { span: Span }, + UnnecessaryUnsafeBlock { location: Location }, #[error("Unnecessary `unsafe` block")] - NestedUnsafeBlock { span: Span }, + NestedUnsafeBlock { location: Location }, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct NoMatchingImplFoundError { pub(crate) constraints: Vec<(Type, String)>, - pub span: Span, + pub location: Location, } impl TypeCheckError { @@ -225,69 +255,179 @@ impl TypeCheckError { pub(crate) fn is_non_constant_evaluated(&self) -> bool { matches!(self, TypeCheckError::NonConstantEvaluated { .. }) } + + pub fn location(&self) -> Location { + match self { + TypeCheckError::OpCannotBeUsed { location, .. } + | TypeCheckError::DivisionByZero { location, .. } + | TypeCheckError::ModuloOnFields { location, .. } + | TypeCheckError::OverflowingAssignment { location, .. } + | TypeCheckError::OverflowingConstant { location, .. } + | TypeCheckError::FailingBinaryOp { location, .. } + | TypeCheckError::TypeCannotBeUsed { location, .. } + | TypeCheckError::TypeMismatch { expr_location: location, .. } + | TypeCheckError::TypeMismatchWithSource { location, .. } + | TypeCheckError::TypeKindMismatch { expr_location: location, .. } + | TypeCheckError::TypeCanonicalizationMismatch { location, .. } + | TypeCheckError::ArityMisMatch { location, .. } + | TypeCheckError::PublicReturnType { location, .. } + | TypeCheckError::InvalidCast { location, .. } + | TypeCheckError::DownsizingCast { location, .. } + | TypeCheckError::ExpectedFunction { location, .. } + | TypeCheckError::AccessUnknownMember { location, .. } + | TypeCheckError::ParameterCountMismatch { location, .. } + | TypeCheckError::AssertionParameterCountMismatch { location, .. } + | TypeCheckError::GenericCountMismatch { location, .. } + | TypeCheckError::UnconstrainedMismatch { location, .. } + | TypeCheckError::UnsupportedCast { location } + | TypeCheckError::TupleIndexOutOfBounds { location, .. } + | TypeCheckError::VariableMustBeMutable { location, .. } + | TypeCheckError::CannotMutateImmutableVariable { location, .. } + | TypeCheckError::MutableCaptureWithoutRef { location, .. } + | TypeCheckError::MutableReferenceToArrayElement { location } + | TypeCheckError::UnresolvedMethodCall { location, .. } + | TypeCheckError::CannotInvokeStructFieldFunctionType { location, .. } + | TypeCheckError::IntegerSignedness { location, .. } + | TypeCheckError::IntegerBitWidth { location, .. } + | TypeCheckError::InvalidInfixOp { location, .. } + | TypeCheckError::InvalidUnaryOp { location, .. } + | TypeCheckError::FieldBitwiseOp { location } + | TypeCheckError::IntegerTypeMismatch { location, .. } + | TypeCheckError::IntegerAndFieldBinaryOperation { location } + | TypeCheckError::FieldModulo { location } + | TypeCheckError::FieldNot { location } + | TypeCheckError::FieldComparison { location } + | TypeCheckError::InvalidShiftSize { location } + | TypeCheckError::AmbiguousBitWidth { location } + | TypeCheckError::NonHomogeneousArray { first_location: location, .. } + | TypeCheckError::TypeAnnotationsNeededForMethodCall { location } + | TypeCheckError::TypeAnnotationsNeededForFieldAccess { location } + | TypeCheckError::MultipleMatchingImpls { location, .. } + | TypeCheckError::CallDeprecated { location, .. } + | TypeCheckError::UnusedResultError { expr_location: location, .. } + | TypeCheckError::TraitMethodParameterTypeMismatch { + parameter_location: location, + .. + } + | TypeCheckError::UnneededTraitConstraint { location, .. } + | TypeCheckError::IncorrectTurbofishGenericCount { location, .. } + | TypeCheckError::ConstrainedReferenceToUnconstrained { location } + | TypeCheckError::UnconstrainedReferenceToConstrained { location } + | TypeCheckError::UnconstrainedSliceReturnToConstrained { location } + | TypeCheckError::Unsafe { location } + | TypeCheckError::UnsafeFn { location } + | TypeCheckError::NonConstantEvaluated { location, .. } + | TypeCheckError::NonConstantSliceLength { location } + | TypeCheckError::InvalidTypeForEntryPoint { location } + | TypeCheckError::MismatchTraitImplNumParameters { location, .. } + | TypeCheckError::StringIndexAssign { location } + | TypeCheckError::MacroReturningNonExpr { location, .. } + | TypeCheckError::MissingNamedTypeArg { location, .. } + | TypeCheckError::UnspecifiedType { location } + | TypeCheckError::CyclicType { location, .. } + | TypeCheckError::TypeAnnotationsNeededForIndex { location } + | TypeCheckError::UnnecessaryUnsafeBlock { location } + | TypeCheckError::NestedUnsafeBlock { location } => *location, + + TypeCheckError::DuplicateNamedTypeArg { name: ident, .. } + | TypeCheckError::NoSuchNamedTypeArg { name: ident, .. } => ident.location(), + + TypeCheckError::NoMatchingImplFound(no_matching_impl_found_error) => { + no_matching_impl_found_error.location + } + TypeCheckError::Context { err, .. } => err.location(), + TypeCheckError::ResolverError(resolver_error) => resolver_error.location(), + } + } } impl<'a> From<&'a TypeCheckError> for Diagnostic { fn from(error: &'a TypeCheckError) -> Diagnostic { match error { - TypeCheckError::TypeCannotBeUsed { typ, place, span } => Diagnostic::simple_error( + TypeCheckError::TypeCannotBeUsed { typ, place, location } => Diagnostic::simple_error( format!("The type {} cannot be used in a {}", &typ, place), String::new(), - *span, + *location, ), TypeCheckError::Context { err, ctx } => { let mut diag = Diagnostic::from(err.as_ref()); diag.add_note(ctx.to_string()); diag } - TypeCheckError::OpCannotBeUsed { op, place, span } => Diagnostic::simple_error( + TypeCheckError::OpCannotBeUsed { op, place, location } => Diagnostic::simple_error( format!("The operator {op:?} cannot be used in a {place}"), String::new(), - *span, + *location, ), - TypeCheckError::DivisionByZero { lhs, rhs, span } => Diagnostic::simple_error( + TypeCheckError::DivisionByZero { lhs, rhs, location } => Diagnostic::simple_error( format!("Division by zero: {lhs} / {rhs}"), String::new(), - *span, + *location, ), - TypeCheckError::ModuloOnFields { lhs, rhs, span } => Diagnostic::simple_error( + TypeCheckError::ModuloOnFields { lhs, rhs, location } => Diagnostic::simple_error( format!("Modulo on Field elements: {lhs} % {rhs}"), String::new(), - *span, + *location, ), - TypeCheckError::TypeMismatch { expected_typ, expr_typ, expr_span } => { + TypeCheckError::TypeMismatch { expected_typ, expr_typ, expr_location } => { Diagnostic::simple_error( format!("Expected type {expected_typ}, found type {expr_typ}"), String::new(), - *expr_span, + *expr_location, ) } - TypeCheckError::TypeKindMismatch { expected_kind, expr_kind, expr_span } => { - Diagnostic::simple_error( - format!("Expected kind {expected_kind}, found kind {expr_kind}"), - String::new(), - *expr_span, - ) + TypeCheckError::TypeKindMismatch { expected_kind, expr_kind, expr_location } => { + // Try to improve the error message for some kind combinations + match (expected_kind, expr_kind) { + (Kind::Normal, Kind::Numeric(_)) => { + Diagnostic::simple_error( + "Expected type, found numeric generic".into(), + "not a type".into(), + *expr_location, + ) + } + (Kind::Numeric(typ), Kind::Normal) => { + Diagnostic::simple_error( + "Type provided when a numeric generic was expected".into(), + format!("the numeric generic is not of type `{typ}`"), + *expr_location, + ) + } + (Kind::Numeric(expected_type), Kind::Numeric(found_type)) => { + Diagnostic::simple_error( + format!("The numeric generic is not of type `{expected_type}`"), + format!("expected `{expected_type}`, found `{found_type}`"), + *expr_location, + ) + } + _ => { + Diagnostic::simple_error( + format!("Expected kind {expected_kind}, found kind {expr_kind}"), + String::new(), + *expr_location, + ) + } + } } - TypeCheckError::TypeCanonicalizationMismatch { to, from, to_value, from_value, span } => { + TypeCheckError::TypeCanonicalizationMismatch { to, from, to_value, from_value, location } => { Diagnostic::simple_error( format!("Evaluating {to} resulted in {to_value}, but {from_value} was expected"), format!("from evaluating {from} without simplifications"), - *span, + *location, ) } - TypeCheckError::TraitMethodParameterTypeMismatch { method_name, expected_typ, actual_typ, parameter_index, parameter_span } => { + TypeCheckError::TraitMethodParameterTypeMismatch { method_name, expected_typ, actual_typ, parameter_index, parameter_location } => { Diagnostic::simple_error( format!("Parameter #{parameter_index} of method `{method_name}` must be of type {expected_typ}, not {actual_typ}"), String::new(), - *parameter_span, + *parameter_location, ) } TypeCheckError::NonHomogeneousArray { - first_span, + first_location, first_type, first_index, - second_span, + second_location, second_type, second_index, } => { @@ -296,114 +436,122 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { "Non homogeneous array, different element types found at indices ({first_index},{second_index})" ), format!("Found type {first_type}"), - *first_span, + *first_location, ); - diag.add_secondary(format!("but then found type {second_type}"), *second_span); + diag.add_secondary(format!("but then found type {second_type}"), *second_location); diag } - TypeCheckError::ArityMisMatch { expected, found, span } => { + TypeCheckError::ArityMisMatch { expected, found, location } => { let plural = if *expected == 1 { "" } else { "s" }; let msg = format!("Expected {expected} argument{plural}, but found {found}"); - Diagnostic::simple_error(msg, String::new(), *span) + Diagnostic::simple_error(msg, String::new(), *location) } - TypeCheckError::ParameterCountMismatch { expected, found, span } => { + TypeCheckError::ParameterCountMismatch { expected, found, location } => { let empty_or_s = if *expected == 1 { "" } else { "s" }; let was_or_were = if *found == 1 { "was" } else { "were" }; let msg = format!("Function expects {expected} parameter{empty_or_s} but {found} {was_or_were} given"); - Diagnostic::simple_error(msg, String::new(), *span) + Diagnostic::simple_error(msg, String::new(), *location) } - TypeCheckError::AssertionParameterCountMismatch { kind, found, span } => { + TypeCheckError::AssertionParameterCountMismatch { kind, found, location } => { let was_or_were = if *found == 1 { "was" } else { "were" }; let min = kind.required_arguments_count(); let max = min + 1; let msg = format!("{kind} expects {min} or {max} parameters but {found} {was_or_were} given"); - Diagnostic::simple_error(msg, String::new(), *span) + Diagnostic::simple_error(msg, String::new(), *location) } - TypeCheckError::GenericCountMismatch { item, expected, found, span } => { + TypeCheckError::GenericCountMismatch { item, expected, found, location } => { let empty_or_s = if *expected == 1 { "" } else { "s" }; let was_or_were = if *found == 1 { "was" } else { "were" }; let msg = format!("{item} expects {expected} generic{empty_or_s} but {found} {was_or_were} given"); - Diagnostic::simple_error(msg, String::new(), *span) + Diagnostic::simple_error(msg, String::new(), *location) } - TypeCheckError::UnconstrainedMismatch { item, expected, span } => { + TypeCheckError::UnconstrainedMismatch { item, expected, location } => { let msg = if *expected { format!("{item} is expected to be unconstrained") } else { format!("{item} is not expected to be unconstrained") }; - Diagnostic::simple_error(msg, String::new(), *span) + Diagnostic::simple_error(msg, String::new(), *location) } - TypeCheckError::InvalidCast { span, reason, .. } => { - Diagnostic::simple_error(error.to_string(), reason.clone(), *span) + TypeCheckError::InvalidCast { location, reason, .. } => { + Diagnostic::simple_error(error.to_string(), reason.clone(), *location) } - TypeCheckError::DownsizingCast { span, reason, .. } => { - Diagnostic::simple_warning(error.to_string(), reason.clone(), *span) + TypeCheckError::DownsizingCast { location, reason, .. } => { + Diagnostic::simple_warning(error.to_string(), reason.clone(), *location) } - TypeCheckError::ExpectedFunction { span, .. } - | TypeCheckError::AccessUnknownMember { span, .. } - | TypeCheckError::UnsupportedCast { span } - | TypeCheckError::TupleIndexOutOfBounds { span, .. } - | TypeCheckError::VariableMustBeMutable { span, .. } - | TypeCheckError::CannotMutateImmutableVariable { span, .. } - | TypeCheckError::UnresolvedMethodCall { span, .. } - | TypeCheckError::IntegerSignedness { span, .. } - | TypeCheckError::IntegerBitWidth { span, .. } - | TypeCheckError::InvalidInfixOp { span, .. } - | TypeCheckError::InvalidUnaryOp { span, .. } - | TypeCheckError::FieldBitwiseOp { span, .. } - | TypeCheckError::IntegerTypeMismatch { span, .. } - | TypeCheckError::FieldComparison { span, .. } - | TypeCheckError::AmbiguousBitWidth { span, .. } - | TypeCheckError::IntegerAndFieldBinaryOperation { span } - | TypeCheckError::OverflowingAssignment { span, .. } - | TypeCheckError::OverflowingConstant { span, .. } - | TypeCheckError::FailingBinaryOp { span, .. } - | TypeCheckError::FieldModulo { span } - | TypeCheckError::FieldNot { span } - | TypeCheckError::ConstrainedReferenceToUnconstrained { span } - | TypeCheckError::UnconstrainedReferenceToConstrained { span } - | TypeCheckError::UnconstrainedSliceReturnToConstrained { span } - | TypeCheckError::NonConstantEvaluated { span, .. } - | TypeCheckError::NonConstantSliceLength { span } - | TypeCheckError::StringIndexAssign { span } - | TypeCheckError::InvalidShiftSize { span } => { - Diagnostic::simple_error(error.to_string(), String::new(), *span) + TypeCheckError::ExpectedFunction { location, .. } + | TypeCheckError::AccessUnknownMember { location, .. } + | TypeCheckError::UnsupportedCast { location } + | TypeCheckError::TupleIndexOutOfBounds { location, .. } + | TypeCheckError::VariableMustBeMutable { location, .. } + | TypeCheckError::CannotMutateImmutableVariable { location, .. } + | TypeCheckError::UnresolvedMethodCall { location, .. } + | TypeCheckError::IntegerSignedness { location, .. } + | TypeCheckError::IntegerBitWidth { location, .. } + | TypeCheckError::InvalidInfixOp { location, .. } + | TypeCheckError::InvalidUnaryOp { location, .. } + | TypeCheckError::FieldBitwiseOp { location, .. } + | TypeCheckError::IntegerTypeMismatch { location, .. } + | TypeCheckError::FieldComparison { location, .. } + | TypeCheckError::AmbiguousBitWidth { location, .. } + | TypeCheckError::IntegerAndFieldBinaryOperation { location } + | TypeCheckError::OverflowingAssignment { location, .. } + | TypeCheckError::OverflowingConstant { location, .. } + | TypeCheckError::FailingBinaryOp { location, .. } + | TypeCheckError::FieldModulo { location } + | TypeCheckError::FieldNot { location } + | TypeCheckError::ConstrainedReferenceToUnconstrained { location } + | TypeCheckError::UnconstrainedReferenceToConstrained { location } + | TypeCheckError::UnconstrainedSliceReturnToConstrained { location } + | TypeCheckError::NonConstantEvaluated { location, .. } + | TypeCheckError::NonConstantSliceLength { location } + | TypeCheckError::StringIndexAssign { location } + | TypeCheckError::InvalidShiftSize { location } => { + Diagnostic::simple_error(error.to_string(), String::new(), *location) } - TypeCheckError::PublicReturnType { typ, span } => Diagnostic::simple_error( + TypeCheckError::MutableCaptureWithoutRef { name, location } => Diagnostic::simple_error( + format!("Mutable variable {name} captured in lambda must be a mutable reference"), + "Use '&mut' instead of 'mut' to capture a mutable variable.".to_string(), + *location, + ), + TypeCheckError::MutableReferenceToArrayElement { location } => { + Diagnostic::simple_error("Mutable references to array elements are currently unsupported".into(), "Try storing the element in a fresh variable first".into(), *location) + }, + TypeCheckError::PublicReturnType { typ, location } => Diagnostic::simple_error( "Functions cannot declare a public return type".to_string(), format!("return type is {typ}"), - *span, + *location, ), - TypeCheckError::TypeAnnotationsNeededForMethodCall { span } => { + TypeCheckError::TypeAnnotationsNeededForMethodCall { location } => { let mut error = Diagnostic::simple_error( "Object type is unknown in method call".to_string(), "Type must be known by this point to know which method to call".to_string(), - *span, + *location, ); error.add_note("Try adding a type annotation for the object type before this method call".to_string()); error }, - TypeCheckError::TypeAnnotationsNeededForFieldAccess { span } => { + TypeCheckError::TypeAnnotationsNeededForFieldAccess { location } => { let mut error = Diagnostic::simple_error( "Object type is unknown in field access".to_string(), "Type must be known by this point".to_string(), - *span, + *location, ); error.add_note("Try adding a type annotation for the object type before this expression".to_string()); error }, - TypeCheckError::MultipleMatchingImpls { object_type, candidates, span } => { + TypeCheckError::MultipleMatchingImpls { object_type, candidates, location } => { let message = format!("Multiple trait impls match the object type `{object_type}`"); let secondary = "Ambiguous impl".to_string(); - let mut error = Diagnostic::simple_error(message, secondary, *span); + let mut error = Diagnostic::simple_error(message, secondary, *location); for (i, candidate) in candidates.iter().enumerate() { error.add_note(format!("Candidate {}: `{candidate}`", i + 1)); } error }, TypeCheckError::ResolverError(error) => error.into(), - TypeCheckError::TypeMismatchWithSource { expected, actual, span, source } => { + TypeCheckError::TypeMismatchWithSource { expected, actual, location, source } => { let message = match source { Source::Binary => format!("Types in a binary operation should match, but found {expected} and {actual}"), Source::Assignment => { @@ -414,125 +562,125 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { Source::StringLen => format!("Can only compare strings of the same length. Here LHS is of length {expected}, and RHS is {actual}"), Source::Comparison => format!("Unsupported types for comparison: {expected} and {actual}"), Source::BinOp(kind) => format!("Unsupported types for operator `{kind}`: {expected} and {actual}"), - Source::Return(ret_ty, expr_span) => { - let ret_ty_span = match ret_ty.clone() { - FunctionReturnType::Default(span) => span, - FunctionReturnType::Ty(ty) => ty.span, + Source::Return(ret_ty, expr_location) => { + let ret_ty_location = match ret_ty.clone() { + FunctionReturnType::Default(location) => location, + FunctionReturnType::Ty(ty) => ty.location, }; - let mut diagnostic = Diagnostic::simple_error(format!("expected type {expected}, found type {actual}"), format!("expected {expected} because of return type"), ret_ty_span); + let mut diagnostic = Diagnostic::simple_error(format!("expected type {expected}, found type {actual}"), format!("expected {expected} because of return type"), ret_ty_location); if let FunctionReturnType::Default(_) = ret_ty { diagnostic.add_note(format!("help: try adding a return type: `-> {actual}`")); } - diagnostic.add_secondary(format!("{actual} returned here"), *expr_span); + diagnostic.add_secondary(format!("{actual} returned here"), *expr_location); return diagnostic }, }; - Diagnostic::simple_error(message, String::new(), *span) + Diagnostic::simple_error(message, String::new(), *location) } - TypeCheckError::CallDeprecated { span, ref note, .. } => { + TypeCheckError::CallDeprecated { location, note, .. } => { let primary_message = error.to_string(); let secondary_message = note.clone().unwrap_or_default(); - let mut diagnostic = Diagnostic::simple_warning(primary_message, secondary_message, *span); + let mut diagnostic = Diagnostic::simple_warning(primary_message, secondary_message, *location); diagnostic.deprecated = true; diagnostic } - TypeCheckError::UnusedResultError { expr_type, expr_span } => { + TypeCheckError::UnusedResultError { expr_type, expr_location } => { let msg = format!("Unused expression result of type {expr_type}"); - Diagnostic::simple_warning(msg, String::new(), *expr_span) + Diagnostic::simple_warning(msg, String::new(), *expr_location) } TypeCheckError::NoMatchingImplFound(error) => error.into(), - TypeCheckError::UnneededTraitConstraint { trait_name, typ, span } => { + TypeCheckError::UnneededTraitConstraint { trait_name, typ, location } => { let msg = format!("Constraint for `{typ}: {trait_name}` is not needed, another matching impl is already in scope"); - Diagnostic::simple_warning(msg, "Unnecessary trait constraint in where clause".into(), *span) + Diagnostic::simple_warning(msg, "Unnecessary trait constraint in where clause".into(), *location) } - TypeCheckError::InvalidTypeForEntryPoint { span } => Diagnostic::simple_error( + TypeCheckError::InvalidTypeForEntryPoint { location } => Diagnostic::simple_error( "Only sized types may be used in the entry point to a program".to_string(), - "Slices, references, or any type containing them may not be used in main, contract functions, or foldable functions".to_string(), *span), + "Slices, references, or any type containing them may not be used in main, contract functions, or foldable functions".to_string(), *location), TypeCheckError::MismatchTraitImplNumParameters { expected_num_parameters, actual_num_parameters, trait_name, method_name, - span, + location, } => { let plural = if *expected_num_parameters == 1 { "" } else { "s" }; let primary_message = format!( "`{trait_name}::{method_name}` expects {expected_num_parameters} parameter{plural}, but this method has {actual_num_parameters}"); - Diagnostic::simple_error(primary_message, "".to_string(), *span) + Diagnostic::simple_error(primary_message, "".to_string(), *location) } - TypeCheckError::IncorrectTurbofishGenericCount { expected_count, actual_count, span } => { + TypeCheckError::IncorrectTurbofishGenericCount { expected_count, actual_count, location } => { let expected_plural = if *expected_count == 1 { "" } else { "s" }; let actual_plural = if *actual_count == 1 { "was" } else { "were" }; let msg = format!("Expected {expected_count} generic{expected_plural} from this function, but {actual_count} {actual_plural} provided"); - Diagnostic::simple_error(msg, "".into(), *span) + Diagnostic::simple_error(msg, "".into(), *location) }, - TypeCheckError::MacroReturningNonExpr { typ, span } => { + TypeCheckError::MacroReturningNonExpr { typ, location } => { let mut error = Diagnostic::simple_error( format!("Expected macro call to return a `Quoted` but found a(n) `{typ}`"), "Macro calls must return quoted values, otherwise there is no code to insert.".into(), - *span, + *location, ); - error.add_secondary("Hint: remove the `!` from the end of the function name.".to_string(), *span); + error.add_secondary("Hint: remove the `!` from the end of the function name.".to_string(), *location); error }, - TypeCheckError::DuplicateNamedTypeArg { name, prev_span } => { + TypeCheckError::DuplicateNamedTypeArg { name, prev_location } => { let msg = format!("`{name}` has already been specified"); - let mut error = Diagnostic::simple_error(msg.to_string(), "".to_string(), name.span()); - error.add_secondary(format!("`{name}` previously specified here"), *prev_span); + let mut error = Diagnostic::simple_error(msg.to_string(), "".to_string(), name.location()); + error.add_secondary(format!("`{name}` previously specified here"), *prev_location); error }, TypeCheckError::NoSuchNamedTypeArg { name, item } => { let msg = format!("`{item}` has no associated type named `{name}`"); - Diagnostic::simple_error(msg.to_string(), "".to_string(), name.span()) + Diagnostic::simple_error(msg.to_string(), "".to_string(), name.location()) }, - TypeCheckError::MissingNamedTypeArg { name, item, span } => { + TypeCheckError::MissingNamedTypeArg { name, item, location } => { let msg = format!("`{item}` is missing the associated type `{name}`"); - Diagnostic::simple_error(msg.to_string(), "".to_string(), *span) + Diagnostic::simple_error(msg.to_string(), "".to_string(), *location) }, - TypeCheckError::Unsafe { span } => { - Diagnostic::simple_error(error.to_string(), String::new(), *span) + TypeCheckError::Unsafe { location } => { + Diagnostic::simple_error(error.to_string(), String::new(), *location) } - TypeCheckError::UnsafeFn { span } => { - Diagnostic::simple_error(error.to_string(), String::new(), *span) + TypeCheckError::UnsafeFn { location } => { + Diagnostic::simple_error(error.to_string(), String::new(), *location) } - TypeCheckError::UnspecifiedType { span } => { - Diagnostic::simple_error(error.to_string(), String::new(), *span) + TypeCheckError::UnspecifiedType { location } => { + Diagnostic::simple_error(error.to_string(), String::new(), *location) } - TypeCheckError::CyclicType { typ: _, span } => { - Diagnostic::simple_error(error.to_string(), "Cyclic types have unlimited size and are prohibited in Noir".into(), *span) + TypeCheckError::CyclicType { typ: _, location } => { + Diagnostic::simple_error(error.to_string(), "Cyclic types have unlimited size and are prohibited in Noir".into(), *location) } - TypeCheckError::CannotInvokeStructFieldFunctionType { method_name, object_type, span } => { + TypeCheckError::CannotInvokeStructFieldFunctionType { method_name, object_type, location } => { Diagnostic::simple_error( format!("Cannot invoke function field '{method_name}' on type '{object_type}' as a method"), format!("to call the function stored in '{method_name}', surround the field access with parentheses: '(', ')'"), - *span, + *location, ) }, - TypeCheckError::TypeAnnotationsNeededForIndex { span } => { + TypeCheckError::TypeAnnotationsNeededForIndex { location } => { Diagnostic::simple_error( "Type annotations required before indexing this array or slice".into(), "Type annotations needed before this point, can't decide if this is an array or slice".into(), - *span, + *location, ) }, - TypeCheckError::UnnecessaryUnsafeBlock { span } => { + TypeCheckError::UnnecessaryUnsafeBlock { location } => { Diagnostic::simple_warning( "Unnecessary `unsafe` block".into(), "".into(), - *span, + *location, ) }, - TypeCheckError::NestedUnsafeBlock { span } => { + TypeCheckError::NestedUnsafeBlock { location } => { Diagnostic::simple_warning( "Unnecessary `unsafe` block".into(), "Because it's nested inside another `unsafe` block".into(), - *span, + *location, ) }, } @@ -542,15 +690,15 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { impl<'a> From<&'a NoMatchingImplFoundError> for Diagnostic { fn from(error: &'a NoMatchingImplFoundError) -> Self { let constraints = &error.constraints; - let span = error.span; + let location = error.location; assert!(!constraints.is_empty()); let msg = format!("No matching impl found for `{}: {}`", constraints[0].0, constraints[0].1); - let mut diagnostic = Diagnostic::from_message(&msg); + let mut diagnostic = Diagnostic::from_message(&msg, location.file); let secondary = format!("No impl for `{}: {}`", constraints[0].0, constraints[0].1); - diagnostic.add_secondary(secondary, span); + diagnostic.add_secondary(secondary, location); // These must be notes since secondaries are unordered for (typ, trait_name) in &constraints[1..] { @@ -565,7 +713,7 @@ impl NoMatchingImplFoundError { pub fn new( interner: &NodeInterner, failing_constraints: Vec, - span: Span, + location: Location, ) -> Option { // Don't show any errors where try_get_trait returns None. // This can happen if a trait is used that was never declared. @@ -578,6 +726,6 @@ impl NoMatchingImplFoundError { }) .collect::>>()?; - Some(Self { constraints, span }) + Some(Self { constraints, location }) } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/generics.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/generics.rs index f823b495040a..29baee8c30a4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/generics.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/generics.rs @@ -3,9 +3,9 @@ use std::cell::Ref; use iter_extended::vecmap; use crate::{ + DataType, ResolvedGeneric, Type, hir_def::traits::NamedType, node_interner::{FuncId, NodeInterner, TraitId, TypeAliasId}, - DataType, ResolvedGeneric, Type, }; /// Represents something that can be generic over type variables diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs index 74028fa38093..0076cab8de5d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs @@ -1,14 +1,14 @@ -use acvm::FieldElement; use fm::FileId; use noirc_errors::Location; +use crate::Shared; use crate::ast::{BinaryOp, BinaryOpKind, Ident, UnaryOp}; use crate::hir::type_check::generics::TraitGenerics; use crate::node_interner::{ DefinitionId, DefinitionKind, ExprId, FuncId, NodeInterner, StmtId, TraitMethodId, }; +use crate::signed_field::SignedField; use crate::token::{FmtStrFragment, Tokens}; -use crate::Shared; use super::stmt::HirPattern; use super::traits::{ResolvedTraitBound, TraitConstraint}; @@ -115,7 +115,7 @@ pub enum HirLiteral { Array(HirArrayLiteral), Slice(HirArrayLiteral), Bool(bool), - Integer(FieldElement, bool), //true for negative integer and false for positive + Integer(SignedField), Str(String), FmtStr(Vec, Vec, u32 /* length */), Unit, @@ -249,13 +249,10 @@ impl HirMethodReference { } HirMethodReference::TraitMethodId(method_id, trait_generics, assumed) => { let id = interner.trait_method_id(method_id); + let trait_id = method_id.trait_id; let constraint = TraitConstraint { typ: object_type, - trait_bound: ResolvedTraitBound { - trait_id: method_id.trait_id, - trait_generics, - span: location.span, - }, + trait_bound: ResolvedTraitBound { trait_id, trait_generics, location }, }; (id, ImplKind::TraitMethod(TraitMethod { method_id, constraint, assumed })) @@ -263,7 +260,7 @@ impl HirMethodReference { }; let func_var = HirIdent { location, id, impl_kind }; let func = interner.push_expr(HirExpression::Ident(func_var.clone(), generics)); - interner.push_expr_location(func, location.span, location.file); + interner.push_expr_location(func, location); (func, func_var) } } @@ -390,49 +387,6 @@ impl Case { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct SignedField { - pub field: FieldElement, - pub is_negative: bool, -} - -impl SignedField { - pub fn new(field: FieldElement, is_negative: bool) -> Self { - Self { field, is_negative } - } -} - -impl std::ops::Neg for SignedField { - type Output = Self; - - fn neg(mut self) -> Self::Output { - self.is_negative = !self.is_negative; - self - } -} - -impl std::cmp::PartialOrd for SignedField { - fn partial_cmp(&self, other: &Self) -> Option { - if self.is_negative != other.is_negative { - if self.is_negative { - return Some(std::cmp::Ordering::Less); - } else { - return Some(std::cmp::Ordering::Greater); - } - } - self.field.partial_cmp(&other.field) - } -} - -impl std::fmt::Display for SignedField { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.is_negative { - write!(f, "-")?; - } - write!(f, "{}", self.field) - } -} - #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum Constructor { True, 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 75bb4f505417..b84e511bd447 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 @@ -171,7 +171,7 @@ pub struct FuncMeta { #[derive(Debug, Clone)] pub enum FunctionBody { - Unresolved(FunctionKind, BlockExpression, Span), + Unresolved(FunctionKind, BlockExpression, Location), Resolving, Resolved, } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/stmt.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/stmt.rs index b0e004349032..21db5971bf0f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -1,8 +1,8 @@ use super::expr::HirIdent; +use crate::Type; use crate::ast::Ident; use crate::node_interner::{ExprId, StmtId}; use crate::token::SecondaryAttribute; -use crate::Type; use noirc_errors::{Location, Span}; /// A HirStatement is the result of performing name resolution on diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/traits.rs index a80c25492a35..a344b2769133 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/traits.rs @@ -1,13 +1,13 @@ use iter_extended::vecmap; use rustc_hash::FxHashMap as HashMap; +use crate::ResolvedGeneric; use crate::ast::{Ident, ItemVisibility, NoirFunction}; use crate::hir::type_check::generics::TraitGenerics; -use crate::ResolvedGeneric; use crate::{ + Generics, Type, TypeBindings, TypeVariable, graph::CrateId, node_interner::{FuncId, TraitId, TraitMethodId}, - Generics, Type, TypeBindings, TypeVariable, }; use fm::FileId; use noirc_errors::{Location, Span}; @@ -121,7 +121,7 @@ impl TraitConstraint { pub struct ResolvedTraitBound { pub trait_id: TraitId, pub trait_generics: TraitGenerics, - pub span: Span, + pub location: Location, } impl ResolvedTraitBound { @@ -186,10 +186,10 @@ impl Trait { (ordered, named) } - pub fn get_trait_generics(&self, span: Span) -> TraitGenerics { + pub fn get_trait_generics(&self, location: Location) -> TraitGenerics { let ordered = vecmap(&self.generics, |generic| generic.clone().as_named_generic()); let named = vecmap(&self.associated_types, |generic| { - let name = Ident::new(generic.name.to_string(), span); + let name = Ident::new(generic.name.to_string(), location); NamedType { name, typ: generic.clone().as_named_generic() } }); TraitGenerics { ordered, named } @@ -197,11 +197,11 @@ impl Trait { /// Returns a TraitConstraint for this trait using Self as the object /// type and the uninstantiated generics for any trait generics. - pub fn as_constraint(&self, span: Span) -> TraitConstraint { - let trait_generics = self.get_trait_generics(span); + pub fn as_constraint(&self, location: Location) -> TraitConstraint { + let trait_generics = self.get_trait_generics(location); TraitConstraint { typ: Type::TypeVariable(self.self_type_typevar.clone()), - trait_bound: ResolvedTraitBound { trait_generics, trait_id: self.id, span }, + trait_bound: ResolvedTraitBound { trait_generics, trait_id: self.id, location }, } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs index 2afaced32c71..e7960c59521f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs @@ -12,11 +12,11 @@ use acvm::{AcirField, FieldElement}; use crate::{ ast::{IntegerBitSize, ItemVisibility}, - hir::type_check::{generics::TraitGenerics, TypeCheckError}, + hir::type_check::{TypeCheckError, generics::TraitGenerics}, node_interner::{ExprId, NodeInterner, TraitId, TypeAliasId}, }; use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use noirc_printable_type::PrintableType; use crate::{ @@ -249,11 +249,7 @@ impl Kind { } pub(crate) fn unify(&self, other: &Kind) -> Result<(), UnificationError> { - if self.unifies(other) { - Ok(()) - } else { - Err(UnificationError) - } + if self.unifies(other) { Ok(()) } else { Err(UnificationError) } } /// Returns the default type this type variable should be bound to if it is still unbound @@ -278,7 +274,7 @@ impl Kind { pub(crate) fn ensure_value_fits( &self, value: FieldElement, - span: Span, + location: Location, ) -> Result { match self.integral_maximum_size() { None => Ok(value), @@ -287,7 +283,7 @@ impl Kind { value, kind: self.clone(), maximum_size, - span, + location, } }), } @@ -395,7 +391,7 @@ pub type Generics = Vec; pub struct ResolvedGeneric { pub name: Rc, pub type_var: TypeVariable, - pub span: Span, + pub location: Location, } impl ResolvedGeneric { @@ -854,12 +850,17 @@ impl TypeVariable { *self.1.borrow_mut() = TypeBinding::Bound(typ); } - pub fn try_bind(&self, binding: Type, kind: &Kind, span: Span) -> Result<(), TypeCheckError> { + pub fn try_bind( + &self, + binding: Type, + kind: &Kind, + location: Location, + ) -> Result<(), TypeCheckError> { if !binding.kind().unifies(kind) { return Err(TypeCheckError::TypeKindMismatch { - expected_kind: format!("{}", kind), - expr_kind: format!("{}", binding.kind()), - expr_span: span, + expected_kind: kind.clone(), + expr_kind: binding.kind(), + expr_location: location, }); } @@ -871,7 +872,7 @@ impl TypeVariable { }; if binding.occurs(id) { - Err(TypeCheckError::CyclicType { span, typ: binding }) + Err(TypeCheckError::CyclicType { location, typ: binding }) } else { *self.1.borrow_mut() = TypeBinding::Bound(binding); Ok(()) @@ -1053,11 +1054,7 @@ impl std::fmt::Display for Type { let this = self.canonicalize_checked(); // Prevent infinite recursion - if this != *self { - write!(f, "{this}") - } else { - write!(f, "({lhs} {op} {rhs})") - } + if this != *self { write!(f, "{this}") } else { write!(f, "({lhs} {op} {rhs})") } } } } @@ -1245,6 +1242,10 @@ impl Type { } } + pub(crate) fn is_mutable_ref(&self) -> bool { + matches!(self.follow_bindings_shallow().as_ref(), Type::MutableReference(_)) + } + /// True if this type can be used as a parameter to `main` or a contract function. /// This is only false for unsized types like slices or slices that do not make sense /// as a program input such as named generics or mutable references. @@ -1457,8 +1458,8 @@ impl Type { Type::NamedGeneric(var, _) => var.kind(), Type::Constant(_, kind) => kind.clone(), Type::TypeVariable(var) => match &*var.borrow() { - TypeBinding::Bound(ref typ) => typ.kind(), - TypeBinding::Unbound(_, ref type_var_kind) => type_var_kind.clone(), + TypeBinding::Bound(typ) => typ.kind(), + TypeBinding::Unbound(_, type_var_kind) => type_var_kind.clone(), }, Type::InfixExpr(lhs, _op, rhs, _) => lhs.infix_kind(rhs), Type::Alias(def, generics) => def.borrow().get_type(generics).kind(), @@ -1486,11 +1487,7 @@ impl Type { fn infix_kind(&self, other: &Self) -> Kind { let self_kind = self.kind(); let other_kind = other.kind(); - if self_kind.unifies(&other_kind) { - self_kind - } else { - Kind::numeric(Type::Error) - } + if self_kind.unifies(&other_kind) { self_kind } else { Kind::numeric(Type::Error) } } /// Creates an `InfixExpr`. @@ -1541,7 +1538,7 @@ impl Type { Type::FieldElement | Type::Integer { .. } | Type::Bool => 1, Type::Array(size, typ) => { let length = size - .evaluate_to_u32(location.span) + .evaluate_to_u32(*location) .expect("Cannot have variable sized arrays as a parameter to main"); let typ = typ.as_ref(); length * typ.field_count(location) @@ -1568,7 +1565,7 @@ impl Type { fields.iter().fold(0, |acc, field_typ| acc + field_typ.field_count(location)) } Type::String(size) => size - .evaluate_to_u32(location.span) + .evaluate_to_u32(*location) .expect("Cannot have variable sized strings as a parameter to main"), Type::FmtString(_, _) | Type::Unit @@ -1658,8 +1655,8 @@ impl Type { typ.try_bind_to_polymorphic_int(var, bindings, only_integer) } // Avoid infinitely recursive bindings - TypeBinding::Unbound(ref id, _) if *id == target_id => Ok(()), - TypeBinding::Unbound(ref new_target_id, Kind::IntegerOrField) => { + TypeBinding::Unbound(id, _) if *id == target_id => Ok(()), + TypeBinding::Unbound(new_target_id, Kind::IntegerOrField) => { let type_var_kind = Kind::IntegerOrField; if only_integer { let var_clone = var.clone(); @@ -1682,7 +1679,7 @@ impl Type { bindings.insert(target_id, (var.clone(), Kind::Integer, this.clone())); Ok(()) } - TypeBinding::Unbound(new_target_id, ref type_var_kind) => { + TypeBinding::Unbound(new_target_id, type_var_kind) => { let var_clone = var.clone(); // Bind to the most specific type variable kind let clone_kind = @@ -1934,8 +1931,8 @@ impl Type { } (Constant(value, kind), other) | (other, Constant(value, kind)) => { - let dummy_span = Span::default(); - if let Ok(other_value) = other.evaluate_to_field_element(kind, dummy_span) { + let dummy_location = Location::dummy(); + if let Ok(other_value) = other.evaluate_to_field_element(kind, dummy_location) { if *value == other_value && kind.unifies(&other.kind()) { Ok(()) } else { @@ -2012,7 +2009,7 @@ impl Type { &self, expected: &Type, expression: ExprId, - span: Span, + location: Location, interner: &mut NodeInterner, errors: &mut Vec, make_error: impl FnOnce() -> TypeCheckError, @@ -2032,14 +2029,16 @@ impl Type { match self.try_fn_to_unconstrained_fn_coercion(expected) { FunctionCoercionResult::NoCoercion => errors.push(make_error()), FunctionCoercionResult::Coerced(coerced_self) => { - coerced_self - .unify_with_coercions(expected, expression, span, interner, errors, make_error); + coerced_self.unify_with_coercions( + expected, expression, location, interner, errors, make_error, + ); } FunctionCoercionResult::UnconstrainedMismatch(coerced_self) => { - errors.push(TypeCheckError::UnsafeFn { span }); + errors.push(TypeCheckError::UnsafeFn { location }); - coerced_self - .unify_with_coercions(expected, expression, span, interner, errors, make_error); + coerced_self.unify_with_coercions( + expected, expression, location, interner, errors, make_error, + ); } } } @@ -2104,8 +2103,8 @@ impl Type { /// If this type is a Type::Constant (used in array lengths), or is bound /// to a Type::Constant, return the constant as a u32. - pub fn evaluate_to_u32(&self, span: Span) -> Result { - self.evaluate_to_field_element(&Kind::u32(), span).map(|field_element| { + pub fn evaluate_to_u32(&self, location: Location) -> Result { + self.evaluate_to_field_element(&Kind::u32(), location).map(|field_element| { field_element .try_to_u32() .expect("ICE: size should have already been checked by evaluate_to_field_element") @@ -2117,17 +2116,17 @@ impl Type { pub(crate) fn evaluate_to_field_element( &self, kind: &Kind, - span: Span, + location: Location, ) -> Result { let run_simplifications = true; - self.evaluate_to_field_element_helper(kind, span, run_simplifications) + self.evaluate_to_field_element_helper(kind, location, run_simplifications) } /// evaluate_to_field_element with optional generic arithmetic simplifications pub(crate) fn evaluate_to_field_element_helper( &self, kind: &Kind, - span: Span, + location: Location, run_simplifications: bool, ) -> Result { if let Some((binding, binding_kind)) = self.get_inner_type_variable() { @@ -2135,7 +2134,7 @@ impl Type { if kind.unifies(&binding_kind) { return binding.evaluate_to_field_element_helper( &binding_kind, - span, + location, run_simplifications, ); } @@ -2146,12 +2145,12 @@ impl Type { match self.canonicalize_helper(could_be_checked_cast, run_simplifications) { Type::Constant(x, constant_kind) => { if kind.unifies(&constant_kind) { - kind.ensure_value_fits(x, span) + kind.ensure_value_fits(x, location) } else { Err(TypeCheckError::TypeKindMismatch { - expected_kind: format!("{}", constant_kind), - expr_kind: format!("{}", kind), - expr_span: span, + expected_kind: constant_kind, + expr_kind: kind.clone(), + expr_location: location, }) } } @@ -2160,31 +2159,31 @@ impl Type { if kind.unifies(&infix_kind) { let lhs_value = lhs.evaluate_to_field_element_helper( &infix_kind, - span, + location, run_simplifications, )?; let rhs_value = rhs.evaluate_to_field_element_helper( &infix_kind, - span, + location, run_simplifications, )?; - op.function(lhs_value, rhs_value, &infix_kind, span) + op.function(lhs_value, rhs_value, &infix_kind, location) } else { Err(TypeCheckError::TypeKindMismatch { - expected_kind: format!("{}", kind), - expr_kind: format!("{}", infix_kind), - expr_span: span, + expected_kind: kind.clone(), + expr_kind: infix_kind, + expr_location: location, }) } } Type::CheckedCast { from, to } => { - let to_value = to.evaluate_to_field_element(kind, span)?; + let to_value = to.evaluate_to_field_element(kind, location)?; // if both 'to' and 'from' evaluate to a constant, // return None unless they match let skip_simplifications = false; if let Ok(from_value) = - from.evaluate_to_field_element_helper(kind, span, skip_simplifications) + from.evaluate_to_field_element_helper(kind, location, skip_simplifications) { if to_value == from_value { Ok(to_value) @@ -2196,14 +2195,14 @@ impl Type { from, to_value, from_value, - span, + location, }) } } else { Ok(to_value) } } - other => Err(TypeCheckError::NonConstantEvaluated { typ: other, span }), + other => Err(TypeCheckError::NonConstantEvaluated { typ: other, location }), } } @@ -2287,7 +2286,11 @@ impl Type { ) -> (Type, TypeBindings) { match self { Type::Forall(typevars, typ) => { - assert_eq!(types.len() + implicit_generic_count, typevars.len(), "Turbofish operator used with incorrect generic count which was not caught by name resolution"); + assert_eq!( + types.len() + implicit_generic_count, + typevars.len(), + "Turbofish operator used with incorrect generic count which was not caught by name resolution" + ); let bindings = (0..implicit_generic_count).map(|_| interner.next_type_variable()).chain(types); @@ -2724,7 +2727,8 @@ impl Type { if sign == &Signedness::Signed { max_bit_size -= 1; } - Some(((1u128 << max_bit_size) - 1).into()) + let max = if max_bit_size == 128 { u128::MAX } else { (1u128 << max_bit_size) - 1 }; + Some(max.into()) } Type::Bool => Some(FieldElement::one()), Type::TypeVariable(var) => { @@ -2781,14 +2785,14 @@ fn convert_array_expression_to_slice( let argument = interner.expression(&expression); let argument = interner.push_expr(argument); interner.push_expr_type(argument, array_type.clone()); - interner.push_expr_location(argument, location.span, location.file); + interner.push_expr_location(argument, location); let arguments = vec![argument]; let is_macro_call = false; let call = HirExpression::Call(HirCallExpression { func, arguments, location, is_macro_call }); interner.replace_expr(&expression, call); - interner.push_expr_location(func, location.span, location.file); + interner.push_expr_location(func, location); interner.push_expr_type(expression, target_type.clone()); let func_type = @@ -2803,7 +2807,7 @@ impl BinaryTypeOperator { a: FieldElement, b: FieldElement, kind: &Kind, - span: Span, + location: Location, ) -> Result { match kind.follow_bindings().integral_maximum_size() { None => match self { @@ -2812,16 +2816,16 @@ impl BinaryTypeOperator { BinaryTypeOperator::Multiplication => Ok(a * b), BinaryTypeOperator::Division => (b != FieldElement::zero()) .then(|| a / b) - .ok_or(TypeCheckError::DivisionByZero { lhs: a, rhs: b, span }), + .ok_or(TypeCheckError::DivisionByZero { lhs: a, rhs: b, location }), BinaryTypeOperator::Modulo => { - Err(TypeCheckError::ModuloOnFields { lhs: a, rhs: b, span }) + Err(TypeCheckError::ModuloOnFields { lhs: a, rhs: b, location }) } }, Some(_maximum_size) => { let a = a.to_i128(); let b = b.to_i128(); - let err = TypeCheckError::FailingBinaryOp { op: self, lhs: a, rhs: b, span }; + let err = TypeCheckError::FailingBinaryOp { op: self, lhs: a, rhs: b, location }; let result = match self { BinaryTypeOperator::Addition => a.checked_add(b).ok_or(err)?, BinaryTypeOperator::Subtraction => a.checked_sub(b).ok_or(err)?, @@ -2875,9 +2879,10 @@ impl From<&Type> for PrintableType { match value { Type::FieldElement => PrintableType::Field, Type::Array(size, typ) => { - let dummy_span = Span::default(); - let length = - size.evaluate_to_u32(dummy_span).expect("Cannot print variable sized arrays"); + let dummy_location = Location::dummy(); + let length = size + .evaluate_to_u32(dummy_location) + .expect("Cannot print variable sized arrays"); let typ = typ.as_ref(); PrintableType::Array { length, typ: Box::new(typ.into()) } } @@ -2902,16 +2907,17 @@ impl From<&Type> for PrintableType { }, Type::Bool => PrintableType::Boolean, Type::String(size) => { - let dummy_span = Span::default(); - let size = - size.evaluate_to_u32(dummy_span).expect("Cannot print variable sized strings"); + let dummy_location = Location::dummy(); + let size = size + .evaluate_to_u32(dummy_location) + .expect("Cannot print variable sized strings"); PrintableType::String { length: size } } Type::FmtString(_, _) => unreachable!("format strings cannot be printed"), Type::Error => unreachable!(), Type::Unit => PrintableType::Unit, Type::Constant(_, _) => unreachable!(), - Type::DataType(def, ref args) => { + Type::DataType(def, args) => { let data_type = def.borrow(); let name = data_type.name.to_string(); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index ce9125cd5f01..83618bf37077 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use acvm::{AcirField, FieldElement}; -use noirc_errors::Span; +use noirc_errors::Location; use crate::{BinaryTypeOperator, Type, TypeBindings, UnificationError}; @@ -60,16 +60,19 @@ impl Type { match self.follow_bindings() { Type::InfixExpr(lhs, op, rhs, inversion) => { let kind = lhs.infix_kind(&rhs); - let dummy_span = Span::default(); + let dummy_location = Location::dummy(); // evaluate_to_field_element also calls canonicalize so if we just called // `self.evaluate_to_field_element(..)` we'd get infinite recursion. if let Ok(lhs_value) = - lhs.evaluate_to_field_element_helper(&kind, dummy_span, run_simplifications) + lhs.evaluate_to_field_element_helper(&kind, dummy_location, run_simplifications) { - if let Ok(rhs_value) = - rhs.evaluate_to_field_element_helper(&kind, dummy_span, run_simplifications) - { - if let Ok(result) = op.function(lhs_value, rhs_value, &kind, dummy_span) { + if let Ok(rhs_value) = rhs.evaluate_to_field_element_helper( + &kind, + dummy_location, + run_simplifications, + ) { + if let Ok(result) = op.function(lhs_value, rhs_value, &kind, dummy_location) + { return Type::Constant(result, kind); } } @@ -139,9 +142,9 @@ impl Type { queue.push(*rhs_inner); } Type::Constant(new_constant, new_constant_kind) => { - let dummy_span = Span::default(); + let dummy_location = Location::dummy(); if let Ok(result) = - op.function(constant, new_constant, &new_constant_kind, dummy_span) + op.function(constant, new_constant, &new_constant_kind, dummy_location) { constant = result; } else { @@ -268,15 +271,14 @@ impl Type { rhs: &Type, ) -> Option<(Box, BinaryTypeOperator, FieldElement, FieldElement)> { let kind = lhs.infix_kind(rhs); - let dummy_span = Span::default(); - let rhs = rhs.evaluate_to_field_element(&kind, dummy_span).ok()?; + let dummy_location = Location::dummy(); + let rhs = rhs.evaluate_to_field_element(&kind, dummy_location).ok()?; let Type::InfixExpr(l_type, l_op, l_rhs, _) = lhs.follow_bindings() else { return None; }; - let dummy_span = Span::default(); - let l_rhs = l_rhs.evaluate_to_field_element(&kind, dummy_span).ok()?; + let l_rhs = l_rhs.evaluate_to_field_element(&kind, dummy_location).ok()?; Some((l_type, l_op, l_rhs, rhs)) } @@ -301,9 +303,9 @@ impl Type { if l_op == Subtraction { op = op.inverse()?; } - let dummy_span = Span::default(); + let dummy_location = Location::dummy(); let result = - op.function(l_const, r_const, &lhs.infix_kind(rhs), dummy_span).ok()?; + op.function(l_const, r_const, &lhs.infix_kind(rhs), dummy_location).ok()?; let constant = Type::Constant(result, lhs.infix_kind(rhs)); Some(Type::infix_expr(l_type, l_op, Box::new(constant))) } @@ -316,9 +318,9 @@ impl Type { if op == Division && (r_const == FieldElement::zero() || !divides_evenly) { None } else { - let dummy_span = Span::default(); + let dummy_location = Location::dummy(); let result = - op.function(l_const, r_const, &lhs.infix_kind(rhs), dummy_span).ok()?; + op.function(l_const, r_const, &lhs.infix_kind(rhs), dummy_location).ok()?; let constant = Box::new(Type::Constant(result, lhs.infix_kind(rhs))); Some(Type::infix_expr(l_type, l_op, constant)) } @@ -337,8 +339,8 @@ impl Type { if let Type::InfixExpr(lhs_a, op_a, rhs_a, _) = self { if let Some(inverse) = op_a.approx_inverse() { let kind = lhs_a.infix_kind(rhs_a); - let dummy_span = Span::default(); - if let Ok(rhs_a_value) = rhs_a.evaluate_to_field_element(&kind, dummy_span) { + let dummy_location = Location::dummy(); + if let Ok(rhs_a_value) = rhs_a.evaluate_to_field_element(&kind, dummy_location) { let rhs_a = Box::new(Type::Constant(rhs_a_value, kind)); let new_other = Type::inverted_infix_expr(Box::new(other.clone()), inverse, rhs_a); @@ -355,8 +357,8 @@ impl Type { if let Type::InfixExpr(lhs_b, op_b, rhs_b, inversion) = other { if let Some(inverse) = op_b.approx_inverse() { let kind = lhs_b.infix_kind(rhs_b); - let dummy_span = Span::default(); - if let Ok(rhs_b_value) = rhs_b.evaluate_to_field_element(&kind, dummy_span) { + let dummy_location = Location::dummy(); + if let Ok(rhs_b_value) = rhs_b.evaluate_to_field_element(&kind, dummy_location) { let rhs_b = Box::new(Type::Constant(rhs_b_value, kind)); let new_self = Type::InfixExpr(Box::new(self.clone()), inverse, rhs_b, !inversion); @@ -598,7 +600,7 @@ mod proptests { #[test] // Expect cases that don't resolve to constants, e.g. see // `arithmetic_generics_checked_cast_indirect_zeros` - #[should_panic(expected = "matches!(infix, Type :: Constant(..))")] + #[should_panic(expected = "matches!(infix, Type::Constant(..))")] fn instantiate_before_or_after_canonicalize(infix_type_bindings in arbitrary_infix_expr_with_bindings(10)) { let (infix, typ, bindings) = infix_type_bindings; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/errors.rs index f95ccba061a6..9adae78febde 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/errors.rs @@ -1,55 +1,55 @@ use crate::hir::def_collector::dc_crate::CompilationError; use crate::parser::ParserError; use crate::parser::ParserErrorReason; -use crate::token::SpannedToken; +use super::token::LocatedToken; use super::token::Token; use noirc_errors::CustomDiagnostic as Diagnostic; -use noirc_errors::Span; +use noirc_errors::Location; use thiserror::Error; #[derive(Error, Clone, Debug, PartialEq, Eq)] pub enum LexerErrorKind { #[error("An unexpected character {:?} was found.", found)] - UnexpectedCharacter { span: Span, expected: String, found: Option }, + UnexpectedCharacter { location: Location, expected: String, found: Option }, #[error("Internal error: Tried to lex {:?} as a double char token", found)] - NotADoubleChar { span: Span, found: Token }, + NotADoubleChar { location: Location, found: Token }, #[error("Invalid integer literal, {:?} is not a integer", found)] - InvalidIntegerLiteral { span: Span, found: String }, + InvalidIntegerLiteral { location: Location, found: String }, #[error("Integer literal is too large")] - IntegerLiteralTooLarge { span: Span, limit: String }, + IntegerLiteralTooLarge { location: Location, limit: String }, #[error("{:?} is not a valid attribute", found)] - MalformedFuncAttribute { span: Span, found: String }, + MalformedFuncAttribute { location: Location, found: String }, #[error("Malformed test attribute")] - MalformedTestAttribute { span: Span }, + MalformedTestAttribute { location: Location }, #[error("{:?} is not a valid inner attribute", found)] - InvalidInnerAttribute { span: Span, found: String }, + InvalidInnerAttribute { location: Location, found: String }, #[error("Logical and used instead of bitwise and")] - LogicalAnd { span: Span }, + LogicalAnd { location: Location }, #[error("Unterminated block comment")] - UnterminatedBlockComment { span: Span }, + UnterminatedBlockComment { location: Location }, #[error("Unterminated string literal")] - UnterminatedStringLiteral { span: Span }, + UnterminatedStringLiteral { location: Location }, #[error("Invalid format string: expected '}}', found {found:?}")] - InvalidFormatString { found: char, span: Span }, + InvalidFormatString { found: char, location: Location }, #[error("Invalid format string: expected letter or underscore, found '}}'")] - EmptyFormatStringInterpolation { span: Span }, + EmptyFormatStringInterpolation { location: Location }, #[error( "'\\{escaped}' is not a valid escape sequence. Use '\\' for a literal backslash character." )] - InvalidEscape { escaped: char, span: Span }, + InvalidEscape { escaped: char, location: Location }, #[error("Invalid quote delimiter `{delimiter}`, valid delimiters are `{{`, `[`, and `(`")] - InvalidQuoteDelimiter { delimiter: SpannedToken }, + InvalidQuoteDelimiter { delimiter: LocatedToken }, #[error("Non-ASCII characters are invalid in comments")] - NonAsciiComment { span: Span }, + NonAsciiComment { location: Location }, #[error("Expected `{end_delim}` to close this {start_delim}")] - UnclosedQuote { start_delim: SpannedToken, end_delim: Token }, + UnclosedQuote { start_delim: LocatedToken, end_delim: Token }, } impl From for ParserError { fn from(value: LexerErrorKind) -> Self { - let span = value.span(); - ParserError::with_reason(ParserErrorReason::Lexer(value), span) + let location = value.location(); + ParserError::with_reason(ParserErrorReason::Lexer(value), location) } } @@ -60,31 +60,31 @@ impl From for CompilationError { } impl LexerErrorKind { - pub fn span(&self) -> Span { + pub fn location(&self) -> Location { match self { - LexerErrorKind::UnexpectedCharacter { span, .. } => *span, - LexerErrorKind::NotADoubleChar { span, .. } => *span, - LexerErrorKind::InvalidIntegerLiteral { span, .. } => *span, - LexerErrorKind::IntegerLiteralTooLarge { span, .. } => *span, - LexerErrorKind::MalformedFuncAttribute { span, .. } => *span, - LexerErrorKind::MalformedTestAttribute { span, .. } => *span, - LexerErrorKind::InvalidInnerAttribute { span, .. } => *span, - LexerErrorKind::LogicalAnd { span } => *span, - LexerErrorKind::UnterminatedBlockComment { span } => *span, - LexerErrorKind::UnterminatedStringLiteral { span } => *span, - LexerErrorKind::InvalidFormatString { span, .. } => *span, - LexerErrorKind::EmptyFormatStringInterpolation { span, .. } => *span, - LexerErrorKind::InvalidEscape { span, .. } => *span, - LexerErrorKind::InvalidQuoteDelimiter { delimiter } => delimiter.to_span(), - LexerErrorKind::NonAsciiComment { span, .. } => *span, - LexerErrorKind::UnclosedQuote { start_delim, .. } => start_delim.to_span(), + LexerErrorKind::UnexpectedCharacter { location, .. } => *location, + LexerErrorKind::NotADoubleChar { location, .. } => *location, + LexerErrorKind::InvalidIntegerLiteral { location, .. } => *location, + LexerErrorKind::IntegerLiteralTooLarge { location, .. } => *location, + LexerErrorKind::MalformedFuncAttribute { location, .. } => *location, + LexerErrorKind::MalformedTestAttribute { location, .. } => *location, + LexerErrorKind::InvalidInnerAttribute { location, .. } => *location, + LexerErrorKind::LogicalAnd { location } => *location, + LexerErrorKind::UnterminatedBlockComment { location } => *location, + LexerErrorKind::UnterminatedStringLiteral { location } => *location, + LexerErrorKind::InvalidFormatString { location, .. } => *location, + LexerErrorKind::EmptyFormatStringInterpolation { location, .. } => *location, + LexerErrorKind::InvalidEscape { location, .. } => *location, + LexerErrorKind::InvalidQuoteDelimiter { delimiter } => delimiter.location(), + LexerErrorKind::NonAsciiComment { location, .. } => *location, + LexerErrorKind::UnclosedQuote { start_delim, .. } => start_delim.location(), } } - fn parts(&self) -> (String, String, Span) { + fn parts(&self) -> (String, String, Location) { match self { LexerErrorKind::UnexpectedCharacter { - span, + location, expected, found, } => { @@ -93,55 +93,55 @@ impl LexerErrorKind { ( "An unexpected character was found".to_string(), format!("Expected {expected}, but found {found}"), - *span, + *location, ) }, - LexerErrorKind::NotADoubleChar { span, found } => ( + LexerErrorKind::NotADoubleChar { location, found } => ( format!("Tried to parse {found} as double char"), format!( " {found:?} is not a double char, this is an internal error" ), - *span, + *location, ), - LexerErrorKind::InvalidIntegerLiteral { span, found } => ( + LexerErrorKind::InvalidIntegerLiteral { location, found } => ( "Invalid integer literal".to_string(), format!(" {found} is not an integer"), - *span, + *location, ), - LexerErrorKind::IntegerLiteralTooLarge { span, limit } => ( + LexerErrorKind::IntegerLiteralTooLarge { location, limit } => ( "Integer literal is too large".to_string(), format!("value exceeds limit of {limit}"), - *span, + *location, ), - LexerErrorKind::MalformedFuncAttribute { span, found } => ( + LexerErrorKind::MalformedFuncAttribute { location, found } => ( "Malformed function attribute".to_string(), format!(" {found} is not a valid attribute"), - *span, + *location, ), - LexerErrorKind::MalformedTestAttribute { span } => ( + LexerErrorKind::MalformedTestAttribute { location } => ( "Malformed test attribute".to_string(), "The test attribute can be written in one of these forms: `#[test]`, `#[test(should_fail)]` or `#[test(should_fail_with = \"message\")]`".to_string(), - *span, + *location, ), - LexerErrorKind::InvalidInnerAttribute { span, found } => ( + LexerErrorKind::InvalidInnerAttribute { location, found } => ( "Invalid inner attribute".to_string(), format!(" {found} is not a valid inner attribute"), - *span, + *location, ), - LexerErrorKind::LogicalAnd { span } => ( + LexerErrorKind::LogicalAnd { location } => ( "Noir has no logical-and (&&) operator since short-circuiting is much less efficient when compiling to circuits".to_string(), "Try `&` instead, or use `if` only if you require short-circuiting".to_string(), - *span, + *location, ), - LexerErrorKind::UnterminatedBlockComment { span } => ("Unterminated block comment".to_string(), "Unterminated block comment".to_string(), *span), - LexerErrorKind::UnterminatedStringLiteral { span } => - ("Unterminated string literal".to_string(), "Unterminated string literal".to_string(), *span), - LexerErrorKind::InvalidFormatString { found, span } => { + LexerErrorKind::UnterminatedBlockComment { location } => ("Unterminated block comment".to_string(), "Unterminated block comment".to_string(), *location), + LexerErrorKind::UnterminatedStringLiteral { location } => + ("Unterminated string literal".to_string(), "Unterminated string literal".to_string(), *location), + LexerErrorKind::InvalidFormatString { found, location } => { if found == &'}' { ( "Invalid format string: unmatched '}}' found".to_string(), "If you intended to print '}', you can escape it using '}}'".to_string(), - *span, + *location, ) } else { ( @@ -151,27 +151,27 @@ impl LexerErrorKind { } else { "If you intended to print '{', you can escape it using '{{'".to_string() }, - *span, + *location, ) } } - LexerErrorKind::EmptyFormatStringInterpolation { span } => { + LexerErrorKind::EmptyFormatStringInterpolation { location } => { ( "Invalid format string: expected letter or underscore, found '}}'".to_string(), "If you intended to print '{' or '}', you can escape them using '{{' and '}}' respectively".to_string(), - *span, + *location, ) } - LexerErrorKind::InvalidEscape { escaped, span } => - (format!("'\\{escaped}' is not a valid escape sequence. Use '\\' for a literal backslash character."), "Invalid escape sequence".to_string(), *span), + LexerErrorKind::InvalidEscape { escaped, location } => + (format!("'\\{escaped}' is not a valid escape sequence. Use '\\' for a literal backslash character."), "Invalid escape sequence".to_string(), *location), LexerErrorKind::InvalidQuoteDelimiter { delimiter } => { - (format!("Invalid quote delimiter `{delimiter}`"), "Valid delimiters are `{`, `[`, and `(`".to_string(), delimiter.to_span()) + (format!("Invalid quote delimiter `{delimiter}`"), "Valid delimiters are `{`, `[`, and `(`".to_string(), delimiter.location()) }, - LexerErrorKind::NonAsciiComment { span } => { - ("Non-ASCII character in comment".to_string(), "Invalid comment character: only ASCII is currently supported.".to_string(), *span) + LexerErrorKind::NonAsciiComment { location } => { + ("Non-ASCII character in comment".to_string(), "Invalid comment character: only ASCII is currently supported.".to_string(), *location) } LexerErrorKind::UnclosedQuote { start_delim, end_delim } => { - ("Unclosed `quote` expression".to_string(), format!("Expected a `{end_delim}` to close this `{start_delim}`"), start_delim.to_span()) + ("Unclosed `quote` expression".to_string(), format!("Expected a `{end_delim}` to close this `{start_delim}`"), start_delim.location()) } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs index ef5706b4d49b..630f192c1095 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs @@ -2,10 +2,11 @@ use crate::token::DocStyle; use super::{ errors::LexerErrorKind, - token::{FmtStrFragment, IntType, Keyword, SpannedToken, Token, Tokens}, + token::{FmtStrFragment, IntType, Keyword, LocatedToken, SpannedToken, Token, Tokens}, }; use acvm::{AcirField, FieldElement}; -use noirc_errors::{Position, Span}; +use fm::FileId; +use noirc_errors::{Location, Position, Span}; use num_bigint::BigInt; use num_traits::{Num, One}; use std::str::{CharIndices, FromStr}; @@ -14,6 +15,7 @@ use std::str::{CharIndices, FromStr}; /// into an iterator of `SpannedToken`. Each `Token` corresponds roughly to 1 word or operator. /// Tokens are tagged with their location in the source file (a `Span`) for use in error reporting. pub struct Lexer<'a> { + file_id: FileId, chars: CharIndices<'a>, position: Position, done: bool, @@ -24,11 +26,13 @@ pub struct Lexer<'a> { pub type SpannedTokenResult = Result; +pub type LocatedTokenResult = Result; + impl<'a> Lexer<'a> { /// Given a source file of noir code, return all the tokens in the file /// in order, along with any lexing errors that occurred. - pub fn lex(source: &'a str) -> (Tokens, Vec) { - let lexer = Lexer::new(source); + pub fn lex(source: &'a str, file_id: FileId) -> (Tokens, Vec) { + let lexer = Lexer::new(source, file_id); let mut tokens = vec![]; let mut errors = vec![]; for result in lexer { @@ -40,8 +44,9 @@ impl<'a> Lexer<'a> { (Tokens(tokens), errors) } - pub fn new(source: &'a str) -> Self { + pub fn new(source: &'a str, file_id: FileId) -> Self { Lexer { + file_id, chars: source.char_indices(), position: 0, done: false, @@ -52,6 +57,10 @@ impl<'a> Lexer<'a> { } } + pub fn new_with_dummy_file(source: &'a str) -> Self { + Self::new(source, FileId::dummy()) + } + pub fn skip_comments(mut self, flag: bool) -> Self { self.skip_comments = flag; self @@ -96,21 +105,28 @@ impl<'a> Lexer<'a> { // When we issue this error the first '&' will already be consumed // and the next token issued will be the next '&'. let span = Span::inclusive(self.position, self.position + 1); - Err(LexerErrorKind::LogicalAnd { span }) + Err(LexerErrorKind::LogicalAnd { location: self.location(span) }) } else { self.single_char_token(Token::Ampersand) } } - fn next_token(&mut self) -> SpannedTokenResult { + fn next_token(&mut self) -> LocatedTokenResult { + self.next_spanned_token().map(|token| { + let span = token.span(); + LocatedToken::new(token.into_token(), Location::new(span, self.file_id)) + }) + } + + fn next_spanned_token(&mut self) -> SpannedTokenResult { if !self.skip_comments { - return self.next_token_without_checking_comments(); + return self.next_spanned_token_without_checking_comments(); } // Read tokens and skip comments. This is done like this to avoid recursion // and hitting stack overflow when there are many comments in a row. loop { - let token = self.next_token_without_checking_comments()?; + let token = self.next_spanned_token_without_checking_comments()?; if matches!(token.token(), Token::LineComment(_, None) | Token::BlockComment(_, None)) { continue; } @@ -119,12 +135,12 @@ impl<'a> Lexer<'a> { } /// Reads the next token, which might be a comment token (these aren't skipped in this method) - fn next_token_without_checking_comments(&mut self) -> SpannedTokenResult { + fn next_spanned_token_without_checking_comments(&mut self) -> SpannedTokenResult { match self.next_char() { Some(x) if Self::is_code_whitespace(x) => { let spanned = self.eat_whitespace(x); if self.skip_whitespaces { - self.next_token_without_checking_comments() + self.next_spanned_token_without_checking_comments() } else { Ok(spanned) } @@ -261,7 +277,7 @@ impl<'a> Lexer<'a> { Ok(prev_token.into_single_span(start)) } _ => Err(LexerErrorKind::NotADoubleChar { - span: Span::single_char(self.position), + location: self.location(Span::single_char(self.position)), found: prev_token, }), } @@ -303,7 +319,7 @@ impl<'a> Lexer<'a> { 'A'..='Z' | 'a'..='z' | '_' => Ok(self.eat_word(initial_char)?), '0'..='9' => self.eat_digit(initial_char), _ => Err(LexerErrorKind::UnexpectedCharacter { - span: Span::single_char(self.position), + location: self.location(Span::single_char(self.position)), found: initial_char.into(), expected: "an alpha numeric character".to_owned(), }), @@ -322,7 +338,7 @@ impl<'a> Lexer<'a> { if !self.peek_char_is('[') { return Err(LexerErrorKind::UnexpectedCharacter { - span: Span::single_char(self.position), + location: self.location(Span::single_char(self.position)), found: self.next_char(), expected: "[".to_owned(), }); @@ -399,7 +415,7 @@ impl<'a> Lexer<'a> { let consecutive_underscores = integer_str.contains("__"); if invalid_underscore_location || consecutive_underscores { return Err(LexerErrorKind::InvalidIntegerLiteral { - span: Span::inclusive(start, end), + location: self.location(Span::inclusive(start, end)), found: integer_str, }); } @@ -416,7 +432,7 @@ impl<'a> Lexer<'a> { Ok(bigint) => { if bigint > self.max_integer { return Err(LexerErrorKind::IntegerLiteralTooLarge { - span: Span::inclusive(start, end), + location: self.location(Span::inclusive(start, end)), limit: self.max_integer.to_string(), }); } @@ -425,9 +441,9 @@ impl<'a> Lexer<'a> { } Err(_) => { return Err(LexerErrorKind::InvalidIntegerLiteral { - span: Span::inclusive(start, end), + location: self.location(Span::inclusive(start, end)), found: integer_str, - }) + }); } }; @@ -452,11 +468,16 @@ impl<'a> Lexer<'a> { Some('\\') => '\\', Some(escaped) => { let span = Span::inclusive(start, self.position); - return Err(LexerErrorKind::InvalidEscape { escaped, span }); + return Err(LexerErrorKind::InvalidEscape { + escaped, + location: self.location(span), + }); } None => { let span = Span::inclusive(start, self.position); - return Err(LexerErrorKind::UnterminatedStringLiteral { span }); + return Err(LexerErrorKind::UnterminatedStringLiteral { + location: self.location(span), + }); } }, other => other, @@ -465,7 +486,9 @@ impl<'a> Lexer<'a> { string.push(char); } else { let span = Span::inclusive(start, self.position); - return Err(LexerErrorKind::UnterminatedStringLiteral { span }); + return Err(LexerErrorKind::UnterminatedStringLiteral { + location: self.location(span), + }); } } @@ -499,11 +522,16 @@ impl<'a> Lexer<'a> { Some('\\') => '\\', Some(escaped) => { let span = Span::inclusive(start, self.position); - return Err(LexerErrorKind::InvalidEscape { escaped, span }); + return Err(LexerErrorKind::InvalidEscape { + escaped, + location: self.location(span), + }); } None => { let span = Span::inclusive(start, self.position); - return Err(LexerErrorKind::UnterminatedStringLiteral { span }); + return Err(LexerErrorKind::UnterminatedStringLiteral { + location: self.location(span), + }); } }, '{' if self.peek_char_is('{') => { @@ -521,7 +549,10 @@ impl<'a> Lexer<'a> { self.skip_until_string_end(); let span = Span::inclusive(error_position, error_position); - return Err(LexerErrorKind::InvalidFormatString { found: '}', span }); + return Err(LexerErrorKind::InvalidFormatString { + found: '}', + location: self.location(span), + }); } '{' => { found_curly = true; @@ -545,7 +576,9 @@ impl<'a> Lexer<'a> { } } else { let span = Span::inclusive(start, self.position); - return Err(LexerErrorKind::UnterminatedStringLiteral { span }); + return Err(LexerErrorKind::UnterminatedStringLiteral { + location: self.location(span), + }); } } @@ -573,7 +606,9 @@ impl<'a> Lexer<'a> { self.skip_until_string_end(); let span = Span::inclusive(error_position, error_position); - return Err(LexerErrorKind::EmptyFormatStringInterpolation { span }); + return Err(LexerErrorKind::EmptyFormatStringInterpolation { + location: self.location(span), + }); } break; @@ -594,7 +629,10 @@ impl<'a> Lexer<'a> { } let span = Span::inclusive(error_position, error_position); - return Err(LexerErrorKind::InvalidFormatString { found: other, span }); + return Err(LexerErrorKind::InvalidFormatString { + found: other, + location: self.location(span), + }); } first_char = false; other @@ -606,8 +644,9 @@ impl<'a> Lexer<'a> { length += 1; // for the closing curly brace - let interpolation_span = Span::from(interpolation_start..self.position); - fragments.push(FmtStrFragment::Interpolation(string, interpolation_span)); + let span = Span::from(interpolation_start..self.position); + let location = Location::new(span, self.file_id); + fragments.push(FmtStrFragment::Interpolation(string, location)); } let token = Token::FmtStr(fragments, length); @@ -626,11 +665,7 @@ impl<'a> Lexer<'a> { } fn eat_format_string_or_alpha_numeric(&mut self) -> SpannedTokenResult { - if self.peek_char_is('"') { - self.eat_fmt_string() - } else { - self.eat_alpha_numeric('f') - } + if self.peek_char_is('"') { self.eat_fmt_string() } else { self.eat_alpha_numeric('f') } } fn eat_raw_string(&mut self) -> SpannedTokenResult { @@ -642,7 +677,7 @@ impl<'a> Lexer<'a> { // too many hashes (unlikely in practice) // also, Rust disallows 256+ hashes as well return Err(LexerErrorKind::UnexpectedCharacter { - span: Span::single_char(start + 255), + location: self.location(Span::single_char(start + 255)), found: Some('#'), expected: "\"".to_owned(), }); @@ -650,7 +685,7 @@ impl<'a> Lexer<'a> { if !self.peek_char_is('"') { return Err(LexerErrorKind::UnexpectedCharacter { - span: Span::single_char(self.position), + location: self.location(Span::single_char(self.position)), found: self.next_char(), expected: "\"".to_owned(), }); @@ -663,7 +698,7 @@ impl<'a> Lexer<'a> { str_literal.push_str(&chars[..]); if !self.peek_char_is('"') { return Err(LexerErrorKind::UnexpectedCharacter { - span: Span::single_char(self.position), + location: self.location(Span::single_char(self.position)), found: self.next_char(), expected: "\"".to_owned(), }); @@ -769,7 +804,7 @@ impl<'a> Lexer<'a> { if !comment.is_ascii() { let span = Span::from(start..self.position); - return Err(LexerErrorKind::NonAsciiComment { span }); + return Err(LexerErrorKind::NonAsciiComment { location: self.location(span) }); } Ok(Token::LineComment(comment, doc_style).into_span(start, self.position)) @@ -814,13 +849,13 @@ impl<'a> Lexer<'a> { if depth == 0 { if !content.is_ascii() { let span = Span::from(start..self.position); - return Err(LexerErrorKind::NonAsciiComment { span }); + return Err(LexerErrorKind::NonAsciiComment { location: self.location(span) }); } Ok(Token::BlockComment(content, doc_style).into_span(start, self.position)) } else { let span = Span::inclusive(start, self.position); - Err(LexerErrorKind::UnterminatedBlockComment { span }) + Err(LexerErrorKind::UnterminatedBlockComment { location: self.location(span) }) } } @@ -834,17 +869,17 @@ impl<'a> Lexer<'a> { let whitespace = self.eat_while(initial_char.into(), Self::is_code_whitespace); SpannedToken::new(Token::Whitespace(whitespace), Span::inclusive(start, self.position)) } + + fn location(&self, span: Span) -> Location { + Location::new(span, self.file_id) + } } -impl<'a> Iterator for Lexer<'a> { - type Item = SpannedTokenResult; +impl Iterator for Lexer<'_> { + type Item = LocatedTokenResult; fn next(&mut self) -> Option { - if self.done { - None - } else { - Some(self.next_token()) - } + if self.done { None } else { Some(self.next_token()) } } } @@ -894,7 +929,7 @@ mod tests { Token::EOF, ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); @@ -905,7 +940,7 @@ mod tests { #[test] fn invalid_attribute() { let input = "#"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next().unwrap(); assert!(token.is_err()); @@ -914,7 +949,7 @@ mod tests { #[test] fn test_attribute_start() { let input = r#"#[something]"#; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next_token().unwrap(); assert_eq!(token.token(), &Token::AttributeStart { is_inner: false, is_tag: false }); @@ -923,7 +958,7 @@ mod tests { #[test] fn test_attribute_start_with_tag() { let input = r#"#['something]"#; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next_token().unwrap(); assert_eq!(token.token(), &Token::AttributeStart { is_inner: false, is_tag: true }); @@ -932,7 +967,7 @@ mod tests { #[test] fn test_inner_attribute_start() { let input = r#"#![something]"#; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next_token().unwrap(); assert_eq!(token.token(), &Token::AttributeStart { is_inner: true, is_tag: false }); @@ -941,7 +976,7 @@ mod tests { #[test] fn test_inner_attribute_start_with_tag() { let input = r#"#!['something]"#; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next_token().unwrap(); assert_eq!(token.token(), &Token::AttributeStart { is_inner: true, is_tag: true }); @@ -960,7 +995,7 @@ mod tests { Token::Int(5_i128.into()), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); assert_eq!(got, token); @@ -972,7 +1007,7 @@ mod tests { let modulus = FieldElement::modulus(); let input = modulus.to_string(); - let mut lexer = Lexer::new(&input); + let mut lexer = Lexer::new_with_dummy_file(&input); let token = lexer.next_token(); assert!( matches!(token, Err(LexerErrorKind::IntegerLiteralTooLarge { .. })), @@ -997,7 +1032,7 @@ mod tests { Token::Assign, ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); assert_eq!(got, token); @@ -1008,7 +1043,7 @@ mod tests { fn unterminated_block_comment() { let input = "/*/"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next().unwrap(); assert!(token.is_err()); @@ -1027,7 +1062,7 @@ mod tests { Token::Int(FieldElement::from(5_i128)), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let first_lexer_output = lexer.next_token().unwrap(); assert_eq!(first_lexer_output, token); @@ -1049,7 +1084,7 @@ mod tests { Token::Int(FieldElement::from(5_i128)), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let first_lexer_output = lexer.next_token().unwrap(); assert_eq!(first_lexer_output, token); @@ -1075,7 +1110,7 @@ mod tests { Token::BlockComment(" inner doc block ".into(), DocStyle::Inner.into()), ]; - let mut lexer = Lexer::new(input).skip_comments(false); + let mut lexer = Lexer::new_with_dummy_file(input).skip_comments(false); for token in expected { let first_lexer_output = lexer.next_token().unwrap(); assert_eq!(token, first_lexer_output); @@ -1097,7 +1132,7 @@ mod tests { Token::Int(FieldElement::from(5_i128)), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let first_lexer_output = lexer.next_token().unwrap(); assert_eq!(first_lexer_output, token); @@ -1113,7 +1148,7 @@ mod tests { Token::Assign, Token::Str("hello".to_string()), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); @@ -1131,7 +1166,7 @@ mod tests { Token::Assign, Token::Str("hello\n\t".to_string()), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); @@ -1142,7 +1177,7 @@ mod tests { #[test] fn test_eat_string_literal_missing_double_quote() { let input = "\"hello"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); assert!(matches!( lexer.next_token(), Err(LexerErrorKind::UnterminatedStringLiteral { .. }) @@ -1159,7 +1194,7 @@ mod tests { Token::Assign, Token::FmtStr(vec![FmtStrFragment::String("hello".to_string())], 5), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); @@ -1177,7 +1212,7 @@ mod tests { Token::Assign, Token::FmtStr(vec![FmtStrFragment::String("hello\n\t{x}".to_string())], 12), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); @@ -1188,6 +1223,7 @@ mod tests { #[test] fn test_eat_fmt_string_literal_with_interpolations() { let input = "let _word = f\"hello {world} and {_another} {vAr_123}\""; + let file = FileId::dummy(); let expected = vec![ Token::Keyword(Keyword::Let), @@ -1196,16 +1232,25 @@ mod tests { Token::FmtStr( vec![ FmtStrFragment::String("hello ".to_string()), - FmtStrFragment::Interpolation("world".to_string(), Span::from(21..26)), + FmtStrFragment::Interpolation( + "world".to_string(), + Location::new(Span::from(21..26), file), + ), FmtStrFragment::String(" and ".to_string()), - FmtStrFragment::Interpolation("_another".to_string(), Span::from(33..41)), + FmtStrFragment::Interpolation( + "_another".to_string(), + Location::new(Span::from(33..41), file), + ), FmtStrFragment::String(" ".to_string()), - FmtStrFragment::Interpolation("vAr_123".to_string(), Span::from(44..51)), + FmtStrFragment::Interpolation( + "vAr_123".to_string(), + Location::new(Span::from(44..51), file), + ), ], 38, ), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap().into_token(); @@ -1216,7 +1261,7 @@ mod tests { #[test] fn test_eat_fmt_string_literal_missing_double_quote() { let input = "f\"hello"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); assert!(matches!( lexer.next_token(), Err(LexerErrorKind::UnterminatedStringLiteral { .. }) @@ -1226,7 +1271,7 @@ mod tests { #[test] fn test_eat_fmt_string_literal_invalid_char_in_interpolation() { let input = "f\"hello {foo.bar}\" true"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); assert!(matches!(lexer.next_token(), Err(LexerErrorKind::InvalidFormatString { .. }))); // Make sure the lexer went past the ending double quote for better recovery @@ -1237,7 +1282,7 @@ mod tests { #[test] fn test_eat_fmt_string_literal_double_quote_inside_interpolation() { let input = "f\"hello {world\" true"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); assert!(matches!(lexer.next_token(), Err(LexerErrorKind::InvalidFormatString { .. }))); // Make sure the lexer stopped parsing the string literal when it found \" inside the interpolation @@ -1248,7 +1293,7 @@ mod tests { #[test] fn test_eat_fmt_string_literal_unmatched_closing_curly() { let input = "f\"hello }\" true"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); assert!(matches!(lexer.next_token(), Err(LexerErrorKind::InvalidFormatString { .. }))); // Make sure the lexer went past the ending double quote for better recovery @@ -1259,7 +1304,7 @@ mod tests { #[test] fn test_eat_fmt_string_literal_empty_interpolation() { let input = "f\"{}\" true"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); assert!(matches!( lexer.next_token(), Err(LexerErrorKind::EmptyFormatStringInterpolation { .. }) @@ -1281,7 +1326,7 @@ mod tests { ]; for (input, expected_token) in test_cases { - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let got = lexer.next_token().unwrap(); assert_eq!(got.token(), &expected_token); } @@ -1292,7 +1337,7 @@ mod tests { let test_cases: Vec<&str> = vec!["0x05_", "5_", "5__5", "0x5__5"]; for input in test_cases { - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next_token(); assert!( matches!(token, Err(LexerErrorKind::InvalidIntegerLiteral { .. })), @@ -1332,12 +1377,12 @@ mod tests { let int_token = Token::Int(5_i128.into()).into_single_span(int_position); let expected = vec![let_token, ident_token, assign_token, int_token]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for spanned_token in expected.into_iter() { let got = lexer.next_token().unwrap(); - assert_eq!(got.to_span(), spanned_token.to_span()); - assert_eq!(got, spanned_token); + assert_eq!(got.span(), spanned_token.span()); + assert_eq!(got.into_spanned_token(), spanned_token); } } @@ -1403,7 +1448,7 @@ mod tests { Token::Semicolon, Token::EOF, ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); @@ -1481,7 +1526,7 @@ mod tests { for (token_discriminator_opt, blns_program_strs) in statements { for blns_program_str in blns_program_strs { let mut expected_token_found = false; - let mut lexer = Lexer::new(&blns_program_str); + let mut lexer = Lexer::new_with_dummy_file(&blns_program_str); let mut result_tokens = Vec::new(); loop { match lexer.next_token() { @@ -1542,7 +1587,8 @@ mod tests { ]; for (source, expected_stream_length) in cases { - let mut tokens = vecmap(Lexer::new(source), |result| result.unwrap().into_token()); + let mut tokens = + vecmap(Lexer::new_with_dummy_file(source), |result| result.unwrap().into_token()); // All examples should be a single TokenStream token followed by an EOF token. assert_eq!(tokens.len(), 2, "Unexpected token count: {tokens:?}"); @@ -1550,7 +1596,9 @@ mod tests { tokens.pop(); match tokens.pop().unwrap() { Token::Quote(stream) => assert_eq!(stream.0.len(), expected_stream_length), - other => panic!("test_quote test failure! Expected a single TokenStream token, got {other} for input `{source}`") + other => panic!( + "test_quote test failure! Expected a single TokenStream token, got {other} for input `{source}`" + ), } } } @@ -1562,7 +1610,7 @@ mod tests { for source in cases { // `quote` is not itself a keyword so if the token stream fails to // parse we don't expect any valid tokens from the quote construct - for token in Lexer::new(source) { + for token in Lexer::new_with_dummy_file(source) { assert!(token.is_err(), "Expected Err, found {token:?}"); } } @@ -1573,7 +1621,7 @@ mod tests { let cases = vec!["// 🙂", "// schön", "/* in the middle 🙂 of a comment */"]; for source in cases { - let mut lexer = Lexer::new(source); + let mut lexer = Lexer::new_with_dummy_file(source); assert!( lexer.any(|token| matches!(token, Err(LexerErrorKind::NonAsciiComment { .. }))), "Expected NonAsciiComment error" diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs index ef90ff4e5818..7367489f6251 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs @@ -1,5 +1,5 @@ use acvm::FieldElement; -use noirc_errors::{Position, Span, Spanned}; +use noirc_errors::{Located, Location, Position, Span, Spanned}; use std::fmt::{self, Display}; use crate::{ @@ -255,18 +255,18 @@ pub enum Token { pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { match token { - Token::Ident(ref s) => BorrowedToken::Ident(s), + Token::Ident(s) => BorrowedToken::Ident(s), Token::Int(n) => BorrowedToken::Int(*n), Token::Bool(b) => BorrowedToken::Bool(*b), - Token::Str(ref b) => BorrowedToken::Str(b), - Token::FmtStr(ref b, length) => BorrowedToken::FmtStr(b, *length), - Token::RawStr(ref b, hashes) => BorrowedToken::RawStr(b, *hashes), + Token::Str(b) => BorrowedToken::Str(b), + Token::FmtStr(b, length) => BorrowedToken::FmtStr(b, *length), + Token::RawStr(b, hashes) => BorrowedToken::RawStr(b, *hashes), Token::Keyword(k) => BorrowedToken::Keyword(*k), Token::AttributeStart { is_inner, is_tag } => { BorrowedToken::AttributeStart { is_inner: *is_inner, is_tag: *is_tag } } - Token::LineComment(ref s, _style) => BorrowedToken::LineComment(s, *_style), - Token::BlockComment(ref s, _style) => BorrowedToken::BlockComment(s, *_style), + Token::LineComment(s, _style) => BorrowedToken::LineComment(s, *_style), + Token::BlockComment(s, _style) => BorrowedToken::BlockComment(s, *_style), Token::Quote(stream) => BorrowedToken::Quote(stream), Token::QuotedType(id) => BorrowedToken::QuotedType(*id), Token::InternedExpr(id) => BorrowedToken::InternedExpression(*id), @@ -274,7 +274,7 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { Token::InternedLValue(id) => BorrowedToken::InternedLValue(*id), Token::InternedUnresolvedTypeData(id) => BorrowedToken::InternedUnresolvedTypeData(*id), Token::InternedPattern(id) => BorrowedToken::InternedPattern(*id), - Token::IntType(ref i) => BorrowedToken::IntType(i.clone()), + Token::IntType(i) => BorrowedToken::IntType(i.clone()), Token::Less => BorrowedToken::Less, Token::LessEqual => BorrowedToken::LessEqual, Token::Greater => BorrowedToken::Greater, @@ -312,7 +312,7 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { Token::DollarSign => BorrowedToken::DollarSign, Token::EOF => BorrowedToken::EOF, Token::Invalid(c) => BorrowedToken::Invalid(*c), - Token::Whitespace(ref s) => BorrowedToken::Whitespace(s), + Token::Whitespace(s) => BorrowedToken::Whitespace(s), Token::UnquoteMarker(id) => BorrowedToken::UnquoteMarker(*id), } } @@ -320,7 +320,7 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { #[derive(Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] pub enum FmtStrFragment { String(String), - Interpolation(String, Span), + Interpolation(String, Location), } impl Display for FmtStrFragment { @@ -339,7 +339,7 @@ impl Display for FmtStrFragment { .replace('\"', "\\\""); write!(f, "{}", string) } - FmtStrFragment::Interpolation(string, _span) => { + FmtStrFragment::Interpolation(string, _) => { write!(f, "{{{}}}", string) } } @@ -352,6 +352,63 @@ pub enum DocStyle { Inner, } +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct LocatedToken(Located); + +impl PartialEq for Token { + fn eq(&self, other: &LocatedToken) -> bool { + self == &other.0.contents + } +} +impl PartialEq for LocatedToken { + fn eq(&self, other: &Token) -> bool { + &self.0.contents == other + } +} + +impl From for Token { + fn from(spt: LocatedToken) -> Self { + spt.0.contents + } +} + +impl<'a> From<&'a LocatedToken> for &'a Token { + fn from(spt: &'a LocatedToken) -> Self { + &spt.0.contents + } +} + +impl LocatedToken { + pub fn new(token: Token, location: Location) -> LocatedToken { + LocatedToken(Located::from(location, token)) + } + pub fn location(&self) -> Location { + self.0.location() + } + pub fn span(&self) -> Span { + self.0.span() + } + pub fn token(&self) -> &Token { + &self.0.contents + } + pub fn into_token(self) -> Token { + self.0.contents + } + pub fn kind(&self) -> TokenKind { + self.token().kind() + } + pub fn into_spanned_token(self) -> SpannedToken { + let span = self.span(); + SpannedToken::new(self.into_token(), span) + } +} + +impl std::fmt::Display for LocatedToken { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.token().fmt(f) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct SpannedToken(Spanned); @@ -382,7 +439,7 @@ impl SpannedToken { pub fn new(token: Token, span: Span) -> SpannedToken { SpannedToken(Spanned::from(span, token)) } - pub fn to_span(&self) -> Span { + pub fn span(&self) -> Span { self.0.span() } pub fn token(&self) -> &Token { @@ -521,7 +578,7 @@ pub enum TokenKind { impl fmt::Display for TokenKind { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - TokenKind::Token(ref tok) => write!(f, "{tok}"), + TokenKind::Token(tok) => write!(f, "{tok}"), TokenKind::Ident => write!(f, "identifier"), TokenKind::Literal => write!(f, "literal"), TokenKind::Keyword => write!(f, "keyword"), @@ -743,11 +800,11 @@ impl Attributes { } pub fn is_foldable(&self) -> bool { - self.function().map_or(false, |func_attribute| func_attribute.is_foldable()) + self.function().is_some_and(|func_attribute| func_attribute.is_foldable()) } pub fn is_no_predicates(&self) -> bool { - self.function().map_or(false, |func_attribute| func_attribute.is_no_predicates()) + self.function().is_some_and(|func_attribute| func_attribute.is_no_predicates()) } pub fn has_varargs(&self) -> bool { @@ -869,9 +926,9 @@ impl fmt::Display for FunctionAttribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { FunctionAttribute::Test(scope) => write!(f, "#[test{scope}]"), - FunctionAttribute::Foreign(ref k) => write!(f, "#[foreign({k})]"), - FunctionAttribute::Builtin(ref k) => write!(f, "#[builtin({k})]"), - FunctionAttribute::Oracle(ref k) => write!(f, "#[oracle({k})]"), + FunctionAttribute::Foreign(k) => write!(f, "#[foreign({k})]"), + FunctionAttribute::Builtin(k) => write!(f, "#[builtin({k})]"), + FunctionAttribute::Oracle(k) => write!(f, "#[oracle({k})]"), FunctionAttribute::Fold => write!(f, "#[fold]"), FunctionAttribute::NoPredicates => write!(f, "#[no_predicates]"), FunctionAttribute::InlineAlways => write!(f, "#[inline_always]"), @@ -944,18 +1001,18 @@ impl SecondaryAttribute { pub(crate) fn contents(&self) -> String { match self { SecondaryAttribute::Deprecated(None) => "deprecated".to_string(), - SecondaryAttribute::Deprecated(Some(ref note)) => { + SecondaryAttribute::Deprecated(Some(note)) => { format!("deprecated({note:?})") } - SecondaryAttribute::Tag(ref attribute) => format!("'{}", attribute.contents), - SecondaryAttribute::Meta(ref meta) => meta.to_string(), + SecondaryAttribute::Tag(attribute) => format!("'{}", attribute.contents), + SecondaryAttribute::Meta(meta) => meta.to_string(), SecondaryAttribute::ContractLibraryMethod => "contract_library_method".to_string(), SecondaryAttribute::Export => "export".to_string(), - SecondaryAttribute::Field(ref k) => format!("field({k})"), - SecondaryAttribute::Abi(ref k) => format!("abi({k})"), + SecondaryAttribute::Field(k) => format!("field({k})"), + SecondaryAttribute::Abi(k) => format!("abi({k})"), SecondaryAttribute::Varargs => "varargs".to_string(), SecondaryAttribute::UseCallersScope => "use_callers_scope".to_string(), - SecondaryAttribute::Allow(ref k) => format!("allow({k})"), + SecondaryAttribute::Allow(k) => format!("allow({k})"), } } } @@ -970,7 +1027,7 @@ impl fmt::Display for SecondaryAttribute { pub struct MetaAttribute { pub name: Path, pub arguments: Vec, - pub span: Span, + pub location: Location, } impl Display for MetaAttribute { @@ -996,13 +1053,9 @@ pub struct CustomAttribute { impl CustomAttribute { fn name(&self) -> Option { - let mut lexer = Lexer::new(&self.contents); + let mut lexer = Lexer::new_with_dummy_file(&self.contents); let token = lexer.next()?.ok()?; - if let Token::Ident(ident) = token.into_token() { - Some(ident) - } else { - None - } + if let Token::Ident(ident) = token.into_token() { Some(ident) } else { None } } } @@ -1202,7 +1255,7 @@ impl Keyword { } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Tokens(pub Vec); +pub struct Tokens(pub Vec); #[cfg(test)] mod keywords { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lib.rs b/noir/noir-repo/compiler/noirc_frontend/src/lib.rs index 9d98b125e324..88a32b2717cc 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lib.rs @@ -9,6 +9,8 @@ #![warn(unused_crate_dependencies, unused_extern_crates)] #![warn(unreachable_pub)] #![warn(clippy::semicolon_if_nothing_returned)] +// Temporary allows. +#![allow(clippy::mutable_key_type, clippy::result_large_err)] pub mod ast; pub mod debug; @@ -20,6 +22,7 @@ pub mod monomorphization; pub mod node_interner; pub mod parser; pub mod resolve_locations; +pub mod signed_field; pub mod usage_tracker; pub mod hir; @@ -29,7 +32,7 @@ pub mod hir_def; pub use lexer::token; // Parser API -pub use parser::{parse_program, ParsedModule}; +pub use parser::{ParsedModule, parse_program, parse_program_with_dummy_file}; // Type API pub use hir_def::types::*; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs index e68a5d8c5d88..6c3925f3e41c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs @@ -1,5 +1,5 @@ use fm::FileId; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use rangemap::RangeMap; use rustc_hash::FxHashMap as HashMap; @@ -36,21 +36,19 @@ impl LocationIndices { pub struct ReferencesTracker<'a> { interner: &'a mut NodeInterner, - file_id: FileId, } impl<'a> ReferencesTracker<'a> { - pub fn new(interner: &'a mut NodeInterner, file_id: FileId) -> Self { - Self { interner, file_id } + pub fn new(interner: &'a mut NodeInterner) -> Self { + Self { interner } } pub(crate) fn add_reference( &mut self, module_def_id: ModuleDefId, - span: Span, + location: Location, is_self_type: bool, ) { - let location = Location::new(span, self.file_id); self.interner.add_module_def_id_reference(module_def_id, location, is_self_type); } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/ast.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/ast.rs index 6cd5073aadb9..da86a466fdbc 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -1,15 +1,15 @@ use std::{collections::BTreeMap, fmt::Display}; -use acvm::FieldElement; use iter_extended::vecmap; use noirc_errors::{ - debug_info::{DebugFunctions, DebugTypes, DebugVariables}, Location, + debug_info::{DebugFunctions, DebugTypes, DebugVariables}, }; use crate::{ ast::{BinaryOpKind, IntegerBitSize, Signedness, Visibility}, hir_def::expr::Constructor, + signed_field::SignedField, token::{Attributes, FunctionAttribute}, }; use crate::{hir_def::function::FunctionSignature, token::FmtStrFragment}; @@ -123,7 +123,7 @@ pub struct While { pub enum Literal { Array(ArrayLiteral), Slice(ArrayLiteral), - Integer(FieldElement, /*sign*/ bool, Type, Location), // false for positive integer and true for negative + Integer(SignedField, Type, Location), Bool(bool), Unit, Str(String), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/debug.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/debug.rs index 3b399c757063..680e78828115 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/debug.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/debug.rs @@ -1,12 +1,13 @@ use acvm::acir::AcirField; use iter_extended::vecmap; -use noirc_errors::debug_info::DebugVarId; use noirc_errors::Location; +use noirc_errors::debug_info::DebugVarId; use noirc_printable_type::PrintableType; use crate::debug::{SourceFieldId, SourceVarId}; use crate::hir_def::expr::*; use crate::node_interner::ExprId; +use crate::signed_field::SignedField; use super::ast::{Expression, Ident}; use super::{MonomorphizationError, Monomorphizer}; @@ -28,7 +29,7 @@ impl From for SourceFieldId { } } -impl<'interner> Monomorphizer<'interner> { +impl Monomorphizer<'_> { /// Patch instrumentation calls inserted for debugging. This will record /// tracked variables and their types, and assign them an ID to use at /// runtime. This ID is different from the source ID assigned at @@ -68,14 +69,14 @@ impl<'interner> Monomorphizer<'interner> { ) -> Result<(), MonomorphizationError> { let hir_arguments = vecmap(&call.arguments, |id| self.interner.expression(id)); let var_id_arg = hir_arguments.get(DEBUG_VAR_ID_ARG_SLOT); - let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id, _))) = var_id_arg else { + let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id))) = var_id_arg else { unreachable!("Missing source_var_id in __debug_var_assign call"); }; // instantiate tracked variable for the value type and associate it with // the ID used by the injected instrumentation code let var_type = self.interner.id_type(call.arguments[DEBUG_VALUE_ARG_SLOT]); - let source_var_id = source_var_id.to_u128().into(); + let source_var_id = source_var_id.field.to_u128().into(); // then update the ID used for tracking at runtime let var_id = self.debug_type_tracker.insert_var(source_var_id, &var_type); let interned_var_id = self.intern_var_id(var_id, &call.location); @@ -93,11 +94,11 @@ impl<'interner> Monomorphizer<'interner> { ) -> Result<(), MonomorphizationError> { let hir_arguments = vecmap(&call.arguments, |id| self.interner.expression(id)); let var_id_arg = hir_arguments.get(DEBUG_VAR_ID_ARG_SLOT); - let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id, _))) = var_id_arg else { + let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id))) = var_id_arg else { unreachable!("Missing source_var_id in __debug_var_drop call"); }; // update variable ID for tracked drops (ie. when the var goes out of scope) - let source_var_id = source_var_id.to_u128().into(); + let source_var_id = source_var_id.field.to_u128().into(); let var_id = self .debug_type_tracker .get_var_id(source_var_id) @@ -121,11 +122,11 @@ impl<'interner> Monomorphizer<'interner> { ) -> Result<(), MonomorphizationError> { let hir_arguments = vecmap(&call.arguments, |id| self.interner.expression(id)); let var_id_arg = hir_arguments.get(DEBUG_VAR_ID_ARG_SLOT); - let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id, _))) = var_id_arg else { + let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id))) = var_id_arg else { unreachable!("Missing source_var_id in __debug_member_assign call"); }; // update variable member assignments - let source_var_id = source_var_id.to_u128().into(); + let source_var_id = source_var_id.field.to_u128().into(); let var_type = self .debug_type_tracker @@ -134,11 +135,11 @@ impl<'interner> Monomorphizer<'interner> { .clone(); let mut cursor_type = &var_type; for i in 0..arity { - if let Some(HirExpression::Literal(HirLiteral::Integer(fe_i, i_neg))) = + if let Some(HirExpression::Literal(HirLiteral::Integer(fe_i))) = hir_arguments.get(DEBUG_MEMBER_FIELD_INDEX_ARG_SLOT + i) { - let index = fe_i.to_i128().unsigned_abs(); - if *i_neg { + let index = fe_i.field.to_i128().unsigned_abs(); + if fe_i.is_negative { // We use negative indices at instrumentation time to indicate // and reference member accesses by name which cannot be // resolved until we have a type. This strategy is also used @@ -152,15 +153,10 @@ impl<'interner> Monomorphizer<'interner> { }); cursor_type = element_type_at_index(cursor_type, field_index); - let index_id = self.interner.push_expr(HirExpression::Literal( - HirLiteral::Integer(field_index.into(), false), - )); + let integer = HirLiteral::Integer(SignedField::positive(field_index)); + let index_id = self.interner.push_expr(HirExpression::Literal(integer)); self.interner.push_expr_type(index_id, crate::Type::FieldElement); - self.interner.push_expr_location( - index_id, - call.location.span, - call.location.file, - ); + self.interner.push_expr_location(index_id, call.location); arguments[DEBUG_MEMBER_FIELD_INDEX_ARG_SLOT + i] = self.expr(index_id)?; } else { // array/string element using constant index @@ -182,10 +178,11 @@ impl<'interner> Monomorphizer<'interner> { } fn intern_var_id(&mut self, var_id: DebugVarId, location: &Location) -> ExprId { - let var_id_literal = HirLiteral::Integer((var_id.0 as u128).into(), false); + let value = SignedField::positive(var_id.0); + let var_id_literal = HirLiteral::Integer(value); let expr_id = self.interner.push_expr(HirExpression::Literal(var_id_literal)); self.interner.push_expr_type(expr_id, crate::Type::FieldElement); - self.interner.push_expr_location(expr_id, location.span, location.file); + self.interner.push_expr_location(expr_id, *location); expr_id } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs index c137a6fc90ab..93a12a46591b 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs @@ -1,8 +1,8 @@ -use noirc_errors::{CustomDiagnostic, FileDiagnostic, Location}; +use noirc_errors::{CustomDiagnostic, Location}; use crate::{ - hir::{comptime::InterpreterError, type_check::TypeCheckError}, Type, + hir::{comptime::InterpreterError, type_check::TypeCheckError}, }; #[derive(Debug)] @@ -29,23 +29,14 @@ impl MonomorphizationError { | MonomorphizationError::CheckedTransmuteFailed { location, .. } | MonomorphizationError::CheckedCastFailed { location, .. } | MonomorphizationError::NoDefaultType { location, .. } => *location, - MonomorphizationError::InterpreterError(error) => error.get_location(), + MonomorphizationError::InterpreterError(error) => error.location(), } } } -impl From for FileDiagnostic { - fn from(error: MonomorphizationError) -> FileDiagnostic { - let location = error.location(); - let call_stack = vec![location]; - let diagnostic = error.into_diagnostic(); - diagnostic.with_call_stack(call_stack).in_file(location.file) - } -} - -impl MonomorphizationError { - fn into_diagnostic(self) -> CustomDiagnostic { - let message = match &self { +impl From for CustomDiagnostic { + fn from(error: MonomorphizationError) -> CustomDiagnostic { + let message = match &error { MonomorphizationError::UnknownArrayLength { length, err, .. } => { format!("Could not determine array length `{length}`, encountered error: `{err}`") } @@ -61,7 +52,7 @@ impl MonomorphizationError { MonomorphizationError::NoDefaultType { location } => { let message = "Type annotation needed".into(); let secondary = "Could not determine type of generic argument".into(); - return CustomDiagnostic::simple_error(message, secondary, location.span); + return CustomDiagnostic::simple_error(message, secondary, *location); } MonomorphizationError::InterpreterError(error) => return error.into(), MonomorphizationError::InternalError { message, .. } => message.to_string(), @@ -69,16 +60,16 @@ impl MonomorphizationError { let message = format!("Comptime function {name} used in runtime code"); let secondary = "Comptime functions must be in a comptime block to be called".into(); - return CustomDiagnostic::simple_error(message, secondary, location.span); + return CustomDiagnostic::simple_error(message, secondary, *location); } MonomorphizationError::ComptimeTypeInRuntimeCode { typ, location } => { let message = format!("Comptime-only type `{typ}` used in runtime code"); let secondary = "Comptime type used here".into(); - return CustomDiagnostic::simple_error(message, secondary, location.span); + return CustomDiagnostic::simple_error(message, secondary, *location); } }; - let location = self.location(); - CustomDiagnostic::simple_error(message, String::new(), location.span) + let location = error.location(); + CustomDiagnostic::simple_error(message, String::new(), location) } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs index d30229ce97d5..62ed1ef2e684 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -12,8 +12,10 @@ use crate::ast::{FunctionKind, IntegerBitSize, Signedness, UnaryOp, Visibility}; use crate::hir::comptime::InterpreterError; use crate::hir::type_check::{NoMatchingImplFoundError, TypeCheckError}; use crate::node_interner::{ExprId, GlobalValue, ImplSearchErrorKind}; +use crate::signed_field::SignedField; use crate::token::FmtStrFragment; use crate::{ + Kind, Type, TypeBinding, TypeBindings, debug::DebugInstrumenter, hir_def::{ expr::*, @@ -22,9 +24,8 @@ use crate::{ types, }, node_interner::{self, DefinitionKind, NodeInterner, StmtId, TraitImplKind, TraitMethodId}, - Kind, Type, TypeBinding, TypeBindings, }; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use ast::{GlobalId, While}; use fxhash::FxHashMap as HashMap; use iter_extended::{btree_map, try_vecmap, vecmap}; @@ -481,10 +482,10 @@ impl<'interner> Monomorphizer<'interner> { )) } HirExpression::Literal(HirLiteral::Bool(value)) => Literal(Bool(value)), - HirExpression::Literal(HirLiteral::Integer(value, sign)) => { + HirExpression::Literal(HirLiteral::Integer(value)) => { let location = self.interner.id_location(expr); let typ = Self::convert_type(&self.interner.id_type(expr), location)?; - Literal(Integer(value, sign, typ, location)) + Literal(Integer(value, typ, location)) } HirExpression::Literal(HirLiteral::Array(array)) => match array { HirArrayLiteral::Standard(array) => self.standard_array(expr, array, false)?, @@ -602,7 +603,9 @@ impl<'interner> Monomorphizer<'interner> { HirExpression::Lambda(lambda) => self.lambda(lambda, expr)?, HirExpression::MethodCall(hir_method_call) => { - unreachable!("Encountered HirExpression::MethodCall during monomorphization {hir_method_call:?}") + unreachable!( + "Encountered HirExpression::MethodCall during monomorphization {hir_method_call:?}" + ) } HirExpression::Error => unreachable!("Encountered Error node during monomorphization"), HirExpression::Quote(_) => unreachable!("quote expression remaining in runtime code"), @@ -646,7 +649,7 @@ impl<'interner> Monomorphizer<'interner> { let location = self.interner.expr_location(&array); let typ = Self::convert_type(&self.interner.id_type(array), location)?; - let length = length.evaluate_to_u32(location.span).map_err(|err| { + let length = length.evaluate_to_u32(location).map_err(|err| { let location = self.interner.expr_location(&array); MonomorphizationError::UnknownArrayLength { location, err, length } })?; @@ -819,7 +822,8 @@ impl<'interner> Monomorphizer<'interner> { })?; let tag_value = FieldElement::from(constructor.variant_index); - let tag = ast::Literal::Integer(tag_value, false, ast::Type::Field, location); + let tag_value = SignedField::positive(tag_value); + let tag = ast::Literal::Integer(tag_value, ast::Type::Field, location); fields.insert(0, ast::Expression::Literal(tag)); Ok(ast::Expression::Tuple(fields)) @@ -1027,7 +1031,7 @@ impl<'interner> Monomorphizer<'interner> { binding .evaluate_to_field_element( &Kind::Numeric(numeric_typ.clone()), - location.span, + location, ) .map_err(|err| MonomorphizationError::UnknownArrayLength { length: binding.clone(), @@ -1044,7 +1048,8 @@ impl<'interner> Monomorphizer<'interner> { } let typ = Self::convert_type(&typ, ident.location)?; - ast::Expression::Literal(ast::Literal::Integer(value, false, typ, location)) + let value = SignedField::positive(value); + ast::Expression::Literal(ast::Literal::Integer(value, typ, location)) } }; @@ -1078,7 +1083,9 @@ impl<'interner> Monomorphizer<'interner> { .map_err(MonomorphizationError::InterpreterError)?; (expr, is_closure) } else { - unreachable!("All global values should be resolved at compile time and before monomorphization"); + unreachable!( + "All global values should be resolved at compile time and before monomorphization" + ); }; let expr = self.expr(expr)?; @@ -1119,7 +1126,7 @@ impl<'interner> Monomorphizer<'interner> { HirType::Integer(sign, bits) => ast::Type::Integer(*sign, *bits), HirType::Bool => ast::Type::Bool, HirType::String(size) => { - let size = match size.evaluate_to_u32(location.span) { + let size = match size.evaluate_to_u32(location) { Ok(size) => size, // only default variable sizes to size 0 Err(TypeCheckError::NonConstantEvaluated { .. }) => 0, @@ -1135,7 +1142,7 @@ impl<'interner> Monomorphizer<'interner> { ast::Type::String(size) } HirType::FmtString(size, fields) => { - let size = match size.evaluate_to_u32(location.span) { + let size = match size.evaluate_to_u32(location) { Ok(size) => size, // only default variable sizes to size 0 Err(TypeCheckError::NonConstantEvaluated { .. }) => 0, @@ -1154,7 +1161,7 @@ impl<'interner> Monomorphizer<'interner> { HirType::Unit => ast::Type::Unit, HirType::Array(length, element) => { let element = Box::new(Self::convert_type(element.as_ref(), location)?); - let length = match length.evaluate_to_u32(location.span) { + let length = match length.evaluate_to_u32(location) { Ok(length) => length, Err(err) => { let length = length.as_ref().clone(); @@ -1175,7 +1182,7 @@ impl<'interner> Monomorphizer<'interner> { unreachable!("All TraitAsType should be replaced before calling convert_type"); } HirType::NamedGeneric(binding, _) => { - if let TypeBinding::Bound(ref binding) = &*binding.borrow() { + if let TypeBinding::Bound(binding) = &*binding.borrow() { return Self::convert_type(binding, location); } @@ -1191,12 +1198,12 @@ impl<'interner> Monomorphizer<'interner> { Self::convert_type(to, location)? } - HirType::TypeVariable(ref binding) => { + HirType::TypeVariable(binding) => { let type_var_kind = match &*binding.borrow() { - TypeBinding::Bound(ref binding) => { + TypeBinding::Bound(binding) => { return Self::convert_type(binding, location); } - TypeBinding::Unbound(_, ref type_var_kind) => type_var_kind.clone(), + TypeBinding::Unbound(_, type_var_kind) => type_var_kind.clone(), }; // Default any remaining unbound type variables. @@ -1321,18 +1328,18 @@ impl<'interner> Monomorphizer<'interner> { 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(ref binding) = &*binding.borrow() { + if let TypeBinding::Bound(binding) = &*binding.borrow() { return Self::check_type(binding, location); } Ok(()) } - HirType::TypeVariable(ref binding) => { + HirType::TypeVariable(binding) => { let type_var_kind = match &*binding.borrow() { TypeBinding::Bound(binding) => { return Self::check_type(binding, location); } - TypeBinding::Unbound(_, ref type_var_kind) => type_var_kind.clone(), + TypeBinding::Unbound(_, type_var_kind) => type_var_kind.clone(), }; // Default any remaining unbound type variables. @@ -1402,14 +1409,11 @@ impl<'interner> Monomorphizer<'interner> { location, }); } - let to_value = to.evaluate_to_field_element(&to.kind(), location.span); + let to_value = to.evaluate_to_field_element(&to.kind(), location); if to_value.is_ok() { let skip_simplifications = false; - let from_value = from.evaluate_to_field_element_helper( - &to.kind(), - location.span, - skip_simplifications, - ); + let from_value = + from.evaluate_to_field_element_helper(&to.kind(), location, skip_simplifications); if from_value.is_err() || from_value.unwrap() != to_value.clone().unwrap() { return Err(MonomorphizationError::CheckedCastFailed { actual: HirType::Constant(to_value.unwrap(), to.kind()), @@ -1628,12 +1632,11 @@ impl<'interner> Monomorphizer<'interner> { let location = self.interner.expr_location(expr_id); return Ok(match opcode.as_str() { "modulus_num_bits" => { - let bits = (FieldElement::max_num_bits() as u128).into(); + let bits = FieldElement::max_num_bits(); let typ = ast::Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour); - Some(ast::Expression::Literal(ast::Literal::Integer( - bits, false, typ, location, - ))) + let bits = SignedField::positive(bits); + Some(ast::Expression::Literal(ast::Literal::Integer(bits, typ, location))) } "zeroed" => { let location = self.interner.expr_location(expr_id); @@ -1697,12 +1700,8 @@ impl<'interner> Monomorphizer<'interner> { let int_type = Type::Integer(crate::ast::Signedness::Unsigned, arr_elem_bits); let bytes_as_expr = vecmap(bytes, |byte| { - Expression::Literal(Literal::Integer( - (byte as u128).into(), - false, - int_type.clone(), - location, - )) + let value = SignedField::positive(byte as u32); + Expression::Literal(Literal::Integer(value, int_type.clone(), location)) }); let typ = Type::Slice(Box::new(int_type)); @@ -2062,7 +2061,8 @@ impl<'interner> Monomorphizer<'interner> { match typ { ast::Type::Field | ast::Type::Integer(..) => { let typ = typ.clone(); - ast::Expression::Literal(ast::Literal::Integer(0_u128.into(), false, typ, location)) + let zero = SignedField::positive(0u32); + ast::Expression::Literal(ast::Literal::Integer(zero, typ, location)) } ast::Type::Bool => ast::Expression::Literal(ast::Literal::Bool(false)), ast::Type::Unit => ast::Expression::Literal(ast::Literal::Unit), @@ -2080,7 +2080,9 @@ impl<'interner> Monomorphizer<'interner> { let zeroed_tuple = self.zeroed_value_of_type(fields, location); let fields_len = match &zeroed_tuple { ast::Expression::Tuple(fields) => fields.len() as u64, - _ => unreachable!("ICE: format string fields should be structured in a tuple, but got a {zeroed_tuple}"), + _ => unreachable!( + "ICE: format string fields should be structured in a tuple, but got a {zeroed_tuple}" + ), }; ast::Expression::Literal(ast::Literal::FmtStr( vec![FmtStrFragment::String("\0".repeat(*length as usize))], @@ -2217,8 +2219,8 @@ impl<'interner> Monomorphizer<'interner> { let operator = if matches!(operator.kind, Less | Greater) { Equal } else { NotEqual }; - let int_value = - ast::Literal::Integer(ordering_value, false, ast::Type::Field, location); + let ordering_value = SignedField::positive(ordering_value); + let int_value = ast::Literal::Integer(ordering_value, ast::Type::Field, location); let rhs = Box::new(ast::Expression::Literal(int_value)); let lhs = Box::new(ast::Expression::ExtractTupleField(Box::new(result), 0)); @@ -2375,10 +2377,9 @@ pub fn resolve_trait_method( } Err(ImplSearchErrorKind::Nested(constraints)) => { if let Some(error) = - NoMatchingImplFoundError::new(interner, constraints, location.span) + NoMatchingImplFoundError::new(interner, constraints, location) { - let file = location.file; - return Err(InterpreterError::NoMatchingImplFound { error, file }); + return Err(InterpreterError::NoMatchingImplFound { error }); } else { return Err(InterpreterError::NoImpl { location }); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/printer.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/printer.rs index df4340b4e0d3..bf783f50f000 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/printer.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/printer.rs @@ -105,7 +105,7 @@ impl AstPrinter { self.print_comma_separated(&array.contents, f)?; write!(f, "]") } - super::ast::Literal::Integer(x, _, _, _) => x.fmt(f), + super::ast::Literal::Integer(x, _, _) => x.fmt(f), super::ast::Literal::Bool(x) => x.fmt(f), super::ast::Literal::Str(s) => write!(f, "\"{s}\""), super::ast::Literal::FmtStr(fragments, _, _) => { 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 f995b20bc1a7..e599b99b3fda 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -6,12 +6,14 @@ use std::marker::Copy; use fm::FileId; use iter_extended::vecmap; use noirc_arena::{Arena, Index}; -use noirc_errors::{Location, Span, Spanned}; +use noirc_errors::Located; +use noirc_errors::{Location, Span}; use petgraph::algo::tarjan_scc; use petgraph::prelude::DiGraph; use petgraph::prelude::NodeIndex as PetGraphIndex; use rustc_hash::FxHashMap as HashMap; +use crate::QuotedType; use crate::ast::{ ExpressionKind, Ident, LValue, Pattern, StatementKind, UnaryOp, UnresolvedTypeData, }; @@ -25,8 +27,9 @@ use crate::hir::type_check::generics::TraitGenerics; use crate::hir_def::traits::NamedType; use crate::hir_def::traits::ResolvedTraitBound; use crate::locations::AutoImportEntry; -use crate::QuotedType; +use crate::GenericTypeVars; +use crate::Generics; use crate::ast::{BinaryOpKind, FunctionDefinition, ItemVisibility}; use crate::hir::resolution::errors::ResolverError; use crate::hir_def::expr::HirIdent; @@ -41,8 +44,6 @@ use crate::hir_def::{ }; use crate::locations::LocationIndices; use crate::token::{Attributes, SecondaryAttribute}; -use crate::GenericTypeVars; -use crate::Generics; use crate::{Shared, TypeAlias, TypeBindings, TypeVariable, TypeVariableId}; /// An arbitrary number to limit the recursion depth when searching for trait impls. @@ -358,16 +359,19 @@ pub enum ImplSearchErrorKind { /// as long as these specialized impls do not overlap. E.g. `impl Struct` and `impl Struct` #[derive(Default, Debug, Clone)] pub struct Methods { - pub direct: Vec, + pub direct: Vec, pub trait_impl_methods: Vec, } +#[derive(Debug, Clone)] +pub struct ImplMethod { + pub typ: Type, + pub method: FuncId, +} + #[derive(Debug, Clone)] pub struct TraitImplMethod { - // This type is only stored for primitive types to be able to - // select the correct static methods between multiple options keyed - // under TypeMethodKey::FieldOrInt - pub typ: Option, + pub typ: Type, pub method: FuncId, pub trait_id: TraitId, } @@ -721,9 +725,18 @@ impl NodeInterner { ExprId(self.nodes.insert(Node::Expression(expr))) } + /// Intern an expression with everything needed for it (location & Type) + /// instead of requiring they be pushed later. + pub fn push_expr_full(&mut self, expr: HirExpression, location: Location, typ: Type) -> ExprId { + let id = self.push_expr(expr); + self.push_expr_location(id, location); + self.push_expr_type(id, typ); + id + } + /// Stores the span for an interned expression. - pub fn push_expr_location(&mut self, expr_id: ExprId, span: Span, file: FileId) { - self.id_to_location.insert(expr_id.into(), Location::new(span, file)); + pub fn push_expr_location(&mut self, expr_id: ExprId, location: Location) { + self.id_to_location.insert(expr_id.into(), location); } /// Interns a HIR Function. @@ -752,7 +765,7 @@ impl NodeInterner { id: type_id, name: unresolved_trait.trait_def.name.clone(), crate_id: unresolved_trait.crate_id, - location: Location::new(unresolved_trait.trait_def.span, unresolved_trait.file_id), + location: unresolved_trait.trait_def.name.location(), generics, visibility: ItemVisibility::Private, self_type_typevar: TypeVariable::unbound(self.next_type_variable_id(), Kind::Normal), @@ -797,7 +810,7 @@ impl NodeInterner { self.type_aliases.push(Shared::new(TypeAlias::new( type_id, typ.type_alias_def.name.clone(), - Location::new(typ.type_alias_def.span, typ.file_id), + typ.type_alias_def.location, Type::Error, generics, ))); @@ -910,11 +923,11 @@ impl NodeInterner { comptime: bool, ) -> GlobalId { let statement = self.push_stmt(HirStatement::Error); - let span = name.span(); + let location = name.location(); let id = self .push_global(name, local_id, crate_id, statement, file, attributes, mutable, comptime); - self.push_stmt_location(statement, span, file); + self.push_stmt_location(statement, location); id } @@ -1089,8 +1102,8 @@ impl NodeInterner { pub fn function_ident(&self, func_id: &FuncId) -> crate::ast::Ident { let name = self.function_name(func_id).to_owned(); - let span = self.function_meta(func_id).name.location.span; - crate::ast::Ident(Spanned::from(span, name)) + let location = self.function_meta(func_id).name.location; + crate::ast::Ident(Located::from(location, name)) } pub fn function_name(&self, func_id: &FuncId) -> &str { @@ -1154,7 +1167,9 @@ impl NodeInterner { HirStatement::Let(let_stmt) => Some(let_stmt.clone()), HirStatement::Error => None, other => { - panic!("ice: all globals should correspond to a let statement in the interner: {other:?}") + panic!( + "ice: all globals should correspond to a let statement in the interner: {other:?}" + ) } }, _ => panic!("ice: all globals should correspond to a statement in the interner"), @@ -1222,8 +1237,8 @@ impl NodeInterner { self.id_location(stmt_id) } - pub fn push_stmt_location(&mut self, id: StmtId, span: Span, file: FileId) { - self.id_to_location.insert(id.into(), Location::new(span, file)); + pub fn push_stmt_location(&mut self, id: StmtId, location: Location) { + self.id_to_location.insert(id.into(), location); } pub fn get_type(&self, id: TypeId) -> Shared { @@ -1401,7 +1416,9 @@ impl NodeInterner { }); if trait_id.is_none() && matches!(self_type, Type::DataType(..)) { - if let Some(existing) = self.lookup_direct_method(self_type, &method_name, true) + let check_self_param = false; + if let Some(existing) = + self.lookup_direct_method(self_type, &method_name, check_self_param) { return Some(existing); } @@ -1409,8 +1426,7 @@ impl NodeInterner { // Only remember the actual type if it's FieldOrInt, // so later we can disambiguate on calls like `u32::call`. - let typ = - if key == TypeMethodKey::FieldOrInt { Some(self_type.clone()) } else { None }; + let typ = self_type.clone(); self.methods .entry(key) .or_default() @@ -1512,7 +1528,7 @@ impl NodeInterner { trait_bound: ResolvedTraitBound { trait_id, trait_generics: TraitGenerics { ordered, named }, - span: Span::default(), + location: Location::dummy(), }, } }; @@ -1596,7 +1612,7 @@ impl NodeInterner { trait_bound: ResolvedTraitBound { trait_id, trait_generics, - span: Span::default(), + location: Location::dummy(), }, }; matching_impls.push((impl_kind.clone(), fresh_bindings, constraint)); @@ -1707,7 +1723,7 @@ impl NodeInterner { impl_id: TraitImplId, impl_generics: GenericTypeVars, trait_impl: Shared, - ) -> Result<(), (Span, FileId)> { + ) -> Result<(), Location> { self.trait_implementations.insert(impl_id, trait_impl.clone()); // Avoid adding error types to impls since they'll conflict with every other type. @@ -1759,7 +1775,7 @@ impl NodeInterner { ) { let existing_impl = self.get_trait_implementation(existing); let existing_impl = existing_impl.borrow(); - return Err((existing_impl.ident.span(), existing_impl.file)); + return Err(existing_impl.ident.location()); } for method in &trait_impl.borrow().methods { @@ -1777,18 +1793,20 @@ impl NodeInterner { } /// Looks up a method that's directly defined in the given type. + /// If `check_self_param` is `true`, only a method that has a `self` parameter with a type + /// that unifies with `typ` will be returned. pub fn lookup_direct_method( &self, typ: &Type, method_name: &str, - has_self_arg: bool, + check_self_param: bool, ) -> Option { let key = get_type_method_key(typ)?; self.methods .get(&key) .and_then(|h| h.get(method_name)) - .and_then(|methods| methods.find_direct_method(typ, has_self_arg, self)) + .and_then(|methods| methods.find_direct_method(typ, check_self_param, self)) } /// Looks up a methods that apply to the given type but are defined in traits. @@ -2029,15 +2047,14 @@ impl NodeInterner { index } - pub(crate) fn check_for_dependency_cycles(&self) -> Vec<(CompilationError, FileId)> { + pub(crate) fn check_for_dependency_cycles(&self) -> Vec { let strongly_connected_components = tarjan_scc(&self.dependency_graph); let mut errors = Vec::new(); let mut push_error = |item: String, scc: &[_], i, location: Location| { let cycle = self.get_cycle_error_string(scc, i); - let span = location.span; - let error = ResolverError::DependencyCycle { item, cycle, span }; - errors.push((error.into(), location.file)); + let error = ResolverError::DependencyCycle { item, cycle, location }; + errors.push(error.into()); }; for scc in strongly_connected_components { @@ -2146,8 +2163,8 @@ impl NodeInterner { self.push_expression_kind(lvalue.as_expression().kind) } - pub fn get_lvalue(&self, id: InternedExpressionKind, span: Span) -> LValue { - LValue::from_expression_kind(self.get_expression_kind(id).clone(), span) + pub fn get_lvalue(&self, id: InternedExpressionKind, location: Location) -> LValue { + LValue::from_expression_kind(self.get_expression_kind(id).clone(), location) .expect("Called LValue::from_expression with an invalid expression") } @@ -2310,20 +2327,21 @@ impl NodeInterner { } impl Methods { - fn add_method(&mut self, method: FuncId, typ: Option, trait_id: Option) { + fn add_method(&mut self, method: FuncId, typ: Type, trait_id: Option) { if let Some(trait_id) = trait_id { let trait_impl_method = TraitImplMethod { typ, method, trait_id }; self.trait_impl_methods.push(trait_impl_method); } else { - self.direct.push(method); + let impl_method = ImplMethod { typ, method }; + self.direct.push(impl_method); } } /// Iterate through each method, starting with the direct methods - pub fn iter(&self) -> impl Iterator, Option)> { + pub fn iter(&self) -> impl Iterator)> { let trait_impl_methods = - self.trait_impl_methods.iter().map(|m| (m.method, m.typ.as_ref(), Some(m.trait_id))); - let direct = self.direct.iter().copied().map(|func_id| (func_id, None, None)); + self.trait_impl_methods.iter().map(|m| (m.method, &m.typ, Some(m.trait_id))); + let direct = self.direct.iter().map(|method| (method.method, &method.typ, None)); direct.chain(trait_impl_methods) } @@ -2345,12 +2363,12 @@ impl Methods { pub fn find_direct_method( &self, typ: &Type, - has_self_param: bool, + check_self_param: bool, interner: &NodeInterner, ) -> Option { for method in &self.direct { - if Self::method_matches(typ, has_self_param, *method, None, interner) { - return Some(*method); + if Self::method_matches(typ, check_self_param, method.method, &method.typ, interner) { + return Some(method.method); } } @@ -2367,7 +2385,7 @@ impl Methods { for trait_impl_method in &self.trait_impl_methods { let method = trait_impl_method.method; - let method_type = trait_impl_method.typ.as_ref(); + let method_type = &trait_impl_method.typ; let trait_id = trait_impl_method.trait_id; if Self::method_matches(typ, has_self_param, method, method_type, interner) { @@ -2380,14 +2398,14 @@ impl Methods { fn method_matches( typ: &Type, - has_self_param: bool, + check_self_param: bool, method: FuncId, - method_type: Option<&Type>, + method_type: &Type, interner: &NodeInterner, ) -> bool { match interner.function_meta(&method).typ.instantiate(interner).0 { Type::Function(args, _, _, _) => { - if has_self_param { + if check_self_param { if let Some(object) = args.first() { if object.unify(typ).is_ok() { return true; @@ -2401,21 +2419,18 @@ impl Methods { } } } else { - // If we recorded the concrete type this trait impl method belongs to, - // and it matches typ, it's an exact match and we return that. - if let Some(method_type) = method_type { + // We still need to make sure the method is for the given type + // (this might be false if for example a method for `Struct` was added but + // now we are looking for a method in `Struct`) + if method_type.unify(typ).is_ok() { + return true; + } + + // Handle auto-dereferencing `&mut T` into `T` + if let Type::MutableReference(method_type) = method_type { if method_type.unify(typ).is_ok() { return true; } - - // Handle auto-dereferencing `&mut T` into `T` - if let Type::MutableReference(method_type) = method_type { - if method_type.unify(typ).is_ok() { - return true; - } - } - } else { - return true; } } } 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 8345b75dbabf..76e2958f668d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/errors.rs @@ -5,9 +5,10 @@ use crate::token::TokenKind; use small_ord_set::SmallOrdSet; use thiserror::Error; +use crate::elaborator::UnstableFeature; use iter_extended::vecmap; -use noirc_errors::CustomDiagnostic as Diagnostic; use noirc_errors::Span; +use noirc_errors::{CustomDiagnostic as Diagnostic, Location}; use super::labels::ParsingRuleLabel; @@ -61,7 +62,9 @@ pub enum ParserErrorReason { MissingSeparatingSemi, #[error("constrain keyword is deprecated")] ConstrainDeprecated, - #[error("Invalid type expression: '{0}'. Only unsigned integer constants up to `u32`, globals, generics, +, -, *, /, and % may be used in this context.")] + #[error( + "Invalid type expression: '{0}'. Only unsigned integer constants up to `u32`, globals, generics, +, -, *, /, and % may be used in this context." + )] InvalidTypeExpression(Expression), #[error("Early 'return' is unsupported")] EarlyReturn, @@ -75,8 +78,8 @@ pub enum ParserErrorReason { TraitImplVisibilityIgnored, #[error("comptime keyword is deprecated")] ComptimeDeprecated, - #[error("{0} are experimental and aren't fully supported yet")] - ExperimentalFeature(&'static str), + #[error("This requires the unstable feature '{0}' which is not enabled")] + ExperimentalFeature(UnstableFeature), #[error( "Multiple primary attributes found. Only one function attribute is allowed per function" )] @@ -132,42 +135,50 @@ pub struct ParserError { expected_labels: SmallOrdSet<[ParsingRuleLabel; 1]>, found: Token, reason: Option, - span: Span, + location: Location, } impl ParserError { - pub fn empty(found: Token, span: Span) -> ParserError { + pub fn empty(found: Token, location: Location) -> ParserError { ParserError { expected_tokens: SmallOrdSet::new(), expected_labels: SmallOrdSet::new(), found, reason: None, - span, + location, } } - pub fn expected_token(token: Token, found: Token, span: Span) -> ParserError { - let mut error = ParserError::empty(found, span); + pub fn expected_token(token: Token, found: Token, location: Location) -> ParserError { + let mut error = ParserError::empty(found, location); error.expected_tokens.insert(token); error } - pub fn expected_one_of_tokens(tokens: &[Token], found: Token, span: Span) -> ParserError { - let mut error = ParserError::empty(found, span); + pub fn expected_one_of_tokens( + tokens: &[Token], + found: Token, + location: Location, + ) -> ParserError { + let mut error = ParserError::empty(found, location); for token in tokens { error.expected_tokens.insert(token.clone()); } error } - pub fn expected_label(label: ParsingRuleLabel, found: Token, span: Span) -> ParserError { - let mut error = ParserError::empty(found, span); + pub fn expected_label( + label: ParsingRuleLabel, + found: Token, + location: Location, + ) -> ParserError { + let mut error = ParserError::empty(found, location); error.expected_labels.insert(label); error } - pub fn with_reason(reason: ParserErrorReason, span: Span) -> ParserError { - let mut error = ParserError::empty(Token::EOF, span); + pub fn with_reason(reason: ParserErrorReason, location: Location) -> ParserError { + let mut error = ParserError::empty(Token::EOF, location); error.reason = Some(reason); error } @@ -177,7 +188,11 @@ impl ParserError { } pub fn span(&self) -> Span { - self.span + self.location.span + } + + pub fn location(&self) -> Location { + self.location } pub fn reason(&self) -> Option<&ParserErrorReason> { @@ -234,7 +249,7 @@ impl<'a> From<&'a ParserError> for Diagnostic { let mut diagnostic = Diagnostic::simple_error( "Use of deprecated keyword 'constrain'".into(), "The 'constrain' keyword is deprecated. Please use the 'assert' function instead.".into(), - error.span, + error.location(), ); diagnostic.deprecated = true; diagnostic @@ -243,7 +258,7 @@ impl<'a> From<&'a ParserError> for Diagnostic { let mut diagnostic = Diagnostic::simple_warning( "Use of deprecated keyword 'comptime'".into(), "The 'comptime' keyword has been deprecated. It can be removed without affecting your program".into(), - error.span, + error.location(), ) ; diagnostic.deprecated = true; diagnostic @@ -258,46 +273,51 @@ impl<'a> From<&'a ParserError> for Diagnostic { .collect::>() .join(", ") ), - error.span, + error.location(), ), - ParserErrorReason::ExperimentalFeature(_) => { - Diagnostic::simple_warning(reason.to_string(), "".into(), error.span) + ParserErrorReason::ExperimentalFeature(feature) => { + let secondary = format!( + "Pass -Z{feature} to nargo to enable this feature at your own risk." + ); + Diagnostic::simple_error(reason.to_string(), secondary, error.location()) } ParserErrorReason::TraitVisibilityIgnored => { - Diagnostic::simple_warning(reason.to_string(), "".into(), error.span) + Diagnostic::simple_warning(reason.to_string(), "".into(), error.location()) } ParserErrorReason::TraitImplVisibilityIgnored => { - Diagnostic::simple_warning(reason.to_string(), "".into(), error.span) + Diagnostic::simple_warning(reason.to_string(), "".into(), error.location()) } ParserErrorReason::ExpectedPatternButFoundType(ty) => Diagnostic::simple_error( format!("Expected a pattern but found a type - {ty}"), format!("{ty} is a type and cannot be used as a variable name"), - error.span, + error.location(), ), ParserErrorReason::Lexer(error) => error.into(), ParserErrorReason::ExpectedMutAfterAmpersand { found } => Diagnostic::simple_error( format!("Expected `mut` after `&`, found `{found}`"), "Noir doesn't have immutable references, only mutable references".to_string(), - error.span, + error.location(), ), ParserErrorReason::MissingSafetyComment => Diagnostic::simple_warning( "Unsafe block must have a safety comment above it".into(), "The comment must start with the \"Safety: \" word".into(), - error.span, + error.location(), ), ParserErrorReason::MissingParametersForFunctionDefinition => { Diagnostic::simple_error( "Missing parameters for function definition".into(), "Add a parameter list: `()`".into(), - error.span, + error.location(), ) } ParserErrorReason::DocCommentDoesNotDocumentAnything => { let primary = "This doc comment doesn't document anything".to_string(); let secondary = "Consider changing it to a regular `//` comment".to_string(); - Diagnostic::simple_warning(primary, secondary, error.span) + Diagnostic::simple_warning(primary, secondary, error.location()) + } + other => { + Diagnostic::simple_error(format!("{other}"), String::new(), error.location()) } - other => Diagnostic::simple_error(format!("{other}"), String::new(), error.span), }, None => { if matches!( @@ -306,10 +326,10 @@ impl<'a> From<&'a ParserError> for Diagnostic { ) { let primary = "This doc comment doesn't document anything".to_string(); let secondary = "Consider changing it to a regular `//` comment".to_string(); - Diagnostic::simple_warning(primary, secondary, error.span) + Diagnostic::simple_warning(primary, secondary, error.location()) } else { let primary = error.to_string(); - Diagnostic::simple_error(primary, String::new(), error.span) + Diagnostic::simple_error(primary, String::new(), error.location()) } } } 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 c433adbfdfb1..0cecbe0814ab 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs @@ -20,8 +20,10 @@ use crate::token::SecondaryAttribute; pub use errors::ParserError; pub use errors::ParserErrorReason; -use noirc_errors::Span; -pub use parser::{parse_program, Parser, StatementOrExpressionOrLValue}; +use noirc_errors::Location; +pub use parser::{ + Parser, StatementOrExpressionOrLValue, parse_program, parse_program_with_dummy_file, +}; #[derive(Clone, Default)] pub struct SortedModule { @@ -128,7 +130,7 @@ impl ParsedModule { #[derive(Clone, Debug)] pub struct Item { pub kind: ItemKind, - pub span: Span, + pub location: Location, pub doc_comments: Vec, } 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 f4491e84471a..a5ea2ea5fe9e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs @@ -1,14 +1,15 @@ use acvm::FieldElement; +use fm::FileId; use modifiers::Modifiers; -use noirc_errors::Span; +use noirc_errors::{Location, Span}; use crate::{ ast::{Ident, ItemVisibility}, - lexer::{Lexer, SpannedTokenResult}, - token::{FmtStrFragment, IntType, Keyword, SpannedToken, Token, TokenKind, Tokens}, + lexer::{Lexer, lexer::LocatedTokenResult}, + token::{FmtStrFragment, IntType, Keyword, LocatedToken, Token, TokenKind, Tokens}, }; -use super::{labels::ParsingRuleLabel, ParsedModule, ParserError, ParserErrorReason}; +use super::{ParsedModule, ParserError, ParserErrorReason, labels::ParsingRuleLabel}; mod arguments; mod attributes; @@ -47,21 +48,25 @@ pub use statement_or_expression_or_lvalue::StatementOrExpressionOrLValue; /// of the program along with any parsing errors encountered. If the parsing errors /// Vec is non-empty, there may be Error nodes in the Ast to fill in the gaps that /// failed to parse. Otherwise the Ast is guaranteed to have 0 Error nodes. -pub fn parse_program(source_program: &str) -> (ParsedModule, Vec) { - let lexer = Lexer::new(source_program); +pub fn parse_program(source_program: &str, file_id: FileId) -> (ParsedModule, Vec) { + let lexer = Lexer::new(source_program, file_id); let mut parser = Parser::for_lexer(lexer); let program = parser.parse_program(); let errors = parser.errors; (program, errors) } +pub fn parse_program_with_dummy_file(source_program: &str) -> (ParsedModule, Vec) { + parse_program(source_program, FileId::dummy()) +} + enum TokenStream<'a> { Lexer(Lexer<'a>), Tokens(Tokens), } -impl<'a> TokenStream<'a> { - fn next(&mut self) -> Option { +impl TokenStream<'_> { + fn next(&mut self) -> Option { match self { TokenStream::Lexer(lexer) => lexer.next(), TokenStream::Tokens(tokens) => { @@ -80,10 +85,10 @@ pub struct Parser<'a> { // We always have one look-ahead token for these cases: // - check if we get `&` or `&mut` // - check if we get `>` or `>>` - token: SpannedToken, - next_token: SpannedToken, - current_token_span: Span, - previous_token_span: Span, + token: LocatedToken, + next_token: LocatedToken, + current_token_location: Location, + previous_token_location: Location, // We also keep track of comments that appear right before a token, // because `unsafe { }` requires one before it. @@ -111,18 +116,22 @@ impl<'a> Parser<'a> { Self::new(TokenStream::Tokens(tokens)) } - pub fn for_str(str: &'a str) -> Self { - Self::for_lexer(Lexer::new(str)) + pub fn for_str(str: &'a str, file_id: FileId) -> Self { + Self::for_lexer(Lexer::new(str, file_id)) + } + + pub fn for_str_with_dummy_file(str: &'a str) -> Self { + Self::for_str(str, FileId::dummy()) } fn new(tokens: TokenStream<'a>) -> Self { let mut parser = Self { errors: Vec::new(), tokens, - token: eof_spanned_token(), - next_token: eof_spanned_token(), - current_token_span: Default::default(), - previous_token_span: Default::default(), + token: eof_located_token(), + next_token: eof_located_token(), + current_token_location: Location::dummy(), + previous_token_location: Location::dummy(), current_token_comments: String::new(), next_token_comments: String::new(), statement_comments: None, @@ -163,16 +172,12 @@ impl<'a> Parser<'a> { } let all_warnings = self.errors.iter().all(|error| error.is_warning()); - if all_warnings { - Ok((item, self.errors)) - } else { - Err(self.errors) - } + if all_warnings { Ok((item, self.errors)) } else { Err(self.errors) } } /// Bumps this parser by one token. Returns the token that was previously the "current" token. - fn bump(&mut self) -> SpannedToken { - self.previous_token_span = self.current_token_span; + fn bump(&mut self) -> LocatedToken { + self.previous_token_location = self.current_token_location; let (next_next_token, next_next_token_comments) = self.read_token_internal(); let next_token = std::mem::replace(&mut self.next_token, next_next_token); let token = std::mem::replace(&mut self.token, next_token); @@ -181,7 +186,7 @@ impl<'a> Parser<'a> { std::mem::replace(&mut self.next_token_comments, next_next_token_comments); let _ = std::mem::replace(&mut self.current_token_comments, next_comments); - self.current_token_span = self.token.to_span(); + self.current_token_location = self.token.location(); token } @@ -189,20 +194,23 @@ impl<'a> Parser<'a> { let (token, comments) = self.read_token_internal(); self.token = token; self.current_token_comments = comments; - self.current_token_span = self.token.to_span(); + self.current_token_location = self.token.location(); let (token, comments) = self.read_token_internal(); self.next_token = token; self.next_token_comments = comments; } - fn read_token_internal(&mut self) -> (SpannedToken, String) { + fn read_token_internal(&mut self) -> (LocatedToken, String) { let mut last_comments = String::new(); loop { match self.tokens.next() { Some(Ok(token)) => match token.token() { Token::LineComment(comment, None) | Token::BlockComment(comment, None) => { + if !last_comments.is_empty() { + last_comments.push('\n'); + } last_comments.push_str(comment); continue; } @@ -211,17 +219,13 @@ impl<'a> Parser<'a> { } }, Some(Err(lexer_error)) => self.errors.push(lexer_error.into()), - None => return (eof_spanned_token(), last_comments), + None => return (eof_located_token(), last_comments), } } } - fn eat_kind(&mut self, kind: TokenKind) -> Option { - if self.token.kind() == kind { - Some(self.bump()) - } else { - None - } + fn eat_kind(&mut self, kind: TokenKind) -> Option { + if self.token.kind() == kind { Some(self.bump()) } else { None } } fn eat_keyword(&mut self, keyword: Keyword) -> bool { @@ -240,7 +244,7 @@ impl<'a> Parser<'a> { fn eat_ident(&mut self) -> Option { if let Some(token) = self.eat_kind(TokenKind::Ident) { match token.into_token() { - Token::Ident(ident) => Some(Ident::new(ident, self.previous_token_span)), + Token::Ident(ident) => Some(Ident::new(ident, self.previous_token_location)), _ => unreachable!(), } } else { @@ -375,7 +379,7 @@ impl<'a> Parser<'a> { fn eat_commas(&mut self) -> bool { if self.eat_comma() { while self.eat_comma() { - self.push_error(ParserErrorReason::UnexpectedComma, self.previous_token_span); + self.push_error(ParserErrorReason::UnexpectedComma, self.previous_token_location); } true } else { @@ -390,7 +394,10 @@ impl<'a> Parser<'a> { fn eat_semicolons(&mut self) -> bool { if self.eat_semicolon() { while self.eat_semicolon() { - self.push_error(ParserErrorReason::UnexpectedSemicolon, self.previous_token_span); + self.push_error( + ParserErrorReason::UnexpectedSemicolon, + self.previous_token_location, + ); } true } else { @@ -479,17 +486,35 @@ impl<'a> Parser<'a> { self.token.token() == &Token::EOF } - fn span_since(&self, start_span: Span) -> Span { - if self.current_token_span == start_span { + fn location_since(&self, start_location: Location) -> Location { + // When taking the span between locations in different files, just keep the first one + if self.current_token_location.file != start_location.file { + return start_location; + } + + let start_span = start_location.span; + + let span = if self.current_token_location.span == start_location.span { start_span } else { - let end_span = self.previous_token_span; - Span::from(start_span.start()..end_span.end()) - } + let end_span = self.previous_token_location.span; + if start_span.start() <= end_span.end() { + Span::from(start_span.start()..end_span.end()) + } else { + // TODO: workaround for now + start_span + } + }; + + Location::new(span, start_location.file) + } + + fn location_at_previous_token_end(&self) -> Location { + Location::new(self.span_at_previous_token_end(), self.previous_token_location.file) } fn span_at_previous_token_end(&self) -> Span { - Span::from(self.previous_token_span.end()..self.previous_token_span.end()) + Span::from(self.previous_token_location.span.end()..self.previous_token_location.span.end()) } fn expected_identifier(&mut self) { @@ -500,7 +525,7 @@ impl<'a> Parser<'a> { self.errors.push(ParserError::expected_token( token, self.token.token().clone(), - self.current_token_span, + self.current_token_location, )); } @@ -508,7 +533,7 @@ impl<'a> Parser<'a> { self.errors.push(ParserError::expected_one_of_tokens( tokens, self.token.token().clone(), - self.current_token_span, + self.current_token_location, )); } @@ -516,18 +541,26 @@ impl<'a> Parser<'a> { self.errors.push(ParserError::expected_label( label, self.token.token().clone(), - self.current_token_span, + self.current_token_location, )); } - fn expected_token_separating_items(&mut self, token: Token, items: &'static str, span: Span) { - self.push_error(ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items }, span); + fn expected_token_separating_items( + &mut self, + token: Token, + items: &'static str, + location: Location, + ) { + self.push_error( + ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items }, + location, + ); } fn expected_mut_after_ampersand(&mut self) { self.push_error( ParserErrorReason::ExpectedMutAfterAmpersand { found: self.token.token().clone() }, - self.current_token_span, + self.current_token_location, ); } @@ -543,20 +576,20 @@ impl<'a> Parser<'a> { ParserErrorReason::VisibilityNotFollowedByAnItem { visibility: modifiers.visibility, }, - modifiers.visibility_span, + modifiers.visibility_location, ); } } fn unconstrained_not_followed_by_an_item(&mut self, modifiers: Modifiers) { - if let Some(span) = modifiers.unconstrained { - self.push_error(ParserErrorReason::UnconstrainedNotFollowedByAnItem, span); + if let Some(location) = modifiers.unconstrained { + self.push_error(ParserErrorReason::UnconstrainedNotFollowedByAnItem, location); } } fn comptime_not_followed_by_an_item(&mut self, modifiers: Modifiers) { - if let Some(span) = modifiers.comptime { - self.push_error(ParserErrorReason::ComptimeNotFollowedByAnItem, span); + if let Some(location) = modifiers.comptime { + self.push_error(ParserErrorReason::ComptimeNotFollowedByAnItem, location); } } @@ -567,28 +600,28 @@ impl<'a> Parser<'a> { } fn mutable_not_applicable(&mut self, modifiers: Modifiers) { - if let Some(span) = modifiers.mutable { - self.push_error(ParserErrorReason::MutableNotApplicable, span); + if let Some(location) = modifiers.mutable { + self.push_error(ParserErrorReason::MutableNotApplicable, location); } } fn comptime_not_applicable(&mut self, modifiers: Modifiers) { - if let Some(span) = modifiers.comptime { - self.push_error(ParserErrorReason::ComptimeNotApplicable, span); + if let Some(location) = modifiers.comptime { + self.push_error(ParserErrorReason::ComptimeNotApplicable, location); } } fn unconstrained_not_applicable(&mut self, modifiers: Modifiers) { - if let Some(span) = modifiers.unconstrained { - self.push_error(ParserErrorReason::UnconstrainedNotApplicable, span); + if let Some(location) = modifiers.unconstrained { + self.push_error(ParserErrorReason::UnconstrainedNotApplicable, location); } } - fn push_error(&mut self, reason: ParserErrorReason, span: Span) { - self.errors.push(ParserError::with_reason(reason, span)); + fn push_error(&mut self, reason: ParserErrorReason, location: Location) { + self.errors.push(ParserError::with_reason(reason, location)); } } -fn eof_spanned_token() -> SpannedToken { - SpannedToken::new(Token::EOF, Default::default()) +fn eof_located_token() -> LocatedToken { + LocatedToken::new(Token::EOF, Location::dummy()) } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/arguments.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/arguments.rs index 380f42809a6e..6b7dd8559c8e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/arguments.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/arguments.rs @@ -1,13 +1,13 @@ use crate::{ast::Expression, token::Token}; -use super::{parse_many::separated_by_comma_until_right_paren, Parser}; +use super::{Parser, parse_many::separated_by_comma_until_right_paren}; pub(crate) struct CallArguments { pub(crate) arguments: Vec, pub(crate) is_macro_call: bool, } -impl<'a> Parser<'a> { +impl Parser<'_> { /// Arguments = '(' ArgumentsList? ')' /// /// ArgumentsList = Expression ( ',' Expression )? ','? diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/attributes.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/attributes.rs index e32e7d3cb235..32aa974fcafe 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -1,31 +1,31 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::ast::{Expression, ExpressionKind, Ident, Literal, Path}; use crate::lexer::errors::LexerErrorKind; -use crate::parser::labels::ParsingRuleLabel; use crate::parser::ParserErrorReason; +use crate::parser::labels::ParsingRuleLabel; use crate::token::{Attribute, FunctionAttribute, MetaAttribute, TestScope, Token}; use crate::token::{CustomAttribute, SecondaryAttribute}; -use super::parse_many::without_separator; use super::Parser; +use super::parse_many::without_separator; -impl<'a> Parser<'a> { +impl Parser<'_> { /// InnerAttribute = '#![' SecondaryAttribute ']' pub(super) fn parse_inner_attribute(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let is_tag = self.eat_inner_attribute_start()?; let attribute = if is_tag { - self.parse_tag_attribute(start_span) + self.parse_tag_attribute(start_location) } else { - self.parse_non_tag_attribute(start_span) + self.parse_non_tag_attribute(start_location) }; match attribute { Attribute::Function(function_attribute) => { self.errors.push( LexerErrorKind::InvalidInnerAttribute { - span: self.span_since(start_span), + location: self.location_since(start_location), found: function_attribute.to_string(), } .into(), @@ -37,7 +37,7 @@ impl<'a> Parser<'a> { } /// Attributes = Attribute* - pub(super) fn parse_attributes(&mut self) -> Vec<(Attribute, Span)> { + pub(super) fn parse_attributes(&mut self) -> Vec<(Attribute, Location)> { self.parse_many("attributes", without_separator(), Self::parse_attribute) } @@ -73,26 +73,26 @@ impl<'a> Parser<'a> { /// AttributeValue /// = Path /// | integer - pub(crate) fn parse_attribute(&mut self) -> Option<(Attribute, Span)> { - let start_span = self.current_token_span; + pub(crate) fn parse_attribute(&mut self) -> Option<(Attribute, Location)> { + let start_location = self.current_token_location; let is_tag = self.eat_attribute_start()?; let attribute = if is_tag { - self.parse_tag_attribute(start_span) + self.parse_tag_attribute(start_location) } else { - self.parse_non_tag_attribute(start_span) + self.parse_non_tag_attribute(start_location) }; - Some((attribute, self.span_since(start_span))) + Some((attribute, self.location_since(start_location))) } pub(super) fn validate_secondary_attributes( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, ) -> Vec { attributes .into_iter() - .filter_map(|(attribute, span)| match attribute { + .filter_map(|(attribute, location)| match attribute { Attribute::Function(..) => { - self.push_error(ParserErrorReason::NoFunctionAttributesAllowedOnType, span); + self.push_error(ParserErrorReason::NoFunctionAttributesAllowedOnType, location); None } Attribute::Secondary(attr) => Some(attr), @@ -100,9 +100,9 @@ impl<'a> Parser<'a> { .collect() } - fn parse_tag_attribute(&mut self, start_span: Span) -> Attribute { - let contents_start_span = self.current_token_span; - let mut contents_span = contents_start_span; + fn parse_tag_attribute(&mut self, start_location: Location) -> Attribute { + let contents_start_location = self.current_token_location; + let mut contents_location = contents_start_location; let mut contents = String::new(); let mut brackets_count = 1; // 1 because of the starting `#[` @@ -113,7 +113,7 @@ impl<'a> Parser<'a> { } else if self.at(Token::RightBracket) { brackets_count -= 1; if brackets_count == 0 { - contents_span = self.span_since(contents_start_span); + contents_location = self.location_since(contents_start_location); self.bump(); break; } @@ -125,66 +125,68 @@ impl<'a> Parser<'a> { Attribute::Secondary(SecondaryAttribute::Tag(CustomAttribute { contents, - span: self.span_since(start_span), - contents_span, + span: self.location_since(start_location).span, + contents_span: contents_location.span, })) } - fn parse_non_tag_attribute(&mut self, start_span: Span) -> Attribute { + fn parse_non_tag_attribute(&mut self, start_location: Location) -> Attribute { if matches!(&self.token.token(), Token::Keyword(..)) && (self.next_is(Token::LeftParen) || self.next_is(Token::RightBracket)) { // This is a Meta attribute with the syntax `keyword(arg1, arg2, .., argN)` - let path = Path::from_single(self.token.to_string(), self.current_token_span); + let path = Path::from_single(self.token.to_string(), self.current_token_location); self.bump(); - self.parse_meta_attribute(path, start_span) + self.parse_meta_attribute(path, start_location) } else if let Some(path) = self.parse_path_no_turbofish() { if let Some(ident) = path.as_ident() { if ident.0.contents == "test" { // The test attribute is the only secondary attribute that has `a = b` in its syntax // (`should_fail_with = "..."``) so we parse it differently. - self.parse_test_attribute(start_span) + self.parse_test_attribute(start_location) } else { // Every other attribute has the form `name(arg1, arg2, .., argN)` - self.parse_ident_attribute_other_than_test(ident, start_span) + self.parse_ident_attribute_other_than_test(ident, start_location) } } else { // This is a Meta attribute with the syntax `path(arg1, arg2, .., argN)` - self.parse_meta_attribute(path, start_span) + self.parse_meta_attribute(path, start_location) } } else { self.expected_label(ParsingRuleLabel::Path); - self.parse_tag_attribute(start_span) + self.parse_tag_attribute(start_location) } } - fn parse_meta_attribute(&mut self, name: Path, start_span: Span) -> Attribute { + fn parse_meta_attribute(&mut self, name: Path, start_location: Location) -> Attribute { let arguments = self.parse_arguments().unwrap_or_default(); self.skip_until_right_bracket(); Attribute::Secondary(SecondaryAttribute::Meta(MetaAttribute { name, arguments, - span: self.span_since(start_span), + location: self.location_since(start_location), })) } fn parse_ident_attribute_other_than_test( &mut self, ident: &Ident, - start_span: Span, + start_location: Location, ) -> Attribute { let arguments = self.parse_arguments().unwrap_or_default(); self.skip_until_right_bracket(); match ident.0.contents.as_str() { - "abi" => self.parse_single_name_attribute(ident, arguments, start_span, |name| { + "abi" => self.parse_single_name_attribute(ident, arguments, start_location, |name| { Attribute::Secondary(SecondaryAttribute::Abi(name)) }), - "allow" => self.parse_single_name_attribute(ident, arguments, start_span, |name| { + "allow" => self.parse_single_name_attribute(ident, arguments, start_location, |name| { Attribute::Secondary(SecondaryAttribute::Allow(name)) }), - "builtin" => self.parse_single_name_attribute(ident, arguments, start_span, |name| { - Attribute::Function(FunctionAttribute::Builtin(name)) - }), + "builtin" => { + self.parse_single_name_attribute(ident, arguments, start_location, |name| { + Attribute::Function(FunctionAttribute::Builtin(name)) + }) + } "deprecated" => self.parse_deprecated_attribute(ident, arguments), "contract_library_method" => { let attr = Attribute::Secondary(SecondaryAttribute::ContractLibraryMethod); @@ -194,16 +196,18 @@ impl<'a> Parser<'a> { let attr = Attribute::Secondary(SecondaryAttribute::Export); self.parse_no_args_attribute(ident, arguments, attr) } - "field" => self.parse_single_name_attribute(ident, arguments, start_span, |name| { + "field" => self.parse_single_name_attribute(ident, arguments, start_location, |name| { Attribute::Secondary(SecondaryAttribute::Field(name)) }), "fold" => { let attr = Attribute::Function(FunctionAttribute::Fold); self.parse_no_args_attribute(ident, arguments, attr) } - "foreign" => self.parse_single_name_attribute(ident, arguments, start_span, |name| { - Attribute::Function(FunctionAttribute::Foreign(name)) - }), + "foreign" => { + self.parse_single_name_attribute(ident, arguments, start_location, |name| { + Attribute::Function(FunctionAttribute::Foreign(name)) + }) + } "inline_always" => { let attr = Attribute::Function(FunctionAttribute::InlineAlways); self.parse_no_args_attribute(ident, arguments, attr) @@ -212,9 +216,11 @@ impl<'a> Parser<'a> { let attr = Attribute::Function(FunctionAttribute::NoPredicates); self.parse_no_args_attribute(ident, arguments, attr) } - "oracle" => self.parse_single_name_attribute(ident, arguments, start_span, |name| { - Attribute::Function(FunctionAttribute::Oracle(name)) - }), + "oracle" => { + self.parse_single_name_attribute(ident, arguments, start_location, |name| { + Attribute::Function(FunctionAttribute::Oracle(name)) + }) + } "use_callers_scope" => { let attr = Attribute::Secondary(SecondaryAttribute::UseCallersScope); self.parse_no_args_attribute(ident, arguments, attr) @@ -226,7 +232,7 @@ impl<'a> Parser<'a> { _ => Attribute::Secondary(SecondaryAttribute::Meta(MetaAttribute { name: Path::from_ident(ident.clone()), arguments, - span: self.span_since(start_span), + location: self.location_since(start_location), })), } } @@ -248,7 +254,7 @@ impl<'a> Parser<'a> { max: 1, found: arguments.len(), }, - ident.span(), + ident.location(), ); return Attribute::Secondary(SecondaryAttribute::Deprecated(None)); } @@ -257,7 +263,7 @@ impl<'a> Parser<'a> { let ExpressionKind::Literal(Literal::Str(message)) = argument.kind else { self.push_error( ParserErrorReason::DeprecatedAttributeExpectsAStringArgument, - argument.span, + argument.location, ); return Attribute::Secondary(SecondaryAttribute::Deprecated(None)); }; @@ -265,7 +271,7 @@ impl<'a> Parser<'a> { Attribute::Secondary(SecondaryAttribute::Deprecated(Some(message))) } - fn parse_test_attribute(&mut self, start_span: Span) -> Attribute { + fn parse_test_attribute(&mut self, start_location: Location) -> Attribute { let scope = if self.eat_left_paren() { let scope = if let Some(ident) = self.eat_ident() { match ident.0.contents.as_str() { @@ -295,7 +301,10 @@ impl<'a> Parser<'a> { scope } else { self.errors.push( - LexerErrorKind::MalformedTestAttribute { span: self.span_since(start_span) }.into(), + LexerErrorKind::MalformedTestAttribute { + location: self.location_since(start_location), + } + .into(), ); TestScope::None }; @@ -307,7 +316,7 @@ impl<'a> Parser<'a> { &mut self, ident: &Ident, mut arguments: Vec, - start_span: Span, + start_location: Location, f: F, ) -> Attribute where @@ -321,7 +330,7 @@ impl<'a> Parser<'a> { max: 1, found: arguments.len(), }, - self.current_token_span, + self.current_token_location, ); return f(String::new()); } @@ -332,10 +341,13 @@ impl<'a> Parser<'a> { f(argument.to_string()) } _ => { - let span = self.span_since(start_span); + let location = self.location_since(start_location); self.errors.push( - LexerErrorKind::MalformedFuncAttribute { span, found: argument.to_string() } - .into(), + LexerErrorKind::MalformedFuncAttribute { + location, + found: argument.to_string(), + } + .into(), ); f(String::new()) } @@ -356,7 +368,7 @@ impl<'a> Parser<'a> { max: 0, found: arguments.len(), }, - ident.span(), + ident.location(), ); } @@ -388,19 +400,19 @@ mod tests { use noirc_errors::Span; use crate::{ - parser::{parser::tests::expect_no_errors, Parser}, + parser::{Parser, parser::tests::expect_no_errors}, token::{Attribute, FunctionAttribute, SecondaryAttribute, TestScope}, }; fn parse_inner_secondary_attribute_no_errors(src: &str, expected: SecondaryAttribute) { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let attribute = parser.parse_inner_attribute(); expect_no_errors(&parser.errors); assert_eq!(attribute.unwrap(), expected); } fn parse_attribute_no_errors(src: &str, expected: Attribute) { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); assert_eq!(attribute, expected); @@ -409,7 +421,7 @@ mod tests { #[test] fn parses_inner_attribute_as_tag() { let src = "#!['hello]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let Some(SecondaryAttribute::Tag(custom)) = parser.parse_inner_attribute() else { panic!("Expected inner tag attribute"); }; @@ -422,7 +434,7 @@ mod tests { #[test] fn parses_inner_attribute_as_tag_with_nested_brackets() { let src = "#!['hello[1]]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let Some(SecondaryAttribute::Tag(custom)) = parser.parse_inner_attribute() else { panic!("Expected inner tag attribute"); }; @@ -570,7 +582,7 @@ mod tests { #[test] fn parses_meta_attribute_single_identifier_no_arguments() { let src = "#[foo]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); let Attribute::Secondary(SecondaryAttribute::Meta(meta)) = attribute else { @@ -583,7 +595,7 @@ mod tests { #[test] fn parses_meta_attribute_single_identifier_as_keyword() { let src = "#[dep]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); let Attribute::Secondary(SecondaryAttribute::Meta(meta)) = attribute else { @@ -596,7 +608,7 @@ mod tests { #[test] fn parses_meta_attribute_single_identifier_with_arguments() { let src = "#[foo(1, 2, 3)]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); let Attribute::Secondary(SecondaryAttribute::Meta(meta)) = attribute else { @@ -610,7 +622,7 @@ mod tests { #[test] fn parses_meta_attribute_path_with_arguments() { let src = "#[foo::bar(1, 2, 3)]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); let Attribute::Secondary(SecondaryAttribute::Meta(meta)) = attribute else { @@ -624,7 +636,7 @@ mod tests { #[test] fn parses_attributes() { let src = "#[test] #[deprecated]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let mut attributes = parser.parse_attributes(); expect_no_errors(&parser.errors); assert_eq!(attributes.len(), 2); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/doc_comments.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/doc_comments.rs index 578a49641f64..0c9329d7027f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/doc_comments.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/doc_comments.rs @@ -1,8 +1,11 @@ -use crate::token::{DocStyle, Token, TokenKind}; +use crate::{ + parser::ParserErrorReason, + token::{DocStyle, Token, TokenKind}, +}; -use super::{parse_many::without_separator, Parser}; +use super::{Parser, parse_many::without_separator}; -impl<'a> Parser<'a> { +impl Parser<'_> { /// InnerDocComments = inner_doc_comment* pub(super) fn parse_inner_doc_comments(&mut self) -> Vec { self.parse_many("inner doc comments", without_separator(), Self::parse_inner_doc_comment) @@ -28,16 +31,28 @@ impl<'a> Parser<'a> { _ => unreachable!(), }) } + + /// Skips any outer doc comments but produces a warning saying that they don't document anything. + pub(super) fn warn_on_outer_doc_comments(&mut self) { + let location_before_doc_comments = self.current_token_location; + let doc_comments = self.parse_outer_doc_comments(); + if !doc_comments.is_empty() { + self.push_error( + ParserErrorReason::DocCommentDoesNotDocumentAnything, + location_before_doc_comments, + ); + } + } } #[cfg(test)] mod tests { - use crate::parser::{parser::tests::expect_no_errors, Parser}; + use crate::parser::{Parser, parser::tests::expect_no_errors}; #[test] fn parses_inner_doc_comments() { let src = "//! Hello\n//! World"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let comments = parser.parse_inner_doc_comments(); expect_no_errors(&parser.errors); assert_eq!(comments.len(), 2); @@ -48,7 +63,7 @@ mod tests { #[test] fn parses_outer_doc_comments() { let src = "/// Hello\n/// World"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let comments = parser.parse_outer_doc_comments(); expect_no_errors(&parser.errors); assert_eq!(comments.len(), 2); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/enums.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/enums.rs index 3b496a438cf3..885bfa871eeb 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/enums.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/enums.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{Documented, EnumVariant, Ident, ItemVisibility, NoirEnumeration, UnresolvedGenerics}, @@ -7,24 +7,22 @@ use crate::{ }; use super::{ - parse_many::{separated_by_comma_until_right_brace, separated_by_comma_until_right_paren}, Parser, + parse_many::{separated_by_comma_until_right_brace, separated_by_comma_until_right_paren}, }; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Enum = 'enum' identifier Generics '{' EnumVariant* '}' /// /// EnumField = OuterDocComments identifier ':' Type pub(crate) fn parse_enum( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, visibility: ItemVisibility, - start_span: Span, + start_location: Location, ) -> NoirEnumeration { let attributes = self.validate_secondary_attributes(attributes); - self.push_error(ParserErrorReason::ExperimentalFeature("Enums"), start_span); - let Some(name) = self.eat_ident() else { self.expected_identifier(); return self.empty_enum( @@ -32,7 +30,7 @@ impl<'a> Parser<'a> { attributes, visibility, Vec::new(), - start_span, + start_location, ); }; @@ -40,7 +38,7 @@ impl<'a> Parser<'a> { if !self.eat_left_brace() { self.expected_token(Token::LeftBrace); - return self.empty_enum(name, attributes, visibility, generics, start_span); + return self.empty_enum(name, attributes, visibility, generics, start_location); } let comma_separated = separated_by_comma_until_right_brace(); @@ -52,7 +50,7 @@ impl<'a> Parser<'a> { visibility, generics, variants, - span: self.span_since(start_span), + location: self.location_since(start_location), } } @@ -62,7 +60,7 @@ impl<'a> Parser<'a> { // Loop until we find an identifier, skipping anything that's not one loop { - let doc_comments_start_span = self.current_token_span; + let doc_comments_start_location = self.current_token_location; doc_comments = self.parse_outer_doc_comments(); if let Some(ident) = self.eat_ident() { @@ -73,7 +71,7 @@ impl<'a> Parser<'a> { if !doc_comments.is_empty() { self.push_error( ParserErrorReason::DocCommentDoesNotDocumentAnything, - self.span_since(doc_comments_start_span), + self.location_since(doc_comments_start_location), ); } @@ -106,7 +104,7 @@ impl<'a> Parser<'a> { attributes: Vec, visibility: ItemVisibility, generics: UnresolvedGenerics, - start_span: Span, + start_location: Location, ) -> NoirEnumeration { NoirEnumeration { name, @@ -114,7 +112,7 @@ impl<'a> Parser<'a> { visibility, generics, variants: Vec::new(), - span: self.span_since(start_span), + location: self.location_since(start_location), } } } @@ -123,17 +121,15 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{IntegerBitSize, NoirEnumeration, Signedness, UnresolvedGeneric, UnresolvedTypeData}, + parse_program_with_dummy_file, parser::{ - parser::{ - parse_program, - tests::{expect_no_errors, get_source_with_error_span}, - }, ItemKind, ParserErrorReason, + parser::tests::{expect_no_errors, get_source_with_error_span}, }, }; fn parse_enum_no_errors(src: &str) -> NoirEnumeration { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -205,7 +201,7 @@ mod tests { #[test] fn parse_empty_enum_with_doc_comments() { let src = "/// Hello\nenum Foo {}"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -219,8 +215,8 @@ mod tests { #[test] fn parse_unclosed_enum() { let src = "enum Foo {"; - let (module, errors) = parse_program(src); - assert_eq!(errors.len(), 2); + let (module, errors) = parse_program_with_dummy_file(src); + assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Enum(noir_enum) = &item.kind else { @@ -236,7 +232,7 @@ mod tests { ^^^^^^^ "; let (src, _) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let reason = errors[0].reason().unwrap(); assert!(matches!(reason, ParserErrorReason::NoFunctionAttributesAllowedOnType)); } @@ -248,7 +244,7 @@ mod tests { ^^ "; let (src, _) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -258,7 +254,8 @@ mod tests { assert_eq!("Foo", noir_enum.name.to_string()); assert_eq!(noir_enum.variants.len(), 1); - let error = &errors[1]; + assert_eq!(errors.len(), 1); + let error = &errors[0]; assert_eq!(error.to_string(), "Expected an identifier but found '42'"); } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/expression.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/expression.rs index b2ddc200ef25..d0f335414da4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1,26 +1,26 @@ use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{ ArrayLiteral, BlockExpression, CallExpression, CastExpression, ConstrainExpression, ConstrainKind, ConstructorExpression, Expression, ExpressionKind, Ident, IfExpression, IndexExpression, Literal, MatchExpression, MemberAccessExpression, MethodCallExpression, - Statement, TypePath, UnaryOp, UnresolvedType, + Statement, TypePath, UnaryOp, UnresolvedType, UnsafeExpression, }, - parser::{labels::ParsingRuleLabel, parser::parse_many::separated_by_comma, ParserErrorReason}, + parser::{ParserErrorReason, labels::ParsingRuleLabel, parser::parse_many::separated_by_comma}, token::{Keyword, Token, TokenKind}, }; use super::{ + Parser, parse_many::{ separated_by_comma_until_right_brace, separated_by_comma_until_right_paren, without_separator, }, - Parser, }; -impl<'a> Parser<'a> { +impl Parser<'_> { pub(crate) fn parse_expression_or_error(&mut self) -> Expression { self.parse_expression_or_error_impl(true) // allow constructors } @@ -47,7 +47,10 @@ impl<'a> Parser<'a> { expr } else { self.push_expected_expression(); - Expression { kind: ExpressionKind::Error, span: self.span_at_previous_token_end() } + Expression { + kind: ExpressionKind::Error, + location: self.location_at_previous_token_end(), + } } } @@ -59,7 +62,7 @@ impl<'a> Parser<'a> { /// = UnaryOp Term /// | AtomOrUnaryRightExpression pub(super) fn parse_term(&mut self, allow_constructors: bool) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if let Some(operator) = self.parse_unary_op() { let Some(rhs) = self.parse_term(allow_constructors) else { @@ -67,8 +70,8 @@ impl<'a> Parser<'a> { return None; }; let kind = ExpressionKind::prefix(operator, rhs); - let span = self.span_since(start_span); - return Some(Expression { kind, span }); + let location = self.location_since(start_location); + return Some(Expression { kind, location }); } self.parse_atom_or_unary_right(allow_constructors) @@ -94,12 +97,12 @@ impl<'a> Parser<'a> { /// AtomOrUnaryRightExpression /// = Atom UnaryRightExpression* fn parse_atom_or_unary_right(&mut self, allow_constructors: bool) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let mut atom = self.parse_atom(allow_constructors)?; let mut parsed; loop { - (atom, parsed) = self.parse_unary_right(atom, start_span); + (atom, parsed) = self.parse_unary_right(atom, start_location); if parsed { continue; } else { @@ -115,37 +118,41 @@ impl<'a> Parser<'a> { /// | MemberAccessOrMethodCallExpression /// | CastExpression /// | IndexExpression - fn parse_unary_right(&mut self, mut atom: Expression, start_span: Span) -> (Expression, bool) { + fn parse_unary_right( + &mut self, + mut atom: Expression, + start_location: Location, + ) -> (Expression, bool) { let mut parsed; - (atom, parsed) = self.parse_call(atom, start_span); + (atom, parsed) = self.parse_call(atom, start_location); if parsed { return (atom, parsed); } - (atom, parsed) = self.parse_member_access_or_method_call(atom, start_span); + (atom, parsed) = self.parse_member_access_or_method_call(atom, start_location); if parsed { return (atom, parsed); } - (atom, parsed) = self.parse_cast(atom, start_span); + (atom, parsed) = self.parse_cast(atom, start_location); if parsed { return (atom, parsed); } - self.parse_index(atom, start_span) + self.parse_index(atom, start_location) } /// CallExpression = Atom CallArguments - fn parse_call(&mut self, atom: Expression, start_span: Span) -> (Expression, bool) { + fn parse_call(&mut self, atom: Expression, start_location: Location) -> (Expression, bool) { if let Some(call_arguments) = self.parse_call_arguments() { let kind = ExpressionKind::Call(Box::new(CallExpression { func: Box::new(atom), arguments: call_arguments.arguments, is_macro_call: call_arguments.is_macro_call, })); - let span = self.span_since(start_span); - let atom = Expression { kind, span }; + let location = self.location_since(start_location); + let atom = Expression { kind, location }; (atom, true) } else { (atom, false) @@ -162,7 +169,7 @@ impl<'a> Parser<'a> { fn parse_member_access_or_method_call( &mut self, atom: Expression, - start_span: Span, + start_location: Location, ) -> (Expression, bool) { if !self.eat_dot() { return (atom, false); @@ -187,8 +194,8 @@ impl<'a> Parser<'a> { })) }; - let span = self.span_since(start_span); - let atom = Expression { kind, span }; + let location = self.location_since(start_location); + let atom = Expression { kind, location }; (atom, true) } @@ -196,31 +203,31 @@ impl<'a> Parser<'a> { if let Some(ident) = self.eat_ident() { Some(ident) } else if let Some(int) = self.eat_int() { - Some(Ident::new(int.to_string(), self.previous_token_span)) + Some(Ident::new(int.to_string(), self.previous_token_location)) } else { self.push_error( ParserErrorReason::ExpectedFieldName(self.token.token().clone()), - self.current_token_span, + self.current_token_location, ); None } } /// CastExpression = Atom 'as' Type - fn parse_cast(&mut self, atom: Expression, start_span: Span) -> (Expression, bool) { + fn parse_cast(&mut self, atom: Expression, start_location: Location) -> (Expression, bool) { if !self.eat_keyword(Keyword::As) { return (atom, false); } let typ = self.parse_type_or_error(); let kind = ExpressionKind::Cast(Box::new(CastExpression { lhs: atom, r#type: typ })); - let span = self.span_since(start_span); - let atom = Expression { kind, span }; + let location = self.location_since(start_location); + let atom = Expression { kind, location }; (atom, true) } /// IndexExpression = Atom '[' Expression ']' - fn parse_index(&mut self, atom: Expression, start_span: Span) -> (Expression, bool) { + fn parse_index(&mut self, atom: Expression, start_location: Location) -> (Expression, bool) { if !self.eat_left_bracket() { return (atom, false); } @@ -228,8 +235,8 @@ impl<'a> Parser<'a> { let index = self.parse_expression_or_error(); self.eat_or_error(Token::RightBracket); let kind = ExpressionKind::Index(Box::new(IndexExpression { collection: atom, index })); - let span = self.span_since(start_span); - let atom = Expression { kind, span }; + let location = self.location_since(start_location); + let atom = Expression { kind, location }; (atom, true) } @@ -261,20 +268,14 @@ impl<'a> Parser<'a> { /// | InternedExpression /// | InternedStatementExpression fn parse_atom(&mut self, allow_constructors: bool) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let kind = self.parse_atom_kind(allow_constructors)?; - Some(Expression { kind, span: self.span_since(start_span) }) + Some(Expression { kind, location: self.location_since(start_location) }) } fn parse_atom_kind(&mut self, allow_constructors: bool) -> Option { - let span_before_doc_comments = self.current_token_span; - let doc_comments = self.parse_outer_doc_comments(); - if !doc_comments.is_empty() { - self.push_error( - ParserErrorReason::DocCommentDoesNotDocumentAnything, - span_before_doc_comments, - ); - } + // Like in Rust, we allow parsing doc comments on top of an expression but they always produce a warning. + self.warn_on_outer_doc_comments(); if let Some(kind) = self.parse_unsafe_expr() { return Some(kind); @@ -298,10 +299,10 @@ impl<'a> Parser<'a> { Token::InternedUnresolvedTypeData(..) | Token::QuotedType(..) ) && self.next_is(Token::LeftBrace) { - let span = self.current_token_span; + let location = self.current_token_location; let typ = self.parse_interned_type().or_else(|| self.parse_resolved_type()).unwrap(); self.eat_or_error(Token::LeftBrace); - let typ = UnresolvedType { typ, span }; + let typ = UnresolvedType { typ, location }; return Some(self.parse_constructor(typ)); } @@ -386,26 +387,32 @@ impl<'a> Parser<'a> { /// UnsafeExpression = 'unsafe' Block fn parse_unsafe_expr(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; + let comments_before_unsafe = self.current_token_comments.clone(); if !self.eat_keyword(Keyword::Unsafe) { return None; } - if self.current_token_comments.is_empty() { - if let Some(statement_comments) = &mut self.statement_comments { - if !statement_comments.trim().to_lowercase().starts_with("safety:") { - self.push_error(ParserErrorReason::MissingSafetyComment, start_span); - } + let comments: &str = if comments_before_unsafe.is_empty() { + if let Some(statement_comments) = &self.statement_comments { + statement_comments } else { - self.push_error(ParserErrorReason::MissingSafetyComment, start_span); + "" } - } else if !self.current_token_comments.trim().to_lowercase().starts_with("safety:") { - self.push_error(ParserErrorReason::MissingSafetyComment, start_span); + } else { + &comments_before_unsafe + }; + + if !comments.lines().any(|line| line.trim().to_lowercase().starts_with("safety:")) { + self.push_error(ParserErrorReason::MissingSafetyComment, start_location); } if let Some(block) = self.parse_block() { - Some(ExpressionKind::Unsafe(block, self.span_since(start_span))) + Some(ExpressionKind::Unsafe(UnsafeExpression { + block, + unsafe_keyword_location: start_location, + })) } else { Some(ExpressionKind::Error) } @@ -439,11 +446,7 @@ impl<'a> Parser<'a> { Self::parse_constructor_field, ); - ExpressionKind::Constructor(Box::new(ConstructorExpression { - typ, - fields, - struct_type: None, - })) + ExpressionKind::Constructor(Box::new(ConstructorExpression { typ, fields })) } fn parse_constructor_field(&mut self) -> Option<(Ident, Expression)> { @@ -471,26 +474,26 @@ impl<'a> Parser<'a> { let condition = self.parse_expression_except_constructor_or_error(); - let start_span = self.current_token_span; + let start_location = self.current_token_location; let Some(consequence) = self.parse_block() else { self.expected_token(Token::LeftBrace); - let span = self.span_at_previous_token_end(); + let location = self.location_at_previous_token_end(); return Some(ExpressionKind::If(Box::new(IfExpression { condition, - consequence: Expression { kind: ExpressionKind::Error, span }, + consequence: Expression { kind: ExpressionKind::Error, location }, alternative: None, }))); }; - let span = self.span_since(start_span); - let consequence = Expression { kind: ExpressionKind::Block(consequence), span }; + let location = self.location_since(start_location); + let consequence = Expression { kind: ExpressionKind::Block(consequence), location }; let alternative = if self.eat_keyword(Keyword::Else) { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if let Some(block) = self.parse_block() { - let span = self.span_since(start_span); - Some(Expression { kind: ExpressionKind::Block(block), span }) + let location = self.location_since(start_location); + Some(Expression { kind: ExpressionKind::Block(block), location }) } else if let Some(if_expr) = self.parse_if_expr() { - Some(Expression { kind: if_expr, span: self.span_since(start_span) }) + Some(Expression { kind: if_expr, location: self.location_since(start_location) }) } else { self.expected_token(Token::LeftBrace); None @@ -504,7 +507,6 @@ impl<'a> Parser<'a> { /// MatchExpression = 'match' ExpressionExceptConstructor '{' MatchRule* '}' pub(super) fn parse_match_expr(&mut self) -> Option { - let start_span = self.current_token_span; if !self.eat_keyword(Keyword::Match) { return None; } @@ -519,7 +521,6 @@ impl<'a> Parser<'a> { Self::parse_match_rule, ); - self.push_error(ParserErrorReason::ExperimentalFeature("Match expressions"), start_span); Some(ExpressionKind::Match(Box::new(MatchExpression { expression, rules }))) } @@ -528,11 +529,11 @@ impl<'a> Parser<'a> { let pattern = self.parse_expression()?; self.eat_or_error(Token::FatArrow); - let start_span = self.current_token_span; + let start_location = self.current_token_location; let branch = match self.parse_block() { Some(block) => { - let span = self.span_since(start_span); - let block = Expression::new(ExpressionKind::Block(block), span); + let location = self.location_since(start_location); + let block = Expression::new(ExpressionKind::Block(block), location); self.eat_comma(); // comma is optional if we have a block block } @@ -551,21 +552,21 @@ impl<'a> Parser<'a> { return None; } - let start_span = self.current_token_span; + let start_location = self.current_token_location; let Some(block) = self.parse_block() else { self.expected_token(Token::LeftBrace); return None; }; - Some(ExpressionKind::Comptime(block, self.span_since(start_span))) + Some(ExpressionKind::Comptime(block, self.location_since(start_location))) } /// UnquoteExpression /// = '$' identifier /// | '$' '(' Expression ')' fn parse_unquote_expr(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.eat(Token::DollarSign) { return None; @@ -574,25 +575,25 @@ impl<'a> Parser<'a> { if let Some(path) = self.parse_path() { let expr = Expression { kind: ExpressionKind::Variable(path), - span: self.span_since(start_span), + location: self.location_since(start_location), }; return Some(ExpressionKind::Unquote(Box::new(expr))); } - let span_at_left_paren = self.current_token_span; + let location_at_left_paren = self.current_token_location; if self.eat_left_paren() { let expr = self.parse_expression_or_error(); self.eat_or_error(Token::RightParen); let expr = Expression { kind: ExpressionKind::Parenthesized(Box::new(expr)), - span: self.span_since(span_at_left_paren), + location: self.location_since(location_at_left_paren), }; return Some(ExpressionKind::Unquote(Box::new(expr))); } self.push_error( ParserErrorReason::ExpectedIdentifierOrLeftParenAfterDollar, - self.current_token_span, + self.current_token_location, ); None @@ -600,9 +601,9 @@ impl<'a> Parser<'a> { /// TypePathExpression = PrimitiveType '::' identifier ( '::' GenericTypeArgs )? fn parse_type_path_expr(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let typ = self.parse_primitive_type()?; - let typ = UnresolvedType { typ, span: self.span_since(start_span) }; + let typ = UnresolvedType { typ, location: self.location_since(start_location) }; self.eat_or_error(Token::DoubleColon); @@ -610,7 +611,7 @@ impl<'a> Parser<'a> { ident } else { self.expected_identifier(); - Ident::new(String::new(), self.span_at_previous_token_end()) + Ident::new(String::new(), self.location_at_previous_token_end()) }; let turbofish = self.eat_double_colon().then(|| { @@ -718,7 +719,7 @@ impl<'a> Parser<'a> { } let comma_after_first_expr = self.eat_comma(); - let second_expr_span = self.current_token_span; + let second_expr_location = self.current_token_location; let mut exprs = self.parse_many( "expressions", @@ -727,7 +728,7 @@ impl<'a> Parser<'a> { ); if !exprs.is_empty() && !comma_after_first_expr { - self.expected_token_separating_items(Token::Comma, "expressions", second_expr_span); + self.expected_token_separating_items(Token::Comma, "expressions", second_expr_location); } exprs.insert(0, first_expr); @@ -791,7 +792,7 @@ impl<'a> Parser<'a> { /// | 'assert' Arguments /// | 'assert_eq' Arguments pub(super) fn parse_constrain_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let kind = self.parse_constrain_kind()?; Some(match kind { @@ -802,16 +803,23 @@ impl<'a> Parser<'a> { } let arguments = arguments.unwrap_or_default(); - ConstrainExpression { kind, arguments, span: self.span_since(start_span) } + ConstrainExpression { + kind, + arguments, + location: self.location_since(start_location), + } } ConstrainKind::Constrain => { - self.push_error(ParserErrorReason::ConstrainDeprecated, self.previous_token_span); + self.push_error( + ParserErrorReason::ConstrainDeprecated, + self.previous_token_location, + ); let expression = self.parse_expression_or_error(); ConstrainExpression { kind, arguments: vec![expression], - span: self.span_since(start_span), + location: self.location_since(start_location), } } }) @@ -846,7 +854,7 @@ impl<'a> Parser<'a> { Some(BlockExpression { statements }) } - fn parse_statement_in_block(&mut self) -> Option<(Statement, (Option, Span))> { + fn parse_statement_in_block(&mut self) -> Option<(Statement, (Option, Location))> { if let Some(statement) = self.parse_statement() { Some(statement) } else { @@ -857,13 +865,13 @@ impl<'a> Parser<'a> { fn check_statements_require_semicolon( &mut self, - statements: Vec<(Statement, (Option, Span))>, + statements: Vec<(Statement, (Option, Location))>, ) -> Vec { let last = statements.len().saturating_sub(1); let iter = statements.into_iter().enumerate(); - vecmap(iter, |(i, (statement, (semicolon, span)))| { + vecmap(iter, |(i, (statement, (semicolon, location)))| { statement - .add_semicolon(semicolon, span, i == last, &mut |error| self.errors.push(error)) + .add_semicolon(semicolon, location, i == last, &mut |error| self.errors.push(error)) }) } @@ -882,19 +890,20 @@ mod tests { StatementKind, UnaryOp, UnresolvedTypeData, }, parser::{ + Parser, ParserErrorReason, parser::tests::{ expect_no_errors, get_single_error, get_single_error_reason, get_source_with_error_span, }, - Parser, ParserErrorReason, }, + signed_field::SignedField, token::Token, }; fn parse_expression_no_errors(src: &str) -> Expression { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let expr = parser.parse_expression_or_error(); - assert_eq!(expr.span.end() as usize, src.len()); + assert_eq!(expr.location.span.end() as usize, src.len()); expect_no_errors(&parser.errors); expr } @@ -914,22 +923,20 @@ mod tests { fn parses_integer_literal() { let src = "42"; let expr = parse_expression_no_errors(src); - let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + let ExpressionKind::Literal(Literal::Integer(value)) = expr.kind else { panic!("Expected integer literal"); }; - assert_eq!(field, 42_u128.into()); - assert!(!negative); + assert_eq!(value, SignedField::positive(42_u128)); } #[test] fn parses_negative_integer_literal() { let src = "-42"; let expr = parse_expression_no_errors(src); - let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + let ExpressionKind::Literal(Literal::Integer(value)) = expr.kind else { panic!("Expected integer literal"); }; - assert_eq!(field, 42_u128.into()); - assert!(negative); + assert_eq!(value, SignedField::negative(42_u128)); } #[test] @@ -939,11 +946,10 @@ mod tests { let ExpressionKind::Parenthesized(expr) = expr.kind else { panic!("Expected parenthesized expression"); }; - let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + let ExpressionKind::Literal(Literal::Integer(value)) = expr.kind else { panic!("Expected integer literal"); }; - assert_eq!(field, 42_u128.into()); - assert!(!negative); + assert_eq!(value, SignedField::positive(42_u128)); } #[test] @@ -995,18 +1001,16 @@ mod tests { assert_eq!(exprs.len(), 2); let expr = exprs.remove(0); - let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + let ExpressionKind::Literal(Literal::Integer(value)) = expr.kind else { panic!("Expected integer literal"); }; - assert_eq!(field, 1_u128.into()); - assert!(!negative); + assert_eq!(value, SignedField::positive(1_u128)); let expr = exprs.remove(0); - let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + let ExpressionKind::Literal(Literal::Integer(value)) = expr.kind else { panic!("Expected integer literal"); }; - assert_eq!(field, 2_u128.into()); - assert!(!negative); + assert_eq!(value, SignedField::positive(2_u128)); } #[test] @@ -1023,11 +1027,10 @@ mod tests { panic!("Expected expression statement"); }; - let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + let ExpressionKind::Literal(Literal::Integer(value)) = expr.kind else { panic!("Expected integer literal"); }; - assert_eq!(field, 1_u128.into()); - assert!(!negative); + assert_eq!(value, SignedField::positive(1_u128)); } #[test] @@ -1056,9 +1059,9 @@ mod tests { 2 3 }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let expr = parser.parse_expression_or_error(); - assert_eq!(expr.span.end() as usize, src.len()); + assert_eq!(expr.location.span.end() as usize, src.len()); assert_eq!(parser.errors.len(), 2); assert!(matches!( parser.errors[0].reason(), @@ -1079,11 +1082,13 @@ mod tests { let src = " // Safety: test unsafe { 1 }"; - let expr = parse_expression_no_errors(src); - let ExpressionKind::Unsafe(block, _) = expr.kind else { + let mut parser = Parser::for_str_with_dummy_file(src); + let expr = parser.parse_expression_or_error(); + assert!(parser.errors.is_empty()); + let ExpressionKind::Unsafe(unsafe_expression) = expr.kind else { panic!("Expected unsafe expression"); }; - assert_eq!(block.statements.len(), 1); + assert_eq!(unsafe_expression.block.statements.len(), 1); } #[test] @@ -1092,12 +1097,12 @@ mod tests { /// Safety: test unsafe { 1 }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let expr = parser.parse_expression().unwrap(); - let ExpressionKind::Unsafe(block, _) = expr.kind else { + let ExpressionKind::Unsafe(unsafe_expression) = expr.kind else { panic!("Expected unsafe expression"); }; - assert_eq!(block.statements.len(), 1); + assert_eq!(unsafe_expression.block.statements.len(), 1); } #[test] @@ -1107,7 +1112,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); parser.parse_expression(); let error = get_single_error(&parser.errors, span); assert_eq!(error.to_string(), "Expected an expression but found end of input"); @@ -1120,7 +1125,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); parser.parse_expression(); let reason = get_single_error_reason(&parser.errors, span); let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { @@ -1173,9 +1178,9 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let expr = parser.parse_expression_or_error(); - assert_eq!(expr.span.end() as usize, src.len()); + assert_eq!(expr.location.span.end() as usize, src.len()); let reason = get_single_error_reason(&parser.errors, span); let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { @@ -1334,9 +1339,9 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let expr = parser.parse_expression_or_error(); - assert_eq!(expr.span.end() as usize, src.len()); + assert_eq!(expr.location.span.end() as usize, src.len()); let reason = get_single_error_reason(&parser.errors, span); let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { panic!("Expected a different error"); @@ -1355,7 +1360,7 @@ mod tests { #[test] fn parses_call_with_wrong_expression() { let src = "foo(]) "; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); parser.parse_expression_or_error(); assert!(!parser.errors.is_empty()); } @@ -1478,7 +1483,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let expr = parser.parse_expression_or_error(); let error = get_single_error(&parser.errors, span); @@ -1506,7 +1511,7 @@ mod tests { ^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let expr = parser.parse_expression_or_error(); let error = get_single_error(&parser.errors, span); @@ -1595,7 +1600,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); parser.parse_expression(); let error = get_single_error(&parser.errors, span); assert_eq!(error.to_string(), "Expected a type but found end of input"); @@ -1616,9 +1621,9 @@ mod tests { fn parses_operators() { for operator in BinaryOpKind::iter() { let src = format!("1 {operator} 2"); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let expr = parser.parse_expression_or_error(); - assert_eq!(expr.span.end() as usize, src.len()); + assert_eq!(expr.location.span.end() as usize, src.len()); assert!(parser.errors.is_empty(), "Expected no errors for {operator}"); let ExpressionKind::Infix(infix_expr) = expr.kind else { panic!("Expected infix for {operator}"); @@ -1637,14 +1642,22 @@ mod tests { let multiply_or_divide_or_modulo = "1 * 2 / 3 % 4"; let expected_multiply_or_divide_or_modulo = "(((1 * 2) / 3) % 4)"; - let add_or_subtract = format!("{multiply_or_divide_or_modulo} + {multiply_or_divide_or_modulo} - {multiply_or_divide_or_modulo}"); - let expected_add_or_subtract = format!("(({expected_multiply_or_divide_or_modulo} + {expected_multiply_or_divide_or_modulo}) - {expected_multiply_or_divide_or_modulo})"); + let add_or_subtract = format!( + "{multiply_or_divide_or_modulo} + {multiply_or_divide_or_modulo} - {multiply_or_divide_or_modulo}" + ); + let expected_add_or_subtract = format!( + "(({expected_multiply_or_divide_or_modulo} + {expected_multiply_or_divide_or_modulo}) - {expected_multiply_or_divide_or_modulo})" + ); let shift = format!("{add_or_subtract} << {add_or_subtract} >> {add_or_subtract}"); - let expected_shift = format!("(({expected_add_or_subtract} << {expected_add_or_subtract}) >> {expected_add_or_subtract})"); + let expected_shift = format!( + "(({expected_add_or_subtract} << {expected_add_or_subtract}) >> {expected_add_or_subtract})" + ); let less_or_greater = format!("{shift} < {shift} > {shift} <= {shift} >= {shift}"); - let expected_less_or_greater = format!("(((({expected_shift} < {expected_shift}) > {expected_shift}) <= {expected_shift}) >= {expected_shift})"); + let expected_less_or_greater = format!( + "(((({expected_shift} < {expected_shift}) > {expected_shift}) <= {expected_shift}) >= {expected_shift})" + ); let xor = format!("{less_or_greater} ^ {less_or_greater}"); let expected_xor = format!("({expected_less_or_greater} ^ {expected_less_or_greater})"); @@ -1810,7 +1823,7 @@ mod tests { ^^^^^^^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let expression = parser.parse_expression_or_error(); let ExpressionKind::Constrain(constrain) = expression.kind else { panic!("Expected constrain expression"); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs index 29e864200f3e..f10b790e63f6 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs @@ -13,28 +13,28 @@ use crate::{ }; use acvm::AcirField; -use noirc_errors::Span; +use noirc_errors::{Location, Span}; use super::parse_many::separated_by_comma_until_right_paren; use super::pattern::SelfPattern; -use super::{pattern::PatternOrSelf, Parser}; +use super::{Parser, pattern::PatternOrSelf}; pub(crate) struct FunctionDefinitionWithOptionalBody { pub(crate) name: Ident, pub(crate) generics: UnresolvedGenerics, pub(crate) parameters: Vec, pub(crate) body: Option, - pub(crate) span: Span, + pub(crate) location: Location, pub(crate) where_clause: Vec, pub(crate) return_type: FunctionReturnType, pub(crate) return_visibility: Visibility, } -impl<'a> Parser<'a> { +impl Parser<'_> { /// Function = 'fn' identifier Generics FunctionParameters ( '->' Visibility Type )? WhereClause ( Block | ';' ) pub(crate) fn parse_function( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, visibility: ItemVisibility, is_comptime: bool, is_unconstrained: bool, @@ -52,7 +52,7 @@ impl<'a> Parser<'a> { pub(crate) fn parse_function_definition( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, visibility: ItemVisibility, is_comptime: bool, is_unconstrained: bool, @@ -74,7 +74,7 @@ impl<'a> Parser<'a> { generics: func.generics, parameters: func.parameters, body: func.body.unwrap_or_else(empty_body), - span: func.span, + location: func.location, where_clause: func.where_clause, return_type: func.return_type, return_visibility: func.return_visibility, @@ -88,7 +88,7 @@ impl<'a> Parser<'a> { ) -> FunctionDefinitionWithOptionalBody { let Some(name) = self.eat_ident() else { self.expected_identifier(); - return empty_function(self.previous_token_span); + return empty_function(self.previous_token_location); }; let generics = self.parse_generics(); @@ -99,7 +99,7 @@ impl<'a> Parser<'a> { None => { self.push_error( ParserErrorReason::MissingParametersForFunctionDefinition, - name.span(), + name.location(), ); Vec::new() } @@ -109,15 +109,32 @@ impl<'a> Parser<'a> { let visibility = self.parse_visibility(); (FunctionReturnType::Ty(self.parse_type_or_error()), visibility) } else { - (FunctionReturnType::Default(self.span_at_previous_token_end()), Visibility::Private) + // This will return the span between `)` and `{` + // + // fn foo() { } + // ^^^ + let mut location = self.previous_token_location.merge(self.current_token_location); + + // Here we change it to this (if there's space) + // + // fn foo() { } + // ^ + if location.span.end() - location.span.start() >= 3 { + location = Location::new( + Span::from(location.span.start() + 1..location.span.end() - 1), + location.file, + ); + } + + (FunctionReturnType::Default(location), Visibility::Private) }; let where_clause = self.parse_where_clause(); - let body_start_span = self.current_token_span; + let body_start_location = self.current_token_location; let body = if self.eat_semicolons() { if !allow_optional_body { - self.push_error(ParserErrorReason::ExpectedFunctionBody, body_start_span); + self.push_error(ParserErrorReason::ExpectedFunctionBody, body_start_location); } None @@ -130,7 +147,7 @@ impl<'a> Parser<'a> { generics, parameters, body, - span: self.span_since(body_start_span), + location: self.location_since(body_start_location), where_clause, return_type, return_visibility, @@ -154,7 +171,7 @@ impl<'a> Parser<'a> { fn parse_function_parameter(&mut self, allow_self: bool) -> Option { loop { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let pattern_or_self = if allow_self { self.parse_pattern_or_self() @@ -175,49 +192,54 @@ impl<'a> Parser<'a> { }; return Some(match pattern_or_self { - PatternOrSelf::Pattern(pattern) => self.pattern_param(pattern, start_span), + PatternOrSelf::Pattern(pattern) => self.pattern_param(pattern, start_location), PatternOrSelf::SelfPattern(self_pattern) => self.self_pattern_param(self_pattern), }); } } - fn pattern_param(&mut self, pattern: Pattern, start_span: Span) -> Param { + fn pattern_param(&mut self, pattern: Pattern, start_location: Location) -> Param { let (visibility, typ) = if !self.eat_colon() { self.push_error( ParserErrorReason::MissingTypeForFunctionParameter, - Span::from(pattern.span().start()..self.current_token_span.end()), + pattern.location().merge(self.current_token_location), ); let visibility = Visibility::Private; - let typ = UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }; + let typ = + UnresolvedType { typ: UnresolvedTypeData::Error, location: Location::dummy() }; (visibility, typ) } else { - (self.parse_visibility(), self.parse_type_or_error()) + ( + self.parse_visibility(), + self.parse_type_or_error_with_recovery(&[Token::Comma, Token::RightParen]), + ) }; - Param { visibility, pattern, typ, span: self.span_since(start_span) } + Param { visibility, pattern, typ, location: self.location_since(start_location) } } fn self_pattern_param(&mut self, self_pattern: SelfPattern) -> Param { - let ident_span = self.previous_token_span; - let ident = Ident::new("self".to_string(), ident_span); - let path = Path::from_single("Self".to_owned(), ident_span); + let ident_location = self.previous_token_location; + let ident = Ident::new("self".to_string(), ident_location); + let path = Path::from_single("Self".to_owned(), ident_location); let no_args = GenericTypeArgs::default(); - let mut self_type = UnresolvedTypeData::Named(path, no_args, true).with_span(ident_span); + let mut self_type = + UnresolvedTypeData::Named(path, no_args, true).with_location(ident_location); let mut pattern = Pattern::Identifier(ident); if self_pattern.reference { - self_type = - UnresolvedTypeData::MutableReference(Box::new(self_type)).with_span(ident_span); + self_type = UnresolvedTypeData::MutableReference(Box::new(self_type)) + .with_location(ident_location); } else if self_pattern.mutable { - pattern = Pattern::Mutable(Box::new(pattern), ident_span, true); + pattern = Pattern::Mutable(Box::new(pattern), ident_location, true); } Param { visibility: Visibility::Private, pattern, typ: self_type, - span: self.span_since(ident_span), + location: self.location_since(ident_location), } } @@ -256,17 +278,20 @@ impl<'a> Parser<'a> { Visibility::Private } - fn validate_attributes(&mut self, attributes: Vec<(Attribute, Span)>) -> Attributes { + fn validate_attributes(&mut self, attributes: Vec<(Attribute, Location)>) -> Attributes { let mut function = None; let mut secondary = Vec::new(); - for (index, (attribute, span)) in attributes.into_iter().enumerate() { + for (index, (attribute, location)) in attributes.into_iter().enumerate() { match attribute { Attribute::Function(attr) => { if function.is_none() { function = Some((attr, index)); } else { - self.push_error(ParserErrorReason::MultipleFunctionAttributesFound, span); + self.push_error( + ParserErrorReason::MultipleFunctionAttributesFound, + location, + ); } } Attribute::Secondary(attr) => secondary.push(attr), @@ -277,15 +302,16 @@ impl<'a> Parser<'a> { } } -fn empty_function(span: Span) -> FunctionDefinitionWithOptionalBody { +fn empty_function(location: Location) -> FunctionDefinitionWithOptionalBody { + let span = Span::from(location.span.end()..location.span.end()); FunctionDefinitionWithOptionalBody { name: Ident::default(), generics: Vec::new(), parameters: Vec::new(), body: None, - span: Span::from(span.end()..span.end()), + location: Location::new(span, location.file), where_clause: Vec::new(), - return_type: FunctionReturnType::Default(Span::default()), + return_type: FunctionReturnType::Default(Location::dummy()), return_visibility: Visibility::Private, } } @@ -297,21 +323,22 @@ fn empty_body() -> BlockExpression { #[cfg(test)] mod tests { use crate::{ - ast::{ItemVisibility, NoirFunction, UnresolvedTypeData, Visibility}, + ast::{ + IntegerBitSize, ItemVisibility, NoirFunction, Signedness, UnresolvedTypeData, + Visibility, + }, + parse_program_with_dummy_file, parser::{ - parser::{ - parse_program, - tests::{ - expect_no_errors, get_single_error, get_single_error_reason, - get_source_with_error_span, - }, - }, ItemKind, ParserErrorReason, + parser::tests::{ + expect_no_errors, get_single_error, get_single_error_reason, + get_source_with_error_span, + }, }, }; fn parse_function_no_error(src: &str) -> NoirFunction { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -405,7 +432,7 @@ mod tests { #[test] fn parse_function_unclosed_parentheses() { let src = "fn foo(x: i32,"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -422,7 +449,7 @@ mod tests { ^^^^^^^^^^^^^^ "; let (src, span) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::MultipleFunctionAttributesFound)); } @@ -434,7 +461,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::ExpectedFunctionBody)); } @@ -446,7 +473,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let ItemKind::Function(noir_function) = &module.items[0].kind else { panic!("Expected function"); @@ -464,7 +491,7 @@ mod tests { ^^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let ItemKind::Function(noir_function) = &module.items[0].kind else { panic!("Expected function"); @@ -482,7 +509,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let ItemKind::Function(noir_function) = &module.items[0].kind else { panic!("Expected function"); @@ -509,8 +536,38 @@ mod tests { ^^^ "; let (src, span) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::MissingParametersForFunctionDefinition)); } + + #[test] + fn parse_function_with_keyword_before_type() { + let src = " + fn foo(x: mut i32, y: i64) {} + ^^^ + "; + let (src, span) = get_source_with_error_span(src); + let (mut module, errors) = parse_program_with_dummy_file(&src); + let error = get_single_error(&errors, span); + assert_eq!(error.to_string(), "Expected a type but found 'mut'"); + + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Function(noir_function) = item.kind else { + panic!("Expected function"); + }; + + let params = noir_function.parameters(); + assert_eq!(params.len(), 2); + + assert_eq!( + params[0].typ.typ, + UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::ThirtyTwo) + ); + assert_eq!( + params[1].typ.typ, + UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::SixtyFour) + ); + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/generics.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/generics.rs index f577a237615a..0bac5d5f34ba 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -3,13 +3,13 @@ use crate::{ GenericTypeArg, GenericTypeArgs, IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, }, - parser::{labels::ParsingRuleLabel, ParserErrorReason}, + parser::{ParserErrorReason, labels::ParsingRuleLabel}, token::{Keyword, Token, TokenKind}, }; -use super::{parse_many::separated_by_comma, Parser}; +use super::{Parser, parse_many::separated_by_comma}; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Generics = ( '<' GenericsList? '>' )? /// /// GenericsList = Generic ( ',' Generic )* ','? @@ -71,11 +71,11 @@ impl<'a> Parser<'a> { // If we didn't get a type after the colon, error and assume it's u32 self.push_error( ParserErrorReason::MissingTypeForNumericGeneric, - self.current_token_span, + self.current_token_location, ); let typ = UnresolvedType { typ: UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo), - span: self.span_at_previous_token_end(), + location: self.location_at_previous_token_end(), }; return Some(UnresolvedGeneric::Numeric { ident, typ }); } @@ -85,7 +85,7 @@ impl<'a> Parser<'a> { if matches!(signedness, Signedness::Signed) || matches!(bit_size, IntegerBitSize::SixtyFour) { - self.push_error(ParserErrorReason::ForbiddenNumericGenericType, typ.span); + self.push_error(ParserErrorReason::ForbiddenNumericGenericType, typ.location); } } @@ -97,7 +97,7 @@ impl<'a> Parser<'a> { let token = self.eat_kind(TokenKind::QuotedType)?; match token.into_token() { Token::QuotedType(id) => { - Some(UnresolvedGeneric::Resolved(id, self.previous_token_span)) + Some(UnresolvedGeneric::Resolved(id, self.previous_token_location)) } _ => unreachable!(), } @@ -167,22 +167,22 @@ mod tests { use crate::{ ast::{GenericTypeArgs, IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedTypeData}, parser::{ + Parser, ParserErrorReason, parser::tests::{ expect_no_errors, get_single_error_reason, get_source_with_error_span, }, - Parser, ParserErrorReason, }, }; fn parse_generics_no_errors(src: &str) -> Vec { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let generics = parser.parse_generics(); expect_no_errors(&parser.errors); generics } fn parse_generic_type_args_no_errors(src: &str) -> GenericTypeArgs { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let generics = parser.parse_generic_type_args(); expect_no_errors(&parser.errors); generics @@ -263,7 +263,7 @@ mod tests { ^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); parser.parse_generics(); let reason = get_single_error_reason(&parser.errors, span); assert!(matches!(reason, ParserErrorReason::ForbiddenNumericGenericType)); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/global.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/global.rs index 2cd8343fe31d..2edb3eeaa187 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/global.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/global.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{ @@ -11,11 +11,11 @@ use crate::{ use super::Parser; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Global = 'global' identifier OptionalTypeAnnotation '=' Expression ';' pub(crate) fn parse_global( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, comptime: bool, mutable: bool, ) -> LetStatement { @@ -31,9 +31,9 @@ impl<'a> Parser<'a> { pattern: ident_to_pattern(Ident::default(), mutable), r#type: UnresolvedType { typ: UnresolvedTypeData::Unspecified, - span: Span::default(), + location: Location::dummy(), }, - expression: Expression { kind: ExpressionKind::Error, span: Span::default() }, + expression: Expression { kind: ExpressionKind::Error, location: Location::dummy() }, attributes, comptime, is_global_let, @@ -47,8 +47,8 @@ impl<'a> Parser<'a> { let expression = if self.eat_assign() { self.parse_expression_or_error() } else { - self.push_error(ParserErrorReason::GlobalWithoutValue, pattern.span()); - Expression { kind: ExpressionKind::Error, span: Span::default() } + self.push_error(ParserErrorReason::GlobalWithoutValue, pattern.location()); + Expression { kind: ExpressionKind::Error, location: Location::dummy() } }; if !self.eat_semicolons() { @@ -61,8 +61,8 @@ impl<'a> Parser<'a> { fn ident_to_pattern(ident: Ident, mutable: bool) -> Pattern { if mutable { - let span = ident.span(); - Pattern::Mutable(Box::new(Pattern::Identifier(ident)), span, false) + let location = ident.location(); + Pattern::Mutable(Box::new(Pattern::Identifier(ident)), location, false) } else { Pattern::Identifier(ident) } @@ -77,20 +77,18 @@ mod tests { ExpressionKind, IntegerBitSize, ItemVisibility, LetStatement, Literal, Pattern, Signedness, UnresolvedTypeData, }, + parse_program_with_dummy_file, parser::{ - parser::{ - parse_program, - tests::{ - expect_no_errors, get_single_error, get_single_error_reason, - get_source_with_error_span, - }, - }, ItemKind, ParserErrorReason, + parser::tests::{ + expect_no_errors, get_single_error, get_single_error_reason, + get_source_with_error_span, + }, }, }; fn parse_global_no_errors(src: &str) -> (LetStatement, ItemVisibility) { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -158,7 +156,7 @@ mod tests { ^^^ "; let (src, span) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::GlobalWithoutValue)); } @@ -170,7 +168,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let error = get_single_error(&errors, span); assert_eq!(error.to_string(), "Expected a ';' but found end of input"); } @@ -188,13 +186,11 @@ mod tests { assert_eq!(let_statement.pattern.span().start(), 16); assert_eq!(let_statement.pattern.span().end(), 19); - let ExpressionKind::Literal(Literal::Integer(abs_value, is_negative)) = - let_statement.expression.kind - else { + let ExpressionKind::Literal(Literal::Integer(value)) = let_statement.expression.kind else { panic!("Expected integer literal expression, got {:?}", let_statement.expression.kind); }; - assert!(is_negative); - assert_eq!(abs_value, FieldElement::from(17u128)); + assert!(value.is_negative); + assert_eq!(value.field, FieldElement::from(17u128)); } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/impls.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/impls.rs index 4b5054984d4f..3fdfbc7d28ad 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{ @@ -6,32 +6,32 @@ use crate::{ TraitImplItem, TraitImplItemKind, TypeImpl, UnresolvedGeneric, UnresolvedType, UnresolvedTypeData, }, - parser::{labels::ParsingRuleLabel, ParserErrorReason}, + parser::{ParserErrorReason, labels::ParsingRuleLabel}, token::{Keyword, Token}, }; -use super::{parse_many::without_separator, Parser}; +use super::{Parser, parse_many::without_separator}; pub(crate) enum Impl { Impl(TypeImpl), TraitImpl(NoirTraitImpl), } -impl<'a> Parser<'a> { +impl Parser<'_> { /// Impl /// = TypeImpl /// | TraitImpl pub(crate) fn parse_impl(&mut self) -> Impl { let generics = self.parse_generics(); - let type_span_start = self.current_token_span; + let type_location_start = self.current_token_location; let object_type = self.parse_type_or_error(); - let type_span = self.span_since(type_span_start); + let type_location = self.location_since(type_location_start); if self.eat_keyword(Keyword::For) { Impl::TraitImpl(self.parse_trait_impl(generics, object_type)) } else { - Impl::Impl(self.parse_type_impl(object_type, type_span, generics)) + Impl::Impl(self.parse_type_impl(object_type, type_location, generics)) } } @@ -39,18 +39,18 @@ impl<'a> Parser<'a> { fn parse_type_impl( &mut self, object_type: UnresolvedType, - type_span: Span, + type_location: Location, generics: Vec, ) -> TypeImpl { let where_clause = self.parse_where_clause(); let methods = self.parse_type_impl_body(); - TypeImpl { object_type, type_span, generics, where_clause, methods } + TypeImpl { object_type, type_location, generics, where_clause, methods } } /// TypeImplBody = '{' TypeImplItem* '}' /// /// TypeImplItem = OuterDocComments Attributes Modifiers Function - fn parse_type_impl_body(&mut self) -> Vec<(Documented, Span)> { + fn parse_type_impl_body(&mut self) -> Vec<(Documented, Location)> { if !self.eat_left_brace() { self.expected_token(Token::LeftBrace); return Vec::new(); @@ -63,10 +63,10 @@ impl<'a> Parser<'a> { ) } - fn parse_type_impl_method(&mut self) -> Option<(Documented, Span)> { + fn parse_type_impl_method(&mut self) -> Option<(Documented, Location)> { self.parse_item_in_list(ParsingRuleLabel::Function, |parser| { let doc_comments = parser.parse_outer_doc_comments(); - let start_span = parser.current_token_span; + let start_location = parser.current_token_location; let attributes = parser.parse_attributes(); let modifiers = parser.parse_modifiers( false, // allow mutable @@ -80,7 +80,7 @@ impl<'a> Parser<'a> { modifiers.unconstrained.is_some(), true, // allow_self ); - Some((Documented::new(method, doc_comments), parser.span_since(start_span))) + Some((Documented::new(method, doc_comments), parser.location_since(start_location))) } else { parser.modifiers_not_followed_by_an_item(modifiers); None @@ -118,11 +118,11 @@ impl<'a> Parser<'a> { fn parse_trait_impl_item(&mut self) -> Option> { self.parse_item_in_list(ParsingRuleLabel::TraitImplItem, |parser| { - let start_span = parser.current_token_span; + let start_location = parser.current_token_location; let doc_comments = parser.parse_outer_doc_comments(); if let Some(kind) = parser.parse_trait_impl_item_kind() { - let item = TraitImplItem { kind, span: parser.span_since(start_span) }; + let item = TraitImplItem { kind, location: parser.location_since(start_location) }; Some(Documented::new(item, doc_comments)) } else { None @@ -157,14 +157,17 @@ impl<'a> Parser<'a> { self.eat_semicolons(); return Some(TraitImplItemKind::Type { name: Ident::default(), - alias: UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }, + alias: UnresolvedType { + typ: UnresolvedTypeData::Error, + location: Location::dummy(), + }, }); }; let alias = if self.eat_assign() { self.parse_type_or_error() } else { - UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() } + UnresolvedType { typ: UnresolvedTypeData::Error, location: Location::dummy() } }; self.eat_semicolons(); @@ -192,7 +195,7 @@ impl<'a> Parser<'a> { self.parse_expression_or_error() } else { self.expected_token(Token::Assign); - Expression { kind: ExpressionKind::Error, span: Span::default() } + Expression { kind: ExpressionKind::Error, location: Location::dummy() } }; self.eat_semicolons(); @@ -210,7 +213,7 @@ impl<'a> Parser<'a> { if modifiers.visibility != ItemVisibility::Private { self.push_error( ParserErrorReason::TraitImplVisibilityIgnored, - modifiers.visibility_span, + modifiers.visibility_location, ); } @@ -236,17 +239,15 @@ mod tests { ast::{ ItemVisibility, NoirTraitImpl, Pattern, TraitImplItemKind, TypeImpl, UnresolvedTypeData, }, + parse_program_with_dummy_file, parser::{ - parser::{ - parse_program, - tests::{expect_no_errors, get_single_error, get_source_with_error_span}, - }, ItemKind, + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, }, }; fn parse_type_impl_no_errors(src: &str) -> TypeImpl { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -257,7 +258,7 @@ mod tests { } fn parse_trait_impl_no_errors(src: &str) -> NoirTraitImpl { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -402,7 +403,7 @@ mod tests { #[test] fn parse_empty_impl_missing_right_brace() { let src = "impl Foo {"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -415,7 +416,7 @@ mod tests { #[test] fn parse_empty_impl_incorrect_body() { let src = "impl Foo { hello fn foo() {} }"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -538,7 +539,7 @@ mod tests { ^^^^^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -558,7 +559,7 @@ mod tests { ^^^^^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let item = &module.items[0]; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/infix.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/infix.rs index f006923b8a25..c900228cc866 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/infix.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/infix.rs @@ -1,4 +1,4 @@ -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Located, Location}; use crate::{ ast::{BinaryOpKind, Expression, ExpressionKind, InfixExpression}, @@ -157,22 +157,22 @@ impl<'a> Parser<'a> { Next: FnMut(&mut Parser<'a>, bool) -> Option, Op: FnMut(&mut Parser<'a>) -> Option, { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let mut lhs = next(self, allow_constructors)?; loop { - let operator_start_span = self.current_token_span; + let operator_start_location = self.current_token_location; let Some(operator) = op(self) else { break; }; - let operator = Spanned::from(operator_start_span, operator); + let operator = Located::from(operator_start_location, operator); let Some(rhs) = next(self, allow_constructors) else { self.push_expected_expression(); break; }; - lhs = self.new_infix_expression(lhs, operator, rhs, start_span); + lhs = self.new_infix_expression(lhs, operator, rhs, start_location); } Some(lhs) @@ -181,13 +181,13 @@ impl<'a> Parser<'a> { fn new_infix_expression( &self, lhs: Expression, - operator: Spanned, + operator: Located, rhs: Expression, - start_span: Span, + start_location: Location, ) -> Expression { let infix_expr = InfixExpression { lhs, operator, rhs }; let kind = ExpressionKind::Infix(Box::new(infix_expr)); - let span = self.span_since(start_span); - Expression { kind, span } + let location = self.location_since(start_location); + Expression { kind, location } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item.rs index 6fa67ff08533..121a1e749f2a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item.rs @@ -1,11 +1,11 @@ use iter_extended::vecmap; use crate::{ - parser::{labels::ParsingRuleLabel, Item, ItemKind, ParserErrorReason}, + parser::{Item, ItemKind, ParserErrorReason, labels::ParsingRuleLabel}, token::{Keyword, Token}, }; -use super::{impls::Impl, parse_many::without_separator, Parser}; +use super::{Parser, impls::Impl, parse_many::without_separator}; impl<'a> Parser<'a> { pub(crate) fn parse_top_level_items(&mut self) -> Vec { @@ -89,16 +89,16 @@ impl<'a> Parser<'a> { /// Item = OuterDocComments ItemKind fn parse_item(&mut self) -> Vec { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let doc_comments = self.parse_outer_doc_comments(); let kinds = self.parse_item_kind(); - let span = self.span_since(start_span); + let location = self.location_since(start_location); if kinds.is_empty() && !doc_comments.is_empty() { - self.push_error(ParserErrorReason::DocCommentDoesNotDocumentAnything, start_span); + self.push_error(ParserErrorReason::DocCommentDoesNotDocumentAnything, start_location); } - vecmap(kinds, |kind| Item { kind, span, doc_comments: doc_comments.clone() }) + vecmap(kinds, |kind| Item { kind, location, doc_comments: doc_comments.clone() }) } /// This method returns one 'ItemKind' in the majority of cases. @@ -123,7 +123,7 @@ impl<'a> Parser<'a> { return vec![ItemKind::InnerAttribute(kind)]; } - let start_span = self.current_token_span; + let start_location = self.current_token_location; let attributes = self.parse_attributes(); let modifiers = self.parse_modifiers( @@ -149,7 +149,7 @@ impl<'a> Parser<'a> { return vec![ItemKind::Struct(self.parse_struct( attributes, modifiers.visibility, - start_span, + start_location, ))]; } @@ -159,7 +159,7 @@ impl<'a> Parser<'a> { return vec![ItemKind::Enum(self.parse_enum( attributes, modifiers.visibility, - start_span, + start_location, ))]; } @@ -176,7 +176,7 @@ impl<'a> Parser<'a> { self.comptime_mutable_and_unconstrained_not_applicable(modifiers); let (noir_trait, noir_impl) = - self.parse_trait(attributes, modifiers.visibility, start_span); + self.parse_trait(attributes, modifiers.visibility, start_location); let mut output = vec![ItemKind::Trait(noir_trait)]; if let Some(noir_impl) = noir_impl { output.push(ItemKind::TraitImpl(noir_impl)); @@ -202,7 +202,7 @@ impl<'a> Parser<'a> { self.comptime_mutable_and_unconstrained_not_applicable(modifiers); return vec![ItemKind::TypeAlias( - self.parse_type_alias(modifiers.visibility, start_span), + self.parse_type_alias(modifiers.visibility, start_location), )]; } @@ -235,7 +235,7 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use crate::{ - parse_program, + parse_program_with_dummy_file, parser::parser::tests::{get_single_error, get_source_with_error_span}, }; @@ -246,7 +246,7 @@ mod tests { ^^^^^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 2); let error = get_single_error(&errors, span); assert_eq!(error.to_string(), "Expected an item but found 'hello'"); @@ -259,7 +259,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let error = get_single_error(&errors, span); assert_eq!(error.to_string(), "Expected a '}' but found end of input"); @@ -273,7 +273,7 @@ mod tests { ^^^^^^^^^^^^^^^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let error = get_single_error(&errors, span); assert!(error.to_string().contains("This doc comment doesn't document anything")); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item_visibility.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item_visibility.rs index 5aea5f6a45f8..91ace6a62ce3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item_visibility.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item_visibility.rs @@ -5,7 +5,7 @@ use crate::{ use super::Parser; -impl<'a> Parser<'a> { +impl Parser<'_> { /// ItemVisibility /// = 'pub' // ItemVisibility::Public /// | 'pub' '(' 'crate' ')' // ItemVisibility::PublicCrate @@ -39,15 +39,15 @@ mod tests { use crate::{ ast::ItemVisibility, parser::{ - parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, Parser, + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, }, }; #[test] fn parses_private_visibility() { let src = "("; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let visibility = parser.parse_item_visibility(); expect_no_errors(&parser.errors); assert_eq!(visibility, ItemVisibility::Private); @@ -56,7 +56,7 @@ mod tests { #[test] fn parses_public_visibility() { let src = "pub"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let visibility = parser.parse_item_visibility(); expect_no_errors(&parser.errors); assert_eq!(visibility, ItemVisibility::Public); @@ -69,7 +69,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let visibility = parser.parse_item_visibility(); assert_eq!(visibility, ItemVisibility::Public); let error = get_single_error(&parser.errors, span); @@ -83,7 +83,7 @@ mod tests { ^^^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let visibility = parser.parse_item_visibility(); assert_eq!(visibility, ItemVisibility::Public); let error = get_single_error(&parser.errors, span); @@ -96,7 +96,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let visibility = parser.parse_item_visibility(); assert_eq!(visibility, ItemVisibility::PublicCrate); let error = get_single_error(&parser.errors, span); @@ -106,7 +106,7 @@ mod tests { #[test] fn parses_public_crate_visibility() { let src = "pub(crate)"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let visibility = parser.parse_item_visibility(); expect_no_errors(&parser.errors); assert_eq!(visibility, ItemVisibility::PublicCrate); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/lambda.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/lambda.rs index a6eeb4286218..25f803c8e1de 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/lambda.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/lambda.rs @@ -4,9 +4,9 @@ use crate::{ token::Token, }; -use super::{parse_many::separated_by_comma, Parser}; +use super::{Parser, parse_many::separated_by_comma}; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Lambda = '|' LambdaParameters? '|' ( '->' Type )? Expression /// /// LambdaParameters = LambdaParameter ( ',' LambdaParameter )? ','? diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/modifiers.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/modifiers.rs index a668d3bae6a7..896a27c04169 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/modifiers.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/modifiers.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ast::ItemVisibility, token::Keyword}; @@ -7,31 +7,31 @@ use super::Parser; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub(crate) struct Modifiers { pub(crate) visibility: ItemVisibility, - pub(crate) visibility_span: Span, - pub(crate) unconstrained: Option, - pub(crate) comptime: Option, - pub(crate) mutable: Option, + pub(crate) visibility_location: Location, + pub(crate) unconstrained: Option, + pub(crate) comptime: Option, + pub(crate) mutable: Option, } -impl<'a> Parser<'a> { +impl Parser<'_> { /// Modifiers = ItemVisibility 'unconstrained'? 'comptime'? 'mut'? /// /// NOTE: we also allow `unconstrained` before the visibility for backwards compatibility. /// The formatter will put it after the visibility. pub(crate) fn parse_modifiers(&mut self, allow_mutable: bool) -> Modifiers { let unconstrained = if self.eat_keyword(Keyword::Unconstrained) { - Some(self.previous_token_span) + Some(self.previous_token_location) } else { None }; - let start_span = self.current_token_span; + let start_location = self.current_token_location; let visibility = self.parse_item_visibility(); - let visibility_span = self.span_since(start_span); + let visibility_location = self.location_since(start_location); let unconstrained = if unconstrained.is_none() { if self.eat_keyword(Keyword::Unconstrained) { - Some(self.previous_token_span) + Some(self.previous_token_location) } else { None } @@ -39,14 +39,17 @@ impl<'a> Parser<'a> { unconstrained }; - let comptime = - if self.eat_keyword(Keyword::Comptime) { Some(self.previous_token_span) } else { None }; + let comptime = if self.eat_keyword(Keyword::Comptime) { + Some(self.previous_token_location) + } else { + None + }; let mutable = if allow_mutable && self.eat_keyword(Keyword::Mut) { - Some(self.previous_token_span) + Some(self.previous_token_location) } else { None }; - Modifiers { visibility, visibility_span, unconstrained, comptime, mutable } + Modifiers { visibility, visibility_location, unconstrained, comptime, mutable } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/module.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/module.rs index 1bc3d7b5bebb..cae0a328d756 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/module.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/module.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{Ident, ItemVisibility, ModuleDeclaration}, @@ -8,12 +8,12 @@ use crate::{ use super::Parser; -impl<'a> Parser<'a> { +impl Parser<'_> { /// ModOrContract /// = ( 'mod' | 'contract' ) identifier ( '{' Module '}' | ';' ) pub(super) fn parse_mod_or_contract( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, is_contract: bool, visibility: ItemVisibility, ) -> ItemKind { @@ -58,16 +58,16 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { - use crate::parser::{ - parser::{parse_program, tests::expect_no_errors}, - ItemKind, + use crate::{ + parse_program_with_dummy_file, + parser::{ItemKind, parser::tests::expect_no_errors}, }; #[test] fn parse_module_declaration() { // TODO: `contract foo;` is parsed correctly but we don't it's considered a module let src = "mod foo;"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -80,7 +80,7 @@ mod tests { #[test] fn parse_submodule() { let src = "mod foo { mod bar; }"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -95,7 +95,7 @@ mod tests { #[test] fn parse_contract() { let src = "contract foo {}"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/parse_many.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/parse_many.rs index be156eb16188..1c77aac7f180 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/parse_many.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/parse_many.rs @@ -42,11 +42,7 @@ impl<'a> Parser<'a> { F: FnMut(&mut Parser<'a>) -> Option, { let f = |x: &mut Parser<'a>| { - if let Some(result) = f(x) { - vec![result] - } else { - vec![] - } + if let Some(result) = f(x) { vec![result] } else { vec![] } }; self.parse_many_to_many_return_trailing_separator_if_any(items, separated_by, f) } @@ -70,7 +66,7 @@ impl<'a> Parser<'a> { } } - let start_span = self.current_token_span; + let start_location = self.current_token_location; let mut new_elements = f(self); if new_elements.is_empty() { if let Some(end) = &separated_by.until { @@ -81,7 +77,7 @@ impl<'a> Parser<'a> { if let Some(separator) = &separated_by.token { if !trailing_separator && !elements.is_empty() { - self.expected_token_separating_items(separator.clone(), items, start_span); + self.expected_token_separating_items(separator.clone(), items, start_location); } } 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 99aedc6df897..a58bd9e1bb18 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 @@ -3,13 +3,13 @@ use crate::parser::ParserErrorReason; use crate::token::{Keyword, Token}; -use noirc_errors::Span; +use noirc_errors::Location; use crate::{parser::labels::ParsingRuleLabel, token::TokenKind}; use super::Parser; -impl<'a> Parser<'a> { +impl Parser<'_> { #[cfg(test)] pub(crate) fn parse_path_or_error(&mut self) -> Path { if let Some(path) = self.parse_path() { @@ -17,11 +17,7 @@ impl<'a> Parser<'a> { } else { self.expected_label(ParsingRuleLabel::Path); - Path { - segments: Vec::new(), - kind: PathKind::Plain, - span: self.span_at_previous_token_end(), - } + Path::plain(Vec::new(), self.location_at_previous_token_end()) } } @@ -44,11 +40,7 @@ impl<'a> Parser<'a> { } else { self.expected_label(ParsingRuleLabel::Path); - Path { - segments: Vec::new(), - kind: PathKind::Plain, - span: self.span_at_previous_token_end(), - } + Path::plain(Vec::new(), self.location_at_previous_token_end()) } } @@ -65,7 +57,7 @@ impl<'a> Parser<'a> { allow_turbofish: bool, allow_trailing_double_colon: bool, ) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let kind = self.parse_path_kind(); @@ -73,7 +65,7 @@ impl<'a> Parser<'a> { kind, allow_turbofish, allow_trailing_double_colon, - start_span, + start_location, )?; if path.segments.is_empty() { if path.kind != PathKind::Plain { @@ -90,20 +82,16 @@ impl<'a> Parser<'a> { kind: PathKind, allow_turbofish: bool, allow_trailing_double_colon: bool, - start_span: Span, + start_location: Location, ) -> Option { let path = self.parse_path_after_kind( kind, allow_turbofish, allow_trailing_double_colon, - start_span, + start_location, ); - if path.segments.is_empty() && path.kind == PathKind::Plain { - None - } else { - Some(path) - } + if path.segments.is_empty() && path.kind == PathKind::Plain { None } else { Some(path) } } /// Parses a path assuming the path's kind (plain, `crate::`, `super::`, etc.) @@ -114,14 +102,14 @@ impl<'a> Parser<'a> { kind: PathKind, allow_turbofish: bool, allow_trailing_double_colon: bool, - start_span: Span, + start_location: Location, ) -> Path { let mut segments = Vec::new(); if self.token.kind() == TokenKind::Ident { loop { let ident = self.eat_ident().unwrap(); - let span = ident.span(); + let location = ident.location(); let generics = if allow_turbofish && self.at(Token::DoubleColon) @@ -133,7 +121,11 @@ impl<'a> Parser<'a> { None }; - segments.push(PathSegment { ident, generics, span }); + segments.push(PathSegment { + ident, + generics, + location: self.location_since(location), + }); if self.at(Token::DoubleColon) && matches!(self.next_token.token(), Token::Ident(..)) @@ -151,7 +143,8 @@ impl<'a> Parser<'a> { } } - Path { segments, kind, span: self.span_since(start_span) } + let location = self.location_since(start_location); + Path { segments, kind, kind_location: start_location, location } } /// PathGenerics = GenericTypeArgs @@ -165,7 +158,7 @@ impl<'a> Parser<'a> { let generics = self.parse_generic_type_args(); for (name, _typ) in &generics.named_args { - self.push_error(on_named_arg_error.clone(), name.span()); + self.push_error(on_named_arg_error.clone(), name.location()); } Some(generics.ordered_args) @@ -208,7 +201,7 @@ impl<'a> Parser<'a> { ident } else { self.expected_identifier(); - Ident::new(String::new(), self.span_at_previous_token_end()) + Ident::new(String::new(), self.location_at_previous_token_end()) }; Some(AsTraitPath { typ, trait_path, trait_generics, impl_item }) @@ -221,13 +214,13 @@ mod tests { use crate::{ ast::{Path, PathKind}, parser::{ - parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, Parser, + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, }, }; fn parse_path_no_errors(src: &str) -> Path { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let path = parser.parse_path_or_error(); expect_no_errors(&parser.errors); path @@ -294,9 +287,9 @@ mod tests { #[test] fn parses_plain_one_segment_with_trailing_colons() { let src = "foo::"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let path = parser.parse_path_or_error(); - assert_eq!(path.span.end() as usize, src.len()); + assert_eq!(path.location.span.end() as usize, src.len()); assert_eq!(parser.errors.len(), 1); assert_eq!(path.kind, PathKind::Plain); assert_eq!(path.segments.len(), 1); @@ -322,9 +315,9 @@ mod tests { #[test] fn parses_path_stops_before_trailing_double_colon() { let src = "foo::bar::"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let path = parser.parse_path_or_error(); - assert_eq!(path.span.end() as usize, src.len()); + assert_eq!(path.location.span.end() as usize, src.len()); assert_eq!(parser.errors.len(), 1); assert_eq!(path.to_string(), "foo::bar"); } @@ -332,9 +325,9 @@ mod tests { #[test] fn parses_path_with_turbofish_stops_before_trailing_double_colon() { let src = "foo::bar::<1>::"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let path = parser.parse_path_or_error(); - assert_eq!(path.span.end() as usize, src.len()); + assert_eq!(path.location.span.end() as usize, src.len()); assert_eq!(parser.errors.len(), 1); assert_eq!(path.to_string(), "foo::bar::<1>"); } @@ -346,7 +339,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let path = parser.parse_path(); assert!(path.is_none()); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/pattern.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/pattern.rs index 50779e9ccffa..61fb1572c172 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -1,14 +1,14 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{Ident, Path, Pattern}, - parser::{labels::ParsingRuleLabel, ParserErrorReason}, + parser::{ParserErrorReason, labels::ParsingRuleLabel}, token::{Keyword, Token, TokenKind}, }; use super::{ - parse_many::{separated_by_comma_until_right_brace, separated_by_comma_until_right_paren}, Parser, + parse_many::{separated_by_comma_until_right_brace, separated_by_comma_until_right_paren}, }; pub(crate) enum PatternOrSelf { @@ -22,29 +22,29 @@ pub(crate) struct SelfPattern { pub(crate) mutable: bool, } -impl<'a> Parser<'a> { +impl Parser<'_> { pub(crate) fn parse_pattern_or_error(&mut self) -> Pattern { if let Some(pattern) = self.parse_pattern() { return pattern; } self.expected_label(ParsingRuleLabel::Pattern); - Pattern::Identifier(Ident::new(String::new(), self.span_at_previous_token_end())) + Pattern::Identifier(Ident::new(String::new(), self.location_at_previous_token_end())) } /// Pattern /// = 'mut' PatternNoMut pub(crate) fn parse_pattern(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let mutable = self.eat_keyword(Keyword::Mut); - self.parse_pattern_after_modifiers(mutable, start_span) + self.parse_pattern_after_modifiers(mutable, start_location) } /// PatternOrSelf /// = Pattern /// | SelfPattern pub(crate) fn parse_pattern_or_self(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.next_is_colon() && self.eat_self() { return Some(PatternOrSelf::SelfPattern(SelfPattern { @@ -61,7 +61,7 @@ impl<'a> Parser<'a> { })); } else { return Some(PatternOrSelf::Pattern( - self.parse_pattern_after_modifiers(true, start_span)?, + self.parse_pattern_after_modifiers(true, start_location)?, )); } } @@ -77,15 +77,15 @@ impl<'a> Parser<'a> { } else { self.push_error( ParserErrorReason::RefMutCanOnlyBeUsedWithSelf, - self.current_token_span, + self.current_token_location, ); return Some(PatternOrSelf::Pattern( - self.parse_pattern_after_modifiers(true, start_span)?, + self.parse_pattern_after_modifiers(true, start_location)?, )); } } - Some(PatternOrSelf::Pattern(self.parse_pattern_after_modifiers(false, start_span)?)) + Some(PatternOrSelf::Pattern(self.parse_pattern_after_modifiers(false, start_location)?)) } fn next_is_colon(&self) -> bool { @@ -95,13 +95,13 @@ impl<'a> Parser<'a> { pub(crate) fn parse_pattern_after_modifiers( &mut self, mutable: bool, - start_span: Span, + start_location: Location, ) -> Option { let pattern = self.parse_pattern_no_mut()?; Some(if mutable { Pattern::Mutable( Box::new(pattern), - self.span_since(start_span), + self.location_since(start_location), false, // is synthesized ) } else { @@ -117,7 +117,7 @@ impl<'a> Parser<'a> { /// /// IdentifierPattern = identifier fn parse_pattern_no_mut(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if let Some(pattern) = self.parse_interned_pattern() { return Some(pattern); @@ -131,18 +131,18 @@ impl<'a> Parser<'a> { if self.at_built_in_type() { self.push_error( ParserErrorReason::ExpectedPatternButFoundType(self.token.token().clone()), - self.current_token_span, + self.current_token_location, ); } return None; }; if self.eat_left_brace() { - return Some(self.parse_struct_pattern(path, start_span)); + return Some(self.parse_struct_pattern(path, start_location)); } if !path.is_ident() { - self.push_error(ParserErrorReason::InvalidPattern, path.span); + self.push_error(ParserErrorReason::InvalidPattern, path.location); let ident = path.segments.pop().unwrap().ident; return Some(Pattern::Identifier(ident)); @@ -158,7 +158,7 @@ impl<'a> Parser<'a> { match token.into_token() { Token::InternedPattern(pattern) => { - Some(Pattern::Interned(pattern, self.previous_token_span)) + Some(Pattern::Interned(pattern, self.previous_token_location)) } _ => unreachable!(), } @@ -168,7 +168,7 @@ impl<'a> Parser<'a> { /// /// PatternList = Pattern ( ',' Pattern )* ','? fn parse_tuple_pattern(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.eat_left_paren() { return None; @@ -180,7 +180,7 @@ impl<'a> Parser<'a> { Self::parse_tuple_pattern_element, ); - Some(Pattern::Tuple(patterns, self.span_since(start_span))) + Some(Pattern::Tuple(patterns, self.location_since(start_location))) } fn parse_tuple_pattern_element(&mut self) -> Option { @@ -197,14 +197,14 @@ impl<'a> Parser<'a> { /// StructPatternFields = StructPatternField ( ',' StructPatternField )? ','? /// /// StructPatternField = identifier ( ':' Pattern )? - fn parse_struct_pattern(&mut self, path: Path, start_span: Span) -> Pattern { + fn parse_struct_pattern(&mut self, path: Path, start_location: Location) -> Pattern { let fields = self.parse_many( "struct fields", separated_by_comma_until_right_brace(), Self::parse_struct_pattern_field, ); - Pattern::Struct(path, fields, self.span_since(start_span)) + Pattern::Struct(path, fields, self.location_since(start_location)) } fn parse_struct_pattern_field(&mut self) -> Option<(Ident, Pattern)> { @@ -254,17 +254,17 @@ mod tests { use crate::{ ast::Pattern, parser::{ + Parser, ParserErrorReason, parser::tests::{ expect_no_errors, get_single_error, get_single_error_reason, get_source_with_error_span, }, - Parser, ParserErrorReason, }, token::{Keyword, Token}, }; fn parse_pattern_no_errors(src: &str) -> Pattern { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let pattern = parser.parse_pattern_or_error(); expect_no_errors(&parser.errors); pattern @@ -307,7 +307,7 @@ mod tests { #[test] fn parses_unclosed_tuple_pattern() { let src = "(foo,"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let pattern = parser.parse_pattern_or_error(); assert_eq!(parser.errors.len(), 1); let Pattern::Tuple(patterns, _) = pattern else { panic!("Expected a tuple pattern") }; @@ -317,7 +317,7 @@ mod tests { #[test] fn parses_struct_pattern_no_fields() { let src = "foo::Bar {}"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let pattern = parser.parse_pattern_or_error(); expect_no_errors(&parser.errors); let Pattern::Struct(path, patterns, _) = pattern else { @@ -353,7 +353,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let pattern = parser.parse_pattern_or_error(); let error = get_single_error(&parser.errors, span); @@ -377,7 +377,7 @@ mod tests { #[test] fn parses_unclosed_struct_pattern() { let src = "foo::Bar { x"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let pattern = parser.parse_pattern_or_error(); assert_eq!(parser.errors.len(), 1); let Pattern::Struct(path, _, _) = pattern else { panic!("Expected a struct pattern") }; @@ -391,7 +391,7 @@ mod tests { ^^^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let pattern = parser.parse_pattern(); assert!(pattern.is_none()); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement.rs index d8679da6ba88..600ddec43c96 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -1,4 +1,4 @@ -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Located, Location}; use crate::{ ast::{ @@ -6,33 +6,30 @@ use crate::{ ForLoopStatement, ForRange, Ident, InfixExpression, LValue, LetStatement, Statement, StatementKind, WhileStatement, }, - parser::{labels::ParsingRuleLabel, ParserErrorReason}, + parser::{ParserErrorReason, labels::ParsingRuleLabel}, token::{Attribute, Keyword, Token, TokenKind}, }; use super::Parser; -impl<'a> Parser<'a> { +impl Parser<'_> { pub(crate) fn parse_statement_or_error(&mut self) -> Statement { if let Some((statement, (_token, _span))) = self.parse_statement() { statement } else { self.expected_label(ParsingRuleLabel::Statement); - Statement { kind: StatementKind::Error, span: self.span_at_previous_token_end() } + Statement { + kind: StatementKind::Error, + location: self.location_at_previous_token_end(), + } } } /// Statement = Attributes StatementKind ';'? - pub(crate) fn parse_statement(&mut self) -> Option<(Statement, (Option, Span))> { + pub(crate) fn parse_statement(&mut self) -> Option<(Statement, (Option, Location))> { loop { - let span_before_doc_comments = self.current_token_span; - let doc_comments = self.parse_outer_doc_comments(); - if !doc_comments.is_empty() { - self.push_error( - ParserErrorReason::DocCommentDoesNotDocumentAnything, - span_before_doc_comments, - ); - } + // Like in Rust, we allow parsing doc comments on top of a statement but they always produce a warning. + self.warn_on_outer_doc_comments(); if !self.current_token_comments.is_empty() { self.statement_comments = Some(std::mem::take(&mut self.current_token_comments)); @@ -41,26 +38,25 @@ impl<'a> Parser<'a> { } let attributes = self.parse_attributes(); - let start_span = self.current_token_span; + let start_location = self.current_token_location; let kind = self.parse_statement_kind(attributes); - self.statement_comments = None; - let (semicolon_token, semicolon_span) = if self.at(Token::Semicolon) { + let (semicolon_token, semicolon_location) = if self.at(Token::Semicolon) { let token = self.token.clone(); self.bump(); - let span = token.to_span(); + let location = token.location(); - (Some(token.into_token()), span) + (Some(token.into_token()), location) } else { - (None, self.previous_token_span) + (None, self.previous_token_location) }; - let span = self.span_since(start_span); + let location = self.location_since(start_location); if let Some(kind) = kind { - let statement = Statement { kind, span }; - return Some((statement, (semicolon_token, semicolon_span))); + let statement = Statement { kind, location }; + return Some((statement, (semicolon_token, semicolon_location))); } self.expected_label(ParsingRuleLabel::Statement); @@ -102,14 +98,14 @@ impl<'a> Parser<'a> { /// ExpressionStatement = Expression fn parse_statement_kind( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, ) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if let Some(token) = self.eat_kind(TokenKind::InternedStatement) { match token.into_token() { Token::InternedStatement(statement) => { - return Some(StatementKind::Interned(statement)) + return Some(StatementKind::Interned(statement)); } _ => unreachable!(), } @@ -125,7 +121,7 @@ impl<'a> Parser<'a> { if self.eat_keyword(Keyword::Return) { self.parse_expression(); - self.push_error(ParserErrorReason::EarlyReturn, self.span_since(start_span)); + self.push_error(ParserErrorReason::EarlyReturn, self.location_since(start_location)); return Some(StatementKind::Error); } @@ -151,26 +147,26 @@ impl<'a> Parser<'a> { } if let Some(kind) = self.parse_if_expr() { - let span = self.span_since(start_span); - return Some(StatementKind::Expression(Expression { kind, span })); + let location = self.location_since(start_location); + return Some(StatementKind::Expression(Expression { kind, location })); } if let Some(kind) = self.parse_match_expr() { - let span = self.span_since(start_span); - return Some(StatementKind::Expression(Expression { kind, span })); + let location = self.location_since(start_location); + return Some(StatementKind::Expression(Expression { kind, location })); } if let Some(block) = self.parse_block() { return Some(StatementKind::Expression(Expression { kind: ExpressionKind::Block(block), - span: self.span_since(start_span), + location: self.location_since(start_location), })); } if let Some(token) = self.eat_kind(TokenKind::InternedLValue) { match token.into_token() { Token::InternedLValue(lvalue) => { - let lvalue = LValue::Interned(lvalue, self.span_since(start_span)); + let lvalue = LValue::Interned(lvalue, self.location_since(start_location)); self.eat_or_error(Token::Assign); let expression = self.parse_expression_or_error(); return Some(StatementKind::Assign(AssignStatement { lvalue, expression })); @@ -188,7 +184,7 @@ impl<'a> Parser<'a> { } else { self.push_error( ParserErrorReason::InvalidLeftHandSideOfAssignment, - expression.span, + expression.location, ); } } @@ -204,13 +200,13 @@ impl<'a> Parser<'a> { }; let expression = Expression::new( ExpressionKind::Infix(Box::new(infix)), - self.span_since(start_span), + self.location_since(start_location), ); return Some(StatementKind::Assign(AssignStatement { lvalue, expression })); } else { self.push_error( ParserErrorReason::InvalidLeftHandSideOfAssignment, - expression.span, + expression.location, ); } } @@ -219,7 +215,7 @@ impl<'a> Parser<'a> { } fn next_is_op_assign(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let operator = if self.next_is(Token::Assign) { match self.token.token() { Token::Plus => Some(BinaryOpKind::Add), @@ -243,7 +239,7 @@ impl<'a> Parser<'a> { if let Some(operator) = operator { self.bump(); self.bump(); - Some(Spanned::from(self.span_since(start_span), operator)) + Some(Located::from(self.location_since(start_location), operator)) } else { None } @@ -251,7 +247,7 @@ impl<'a> Parser<'a> { /// ForStatement = 'for' identifier 'in' ForRange Block fn parse_for(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.eat_keyword(Keyword::For) { return None; @@ -260,76 +256,86 @@ impl<'a> Parser<'a> { let Some(identifier) = self.eat_ident() else { self.expected_identifier(); let identifier = Ident::default(); - return Some(self.empty_for_loop(identifier, start_span)); + return Some(self.empty_for_loop(identifier, start_location)); }; if !self.eat_keyword(Keyword::In) { self.expected_token(Token::Keyword(Keyword::In)); - return Some(self.empty_for_loop(identifier, start_span)); + return Some(self.empty_for_loop(identifier, start_location)); } let range = self.parse_for_range(); - let block_start_span = self.current_token_span; + let block_start_location = self.current_token_location; let block = if let Some(block) = self.parse_block() { Expression { kind: ExpressionKind::Block(block), - span: self.span_since(block_start_span), + location: self.location_since(block_start_location), } } else { self.expected_token(Token::LeftBrace); - Expression { kind: ExpressionKind::Error, span: self.span_since(block_start_span) } + Expression { + kind: ExpressionKind::Error, + location: self.location_since(block_start_location), + } }; - Some(ForLoopStatement { identifier, range, block, span: self.span_since(start_span) }) + Some(ForLoopStatement { + identifier, + range, + block, + location: self.location_since(start_location), + }) } /// LoopStatement = 'loop' Block - fn parse_loop(&mut self) -> Option<(Expression, Span)> { - let start_span = self.current_token_span; + fn parse_loop(&mut self) -> Option<(Expression, Location)> { + let start_location = self.current_token_location; if !self.eat_keyword(Keyword::Loop) { return None; } - self.push_error(ParserErrorReason::ExperimentalFeature("loops"), start_span); - - let block_start_span = self.current_token_span; + let block_start_location = self.current_token_location; let block = if let Some(block) = self.parse_block() { Expression { kind: ExpressionKind::Block(block), - span: self.span_since(block_start_span), + location: self.location_since(block_start_location), } } else { self.expected_token(Token::LeftBrace); - Expression { kind: ExpressionKind::Error, span: self.span_since(block_start_span) } + Expression { + kind: ExpressionKind::Error, + location: self.location_since(block_start_location), + } }; - Some((block, start_span)) + Some((block, start_location)) } /// WhileStatement = 'while' ExpressionExceptConstructor Block fn parse_while(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.eat_keyword(Keyword::While) { return None; } - self.push_error(ParserErrorReason::ExperimentalFeature("while loops"), start_span); - let condition = self.parse_expression_except_constructor_or_error(); - let block_start_span = self.current_token_span; + let block_start_location = self.current_token_location; let block = if let Some(block) = self.parse_block() { Expression { kind: ExpressionKind::Block(block), - span: self.span_since(block_start_span), + location: self.location_since(block_start_location), } } else { self.expected_token(Token::LeftBrace); - Expression { kind: ExpressionKind::Error, span: self.span_since(block_start_span) } + Expression { + kind: ExpressionKind::Error, + location: self.location_since(block_start_location), + } }; - Some(WhileStatement { condition, body: block, while_keyword_span: start_span }) + Some(WhileStatement { condition, body: block, while_keyword_location: start_location }) } /// ForRange @@ -349,15 +355,15 @@ impl<'a> Parser<'a> { } } - fn empty_for_loop(&mut self, identifier: Ident, start_span: Span) -> ForLoopStatement { + fn empty_for_loop(&mut self, identifier: Ident, start_location: Location) -> ForLoopStatement { ForLoopStatement { identifier, range: ForRange::Array(Expression { kind: ExpressionKind::Error, - span: Span::default(), + location: Location::dummy(), }), - block: Expression { kind: ExpressionKind::Error, span: Span::default() }, - span: self.span_since(start_span), + block: Expression { kind: ExpressionKind::Error, location: Location::dummy() }, + location: self.location_since(start_location), } } @@ -373,9 +379,9 @@ impl<'a> Parser<'a> { /// ComptimeFor = 'comptime' ForStatement fn parse_comptime_statement( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, ) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.eat_keyword(Keyword::Comptime) { return None; @@ -384,7 +390,7 @@ impl<'a> Parser<'a> { if let Some(kind) = self.parse_comptime_statement_kind(attributes) { return Some(StatementKind::Comptime(Box::new(Statement { kind, - span: self.span_since(start_span), + location: self.location_since(start_location), }))); } @@ -399,14 +405,14 @@ impl<'a> Parser<'a> { fn parse_comptime_statement_kind( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, ) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if let Some(block) = self.parse_block() { return Some(StatementKind::Expression(Expression { kind: ExpressionKind::Block(block), - span: self.span_since(start_span), + location: self.location_since(start_location), })); } @@ -422,7 +428,10 @@ impl<'a> Parser<'a> { } /// LetStatement = 'let' pattern OptionalTypeAnnotation '=' Expression - fn parse_let_statement(&mut self, attributes: Vec<(Attribute, Span)>) -> Option { + fn parse_let_statement( + &mut self, + attributes: Vec<(Attribute, Location)>, + ) -> Option { if !self.eat_keyword(Keyword::Let) { return None; } @@ -434,7 +443,7 @@ impl<'a> Parser<'a> { self.parse_expression_or_error() } else { self.expected_token(Token::Assign); - Expression { kind: ExpressionKind::Error, span: self.current_token_span } + Expression { kind: ExpressionKind::Error, location: self.current_token_location } }; Some(LetStatement { @@ -453,16 +462,16 @@ mod tests { use crate::{ ast::{ExpressionKind, ForRange, LValue, Statement, StatementKind, UnresolvedTypeData}, parser::{ + Parser, ParserErrorReason, parser::tests::{ expect_no_errors, get_single_error, get_single_error_reason, get_source_with_error_span, }, - Parser, ParserErrorReason, }, }; fn parse_statement_no_errors(src: &str) -> Statement { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement_or_error(); expect_no_errors(&parser.errors); statement @@ -512,7 +521,9 @@ mod tests { fn parses_let_statement_with_unsafe() { let src = "// Safety: comment let x = unsafe { 1 };"; - let statement = parse_statement_no_errors(src); + let mut parser = Parser::for_str_with_dummy_file(src); + let statement = parser.parse_statement_or_error(); + assert!(parser.errors.is_empty()); let StatementKind::Let(let_statement) = statement.kind else { panic!("Expected let statement"); }; @@ -523,7 +534,7 @@ mod tests { fn parses_let_statement_with_unsafe_doc_comment() { let src = "/// Safety: doc comment let x = unsafe { 1 };"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let (statement, _) = parser.parse_statement().unwrap(); let StatementKind::Let(let_statement) = statement.kind else { panic!("Expected let statement"); @@ -531,6 +542,20 @@ mod tests { assert_eq!(let_statement.pattern.to_string(), "x"); } + #[test] + fn parses_let_statement_with_unsafe_after_some_other_comment() { + let src = "// Top comment + // Safety: comment + let x = unsafe { 1 };"; + let mut parser = Parser::for_str_with_dummy_file(src); + let statement = parser.parse_statement_or_error(); + assert!(parser.errors.is_empty()); + let StatementKind::Let(let_statement) = statement.kind else { + panic!("Expected let statement"); + }; + assert_eq!(let_statement.pattern.to_string(), "x"); + } + #[test] fn parses_comptime_block() { let src = "comptime { 1 }"; @@ -644,7 +669,7 @@ mod tests { #[test] fn parses_assignment_with_unsafe() { - let src = "// Safety: test + let src = "// Safety: test x = unsafe { 1 }"; let statement = parse_statement_no_errors(src); let StatementKind::Assign(assign) = statement.kind else { @@ -722,7 +747,7 @@ mod tests { ^^^^^^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let statement = parser.parse_statement_or_error(); assert!(matches!(statement.kind, StatementKind::Error)); let reason = get_single_error_reason(&parser.errors, span); @@ -736,7 +761,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let statement = parser.parse_statement_or_error(); assert!(matches!(statement.kind, StatementKind::Let(..))); let error = get_single_error(&parser.errors, span); @@ -746,7 +771,7 @@ mod tests { #[test] fn recovers_on_unknown_statement_followed_by_semicolon() { let src = " ] ;"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement(); assert!(statement.is_none()); assert_eq!(parser.errors.len(), 2); @@ -755,7 +780,7 @@ mod tests { #[test] fn recovers_on_unknown_statement_followed_by_right_brace() { let src = " ] }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement(); assert!(statement.is_none()); assert_eq!(parser.errors.len(), 2); @@ -764,23 +789,23 @@ mod tests { #[test] fn parses_empty_loop() { let src = "loop { }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement_or_error(); - let StatementKind::Loop(block, span) = statement.kind else { + let StatementKind::Loop(block, location) = statement.kind else { panic!("Expected loop"); }; let ExpressionKind::Block(block) = block.kind else { panic!("Expected block"); }; assert!(block.statements.is_empty()); - assert_eq!(span.start(), 0); - assert_eq!(span.end(), 4); + assert_eq!(location.span.start(), 0); + assert_eq!(location.span.end(), 4); } #[test] fn parses_loop_with_statements() { let src = "loop { 1; 2 }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement_or_error(); let StatementKind::Loop(block, _) = statement.kind else { panic!("Expected loop"); @@ -794,7 +819,7 @@ mod tests { #[test] fn parses_let_with_assert() { let src = "let _ = assert(true);"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement_or_error(); let StatementKind::Let(let_statement) = statement.kind else { panic!("Expected let"); @@ -805,7 +830,7 @@ mod tests { #[test] fn parses_empty_while() { let src = "while true { }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement_or_error(); let StatementKind::While(while_) = statement.kind else { panic!("Expected while"); @@ -814,8 +839,8 @@ mod tests { panic!("Expected block"); }; assert!(block.statements.is_empty()); - assert_eq!(while_.while_keyword_span.start(), 0); - assert_eq!(while_.while_keyword_span.end(), 5); + assert_eq!(while_.while_keyword_location.span.start(), 0); + assert_eq!(while_.while_keyword_location.span.end(), 5); assert_eq!(while_.condition.to_string(), "true"); } @@ -823,7 +848,7 @@ mod tests { #[test] fn parses_while_with_statements() { let src = "while true { 1; 2 }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement_or_error(); let StatementKind::While(while_) = statement.kind else { panic!("Expected while"); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement_or_expression_or_lvalue.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement_or_expression_or_lvalue.rs index fdc187f3fb2a..0ac1b1972c4f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement_or_expression_or_lvalue.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement_or_expression_or_lvalue.rs @@ -12,7 +12,7 @@ pub enum StatementOrExpressionOrLValue { LValue(LValue), } -impl<'a> Parser<'a> { +impl Parser<'_> { /// Parses either a statement, an expression or an LValue. Returns `StatementKind::Error` /// if none can be parsed, recording an error if so. /// @@ -20,13 +20,13 @@ impl<'a> Parser<'a> { pub(crate) fn parse_statement_or_expression_or_lvalue( &mut self, ) -> StatementOrExpressionOrLValue { - let start_span = self.current_token_span; + let start_location = self.current_token_location; // First check if it's an interned LValue if let Some(token) = self.eat_kind(TokenKind::InternedLValue) { match token.into_token() { Token::InternedLValue(lvalue) => { - let lvalue = LValue::Interned(lvalue, self.span_since(start_span)); + let lvalue = LValue::Interned(lvalue, self.location_since(start_location)); // If it is, it could be something like `lvalue = expr`: check that. if self.eat(Token::Assign) { @@ -34,7 +34,7 @@ impl<'a> Parser<'a> { let kind = StatementKind::Assign(AssignStatement { lvalue, expression }); return StatementOrExpressionOrLValue::Statement(Statement { kind, - span: self.span_since(start_span), + location: self.location_since(start_location), }); } else { return StatementOrExpressionOrLValue::LValue(lvalue); 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 b066565e6801..62f49035f722 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,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{Documented, Ident, ItemVisibility, NoirStruct, StructField, UnresolvedGenerics}, @@ -6,17 +6,17 @@ use crate::{ token::{Attribute, SecondaryAttribute, Token}, }; -use super::{parse_many::separated_by_comma_until_right_brace, Parser}; +use super::{Parser, parse_many::separated_by_comma_until_right_brace}; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Struct = 'struct' identifier Generics '{' StructField* '}' /// /// StructField = OuterDocComments identifier ':' Type pub(crate) fn parse_struct( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, visibility: ItemVisibility, - start_span: Span, + start_location: Location, ) -> NoirStruct { let attributes = self.validate_secondary_attributes(attributes); @@ -27,19 +27,19 @@ impl<'a> Parser<'a> { attributes, visibility, Vec::new(), - start_span, + start_location, ); }; let generics = self.parse_generics(); if self.eat_semicolons() { - return self.empty_struct(name, attributes, visibility, generics, start_span); + return self.empty_struct(name, attributes, visibility, generics, start_location); } if !self.eat_left_brace() { self.expected_token(Token::LeftBrace); - return self.empty_struct(name, attributes, visibility, generics, start_span); + return self.empty_struct(name, attributes, visibility, generics, start_location); } let fields = self.parse_many( @@ -54,7 +54,7 @@ impl<'a> Parser<'a> { visibility, generics, fields, - span: self.span_since(start_span), + location: self.location_since(start_location), } } @@ -65,7 +65,7 @@ impl<'a> Parser<'a> { // Loop until we find an identifier, skipping anything that's not one loop { - let doc_comments_start_span = self.current_token_span; + let doc_comments_start_location = self.current_token_location; doc_comments = self.parse_outer_doc_comments(); visibility = self.parse_item_visibility(); @@ -82,7 +82,7 @@ impl<'a> Parser<'a> { if !doc_comments.is_empty() { self.push_error( ParserErrorReason::DocCommentDoesNotDocumentAnything, - self.span_since(doc_comments_start_span), + self.location_since(doc_comments_start_location), ); } @@ -113,7 +113,7 @@ impl<'a> Parser<'a> { attributes: Vec, visibility: ItemVisibility, generics: UnresolvedGenerics, - start_span: Span, + start_location: Location, ) -> NoirStruct { NoirStruct { name, @@ -121,7 +121,7 @@ impl<'a> Parser<'a> { visibility, generics, fields: Vec::new(), - span: self.span_since(start_span), + location: self.location_since(start_location), } } } @@ -130,20 +130,18 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{IntegerBitSize, NoirStruct, Signedness, UnresolvedGeneric, UnresolvedTypeData}, + parse_program_with_dummy_file, parser::{ - parser::{ - parse_program, - tests::{ - expect_no_errors, get_single_error, get_single_error_reason, - get_source_with_error_span, - }, - }, ItemKind, ParserErrorReason, + parser::tests::{ + expect_no_errors, get_single_error, get_single_error_reason, + get_source_with_error_span, + }, }, }; fn parse_struct_no_errors(src: &str) -> NoirStruct { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -218,7 +216,7 @@ mod tests { #[test] fn parse_empty_struct_with_doc_comments() { let src = "/// Hello\nstruct Foo {}"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -232,7 +230,7 @@ mod tests { #[test] fn parse_unclosed_struct() { let src = "struct Foo {"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -249,7 +247,7 @@ mod tests { ^^^^^^^ "; let (src, span) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::NoFunctionAttributesAllowedOnType)); } @@ -261,7 +259,7 @@ mod tests { ^^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let item = &module.items[0]; 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 53df5cd00b1b..c37163ebc36c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -1,6 +1,6 @@ use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::{Located, Location}; use crate::ast::{ Documented, GenericTypeArg, GenericTypeArgs, ItemVisibility, NoirTrait, Path, Pattern, @@ -8,27 +8,28 @@ use crate::ast::{ }; use crate::{ ast::{Ident, UnresolvedTypeData}, - parser::{labels::ParsingRuleLabel, NoirTraitImpl, ParserErrorReason}, + parser::{NoirTraitImpl, ParserErrorReason, labels::ParsingRuleLabel}, token::{Attribute, Keyword, SecondaryAttribute, Token}, }; -use super::parse_many::without_separator; use super::Parser; +use super::parse_many::without_separator; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Trait = 'trait' identifier Generics ( ':' TraitBounds )? WhereClause TraitBody /// | 'trait' identifier Generics '=' TraitBounds WhereClause ';' pub(crate) fn parse_trait( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, visibility: ItemVisibility, - start_span: Span, + start_location: Location, ) -> (NoirTrait, Option) { let attributes = self.validate_secondary_attributes(attributes); let Some(name) = self.eat_ident() else { self.expected_identifier(); - let noir_trait = empty_trait(attributes, visibility, self.span_since(start_span)); + let noir_trait = + empty_trait(attributes, visibility, self.location_since(start_location)); let no_implicit_impl = None; return (noir_trait, no_implicit_impl); }; @@ -41,7 +42,7 @@ impl<'a> Parser<'a> { let bounds = self.parse_trait_bounds(); if bounds.is_empty() { - self.push_error(ParserErrorReason::EmptyTraitAlias, self.previous_token_span); + self.push_error(ParserErrorReason::EmptyTraitAlias, self.previous_token_location); } let where_clause = self.parse_where_clause(); @@ -60,17 +61,17 @@ impl<'a> Parser<'a> { (bounds, where_clause, items, is_alias) }; - let span = self.span_since(start_span); + let location = self.location_since(start_location); let noir_impl = is_alias.then(|| { - let object_type_ident = Ident::new("#T".to_string(), span); + let object_type_ident = Ident::from(Located::from(location, "#T".to_string())); let object_type_path = Path::from_ident(object_type_ident.clone()); let object_type_generic = UnresolvedGeneric::Variable(object_type_ident); let is_synthesized = true; let object_type = UnresolvedType { typ: UnresolvedTypeData::Named(object_type_path, vec![].into(), is_synthesized), - span, + location, }; let mut impl_generics = generics.clone(); @@ -85,7 +86,7 @@ impl<'a> Parser<'a> { vec![].into(), is_synthesized, ), - span, + location, }; GenericTypeArg::Ordered(generic_type) @@ -94,7 +95,7 @@ impl<'a> Parser<'a> { let r#trait = UnresolvedType { typ: UnresolvedTypeData::Named(trait_name, trait_generics, false), - span, + location, }; // bounds from trait @@ -117,7 +118,7 @@ impl<'a> Parser<'a> { generics, bounds, where_clause, - span, + location, items, attributes, visibility, @@ -205,7 +206,7 @@ impl<'a> Parser<'a> { self.parse_type_or_error() } else { self.expected_token(Token::Colon); - UnresolvedType { typ: UnresolvedTypeData::Unspecified, span: Span::default() } + UnresolvedType { typ: UnresolvedTypeData::Unspecified, location: Location::dummy() } }; let default_value = @@ -223,7 +224,10 @@ impl<'a> Parser<'a> { ); if modifiers.visibility != ItemVisibility::Private { - self.push_error(ParserErrorReason::TraitVisibilityIgnored, modifiers.visibility_span); + self.push_error( + ParserErrorReason::TraitVisibilityIgnored, + modifiers.visibility_location, + ); } if !self.eat_keyword(Keyword::Fn) { @@ -243,7 +247,7 @@ impl<'a> Parser<'a> { if let Pattern::Identifier(ident) = param.pattern { Some((ident, param.typ)) } else { - self.push_error(ParserErrorReason::InvalidPattern, param.pattern.span()); + self.push_error(ParserErrorReason::InvalidPattern, param.pattern.location()); None } }) @@ -266,14 +270,14 @@ impl<'a> Parser<'a> { fn empty_trait( attributes: Vec, visibility: ItemVisibility, - span: Span, + location: Location, ) -> NoirTrait { NoirTrait { name: Ident::default(), generics: Vec::new(), bounds: Vec::new(), where_clause: Vec::new(), - span, + location, items: Vec::new(), attributes, visibility, @@ -285,18 +289,18 @@ fn empty_trait( mod tests { use crate::{ ast::{NoirTrait, NoirTraitImpl, TraitItem, UnresolvedTypeData}, + parse_program_with_dummy_file, parser::{ + ItemKind, parser::{ - parse_program, - tests::{expect_no_errors, get_single_error, get_source_with_error_span}, ParserErrorReason, + tests::{expect_no_errors, get_single_error, get_source_with_error_span}, }, - ItemKind, }, }; fn parse_trait_opt_impl_no_errors(src: &str) -> (NoirTrait, Option) { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); let (item, impl_item) = if module.items.len() == 2 { let item = module.items.remove(0); @@ -342,7 +346,7 @@ mod tests { #[test] fn parse_empty_trait_alias() { let src = "trait Foo = ;"; - let (_module, errors) = parse_program(src); + let (_module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 2); assert_eq!(errors[1].reason(), Some(ParserErrorReason::EmptyTraitAlias).as_ref()); } @@ -401,7 +405,7 @@ mod tests { #[test] fn parse_empty_trait_alias_with_generics() { let src = "trait Foo = ;"; - let (_module, errors) = parse_program(src); + let (_module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 2); assert_eq!(errors[1].reason(), Some(ParserErrorReason::EmptyTraitAlias).as_ref()); } @@ -464,7 +468,7 @@ mod tests { #[test] fn parse_empty_trait_alias_with_where_clause() { let src = "trait Foo = where A: Z;"; - let (_module, errors) = parse_program(src); + let (_module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 2); assert_eq!(errors[1].reason(), Some(ParserErrorReason::EmptyTraitAlias).as_ref()); } @@ -534,7 +538,7 @@ mod tests { ^^^ "; let (src, span) = get_source_with_error_span(src); - let (_module, errors) = parse_program(&src); + let (_module, errors) = parse_program_with_dummy_file(&src); let error = get_single_error(&errors, span); assert!(error.to_string().contains("Visibility is ignored on a trait method")); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_alias.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_alias.rs index 52815dc37831..464e3e897c50 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_alias.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_alias.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{Ident, ItemVisibility, NoirTypeAlias, UnresolvedType, UnresolvedTypeData}, @@ -7,12 +7,12 @@ use crate::{ use super::Parser; -impl<'a> Parser<'a> { +impl Parser<'_> { /// TypeAlias = 'type' identifier Generics '=' Type ';' pub(crate) fn parse_type_alias( &mut self, visibility: ItemVisibility, - start_span: Span, + start_location: Location, ) -> NoirTypeAlias { let Some(name) = self.eat_ident() else { self.expected_identifier(); @@ -20,8 +20,8 @@ impl<'a> Parser<'a> { visibility, name: Ident::default(), generics: Vec::new(), - typ: UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }, - span: start_span, + typ: UnresolvedType { typ: UnresolvedTypeData::Error, location: Location::dummy() }, + location: start_location, }; }; @@ -30,25 +30,25 @@ impl<'a> Parser<'a> { if !self.eat_assign() { self.expected_token(Token::Assign); - let span = self.span_since(start_span); + let location = self.location_since(start_location); self.eat_semicolons(); return NoirTypeAlias { visibility, name, generics, - typ: UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }, - span, + typ: UnresolvedType { typ: UnresolvedTypeData::Error, location: Location::dummy() }, + location, }; } let typ = self.parse_type_or_error(); - let span = self.span_since(start_span); + let location = self.location_since(start_location); if !self.eat_semicolons() { self.expected_token(Token::Semicolon); } - NoirTypeAlias { visibility, name, generics, typ, span } + NoirTypeAlias { visibility, name, generics, typ, location } } } @@ -56,14 +56,12 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{NoirTypeAlias, UnresolvedTypeData}, - parser::{ - parser::{parse_program, tests::expect_no_errors}, - ItemKind, - }, + parse_program_with_dummy_file, + parser::{ItemKind, parser::tests::expect_no_errors}, }; fn parse_type_alias_no_errors(src: &str) -> NoirTypeAlias { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_expression.rs index 83b04bb157a2..f6d6dbd8a25d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -1,16 +1,16 @@ use crate::{ + BinaryTypeOperator, ast::{GenericTypeArgs, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, - parser::{labels::ParsingRuleLabel, ParserError}, + parser::{ParserError, labels::ParsingRuleLabel}, token::Token, - BinaryTypeOperator, }; use acvm::acir::{AcirField, FieldElement}; -use noirc_errors::Span; +use noirc_errors::Location; -use super::{parse_many::separated_by_comma_until_right_paren, Parser}; +use super::{Parser, parse_many::separated_by_comma_until_right_paren}; -impl<'a> Parser<'a> { +impl Parser<'_> { /// TypeExpression= AddOrSubtractTypeExpression pub(crate) fn parse_type_expression( &mut self, @@ -24,15 +24,15 @@ impl<'a> Parser<'a> { /// AddOrSubtractTypeExpression /// = MultiplyOrDivideOrModuloTypeExpression ( ( '+' | '-' ) MultiplyOrDivideOrModuloTypeExpression )* fn parse_add_or_subtract_type_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let lhs = self.parse_multiply_or_divide_or_modulo_type_expression()?; - Some(self.parse_add_or_subtract_type_expression_after_lhs(lhs, start_span)) + Some(self.parse_add_or_subtract_type_expression_after_lhs(lhs, start_location)) } fn parse_add_or_subtract_type_expression_after_lhs( &mut self, mut lhs: UnresolvedTypeExpression, - start_span: Span, + start_location: Location, ) -> UnresolvedTypeExpression { loop { let operator = if self.eat(Token::Plus) { @@ -45,12 +45,12 @@ impl<'a> Parser<'a> { match self.parse_multiply_or_divide_or_modulo_type_expression() { Some(rhs) => { - let span = self.span_since(start_span); + let location = self.location_since(start_location); lhs = UnresolvedTypeExpression::BinaryOperation( Box::new(lhs), operator, Box::new(rhs), - span, + location, ); } None => { @@ -67,15 +67,15 @@ impl<'a> Parser<'a> { fn parse_multiply_or_divide_or_modulo_type_expression( &mut self, ) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let lhs = self.parse_term_type_expression()?; - Some(self.parse_multiply_or_divide_or_modulo_type_expression_after_lhs(lhs, start_span)) + Some(self.parse_multiply_or_divide_or_modulo_type_expression_after_lhs(lhs, start_location)) } fn parse_multiply_or_divide_or_modulo_type_expression_after_lhs( &mut self, mut lhs: UnresolvedTypeExpression, - start_span: Span, + start_location: Location, ) -> UnresolvedTypeExpression { loop { let operator = if self.eat(Token::Star) { @@ -90,12 +90,12 @@ impl<'a> Parser<'a> { match self.parse_term_type_expression() { Some(rhs) => { - let span = self.span_since(start_span); + let location = self.location_since(start_location); lhs = UnresolvedTypeExpression::BinaryOperation( Box::new(lhs), operator, Box::new(rhs), - span, + location, ); } None => { @@ -112,18 +112,19 @@ impl<'a> Parser<'a> { /// = '- TermTypeExpression /// | AtomTypeExpression fn parse_term_type_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if self.eat(Token::Minus) { return match self.parse_term_type_expression() { Some(rhs) => { - let lhs = UnresolvedTypeExpression::Constant(FieldElement::zero(), start_span); + let lhs = + UnresolvedTypeExpression::Constant(FieldElement::zero(), start_location); let op = BinaryTypeOperator::Subtraction; - let span = self.span_since(start_span); + let location = self.location_since(start_location); Some(UnresolvedTypeExpression::BinaryOperation( Box::new(lhs), op, Box::new(rhs), - span, + location, )) } None => { @@ -159,8 +160,7 @@ impl<'a> Parser<'a> { /// ConstantTypeExpression = int fn parse_constant_type_expression(&mut self) -> Option { let int = self.eat_int()?; - - Some(UnresolvedTypeExpression::Constant(int, self.previous_token_span)) + Some(UnresolvedTypeExpression::Constant(int, self.previous_token_location)) } /// VariableTypeExpression = Path @@ -193,7 +193,7 @@ impl<'a> Parser<'a> { /// TypeOrTypeExpression = Type | TypeExpression pub(crate) fn parse_type_or_type_expression(&mut self) -> Option { let typ = self.parse_add_or_subtract_type_or_type_expression()?; - let span = typ.span; + let span = typ.location; // If we end up with a Variable type expression, make it a Named type (they are equivalent), // but for testing purposes and simplicity we default to types instead of type expressions. @@ -203,7 +203,7 @@ impl<'a> Parser<'a> { { UnresolvedType { typ: UnresolvedTypeData::Named(path, GenericTypeArgs::default(), false), - span, + location: span, } } else { typ @@ -212,7 +212,7 @@ impl<'a> Parser<'a> { } fn parse_add_or_subtract_type_or_type_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let lhs = self.parse_multiply_or_divide_or_modulo_type_or_type_expression()?; // If lhs is a type then no operator can follow, so we stop right away @@ -221,14 +221,14 @@ impl<'a> Parser<'a> { } let lhs = type_to_type_expr(lhs).unwrap(); - let lhs = self.parse_add_or_subtract_type_expression_after_lhs(lhs, start_span); - Some(type_expr_to_type(lhs, self.span_since(start_span))) + let lhs = self.parse_add_or_subtract_type_expression_after_lhs(lhs, start_location); + Some(type_expr_to_type(lhs, self.location_since(start_location))) } fn parse_multiply_or_divide_or_modulo_type_or_type_expression( &mut self, ) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let lhs = self.parse_term_type_or_type_expression()?; // If lhs is a type then no operator can follow, so we stop right away @@ -238,27 +238,28 @@ impl<'a> Parser<'a> { let lhs = type_to_type_expr(lhs).unwrap(); let lhs = - self.parse_multiply_or_divide_or_modulo_type_expression_after_lhs(lhs, start_span); - Some(type_expr_to_type(lhs, self.span_since(start_span))) + self.parse_multiply_or_divide_or_modulo_type_expression_after_lhs(lhs, start_location); + Some(type_expr_to_type(lhs, self.location_since(start_location))) } fn parse_term_type_or_type_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if self.eat(Token::Minus) { // If we ate '-' what follows must be a type expression, never a type return match self.parse_term_type_expression() { Some(rhs) => { - let lhs = UnresolvedTypeExpression::Constant(FieldElement::zero(), start_span); + let lhs = + UnresolvedTypeExpression::Constant(FieldElement::zero(), start_location); let op = BinaryTypeOperator::Subtraction; - let span = self.span_since(start_span); + let location = self.location_since(start_location); let type_expr = UnresolvedTypeExpression::BinaryOperation( Box::new(lhs), op, Box::new(rhs), - span, + location, ); let typ = UnresolvedTypeData::Expression(type_expr); - Some(UnresolvedType { typ, span }) + Some(UnresolvedType { typ, location }) } None => { self.push_expected_expression(); @@ -271,19 +272,19 @@ impl<'a> Parser<'a> { } fn parse_atom_type_or_type_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if let Some(path) = self.parse_path() { let generics = self.parse_generic_type_args(); let typ = UnresolvedTypeData::Named(path, generics, false); - let span = self.span_since(start_span); - return Some(UnresolvedType { typ, span }); + let location = self.location_since(start_location); + return Some(UnresolvedType { typ, location }); } if let Some(type_expr) = self.parse_constant_type_expression() { let typ = UnresolvedTypeData::Expression(type_expr); - let span = self.span_since(start_span); - return Some(UnresolvedType { typ, span }); + let location = self.location_since(start_location); + return Some(UnresolvedType { typ, location }); } if let Some(typ) = self.parse_parenthesized_type_or_type_expression() { @@ -294,7 +295,7 @@ impl<'a> Parser<'a> { } fn parse_parenthesized_type_or_type_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.eat_left_paren() { return None; @@ -303,7 +304,7 @@ impl<'a> Parser<'a> { if self.eat_right_paren() { return Some(UnresolvedType { typ: UnresolvedTypeData::Unit, - span: self.span_since(start_span), + location: self.location_since(start_location), }); } @@ -318,19 +319,19 @@ impl<'a> Parser<'a> { self.eat_or_error(Token::RightParen); return Some(UnresolvedType { typ: UnresolvedTypeData::Expression(type_expr), - span: typ.span, + location: typ.location, }); } if self.eat_right_paren() { return Some(UnresolvedType { typ: UnresolvedTypeData::Parenthesized(Box::new(typ)), - span: self.span_since(start_span), + location: self.location_since(start_location), }); } let comma_after_first_type = self.eat_comma(); - let second_type_span = self.current_token_span; + let second_type_location = self.current_token_location; let mut types = self.parse_many( "tuple items", @@ -339,14 +340,14 @@ impl<'a> Parser<'a> { ); if !types.is_empty() && !comma_after_first_type { - self.expected_token_separating_items(Token::Comma, "tuple items", second_type_span); + self.expected_token_separating_items(Token::Comma, "tuple items", second_type_location); } types.insert(0, typ); Some(UnresolvedType { typ: UnresolvedTypeData::Tuple(types), - span: self.span_since(start_span), + location: self.location_since(start_location), }) } @@ -356,7 +357,7 @@ impl<'a> Parser<'a> { Err(ParserError::expected_label( ParsingRuleLabel::TypeExpression, self.token.token().clone(), - self.current_token_span, + self.current_token_location, )) } } @@ -383,9 +384,9 @@ fn type_is_type_expr(typ: &UnresolvedType) -> bool { } } -fn type_expr_to_type(lhs: UnresolvedTypeExpression, span: Span) -> UnresolvedType { +fn type_expr_to_type(lhs: UnresolvedTypeExpression, location: Location) -> UnresolvedType { let lhs = UnresolvedTypeData::Expression(lhs); - UnresolvedType { typ: lhs, span } + UnresolvedType { typ: lhs, location } } #[cfg(test)] @@ -393,26 +394,26 @@ mod tests { use core::panic; use crate::{ + BinaryTypeOperator, ast::{UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, parser::{ + Parser, ParserErrorReason, parser::tests::{ expect_no_errors, get_single_error_reason, get_source_with_error_span, }, - Parser, ParserErrorReason, }, token::Token, - BinaryTypeOperator, }; fn parse_type_expression_no_errors(src: &str) -> UnresolvedTypeExpression { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let expr = parser.parse_type_expression().unwrap(); expect_no_errors(&parser.errors); expr } fn parse_type_or_type_expression_no_errors(src: &str) -> UnresolvedType { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let typ = parser.parse_type_or_type_expression().unwrap(); expect_no_errors(&parser.errors); typ @@ -569,7 +570,7 @@ mod tests { ^^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let typ = parser.parse_type_or_type_expression().unwrap(); @@ -594,7 +595,7 @@ mod tests { #[test] fn parses_type_or_type_expression_tuple_type_single_element() { let src = "(Field,)"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let typ = parser.parse_type_or_type_expression().unwrap(); expect_no_errors(&parser.errors); let UnresolvedTypeData::Tuple(types) = typ.typ else { 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 6b3575e19eef..bcbf57d863d6 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,29 +1,49 @@ use acvm::{AcirField, FieldElement}; use crate::{ + QuotedType, ast::{UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, - parser::{labels::ParsingRuleLabel, ParserErrorReason}, + parser::{ParserErrorReason, labels::ParsingRuleLabel}, token::{Keyword, Token, TokenKind}, - QuotedType, }; -use super::{parse_many::separated_by_comma_until_right_paren, Parser}; +use super::{Parser, parse_many::separated_by_comma_until_right_paren}; -impl<'a> Parser<'a> { +impl Parser<'_> { pub(crate) fn parse_type_or_error(&mut self) -> UnresolvedType { if let Some(typ) = self.parse_type() { typ } else { self.expected_label(ParsingRuleLabel::Type); - UnresolvedTypeData::Error.with_span(self.span_at_previous_token_end()) + UnresolvedTypeData::Error.with_location(self.location_at_previous_token_end()) + } + } + + /// Tries to parse a type. If the current token doesn't denote a type and it's not + /// one of `stop_tokens`, try to parse a type starting from the next token (and so on). + pub(crate) fn parse_type_or_error_with_recovery( + &mut self, + stop_tokens: &[Token], + ) -> UnresolvedType { + loop { + let typ = self.parse_type_or_error(); + if typ.typ != UnresolvedTypeData::Error { + return typ; + } + + if self.at_eof() || stop_tokens.contains(self.token.token()) { + return typ; + } + + self.bump(); } } pub(crate) fn parse_type(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let typ = self.parse_unresolved_type_data()?; - let span = self.span_since(start_span); - Some(UnresolvedType { typ, span }) + let location = self.location_since(start_location); + Some(UnresolvedType { typ, location }) } fn parse_unresolved_type_data(&mut self) -> Option { @@ -122,7 +142,7 @@ impl<'a> Parser<'a> { Err(err) => { self.push_error( ParserErrorReason::InvalidBitSize(err.0), - self.previous_token_span, + self.previous_token_location, ); UnresolvedTypeData::Error } @@ -139,8 +159,10 @@ impl<'a> Parser<'a> { if !self.eat_less() { self.expected_token(Token::Less); - let expr = - UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span); + let expr = UnresolvedTypeExpression::Constant( + FieldElement::zero(), + self.current_token_location, + ); return Some(UnresolvedTypeData::String(expr)); } @@ -148,7 +170,10 @@ impl<'a> Parser<'a> { Ok(expr) => expr, Err(error) => { self.errors.push(error); - UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span) + UnresolvedTypeExpression::Constant( + FieldElement::zero(), + self.current_token_location, + ) } }; @@ -164,9 +189,12 @@ impl<'a> Parser<'a> { if !self.eat_less() { self.expected_token(Token::Less); - let expr = - UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span); - let typ = UnresolvedTypeData::Error.with_span(self.span_at_previous_token_end()); + let expr = UnresolvedTypeExpression::Constant( + FieldElement::zero(), + self.current_token_location, + ); + let typ = + UnresolvedTypeData::Error.with_location(self.location_at_previous_token_end()); return Some(UnresolvedTypeData::FormatString(expr, Box::new(typ))); } @@ -174,7 +202,10 @@ impl<'a> Parser<'a> { Ok(expr) => expr, Err(error) => { self.errors.push(error); - UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span) + UnresolvedTypeExpression::Constant( + FieldElement::zero(), + self.current_token_location, + ) } }; @@ -257,7 +288,7 @@ impl<'a> Parser<'a> { self.eat_or_error(Token::RightBracket); typ } else { - UnresolvedTypeData::Unit.with_span(self.span_at_previous_token_end()) + UnresolvedTypeData::Unit.with_location(self.location_at_previous_token_end()) }; if !self.eat_left_paren() { @@ -281,7 +312,7 @@ impl<'a> Parser<'a> { self.parse_type_or_error() } else { self.expected_token(Token::Arrow); - UnresolvedTypeData::Unit.with_span(self.span_at_previous_token_end()) + UnresolvedTypeData::Unit.with_location(self.location_at_previous_token_end()) }; Some(UnresolvedTypeData::Function(args, Box::new(ret), Box::new(env), unconstrained)) @@ -289,11 +320,7 @@ impl<'a> Parser<'a> { fn parse_parameter(&mut self) -> Option { let typ = self.parse_type_or_error(); - if let UnresolvedTypeData::Error = typ.typ { - None - } else { - Some(typ) - } + if let UnresolvedTypeData::Error = typ.typ { None } else { Some(typ) } } fn parse_trait_as_type(&mut self) -> Option { @@ -422,7 +449,7 @@ impl<'a> Parser<'a> { } pub(super) fn unspecified_type_at_previous_token_end(&self) -> UnresolvedType { - UnresolvedTypeData::Unspecified.with_span(self.span_at_previous_token_end()) + UnresolvedTypeData::Unspecified.with_location(self.location_at_previous_token_end()) } } @@ -431,16 +458,16 @@ mod tests { use strum::IntoEnumIterator; use crate::{ + QuotedType, ast::{IntegerBitSize, Signedness, UnresolvedType, UnresolvedTypeData}, parser::{ + Parser, ParserErrorReason, parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, - Parser, }, - QuotedType, }; fn parse_type_no_errors(src: &str) -> UnresolvedType { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let typ = parser.parse_type_or_error(); expect_no_errors(&parser.errors); typ @@ -470,6 +497,28 @@ mod tests { )); } + #[test] + fn errors_on_invalid_bit_size() { + let src = "u31"; + let mut parser = Parser::for_str_with_dummy_file(src); + let typ = parser.parse_type_or_error(); + assert_eq!(typ.typ, UnresolvedTypeData::Error); + assert_eq!(parser.errors.len(), 1); + let error = &parser.errors[0]; + assert!(matches!(error.reason(), Some(ParserErrorReason::InvalidBitSize(..)))); + } + + #[test] + fn errors_on_i128() { + let src = "i128"; + let mut parser = Parser::for_str_with_dummy_file(src); + let typ = parser.parse_type_or_error(); + assert_eq!(typ.typ, UnresolvedTypeData::Error); + assert_eq!(parser.errors.len(), 1); + let error = &parser.errors[0]; + assert!(matches!(error.reason(), Some(ParserErrorReason::InvalidBitSize(..)))); + } + #[test] fn parses_field_type() { let src = "Field"; @@ -546,7 +595,7 @@ mod tests { #[test] fn parses_unclosed_parentheses_type() { let src = "(Field"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let typ = parser.parse_type_or_error(); assert_eq!(parser.errors.len(), 1); let UnresolvedTypeData::Parenthesized(typ) = typ.typ else { @@ -591,7 +640,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); parser.parse_type(); let error = get_single_error(&parser.errors, span); assert_eq!(error.to_string(), "Expected a ']' but found end of input"); @@ -666,7 +715,7 @@ mod tests { #[test] fn parses_function_type_with_colon_in_parameter() { let src = "fn(value: T) -> Field"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let _ = parser.parse_type_or_error(); assert!(!parser.errors.is_empty()); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/use_tree.rs index 08834383f99c..ebe058632113 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{Ident, Path, PathKind, UseTree, UseTreeKind}, @@ -6,21 +6,23 @@ use crate::{ token::{Keyword, Token}, }; -use super::{parse_many::separated_by_comma_until_right_brace, Parser}; +use super::{Parser, parse_many::separated_by_comma_until_right_brace}; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Use = 'use' PathKind PathNoTurbofish UseTree /// /// UseTree = PathNoTurbofish ( '::' '{' UseTreeList? '}' )? /// /// UseTreeList = UseTree (',' UseTree)* ','? pub(super) fn parse_use_tree(&mut self) -> UseTree { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let kind = self.parse_path_kind(); let use_tree = self.parse_use_tree_without_kind( - start_span, kind, false, // nested + start_location, + kind, + false, // nested ); if !self.eat_semicolons() { self.expected_token(Token::Semicolon); @@ -30,14 +32,15 @@ impl<'a> Parser<'a> { pub(super) fn parse_use_tree_without_kind( &mut self, - start_span: Span, + start_location: Location, kind: PathKind, nested: bool, ) -> UseTree { let prefix = self.parse_path_after_kind( - kind, false, // allow turbofish + kind, + false, // allow turbofish false, // allow trailing double colon - start_span, + start_location, ); let trailing_double_colon = if prefix.segments.is_empty() && kind != PathKind::Plain { true @@ -56,37 +59,37 @@ impl<'a> Parser<'a> { UseTree { prefix, kind: UseTreeKind::List(use_trees), - span: self.span_since(start_span), + location: self.location_since(start_location), } } else { self.expected_token(Token::LeftBrace); - self.parse_path_use_tree_end(prefix, nested, start_span) + self.parse_path_use_tree_end(prefix, nested, start_location) } } else { - self.parse_path_use_tree_end(prefix, nested, start_span) + self.parse_path_use_tree_end(prefix, nested, start_location) } } fn parse_use_tree_in_list(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; // Special case: "self" cannot be followed by anything else if self.eat_self() { return Some(UseTree { - prefix: Path { segments: Vec::new(), kind: PathKind::Plain, span: start_span }, - kind: UseTreeKind::Path(Ident::new("self".to_string(), start_span), None), - span: start_span, + prefix: Path::plain(Vec::new(), start_location), + kind: UseTreeKind::Path(Ident::new("self".to_string(), start_location), None), + location: start_location, }); } let use_tree = self.parse_use_tree_without_kind( - start_span, + start_location, PathKind::Plain, true, // nested ); // If we didn't advance at all, we are done - if start_span == self.current_token_span { + if start_location.span == self.current_token_location.span { self.expected_label(ParsingRuleLabel::UseSegment); None } else { @@ -98,7 +101,7 @@ impl<'a> Parser<'a> { &mut self, mut prefix: Path, nested: bool, - start_span: Span, + start_location: Location, ) -> UseTree { if prefix.segments.is_empty() { if nested { @@ -109,7 +112,7 @@ impl<'a> Parser<'a> { UseTree { prefix, kind: UseTreeKind::Path(Ident::default(), None), - span: self.span_since(start_span), + location: self.location_since(start_location), } } else { let ident = prefix.segments.pop().unwrap().ident; @@ -118,21 +121,21 @@ impl<'a> Parser<'a> { UseTree { prefix, kind: UseTreeKind::Path(ident, Some(alias)), - span: self.span_since(start_span), + location: self.location_since(start_location), } } else { self.expected_identifier(); UseTree { prefix, kind: UseTreeKind::Path(ident, None), - span: self.span_since(start_span), + location: self.location_since(start_location), } } } else { UseTree { prefix, kind: UseTreeKind::Path(ident, None), - span: self.span_since(start_span), + location: self.location_since(start_location), } } } @@ -143,14 +146,12 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{ItemVisibility, PathKind, UseTree, UseTreeKind}, - parser::{ - parser::{parse_program, tests::expect_no_errors}, - ItemKind, - }, + parse_program_with_dummy_file, + parser::{ItemKind, parser::tests::expect_no_errors}, }; fn parse_use_tree_no_errors(src: &str) -> (UseTree, ItemVisibility) { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -281,14 +282,14 @@ mod tests { #[test] fn errors_on_crate_in_subtree() { let src = "use foo::{crate::bar}"; - let (_, errors) = parse_program(src); + let (_, errors) = parse_program_with_dummy_file(src); assert!(!errors.is_empty()); } #[test] fn errors_on_double_colon_after_self() { let src = "use foo::{self::bar};"; - let (_, errors) = parse_program(src); + let (_, errors) = parse_program_with_dummy_file(src); assert!(!errors.is_empty()); } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/where_clause.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/where_clause.rs index 8945e6f29f5f..08adea0a20a2 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/where_clause.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -1,15 +1,15 @@ use crate::{ - ast::{GenericTypeArgs, Path, PathKind, TraitBound, UnresolvedTraitConstraint, UnresolvedType}, + ast::{GenericTypeArgs, Path, TraitBound, UnresolvedTraitConstraint, UnresolvedType}, parser::labels::ParsingRuleLabel, token::{Keyword, Token}, }; use super::{ - parse_many::{separated_by, separated_by_comma}, Parser, + parse_many::{separated_by, separated_by_comma}, }; -impl<'a> Parser<'a> { +impl Parser<'_> { /// WhereClause = 'where' WhereClauseItems? /// /// WhereClauseItems = WhereClauseItem ( ',' WhereClauseItem )* ','? @@ -70,11 +70,7 @@ impl<'a> Parser<'a> { self.expected_label(ParsingRuleLabel::TraitBound); TraitBound { - trait_path: Path { - kind: PathKind::Plain, - segments: Vec::new(), - span: self.span_at_previous_token_end(), - }, + trait_path: Path::plain(Vec::new(), self.location_at_previous_token_end()), trait_id: None, trait_generics: GenericTypeArgs::default(), } @@ -93,16 +89,16 @@ mod tests { use crate::{ ast::UnresolvedTraitConstraint, parser::{ + Parser, ParserErrorReason, parser::tests::{ expect_no_errors, get_single_error_reason, get_source_with_error_span, }, - Parser, ParserErrorReason, }, token::Token, }; fn parse_where_clause_no_errors(src: &str) -> Vec { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let constraints = parser.parse_where_clause(); expect_no_errors(&parser.errors); constraints @@ -154,7 +150,7 @@ mod tests { ^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let mut constraints = parser.parse_where_clause(); let reason = get_single_error_reason(&parser.errors, span); @@ -179,7 +175,7 @@ mod tests { #[test] fn parses_where_clause_missing_trait_bound() { let src = "where Foo: "; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); parser.parse_where_clause(); assert!(!parser.errors.is_empty()); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/signed_field.rs b/noir/noir-repo/compiler/noirc_frontend/src/signed_field.rs new file mode 100644 index 000000000000..dcddd52daa8f --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/signed_field.rs @@ -0,0 +1,175 @@ +use acvm::{AcirField, FieldElement}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct SignedField { + pub field: FieldElement, + pub is_negative: bool, +} + +impl SignedField { + pub fn new(field: FieldElement, is_negative: bool) -> Self { + Self { field, is_negative } + } + + pub fn positive(field: impl Into) -> Self { + Self { field: field.into(), is_negative: false } + } + + pub fn negative(field: impl Into) -> Self { + Self { field: field.into(), is_negative: true } + } + + /// Convert a signed integer to a SignedField, carefully handling + /// INT_MIN in the process. Note that to convert an unsigned integer + /// you can call `SignedField::positive`. + #[inline] + pub fn from_signed(value: T) -> Self + where + T: num_traits::Signed + AbsU128, + { + let negative = value.is_negative(); + let value = value.abs_u128(); + SignedField::new(value.into(), negative) + } + + /// Convert a SignedField into an unsigned integer type (up to u128), + /// returning None if the value does not fit (e.g. if it is negative). + #[inline] + pub fn try_to_unsigned>(self) -> Option { + if self.is_negative { + return None; + } + + assert!(std::mem::size_of::() <= std::mem::size_of::()); + let u128_value = self.field.try_into_u128()?; + u128_value.try_into().ok() + } + + /// Convert a SignedField into a signed integer type (up to i128), + /// returning None if the value does not fit. This function is more complex + /// for handling negative values, specifically INT_MIN which we can't cast from + /// a u128 to i128 without wrapping it. + #[inline] + pub fn try_to_signed(self) -> Option + where + T: TryFrom + TryFrom + num_traits::Signed + num_traits::Bounded + AbsU128, + u128: TryFrom, + { + let u128_value = self.field.try_into_u128()?; + + if self.is_negative { + // The positive version of the minimum value of this type. + // E.g. 128 for i8. + let positive_min = T::min_value().abs_u128(); + + // If it is the min value, we can't negate it without overflowing + // so test for it and return it directly + if u128_value == positive_min { + Some(T::min_value()) + } else { + let i128_value = -(u128_value as i128); + T::try_from(i128_value).ok() + } + } else { + T::try_from(u128_value).ok() + } + } +} + +impl std::ops::Neg for SignedField { + type Output = Self; + + fn neg(mut self) -> Self::Output { + self.is_negative = !self.is_negative; + self + } +} + +impl Ord for SignedField { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + if self.is_negative != other.is_negative { + if self.is_negative { std::cmp::Ordering::Less } else { std::cmp::Ordering::Greater } + } else if self.is_negative { + // Negative comparisons should be reversed so that -2 < -1 + other.field.cmp(&self.field) + } else { + self.field.cmp(&other.field) + } + } +} + +impl PartialOrd for SignedField { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl From for SignedField { + fn from(value: FieldElement) -> Self { + Self::new(value, false) + } +} + +impl From for FieldElement { + fn from(value: SignedField) -> Self { + if value.is_negative { -value.field } else { value.field } + } +} + +impl std::fmt::Display for SignedField { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.is_negative { + write!(f, "-")?; + } + write!(f, "{}", self.field) + } +} + +pub trait AbsU128 { + /// Necessary to handle casting to unsigned generically without overflowing on INT_MIN. + fn abs_u128(self) -> u128; +} + +macro_rules! impl_unsigned_abs_for { + ($typ:ty) => { + impl AbsU128 for $typ { + fn abs_u128(self) -> u128 { + self.unsigned_abs() as u128 + } + } + }; +} + +impl_unsigned_abs_for!(i8); +impl_unsigned_abs_for!(i16); +impl_unsigned_abs_for!(i32); +impl_unsigned_abs_for!(i64); +impl_unsigned_abs_for!(i128); + +#[cfg(test)] +mod tests { + use super::SignedField; + + #[test] + fn int_min_to_signed_field_roundtrip() { + let x = i128::MIN; + let field = SignedField::from_signed(x); + assert_eq!(field.try_to_signed(), Some(x)); + } + + #[test] + fn comparisons() { + let neg_two = SignedField::negative(2u32); + let neg_one = SignedField::negative(1u32); + let zero = SignedField::positive(0u32); + let one = SignedField::positive(1u32); + let two = SignedField::positive(2u32); + + assert!(one < two); + assert!(zero < one); + assert!(neg_one < zero); + assert!(neg_two < neg_one); + + assert!(two > neg_two); + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs index 6ff3b204f2e0..e53aa392fa96 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs @@ -3,6 +3,7 @@ mod aliases; mod arithmetic_generics; mod bound_checks; +mod enums; mod imports; mod metaprogramming; mod name_shadowing; @@ -15,22 +16,17 @@ mod visibility; // XXX: These tests repeat a lot of code // what we should do is have test cases which are passed to a test harness // A test harness will allow for more expressive and readable tests -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; +use crate::elaborator::{FrontendOptions, UnstableFeature}; use fm::FileId; use iter_extended::vecmap; -use noirc_errors::Location; +use noirc_errors::{CustomDiagnostic, Location, Span}; -use crate::ast::IntegerBitSize; -use crate::hir::comptime::InterpreterError; +use crate::hir::Context; use crate::hir::def_collector::dc_crate::CompilationError; -use crate::hir::def_collector::errors::{DefCollectorErrorKind, DuplicateType}; use crate::hir::def_map::ModuleData; -use crate::hir::resolution::errors::ResolverError; -use crate::hir::resolution::import::PathResolutionError; -use crate::hir::type_check::TypeCheckError; -use crate::hir::Context; use crate::node_interner::{NodeInterner, StmtId}; use crate::hir::def_collector::dc_crate::DefCollector; @@ -42,16 +38,16 @@ use crate::monomorphization::errors::MonomorphizationError; use crate::monomorphization::monomorphize; use crate::parser::{ItemKind, ParserErrorReason}; use crate::token::SecondaryAttribute; -use crate::{parse_program, ParsedModule}; +use crate::{ParsedModule, parse_program}; use fm::FileManager; use noirc_arena::Arena; -pub(crate) fn has_parser_error(errors: &[(CompilationError, FileId)]) -> bool { - errors.iter().any(|(e, _f)| matches!(e, CompilationError::ParseError(_))) +pub(crate) fn has_parser_error(errors: &[CompilationError]) -> bool { + errors.iter().any(|e| matches!(e, CompilationError::ParseError(_))) } -pub(crate) fn remove_experimental_warnings(errors: &mut Vec<(CompilationError, FileId)>) { - errors.retain(|(error, _)| match error { +pub(crate) fn remove_experimental_warnings(errors: &mut Vec) { + errors.retain(|error| match error { CompilationError::ParseError(error) => { !matches!(error.reason(), Some(ParserErrorReason::ExperimentalFeature(..))) } @@ -59,19 +55,29 @@ pub(crate) fn remove_experimental_warnings(errors: &mut Vec<(CompilationError, F }); } -pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec<(CompilationError, FileId)>) { - get_program_with_maybe_parser_errors( - src, false, // allow parser errors - ) +pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec) { + let allow_parser_errors = false; + get_program_with_options(src, allow_parser_errors, FrontendOptions::test_default()) +} + +pub(crate) fn get_program_using_features( + src: &str, + features: &[UnstableFeature], +) -> (ParsedModule, Context<'static, 'static>, Vec) { + let allow_parser_errors = false; + let mut options = FrontendOptions::test_default(); + options.enabled_unstable_features = features; + get_program_with_options(src, allow_parser_errors, options) } /// Compile a program. /// /// The stdlib is not available for these snippets. -pub(crate) fn get_program_with_maybe_parser_errors( +pub(crate) fn get_program_with_options( src: &str, allow_parser_errors: bool, -) -> (ParsedModule, Context, Vec<(CompilationError, FileId)>) { + options: FrontendOptions, +) -> (ParsedModule, Context<'static, 'static>, Vec) { let root = std::path::Path::new("/"); let fm = FileManager::new(root); @@ -80,8 +86,8 @@ pub(crate) fn get_program_with_maybe_parser_errors( let root_file_id = FileId::dummy(); let root_crate_id = context.crate_graph.add_crate_root(root_file_id); - let (program, parser_errors) = parse_program(src); - let mut errors = vecmap(parser_errors, |e| (e.into(), root_file_id)); + let (program, parser_errors) = parse_program(src, root_file_id); + let mut errors = vecmap(parser_errors, |e| e.into()); remove_experimental_warnings(&mut errors); if allow_parser_errors || !has_parser_error(&errors) { @@ -116,23 +122,19 @@ pub(crate) fn get_program_with_maybe_parser_errors( extern_prelude: BTreeMap::new(), }; - let debug_comptime_in_file = None; - let pedantic_solving = true; - // Now we want to populate the CrateDefMap using the DefCollector errors.extend(DefCollector::collect_crate_and_dependencies( def_map, &mut context, program.clone().into_sorted(), root_file_id, - debug_comptime_in_file, - pedantic_solving, + options, )); } (program, context, errors) } -pub(crate) fn get_program_errors(src: &str) -> Vec<(CompilationError, FileId)> { +pub(crate) fn get_program_errors(src: &str) -> Vec { get_program(src).2 } @@ -143,6 +145,161 @@ fn assert_no_errors(src: &str) { } } +/// Given a source file with annotated errors, like this +/// +/// fn main() -> pub i32 { +/// ^^^ expected i32 because of return type +/// true +/// ~~~~ bool returned here +/// } +/// +/// where: +/// - lines with "^^^" are primary errors +/// - lines with "~~~" are secondary errors +/// +/// this method will check that compiling the program without those error markers +/// will produce errors at those locations and with/ those messages. +fn check_errors(src: &str) { + let allow_parser_errors = false; + check_errors_with_options(src, allow_parser_errors, FrontendOptions::test_default()); +} + +fn check_errors_using_features(src: &str, features: &[UnstableFeature]) { + let allow_parser_errors = false; + let mut options = FrontendOptions::test_default(); + options.enabled_unstable_features = features; + check_errors_with_options(src, allow_parser_errors, options); +} + +fn check_errors_with_options(src: &str, allow_parser_errors: bool, options: FrontendOptions) { + let lines = src.lines().collect::>(); + + // Here we'll hold just the lines that are code + let mut code_lines = Vec::new(); + // Here we'll capture lines that are primary error spans, like: + // + // ^^^ error message + let mut primary_spans_with_errors: Vec<(Span, String)> = Vec::new(); + // Here we'll capture lines that are secondary error spans, like: + // + // ~~~ error message + let mut secondary_spans_with_errors: Vec<(Span, String)> = Vec::new(); + // The byte at the start of this line + let mut byte = 0; + // The length of the last line, needed to go back to the byte at the beginning of the last line + let mut last_line_length = 0; + for line in lines { + if let Some((span, message)) = + get_error_line_span_and_message(line, '^', byte, last_line_length) + { + primary_spans_with_errors.push((span, message)); + continue; + } + + if let Some((span, message)) = + get_error_line_span_and_message(line, '~', byte, last_line_length) + { + secondary_spans_with_errors.push((span, message)); + continue; + } + + code_lines.push(line); + + byte += line.len() + 1; // For '\n' + last_line_length = line.len(); + } + + let mut primary_spans_with_errors: HashMap = + primary_spans_with_errors.into_iter().collect(); + + let mut secondary_spans_with_errors: HashMap = + secondary_spans_with_errors.into_iter().collect(); + + let src = code_lines.join("\n"); + let (_, _, errors) = get_program_with_options(&src, allow_parser_errors, options); + if errors.is_empty() && !primary_spans_with_errors.is_empty() { + panic!("Expected some errors but got none"); + } + + let errors = errors.iter().map(CustomDiagnostic::from).collect::>(); + for error in &errors { + let secondary = error + .secondaries + .first() + .unwrap_or_else(|| panic!("Expected {:?} to have a secondary label", error)); + let span = secondary.location.span; + let message = &error.message; + + let Some(expected_message) = primary_spans_with_errors.remove(&span) else { + if let Some(message) = secondary_spans_with_errors.get(&span) { + panic!( + "Error at {span:?} with message {message:?} is annotated as secondary but should be primary" + ); + } else { + panic!( + "Couldn't find primary error at {span:?} with message {message:?}.\nAll errors: {errors:?}" + ); + } + }; + + assert_eq!(message, &expected_message, "Primary error at {span:?} has unexpected message"); + + for secondary in &error.secondaries { + let message = &secondary.message; + if message.is_empty() { + continue; + } + + let span = secondary.location.span; + let Some(expected_message) = secondary_spans_with_errors.remove(&span) else { + if let Some(message) = primary_spans_with_errors.get(&span) { + panic!( + "Error at {span:?} with message {message:?} is annotated as primary but should be secondary" + ); + } else { + panic!( + "Couldn't find secondary error at {span:?} with message {message:?}.\nAll errors: {errors:?}" + ); + }; + }; + + assert_eq!( + message, &expected_message, + "Secondary error at {span:?} has unexpected message" + ); + } + } + + if !primary_spans_with_errors.is_empty() { + panic!("These primary errors didn't happen: {primary_spans_with_errors:?}"); + } + + if !secondary_spans_with_errors.is_empty() { + panic!("These secondary errors didn't happen: {secondary_spans_with_errors:?}"); + } +} + +/// Helper function for `check_errors` that returns the span that +/// `^^^^` or `~~~~` occupy, together with the message that follows it. +fn get_error_line_span_and_message( + line: &str, + char: char, + byte: usize, + last_line_length: usize, +) -> Option<(Span, String)> { + if !line.trim().starts_with(char) { + return None; + } + + let chars = line.chars().collect::>(); + let first_caret = chars.iter().position(|c| *c == char).unwrap(); + let last_caret = chars.iter().rposition(|c| *c == char).unwrap(); + let start = byte - last_line_length; + let span = Span::from((start + first_caret - 1) as u32..(start + last_caret) as u32); + let error = line.trim().trim_start_matches(char).trim().to_string(); + Some((span, error)) +} + #[test] fn check_trait_implemented_for_all_t() { let src = " @@ -205,10 +362,13 @@ fn check_trait_implementation_duplicate_method() { impl Default for Foo { // Duplicate trait methods should not compile fn default(x: Field, y: Field) -> Field { + ~~~~~~~ First trait associated function found here y + 2 * x } // Duplicate trait methods should not compile fn default(x: Field, y: Field) -> Field { + ^^^^^^^ Duplicate definitions of trait associated function with name default found + ~~~~~~~ Second trait associated function found here x + 2 * y } } @@ -216,27 +376,7 @@ fn check_trait_implementation_duplicate_method() { fn main() { let _ = Foo { bar: 1, array: [2, 3] }; // silence Foo never constructed warning }"; - - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { - typ, - first_def, - second_def, - }) => { - assert_eq!(typ, &DuplicateType::TraitAssociatedFunction); - assert_eq!(first_def, "default"); - assert_eq!(second_def, "default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -251,6 +391,7 @@ fn check_trait_wrong_method_return_type() { impl Default for Foo { fn default() -> Field { + ^^^^^ Expected type Foo, found type Field 0 } } @@ -259,25 +400,7 @@ fn check_trait_wrong_method_return_type() { let _ = Foo {}; // silence Foo never constructed warning } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) => { - assert_eq!(expected_typ, "Foo"); - assert_eq!(expr_typ, "Field"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -294,6 +417,7 @@ fn check_trait_wrong_method_return_type2() { impl Default for Foo { fn default(x: Field, _y: Field) -> Field { + ^^^^^ Expected type Foo, found type Field x } } @@ -301,25 +425,32 @@ fn check_trait_wrong_method_return_type2() { fn main() { let _ = Foo { bar: 1, array: [2, 3] }; // silence Foo never constructed warning }"; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) => { - assert_eq!(expected_typ, "Foo"); - assert_eq!(expr_typ, "Field"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; + check_errors(src); +} + +#[test] +fn check_trait_wrong_method_return_type3() { + let src = " + trait Default { + fn default(x: Field, y: Field) -> Self; + } + + struct Foo { + bar: Field, + array: [Field; 2], + } + + impl Default for Foo { + fn default(_x: Field, _y: Field) { + ^ Expected type Foo, found type () + } + } + + fn main() { + let _ = Foo { bar: 1, array: [2, 3] }; // silence Foo never constructed warning } + "; + check_errors(src); } #[test] @@ -338,6 +469,8 @@ fn check_trait_missing_implementation() { } impl Default for Foo { + ^^^ Method `method2` from trait `Default` is not implemented + ~~~ Please implement method2 here fn default(x: Field, y: Field) -> Self { Self { bar: x, array: [x,y] } } @@ -346,25 +479,7 @@ fn check_trait_missing_implementation() { fn main() { } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::TraitMissingMethod { - trait_name, - method_name, - trait_impl_span: _, - }) => { - assert_eq!(trait_name, "Default"); - assert_eq!(method_name, "method2"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -375,8 +490,8 @@ fn check_trait_not_in_scope() { array: [Field; 2], } - // Default trait does not exist impl Default for Foo { + ^^^^^^^ Trait Default not found fn default(x: Field, y: Field) -> Self { Self { bar: x, array: [x,y] } } @@ -384,23 +499,8 @@ fn check_trait_not_in_scope() { fn main() { } - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::TraitNotFound { - trait_path, - }) => { - assert_eq!(trait_path.as_string(), "Default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -414,9 +514,9 @@ fn check_trait_wrong_method_name() { array: [Field; 2], } - // wrong trait name method should not compile impl Default for Foo { fn does_not_exist(x: Field, y: Field) -> Self { + ^^^^^^^^^^^^^^ Method with name `does_not_exist` is not part of trait `Default`, therefore it can't be implemented Self { bar: x, array: [x,y] } } } @@ -424,28 +524,7 @@ fn check_trait_wrong_method_name() { fn main() { let _ = Foo { bar: 1, array: [2, 3] }; // silence Foo never constructed warning }"; - let compilation_errors = get_program_errors(src); - assert!(!has_parser_error(&compilation_errors)); - assert!( - compilation_errors.len() == 1, - "Expected 1 compilation error, got: {:?}", - compilation_errors - ); - - for (err, _file_id) in compilation_errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::MethodNotInTrait { - trait_name, - impl_method, - }) => { - assert_eq!(trait_name, "Default"); - assert_eq!(impl_method, "does_not_exist"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -461,6 +540,7 @@ fn check_trait_wrong_parameter() { impl Default for Foo { fn default(x: u32) -> Self { + ^^^ Parameter #1 of method `default` must be of type Field, not u32 Foo {bar: x} } } @@ -468,27 +548,7 @@ fn check_trait_wrong_parameter() { fn main() { } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { - method_name, - expected_typ, - actual_typ, - .. - }) => { - assert_eq!(method_name, "default"); - assert_eq!(expected_typ, "Field"); - assert_eq!(actual_typ, "u32"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -505,34 +565,15 @@ fn check_trait_wrong_parameter2() { impl Default for Foo { fn default(x: Field, y: Foo) -> Self { + ^^^ Parameter #2 of method `default` must be of type Field, not Foo Self { bar: x, array: [x, y.bar] } } } fn main() { - }"; - - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { - method_name, - expected_typ, - actual_typ, - .. - }) => { - assert_eq!(method_name, "default"); - assert_eq!(expected_typ, "Field"); - assert_eq!(actual_typ, "Foo"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; } + "; + check_errors(src); } #[test] @@ -540,30 +581,14 @@ fn check_trait_wrong_parameter_type() { let src = " pub trait Default { fn default(x: Field, y: NotAType) -> Field; + ^^^^^^^^ Could not resolve 'NotAType' in path } fn main(x: Field, y: Field) { assert(y == x); - }"; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - - // This is a duplicate error in the name resolver & type checker. - // In the elaborator there is no duplicate and only 1 error is issued - assert!(errors.len() <= 2, "Expected 1 or 2 errors, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Unresolved(ident), - )) => { - assert_eq!(ident, "NotAType"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; } + "; + check_errors(src); } #[test] @@ -580,6 +605,7 @@ fn check_trait_wrong_parameters_count() { impl Default for Foo { fn default(x: Field) -> Self { + ^^^^^^^ `Default::default` expects 2 parameters, but this method has 1 Self { bar: x, array: [x, x] } } } @@ -587,28 +613,7 @@ fn check_trait_wrong_parameters_count() { fn main() { } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::MismatchTraitImplNumParameters { - actual_num_parameters, - expected_num_parameters, - trait_name, - method_name, - .. - }) => { - assert_eq!(actual_num_parameters, &1_usize); - assert_eq!(expected_num_parameters, &2_usize); - assert_eq!(method_name, "default"); - assert_eq!(trait_name, "Default"); - } - _ => { - panic!("No other errors are expected in this test case! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -619,6 +624,7 @@ fn check_trait_impl_for_non_type() { } impl Default for main { + ^^^^ expected type got function fn default(x: Field, y: Field) -> Field { x + y } @@ -626,20 +632,7 @@ fn check_trait_impl_for_non_type() { fn main() {} "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::ResolverError(ResolverError::Expected { expected, got, .. }) => { - assert_eq!(*expected, "type"); - assert_eq!(*got, "function"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -655,8 +648,8 @@ fn check_impl_struct_not_trait() { z: Field, } - // Default is a struct not a trait impl Default for Foo { + ^^^^^^^ Default is not a trait, therefore it can't be implemented fn default(x: Field, y: Field) -> Self { Self { bar: x, array: [x,y] } } @@ -666,27 +659,14 @@ fn check_impl_struct_not_trait() { let _ = Default { x: 1, z: 1 }; // silence Default never constructed warning } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::NotATrait { - not_a_trait_name, - }) => { - assert_eq!(not_a_trait_name.to_string(), "Default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] fn check_trait_duplicate_declaration() { let src = " trait Default { + ~~~~~~~ First trait definition found here fn default(x: Field, y: Field) -> Self; } @@ -701,32 +681,16 @@ fn check_trait_duplicate_declaration() { } } - trait Default { + ^^^^^^^ Duplicate definitions of trait definition with name Default found + ~~~~~~~ Second trait definition found here fn default(x: Field) -> Self; } fn main() { - }"; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { - typ, - first_def, - second_def, - }) => { - assert_eq!(typ, &DuplicateType::Trait); - assert_eq!(first_def, "Default"); - assert_eq!(second_def, "Default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; } + "; + check_errors(src); } #[test] @@ -739,29 +703,17 @@ fn check_trait_duplicate_implementation() { } impl Default for Foo { + ~~~~~~~ Previous impl defined here } impl Default for Foo { + ^^^ Impl for type `Foo` overlaps with existing impl + ~~~ Overlapping impl } fn main() { let _ = Foo { bar: 1 }; // silence Foo never constructed warning } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { - .. - }) => (), - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { - .. - }) => (), - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -776,31 +728,19 @@ fn check_trait_duplicate_implementation_with_alias() { type MyType = MyStruct; impl Default for MyStruct { + ~~~~~~~ Previous impl defined here } impl Default for MyType { + ^^^^^^ Impl for type `MyType` overlaps with existing impl + ~~~~~~ Overlapping impl } fn main() { let _ = MyStruct {}; // silence MyStruct never constructed warning } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { - .. - }) => (), - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { - .. - }) => (), - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -818,7 +758,8 @@ fn test_impl_self_within_default_def() { fn ok(self) -> Self { self } - }"; + } + "; assert_no_errors(src); } @@ -945,6 +886,7 @@ fn resolve_empty_function() { "; assert_no_errors(src); } + #[test] fn resolve_basic_function() { let src = r#" @@ -955,24 +897,18 @@ fn resolve_basic_function() { "#; assert_no_errors(src); } + #[test] fn resolve_unused_var() { let src = r#" fn main(x : Field) { let y = x + x; + ^ unused variable y + ~ unused variable assert(x == x); } "#; - - let errors = get_program_errors(src); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - // It should be regarding the unused variable - match &errors[0].0 { - CompilationError::ResolverError(ResolverError::UnusedVariable { ident }) => { - assert_eq!(&ident.0.contents, "y"); - } - _ => unreachable!("we should only have an unused var error"), - } + check_errors(src); } #[test] @@ -981,17 +917,11 @@ fn resolve_unresolved_var() { fn main(x : Field) { let y = x + x; assert(y == z); + ^ cannot find `z` in this scope + ~ not found in this scope } "#; - let errors = get_program_errors(src); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - // It should be regarding the unresolved var `z` (Maybe change to undeclared and special case) - match &errors[0].0 { - CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, span: _ }) => { - assert_eq!(name, "z"); - } - _ => unimplemented!("we should only have an unresolved variable"), - } + check_errors(src); } #[test] @@ -999,23 +929,10 @@ fn unresolved_path() { let src = " fn main(x : Field) { let _z = some::path::to::a::func(x); + ^^^^ Could not resolve 'some' in path } "; - let errors = get_program_errors(src); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (compilation_error, _file_id) in errors { - match compilation_error { - CompilationError::ResolverError(err) => { - match err { - ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { - assert_eq!(name.to_string(), "some"); - } - _ => unimplemented!("we should only have an unresolved function"), - }; - } - _ => unimplemented!(), - } - } + check_errors(src); } #[test] @@ -1034,36 +951,16 @@ fn multiple_resolution_errors() { let src = r#" fn main(x : Field) { let y = foo::bar(x); + ^^^ Could not resolve 'foo' in path let z = y + a; + ^ cannot find `a` in this scope + ~ not found in this scope + ^ unused variable z + ~ unused variable + } "#; - - let errors = get_program_errors(src); - assert!(errors.len() == 3, "Expected 3 errors, got: {:?}", errors); - - // Errors are: - // `a` is undeclared - // `z` is unused - // `foo::bar` does not exist - for (compilation_error, _file_id) in errors { - match compilation_error { - CompilationError::ResolverError(err) => { - match err { - ResolverError::UnusedVariable { ident } => { - assert_eq!(&ident.0.contents, "z"); - } - ResolverError::VariableNotDeclared { name, .. } => { - assert_eq!(name, "a"); - } - ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { - assert_eq!(name.to_string(), "foo"); - } - _ => unimplemented!(), - }; - } - _ => unimplemented!(), - } - } + check_errors(src); } #[test] @@ -1145,8 +1042,8 @@ fn resolve_basic_closure() { #[test] fn resolve_simplified_closure() { // based on bug https://github.com/noir-lang/noir/issues/1088 - - let src = r#"fn do_closure(x: Field) -> Field { + let src = r#" + fn do_closure(x: Field) -> Field { let y = x; let ret_capture = || { y @@ -1211,38 +1108,20 @@ fn resolve_fmt_strings() { let src = r#" fn main() { let string = f"this is i: {i}"; + ^ cannot find `i` in this scope + ~ not found in this scope println(string); + ^^^^^^^^^^^^^^^ Unused expression result of type fmtstr<14, ()> let new_val = 10; println(f"random_string{new_val}{new_val}"); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unused expression result of type fmtstr<31, (Field, Field)> } fn println(x : T) -> T { x } "#; - - let errors = get_program_errors(src); - assert!(errors.len() == 3, "Expected 5 errors, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::ResolverError(ResolverError::VariableNotDeclared { - name, .. - }) => { - assert_eq!(name, "i"); - } - CompilationError::TypeError(TypeCheckError::UnusedResultError { - expr_type: _, - expr_span, - }) => { - let a = src.get(expr_span.start() as usize..expr_span.end() as usize).unwrap(); - assert!( - a == "println(string)" || a == "println(f\"random_string{new_val}{new_val}\")" - ); - } - _ => unimplemented!(), - }; - } + check_errors(src); } fn monomorphize_program(src: &str) -> Result { @@ -1291,24 +1170,20 @@ fn lambda$f1(mut env$l1: (Field)) -> Field { check_rewrite(src, expected_rewrite); } -// TODO(https://github.com/noir-lang/noir/issues/6780): currently failing -// with a stack overflow #[test] -#[ignore] fn deny_cyclic_globals() { let src = r#" global A: u32 = B; + ^ This global recursively depends on itself + ^ Dependency cycle found + ~ 'A' recursively depends on itself: A -> B -> A global B: u32 = A; + ^ Variable not in scope + ~ Could not find variable fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::DependencyCycle { .. }) - )); + check_errors(src); } #[test] @@ -1316,9 +1191,11 @@ fn deny_cyclic_type_aliases() { let src = r#" type A = B; type B = A; + ^^^^^^^^^^ Dependency cycle found + ~~~~~~~~~~ 'B' recursively depends on itself: B -> A -> B fn main() {} "#; - assert_eq!(get_program_errors(src).len(), 1); + check_errors(src); } #[test] @@ -1328,9 +1205,10 @@ fn ensure_nested_type_aliases_type_check() { type B = u8; fn main() { let _a: A = 0 as u16; + ^^^^^^^^ Expected type A, found type u16 } "#; - assert_eq!(get_program_errors(src).len(), 1); + check_errors(src); } #[test] @@ -1339,7 +1217,7 @@ fn type_aliases_in_entry_point() { type Foo = u8; fn main(_x: Foo) {} "#; - assert_eq!(get_program_errors(src).len(), 0); + assert_no_errors(src); } #[test] @@ -1351,7 +1229,7 @@ fn operators_in_global_used_in_type() { let _array: [Field; COUNT] = [1, 2, 3]; } "#; - assert_eq!(get_program_errors(src).len(), 0); + assert_no_errors(src); } #[test] @@ -1361,14 +1239,18 @@ fn break_and_continue_in_constrained_fn() { for i in 0 .. 10 { if i == 2 { continue; + ^^^^^^^^^ continue is only allowed in unconstrained functions + ~~~~~~~~~ Constrained code must always have a known number of loop iterations } if i == 5 { break; + ^^^^^^ break is only allowed in unconstrained functions + ~~~~~~ Constrained code must always have a known number of loop iterations } } } "#; - assert_eq!(get_program_errors(src).len(), 2); + check_errors(src); } #[test] @@ -1376,10 +1258,12 @@ fn break_and_continue_outside_loop() { let src = r#" unconstrained fn main() { continue; + ^^^^^^^^^ continue is only allowed within loops break; + ^^^^^^ break is only allowed within loops } "#; - assert_eq!(get_program_errors(src).len(), 2); + check_errors(src); } // Regression for #2540 @@ -1395,8 +1279,7 @@ fn for_loop_over_array() { hello(array); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } // Regression for #4545 @@ -1406,51 +1289,47 @@ fn type_aliases_in_main() { type Outer = [u8; N]; fn main(_arg: Outer<1>) {} "#; - assert_eq!(get_program_errors(src).len(), 0); + assert_no_errors(src); } #[test] fn ban_mutable_globals() { - // Mutable globals are only allowed in a comptime context let src = r#" mut global FOO: Field = 0; + ^^^ Only `comptime` globals may be mutable fn main() { let _ = FOO; // silence FOO never used warning } "#; - assert_eq!(get_program_errors(src).len(), 1); + check_errors(src); } #[test] fn deny_inline_attribute_on_unconstrained() { + // TODO: improve the error location let src = r#" #[no_predicates] unconstrained pub fn foo(x: Field, y: Field) { + ^^^ misplaced #[no_predicates] attribute on unconstrained function foo. Only allowed on constrained functions + ~~~ misplaced #[no_predicates] attribute assert(x != y); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::NoPredicatesAttributeOnUnconstrained { .. }) - )); + check_errors(src); } #[test] fn deny_fold_attribute_on_unconstrained() { + // TODO: improve the error location let src = r#" #[fold] unconstrained pub fn foo(x: Field, y: Field) { + ^^^ misplaced #[fold] attribute on unconstrained function foo. Only allowed on constrained functions + ~~~ misplaced #[fold] attribute assert(x != y); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::FoldAttributeOnUnconstrained { .. }) - )); + check_errors(src); } #[test] @@ -1480,8 +1359,7 @@ fn specify_function_types_with_turbofish() { let _ = generic_func::(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -1514,8 +1392,7 @@ fn specify_method_types_with_turbofish() { let _ = foo.generic_method::(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -1543,14 +1420,10 @@ fn incorrect_turbofish_count_function_call() { fn main() { let _ = generic_func::(); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Expected 2 generics from this function, but 3 were provided } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), - )); + check_errors(src); } #[test] @@ -1581,14 +1454,10 @@ fn incorrect_turbofish_count_method_call() { fn main() { let foo: Foo = Foo { inner: 1 }; let _ = foo.generic_method::(); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Expected 1 generic from this function, but 2 were provided } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), - )); + check_errors(src); } #[test] @@ -1599,15 +1468,12 @@ fn struct_numeric_generic_in_function() { } pub fn bar() { + ^ N has a type of Foo. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`. + ~ Unsupported numeric generic type let _ = Foo { inner: 1 }; // silence Foo never constructed warning } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); + check_errors(src); } #[test] @@ -1618,19 +1484,18 @@ fn struct_numeric_generic_in_struct() { } pub struct Bar { } + ^ N has a type of Foo. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`. + ~ Unsupported numeric generic type "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType(_)), - )); + check_errors(src); } #[test] fn bool_numeric_generic() { let src = r#" pub fn read() -> Field { + ^ N has a type of bool. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`. + ~ Unsupported numeric generic type if N { 0 } else { @@ -1638,12 +1503,7 @@ fn bool_numeric_generic() { } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); + check_errors(src); } #[test] @@ -1652,49 +1512,43 @@ fn numeric_generic_binary_operation_type_mismatch() { pub fn foo() -> bool { let mut check: bool = true; check = N; + ^ Cannot assign an expression of type Field to a value of type bool check } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }), - )); + check_errors(src); } #[test] fn bool_generic_as_loop_bound() { let src = r#" - pub fn read() { // error here - let mut fields = [0; N]; // error here - for i in 0..N { // error here + pub fn read() { + ^ N has a type of bool. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`. + ~ Unsupported numeric generic type + let mut fields = [0; N]; + ^ The numeric generic is not of type `u32` + ~ expected `u32`, found `bool` + for i in 0..N { + ^ Expected type Field, found type bool fields[i] = i + 1; } assert(fields[0] == 1); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); - - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, expr_typ, .. - }) = &errors[2].0 - else { - panic!("Got an error other than a type mismatch"); - }; + check_errors(src); +} - assert_eq!(expected_typ, "Field"); - assert_eq!(expr_typ, "bool"); +#[test] +fn wrong_type_in_for_range() { + let src = r#" + pub fn foo() { + for _ in true..false { + ^^^^ The type bool cannot be used in a for loop + + } + } + "#; + check_errors(src); } #[test] @@ -1711,91 +1565,68 @@ fn numeric_generic_as_struct_field_type_fails() { pub struct Foo { a: Field, b: N, + ^ Expected type, found numeric generic + ~ not a type } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] fn normal_generic_as_array_length() { + // TODO: improve error location, should be just on N let src = r#" pub struct Foo { a: Field, b: [Field; N], + ^^^^^^^^^^ Type provided when a numeric generic was expected + ~~~~~~~~~~ the numeric generic is not of type `u32` } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] fn numeric_generic_as_param_type() { let src = r#" pub fn foo(x: I) -> I { + ^ Expected type, found numeric generic + ~ not a type + ^ Expected type, found numeric generic + ~ not a type + + let _q: I = 5; + ^ Expected type, found numeric generic + ~ not a type x } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); - - // Error from the parameter type - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // Error from the let statement annotated type - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // Error from the return type - assert!(matches!( - errors[2].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] fn numeric_generic_as_unused_param_type() { let src = r#" pub fn foo(_x: I) { } + ^ Expected type, found numeric generic + ~ not a type "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] fn numeric_generic_as_unused_trait_fn_param_type() { let src = r#" trait Foo { + ^^^ unused trait Foo + ~~~ unused trait fn foo(_x: I) { } + ^ Expected type, found numeric generic + ~ not a type } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // Foo is unused - assert!(matches!( - errors[1].0, - CompilationError::ResolverError(ResolverError::UnusedItem { .. }), - )); + check_errors(src); } #[test] @@ -1807,24 +1638,16 @@ fn numeric_generic_as_return_type() { } fn foo(x: T) -> I where T: Zeroed { + ^ Expected type, found numeric generic + ~ not a type + ^^^ unused function foo + ~~~ unused function x.zeroed() } fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - // Error from the return type - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // foo is unused - assert!(matches!( - errors[1].0, - CompilationError::ResolverError(ResolverError::UnusedItem { .. }), - )); + check_errors(src); } #[test] @@ -1836,14 +1659,11 @@ fn numeric_generic_used_in_nested_type_fails() { } pub struct Bar { inner: N + ^ Expected type, found numeric generic + ~ not a type } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] @@ -1852,17 +1672,14 @@ fn normal_generic_used_in_nested_array_length_fail() { pub struct Foo { a: Field, b: Bar, + ^ Type provided when a numeric generic was expected + ~ the numeric generic is not of type `u32` } pub struct Bar { inner: [Field; N] } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] @@ -1977,6 +1794,7 @@ fn numeric_generic_used_in_turbofish() { // allow u16 to be used as an array size #[test] fn numeric_generic_u16_array_size() { + // TODO: improve the error location let src = r#" fn len(_arr: [Field; N]) -> u32 { N @@ -1984,19 +1802,14 @@ fn numeric_generic_u16_array_size() { pub fn foo() -> u32 { let fields: [Field; N] = [0; N]; + ^ The numeric generic is not of type `u32` + ~ expected `u32`, found `u16` + ^^^^^^^^^^ The numeric generic is not of type `u32` + ~~~~~~~~~~ expected `u32`, found `u16` len(fields) } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] @@ -2010,8 +1823,7 @@ fn numeric_generic_field_larger_than_u32() { let _ = foo::(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -2034,8 +1846,7 @@ fn numeric_generic_field_arithmetic_larger_than_u32() { let _ = size(foo::()); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -2043,14 +1854,11 @@ fn cast_256_to_u8_size_checks() { let src = r#" fn main() { assert(256 as u8 == 0); + ^^^^^^^^^ Casting value of type Field to a smaller type (u8) + ~~~~~~~~~ casting untyped value (256) to a type with a maximum size (255) that's smaller than it } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::DownsizingCast { .. }), - )); + check_errors(src); } // TODO(https://github.com/noir-lang/noir/issues/6247): @@ -2062,8 +1870,7 @@ fn cast_negative_one_to_u8_size_checks() { assert((-1) as u8 != 0); } "#; - let errors = get_program_errors(src); - assert!(errors.is_empty()); + assert_no_errors(src); } #[test] @@ -2098,48 +1905,36 @@ fn normal_generic_used_when_numeric_expected_in_where_clause() { } pub fn read() -> T where T: Deserialize { + ^ Type provided when a numeric generic was expected + ~ the numeric generic is not of type `u32` T::deserialize([0, 1]) } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); + // TODO: improve the error location for the array (should be on N) let src = r#" trait Deserialize { fn deserialize(fields: [Field; N]) -> Self; } pub fn read() -> T where T: Deserialize { + ^ Type provided when a numeric generic was expected + ~ the numeric generic is not of type `u32` let mut fields: [Field; N] = [0; N]; + ^ Type provided when a numeric generic was expected + ~ the numeric generic is not of type `u32` + ^^^^^^^^^^ Type provided when a numeric generic was expected + ~~~~~~~~~~ the numeric generic is not of type `u32` for i in 0..N { + ^ cannot find `N` in this scope + ~ not found in this scope fields[i] = i as Field + 1; } T::deserialize(fields) } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 4); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[2].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // N - assert!(matches!( - errors[3].0, - CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), - )); + check_errors(src); } #[test] @@ -2153,6 +1948,8 @@ fn numeric_generics_type_kind_mismatch() { fn bar() -> u16 { foo::() + ^ The numeric generic is not of type `u32` + ~ expected `u32`, found `u16` } global M: u16 = 3; @@ -2161,12 +1958,7 @@ fn numeric_generics_type_kind_mismatch() { let _ = bar::(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] @@ -2188,6 +1980,7 @@ fn numeric_generics_value_kind_mismatch_u32_u64() { pub fn push(&mut self, elem: T) { assert(self.len < MaxLen, "push out of bounds"); + ^^^^^^^^^^^^^^^^^ Integers must have the same bit width LHS is 64, RHS is 32 self.storage[self.len] = elem; self.len += 1; } @@ -2197,20 +1990,12 @@ fn numeric_generics_value_kind_mismatch_u32_u64() { let _ = BoundedVec { storage: [1], len: 1 }; // silence never constructed warning } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::IntegerBitWidth { - bit_width_x: IntegerBitSize::SixtyFour, - bit_width_y: IntegerBitSize::ThirtyTwo, - .. - }), - )); + check_errors(src); } #[test] fn quote_code_fragments() { + // TODO: have the error also point to `contact!` as a secondary // This test ensures we can quote (and unquote/splice) code fragments // which by themselves are not valid code. They only need to be valid // by the time they are unquoted into the macro's call site. @@ -2218,6 +2003,7 @@ fn quote_code_fragments() { fn main() { comptime { concat!(quote { assert( }, quote { false); }); + ^^^^^ Assertion failed } } @@ -2225,11 +2011,7 @@ fn quote_code_fragments() { quote { $a $b } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use InterpreterError::FailingConstraint; - assert!(matches!(&errors[0].0, CompilationError::InterpreterError(FailingConstraint { .. }))); + check_errors(src); } #[test] @@ -2241,6 +2023,7 @@ fn impl_stricter_than_trait_no_trait_method_constraints() { // We want to make sure we trigger the error when override a trait method // which itself has no trait constraints. fn serialize(self) -> [Field; N]; + ~~~~~~~~~ definition of `serialize` from trait } trait ToField { @@ -2262,6 +2045,8 @@ fn impl_stricter_than_trait_no_trait_method_constraints() { impl Serialize<2> for MyType { fn serialize(self) -> [Field; 2] where T: ToField { + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `T: ToField` [ self.a.to_field(), self.b.to_field() ] } } @@ -2276,13 +2061,7 @@ fn impl_stricter_than_trait_no_trait_method_constraints() { let _ = MyType { a: 1, b: 1 }; // silence MyType never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { .. }) - )); + check_errors(src); } #[test] @@ -2295,26 +2074,18 @@ fn impl_stricter_than_trait_different_generics() { fn foo_good() where T: Default; fn foo_bad() where T: Default; + ~~~~~~~ definition of `foo_bad` from trait } impl Foo for () { fn foo_good() where A: Default {} fn foo_bad() where B: Default {} + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `B: Default` } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "B")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } + check_errors(src); } #[test] @@ -2336,14 +2107,17 @@ fn impl_stricter_than_trait_different_object_generics() { fn bar_good() where Option: MyTrait, OtherOption>: OtherTrait; fn bar_bad() where Option: MyTrait, OtherOption>: OtherTrait; + ~~~~~~~ definition of `bar_bad` from trait fn array_good() where [T; 8]: MyTrait; fn array_bad() where [T; 8]: MyTrait; + ~~~~~~~~~ definition of `array_bad` from trait fn tuple_good() where (Option, Option): MyTrait; fn tuple_bad() where (Option, Option): MyTrait; + ~~~~~~~~~ definition of `tuple_bad` from trait } impl Bar for () { @@ -2356,58 +2130,27 @@ fn impl_stricter_than_trait_different_object_generics() { where OtherOption>: OtherTrait, Option: MyTrait { } + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `Option: MyTrait` fn array_good() where [A; 8]: MyTrait { } fn array_bad() where [B; 8]: MyTrait { } + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `[B; 8]: MyTrait` fn tuple_good() where (Option, Option): MyTrait { } fn tuple_bad() where (Option, Option): MyTrait { } + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `(Option, Option): MyTrait` } fn main() { let _ = OtherOption { inner: Option { inner: 1 } }; // silence unused warnings } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "Option")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } - - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[1].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "[B; 8]")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } - - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[2].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "(Option, Option)")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } + check_errors(src); } #[test] @@ -2423,32 +2166,22 @@ fn impl_stricter_than_trait_different_trait() { trait Bar { fn bar() where Option: Default; + ~~~ definition of `bar` from trait } impl Bar for () { // Trait constraint differs due to the trait even though the constraint // types are the same. fn bar() where Option: OtherDefault {} + ^^^^^^^^^^^^ impl has stricter requirements than trait + ~~~~~~~~~~~~ impl has extra requirement `Option: OtherDefault` } fn main() { let _ = Option { inner: 1 }; // silence Option never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "Option")); - assert!(matches!(constraint_name.as_str(), "OtherDefault")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } + check_errors(src); } #[test] @@ -2458,6 +2191,7 @@ fn trait_impl_where_clause_stricter_pass() { fn good_foo() where H: OtherTrait; fn bad_foo() where H: OtherTrait; + ~~~~~~~ definition of `bad_foo` from trait } trait OtherTrait {} @@ -2470,58 +2204,35 @@ fn trait_impl_where_clause_stricter_pass() { fn good_foo() where B: OtherTrait { } fn bad_foo() where A: OtherTrait { } + ^^^^^^^^^^ impl has stricter requirements than trait + ~~~~~~~~~~ impl has extra requirement `A: OtherTrait` } fn main() { let _ = Option { inner: 1 }; // silence Option never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "A")); - assert!(matches!(constraint_name.as_str(), "OtherTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } -} + check_errors(src); +} #[test] fn impl_stricter_than_trait_different_trait_generics() { let src = r#" trait Foo { fn foo() where T: T2; + ~~~ definition of `foo` from trait } impl Foo for () { // Should be A: T2 fn foo() where A: T2 {} + ^^ impl has stricter requirements than trait + ~~ impl has extra requirement `A: T2` } trait T2 {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - constraint_generics, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "A")); - assert!(matches!(constraint_name.as_str(), "T2")); - assert!(matches!(constraint_generics.ordered[0].to_string().as_str(), "B")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } + check_errors(src); } #[test] @@ -2558,6 +2269,8 @@ fn impl_not_found_for_inner_impl() { impl MyType { fn do_thing_with_serialization_with_extra_steps(self) -> Field { process_array(serialize_thing(self)) + ^^^^^^^^^^^^^^^ No matching impl found for `T: ToField` + ~~~~~~~~~~~~~~~ No impl for `T: ToField` } } @@ -2565,13 +2278,7 @@ fn impl_not_found_for_inner_impl() { let _ = MyType { a: 1, b: 1 }; // silence MyType never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::TypeError(TypeCheckError::NoMatchingImplFound { .. }) - )); + check_errors(src); } #[test] @@ -2579,16 +2286,12 @@ fn cannot_call_unconstrained_function_outside_of_unsafe() { let src = r#" fn main() { foo(); + ^^^^^ Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block } unconstrained fn foo() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); - }; + check_errors(src); } #[test] @@ -2596,26 +2299,19 @@ fn cannot_call_unconstrained_first_class_function_outside_of_unsafe() { let src = r#" fn main() { let func = foo; - // Warning should trigger here func(); + ^^^^^^ Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block inner(func); } fn inner(x: unconstrained fn() -> ()) { - // Warning should trigger here x(); + ^^^ Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block } unconstrained fn foo() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - for error in &errors { - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &error.0 else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); - }; - } + check_errors(src); } #[test] @@ -2649,15 +2345,11 @@ fn missing_unsafe_block_when_needing_type_annotations() { impl BigNumTrait for BigNum { fn __is_zero(self) -> bool { self.__is_zero_impl() + ^^^^^^^^^^^^^^^^^^^ Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); - }; + check_errors(src); } #[test] @@ -2666,6 +2358,7 @@ fn cannot_pass_unconstrained_function_to_regular_function() { fn main() { let func = foo; expect_regular(func); + ^^^^ Converting an unconstrained fn to a non-unconstrained fn is unsafe } unconstrained fn foo() {} @@ -2673,12 +2366,7 @@ fn cannot_pass_unconstrained_function_to_regular_function() { fn expect_regular(_func: fn() -> ()) { } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { - panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); - }; + check_errors(src); } #[test] @@ -2686,24 +2374,13 @@ fn cannot_assign_unconstrained_and_regular_fn_to_variable() { let src = r#" fn main() { let _func = if true { foo } else { bar }; + ^^^ Expected type fn() -> (), found type unconstrained fn() -> () } fn foo() {} unconstrained fn bar() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0].0 else { - panic!("Expected a context error, got {:?}", errors[0].0); - }; - - if let TypeCheckError::TypeMismatch { expected_typ, expr_typ, .. } = err.as_ref() { - assert_eq!(expected_typ, "fn() -> ()"); - assert_eq!(expr_typ, "unconstrained fn() -> ()"); - } else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; + check_errors(src); } #[test] @@ -2727,18 +2404,14 @@ fn cannot_pass_unconstrained_function_to_constrained_function() { fn main() { let func = foo; expect_regular(func); + ^^^^ Converting an unconstrained fn to a non-unconstrained fn is unsafe } unconstrained fn foo() {} fn expect_regular(_func: fn() -> ()) {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { - panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); - }; + check_errors(src); } #[test] @@ -2775,24 +2448,11 @@ fn trait_impl_generics_count_mismatch() { trait Foo {} impl Foo<()> for Field {} + ^^^ Foo expects 0 generics but 1 was given - fn main() {}"#; - 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, "Foo"); - assert_eq!(*expected, 0); - assert_eq!(*found, 1); + fn main() {} + "#; + check_errors(src); } #[test] @@ -2810,28 +2470,15 @@ fn duplicate_struct_field() { let src = r#" pub struct Foo { x: i32, + ~ First struct field found here x: i32, + ^ Duplicate definitions of struct field with name x found + ~ Second struct field found here } fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { - typ: _, - first_def, - second_def, - }) = &errors[0].0 - else { - panic!("Expected a 'duplicate' 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(), 30); - assert_eq!(second_def.span().start(), 46); + check_errors(src); } #[test] @@ -2869,23 +2516,12 @@ fn incorrect_generic_count_on_struct_impl() { let src = r#" struct Foo {} impl Foo {} + ^^^ Foo expects 0 generics but 1 was given fn main() { let _ = Foo {}; // silence Foo never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { - found, expected, .. - }) = errors[0].0 - else { - panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(found, 1); - assert_eq!(expected, 0); + check_errors(src); } #[test] @@ -2893,23 +2529,12 @@ fn incorrect_generic_count_on_type_alias() { let src = r#" pub struct Foo {} pub type Bar = Foo; + ^^^ Foo expects 0 generics but 1 was given fn main() { let _ = Foo {}; // silence Foo never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { - found, expected, .. - }) = errors[0].0 - else { - panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(found, 1); - assert_eq!(expected, 0); + check_errors(src); } #[test] @@ -2966,14 +2591,18 @@ fn uses_self_type_in_trait_where_clause() { } pub trait Foo where Self: Trait { + ~~~~~ required by this bound in `Foo` fn foo(self) -> bool { self.trait_func() + ^^^^^^^^^^^^^^^^^ No method named 'trait_func' found for type 'Bar' } } struct Bar {} impl Foo for Bar { + ^^^ The trait bound `_: Trait` is not satisfied + ~~~ The trait `Trait` is not implemented for `_` } @@ -2981,22 +2610,7 @@ fn uses_self_type_in_trait_where_clause() { let _ = Bar {}; // silence Bar never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - let CompilationError::ResolverError(ResolverError::TraitNotImplemented { .. }) = &errors[0].0 - else { - panic!("Expected a trait not implemented error, got {:?}", errors[0].0); - }; - - let CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { method_name, .. }) = - &errors[1].0 - else { - panic!("Expected an unresolved method call error, got {:?}", errors[1].0); - }; - - assert_eq!(method_name, "trait_func"); + check_errors(src); } #[test] @@ -3024,16 +2638,10 @@ fn error_on_cast_over_type_variable() { fn main() { let x = "a"; let _: Field = foo(|x| x as Field, x); + ^ Expected type Field, found type str<1> } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) - )); + check_errors(src); } #[test] @@ -3116,15 +2724,9 @@ fn impl_missing_associated_type() { } impl Foo for () {} + ^^^ `Foo` is missing the associated type `Assoc` "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - &errors[0].0, - CompilationError::TypeError(TypeCheckError::MissingNamedTypeArg { .. }) - )); + check_errors(src); } #[test] @@ -3144,22 +2746,12 @@ fn as_trait_path_syntax_resolves_outside_impl() { // AsTraitPath syntax is a bit silly when associated types // are explicitly specified let _: i64 = 1 as >::Assoc; + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Expected type i64, found type i32 let _ = Bar {}; // silence Bar never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use CompilationError::TypeError; - use TypeCheckError::TypeMismatch; - let TypeError(TypeMismatch { expected_typ, expr_typ, .. }) = errors[0].0.clone() else { - panic!("Expected TypeMismatch error, found {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "i64".to_string()); - assert_eq!(expr_typ, "i32".to_string()); + check_errors(src); } #[test] @@ -3177,70 +2769,71 @@ fn as_trait_path_syntax_no_impl() { fn main() { let _: i64 = 1 as >::Assoc; + ^^^ No matching impl found for `Bar: Foo` + ~~~ No impl for `Bar: Foo` let _ = Bar {}; // silence Bar never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use CompilationError::TypeError; - assert!(matches!(&errors[0].0, TypeError(TypeCheckError::NoMatchingImplFound { .. }))); + check_errors(src); } #[test] -fn dont_infer_globals_to_u32_from_type_use() { +fn do_not_infer_globals_to_u32_from_type_use() { let src = r#" global ARRAY_LEN = 3; + ^^^^^^^^^ Globals must have a specified type + ~ Inferred type is `Field` global STR_LEN: _ = 2; + ^^^^^^^ Globals must have a specified type + ~ Inferred type is `Field` global FMT_STR_LEN = 2; + ^^^^^^^^^^^ Globals must have a specified type + ~ Inferred type is `Field` fn main() { let _a: [u32; ARRAY_LEN] = [1, 2, 3]; + ^^^^^^^^^^^^^^^^ The numeric generic is not of type `u32` + ~~~~~~~~~~~~~~~~ expected `u32`, found `Field` let _b: str = "hi"; + ^^^^^^^^^^^^ The numeric generic is not of type `u32` + ~~~~~~~~~~~~ expected `u32`, found `Field` let _c: fmtstr = f"hi"; + ^^^^^^^^^^^^^^^^^^^^^^ The numeric generic is not of type `u32` + ~~~~~~~~~~~~~~~~~~~~~~ expected `u32`, found `Field` } "#; - - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 6); - for (error, _file_id) in errors.drain(0..3) { - assert!(matches!( - error, - CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) - )); - } - for (error, _file_id) in errors { - assert!(matches!( - error, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) - )); - } + check_errors(src); } #[test] -fn dont_infer_partial_global_types() { +fn do_not_infer_partial_global_types() { let src = r#" pub global ARRAY: [Field; _] = [0; 3]; + ^^^^^ Globals must have a specified type + ~~~~~~ Inferred type is `[Field; 3]` pub global NESTED_ARRAY: [[Field; _]; 3] = [[]; 3]; + ^^^^^^^^^^^^ Globals must have a specified type + ~~~~~~~ Inferred type is `[[Field; 0]; 3]` pub global STR: str<_> = "hi"; + ^^^ Globals must have a specified type + ~~~~ Inferred type is `str<2>` + pub global NESTED_STR: [str<_>] = &["hi"]; + ^^^^^^^^^^ Globals must have a specified type + ~~~~~~~ Inferred type is `[str<2>]` pub global FORMATTED_VALUE: str<5> = "there"; pub global FMT_STR: fmtstr<_, _> = f"hi {FORMATTED_VALUE}"; - pub global TUPLE_WITH_MULTIPLE: ([str<_>], [[Field; _]; 3]) = (&["hi"], [[]; 3]); + ^^^^^^^ Globals must have a specified type + ~~~~~~~~~~~~~~~~~~~~~~~ Inferred type is `fmtstr<20, (str<5>)>` + pub global TUPLE_WITH_MULTIPLE: ([str<_>], [[Field; _]; 3]) = + ^^^^^^^^^^^^^^^^^^^ Globals must have a specified type + (&["hi"], [[]; 3]); + ~~~~~~~~~~~~~~~~~~ Inferred type is `([str<2>], [[Field; 0]; 3])` fn main() { } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 6); - for (error, _file_id) in errors { - assert!(matches!( - error, - CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) - )); - } + check_errors(src); } #[test] @@ -3256,9 +2849,7 @@ fn u32_globals_as_sizes_in_types() { let _c: fmtstr = f"hi"; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -3270,6 +2861,8 @@ fn struct_array_len() { impl Array { pub fn len(self) -> u32 { + ^^^^ unused variable self + ~~~~ unused variable N as u32 } } @@ -3281,17 +2874,11 @@ fn struct_array_len() { assert(ys.len() == 2); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnusedVariable { .. }) - )); + check_errors(src); } // TODO(https://github.com/noir-lang/noir/issues/6245): -// support u16 as an array size +// support u8 as an array size #[test] fn non_u32_as_array_length() { let src = r#" @@ -3299,15 +2886,11 @@ fn non_u32_as_array_length() { fn main() { let _a: [u32; ARRAY_LEN] = [1, 2, 3]; + ^^^^^^^^^^^^^^^^ The numeric generic is not of type `u32` + ~~~~~~~~~~~~~~~~ expected `u32`, found `u8` } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) - )); + check_errors(src); } #[test] @@ -3319,9 +2902,7 @@ fn use_non_u32_generic_in_struct() { let _: S<3> = S {}; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -3344,10 +2925,7 @@ fn use_numeric_generic_in_trait_method() { let _ = Bar{}.foo(bytes); } "#; - - let errors = get_program_errors(src); - println!("{errors:?}"); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -3373,26 +2951,17 @@ fn trait_unconstrained_methods_typechecked_correctly() { assert_eq(2.foo(), 2.identity() as Field); } "#; - - let errors = get_program_errors(src); - println!("{errors:?}"); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] fn error_if_attribute_not_in_scope() { let src = r#" #[not_in_scope] + ^^^^^^^^^^^^^^^ Attribute function `not_in_scope` is not in scope fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::AttributeFunctionNotInScope { .. }) - )); + check_errors(src); } #[test] @@ -3405,9 +2974,7 @@ fn arithmetic_generics_rounding_pass() { fn round(_x: [Field; N / M * M]) {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -3417,17 +2984,12 @@ fn arithmetic_generics_rounding_fail() { // Do not simplify N/M*M to just N // This should be 3/2*2 = 2, not 3 round::<3, 2>([1, 2, 3]); + ^^^^^^^^^ Expected type [Field; 2], found type [Field; 3] } fn round(_x: [Field; N / M * M]) {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) - )); + check_errors(src); } #[test] @@ -3445,15 +3007,10 @@ fn arithmetic_generics_rounding_fail_on_struct() { // Do not simplify N/M*M to just N // This should be 3/2*2 = 2, not 3 let _: W<3> = foo(w_3, w_2); + ^^^^^^^^^^^^^ Expected type W<3>, found type W<2> } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) - )); + check_errors(src); } #[test] @@ -3466,42 +3023,58 @@ fn unconditional_recursion_fail() { let srcs = vec![ r#" fn main() { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing main() } "#, r#" fn main() -> pub bool { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing if main() { true } else { false } } "#, r#" fn main() -> pub bool { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing if true { main() } else { main() } } "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing main() + main() } "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing 1 + main() } "#, r#" fn main() -> pub bool { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing let _ = main(); true } "#, r#" fn main(a: u64, b: u64) -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing main(a + b, main(a, b)) } "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing foo(1, main()) } fn foo(a: u64, b: u64) -> u64 { @@ -3510,12 +3083,16 @@ fn unconditional_recursion_fail() { "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing let (a, b) = (main(), main()); a + b } "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing let mut sum = 0; for i in 0 .. main() { sum += i; @@ -3526,19 +3103,7 @@ fn unconditional_recursion_fail() { ]; for src in srcs { - let errors = get_program_errors(src); - assert!( - !errors.is_empty(), - "expected 'unconditional recursion' error, got nothing; src = {src}" - ); - - for (error, _) in errors { - let CompilationError::ResolverError(ResolverError::UnconditionalRecursion { .. }) = - error - else { - panic!("Expected an 'unconditional recursion' error, got {:?}; src = {src}", error); - }; - } + check_errors(src); } } @@ -3738,117 +3303,89 @@ fn errors_with_better_message_when_trying_to_invoke_struct_field_that_is_a_funct impl Foo { fn call(self) -> bool { self.wrapped(1) + ^^^^^^^^^^^^^^^ Cannot invoke function field 'wrapped' on type 'Foo' as a method + ~~~~~~~~~~~~~~~ to call the function stored in 'wrapped', surround the field access with parentheses: '(', ')' } } fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::CannotInvokeStructFieldFunctionType { - method_name, - .. - }) = &errors[0].0 - else { - panic!("Expected a 'CannotInvokeStructFieldFunctionType' error, got {:?}", errors[0].0); - }; - - assert_eq!(method_name, "wrapped"); -} - -fn test_disallows_attribute_on_impl_method( - attr: &str, - check_error: impl FnOnce(&CompilationError), -) { - let src = format!( - " - pub struct Foo {{ }} - - impl Foo {{ - #[{attr}] - fn foo() {{ }} - }} - - fn main() {{ }} - " - ); - let errors = get_program_errors(&src); - assert_eq!(errors.len(), 1); - check_error(&errors[0].0); -} - -fn test_disallows_attribute_on_trait_impl_method( - attr: &str, - check_error: impl FnOnce(&CompilationError), -) { - let src = format!( - " - pub trait Trait {{ - fn foo() {{ }} - }} - - pub struct Foo {{ }} - - impl Trait for Foo {{ - #[{attr}] - fn foo() {{ }} - }} - - fn main() {{ }} - " - ); - let errors = get_program_errors(&src); - assert_eq!(errors.len(), 1); - check_error(&errors[0].0); + check_errors(src); } #[test] fn disallows_test_attribute_on_impl_method() { - test_disallows_attribute_on_impl_method("test", |error| { - assert!(matches!( - error, - CompilationError::DefinitionError( - DefCollectorErrorKind::TestOnAssociatedFunction { .. } - ) - )); - }); + // TODO: improve the error location + let src = " + pub struct Foo { } + + impl Foo { + #[test] + fn foo() { } + ^^^ The `#[test]` attribute is disallowed on `impl` methods + } + + fn main() { } + "; + check_errors(src); } #[test] fn disallows_test_attribute_on_trait_impl_method() { - test_disallows_attribute_on_trait_impl_method("test", |error| { - assert!(matches!( - error, - CompilationError::DefinitionError( - DefCollectorErrorKind::TestOnAssociatedFunction { .. } - ) - )); - }); + let src = " + pub trait Trait { + fn foo() { } + } + + pub struct Foo { } + + impl Trait for Foo { + #[test] + fn foo() { } + ^^^ The `#[test]` attribute is disallowed on `impl` methods + } + + fn main() { } + "; + check_errors(src); } #[test] fn disallows_export_attribute_on_impl_method() { - test_disallows_attribute_on_impl_method("export", |error| { - assert!(matches!( - error, - CompilationError::DefinitionError( - DefCollectorErrorKind::ExportOnAssociatedFunction { .. } - ) - )); - }); + // TODO: improve the error location + let src = " + pub struct Foo { } + + impl Foo { + #[export] + fn foo() { } + ^^^ The `#[export]` attribute is disallowed on `impl` methods + } + + fn main() { } + "; + check_errors(src); } #[test] fn disallows_export_attribute_on_trait_impl_method() { - test_disallows_attribute_on_trait_impl_method("export", |error| { - assert!(matches!( - error, - CompilationError::DefinitionError( - DefCollectorErrorKind::ExportOnAssociatedFunction { .. } - ) - )); - }); + // TODO: improve the error location + let src = " + pub trait Trait { + fn foo() { } + } + + pub struct Foo { } + + impl Trait for Foo { + #[export] + fn foo() { } + ^^^ The `#[export]` attribute is disallowed on `impl` methods + } + + fn main() { } + "; + check_errors(src); } #[test] @@ -3867,38 +3404,27 @@ fn disallows_underscore_on_right_hand_side() { fn main() { let _ = 1; let _x = _; + ^ in expressions, `_` can only be used on the left-hand side of an assignment + ~ `_` not allowed here } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, .. }) = - &errors[0].0 - else { - panic!("Expected a VariableNotDeclared error, got {:?}", errors[0].0); - }; - - assert_eq!(name, "_"); + check_errors(src); } #[test] fn errors_on_cyclic_globals() { let src = r#" pub comptime global A: u32 = B; + ^ This global recursively depends on itself + ^ Dependency cycle found + ~ 'A' recursively depends on itself: A -> B -> A pub comptime global B: u32 = A; + ^ Variable not in scope + ~ Could not find variable fn main() { } "#; - let errors = get_program_errors(src); - - assert!(errors.iter().any(|(error, _)| matches!( - error, - CompilationError::InterpreterError(InterpreterError::GlobalsDependencyCycle { .. }) - ))); - assert!(errors.iter().any(|(error, _)| matches!( - error, - CompilationError::ResolverError(ResolverError::DependencyCycle { .. }) - ))); + check_errors(src); } #[test] @@ -3907,18 +3433,14 @@ fn warns_on_unneeded_unsafe() { fn main() { // Safety: test unsafe { + ^^^^^^ Unnecessary `unsafe` block foo() } } fn foo() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::TypeError(TypeCheckError::UnnecessaryUnsafeBlock { .. }) - )); + check_errors(src); } #[test] @@ -3929,6 +3451,8 @@ fn warns_on_nested_unsafe() { unsafe { // Safety: test unsafe { + ^^^^^^ Unnecessary `unsafe` block + ~~~~~~ Because it's nested inside another `unsafe` block foo() } } @@ -3936,12 +3460,7 @@ fn warns_on_nested_unsafe() { unconstrained fn foo() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::TypeError(TypeCheckError::NestedUnsafeBlock { .. }) - )); + check_errors(src); } #[test] @@ -4228,28 +3747,6 @@ fn regression_7088() { assert_no_errors(src); } -#[test] -fn error_with_duplicate_enum_variant() { - let src = r#" - enum Foo { - Bar(i32), - Bar(u8), - } - - fn main() {} - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - assert!(matches!( - &errors[0].0, - CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { .. }) - )); - assert!(matches!( - &errors[1].0, - CompilationError::ResolverError(ResolverError::UnusedItem { .. }) - )); -} - #[test] fn errors_on_empty_loop_no_break() { let src = r#" @@ -4262,14 +3759,11 @@ fn errors_on_empty_loop_no_break() { unconstrained fn foo() { loop {} + ^^^^ `loop` must have at least one `break` in it + ~~~~ Infinite loops are disallowed } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::ResolverError(ResolverError::LoopWithoutBreak { .. }) - )); + check_errors(src); } #[test] @@ -4285,6 +3779,8 @@ fn errors_on_loop_without_break() { unconstrained fn foo() { let mut x = 1; loop { + ^^^^ `loop` must have at least one `break` in it + ~~~~ Infinite loops are disallowed x += 1; bar(x); } @@ -4292,12 +3788,7 @@ fn errors_on_loop_without_break() { fn bar(_: Field) {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::ResolverError(ResolverError::LoopWithoutBreak { .. }) - )); + check_errors(src); } #[test] @@ -4313,6 +3804,8 @@ fn errors_on_loop_without_break_with_nested_loop() { unconstrained fn foo() { let mut x = 1; loop { + ^^^^ `loop` must have at least one `break` in it + ~~~~ Infinite loops are disallowed x += 1; bar(x); loop { @@ -4324,12 +3817,7 @@ fn errors_on_loop_without_break_with_nested_loop() { fn bar(_: Field) {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::ResolverError(ResolverError::LoopWithoutBreak { .. }) - )); + check_errors(src); } #[test] @@ -4354,16 +3842,11 @@ fn errors_on_if_without_else_type_mismatch() { fn main() { if true { 1 + ^ Expected type Field, found type () } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0].0 else { - panic!("Expected a Context error"); - }; - assert!(matches!(**err, TypeCheckError::TypeMismatch { .. })); + check_errors(src); } #[test] @@ -4378,15 +3861,11 @@ fn errors_if_for_body_type_is_not_unit() { fn main() { for _ in 0..1 { 1 + ^ Expected type (), found type Field } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) = &errors[0].0 else { - panic!("Expected a TypeMismatch error"); - }; + check_errors(src); } #[test] @@ -4397,15 +3876,11 @@ fn errors_if_loop_body_type_is_not_unit() { if false { break; } 1 + ^ Expected type (), found type Field } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) = &errors[0].0 else { - panic!("Expected a TypeMismatch error"); - }; + check_errors(src); } #[test] @@ -4414,13 +3889,228 @@ fn errors_if_while_body_type_is_not_unit() { unconstrained fn main() { while 1 == 1 { 1 + ^ Expected type (), found type Field } } "#; + check_errors(src); +} + +#[test] +fn check_impl_duplicate_method_without_self() { + let src = " + pub struct Foo {} + + impl Foo { + fn foo() {} + ~~~ first definition found here + fn foo() {} + ^^^ duplicate definitions of foo found + ~~~ second definition found here + } + + fn main() {} + "; + check_errors(src); +} + +#[test] +fn int_min_global() { + let src = r#" + global MIN: i8 = -128; + fn main() { + let _x = MIN; + } + "#; + let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); + assert_eq!(errors.len(), 0); +} - let CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) = &errors[0].0 else { - panic!("Expected a TypeMismatch error"); - }; +#[test] +fn subtract_to_int_min() { + // This would cause an integer underflow panic before + let src = r#" + fn main() { + let _x: i8 = comptime { + let y: i8 = -127; + let z = y - 1; + z + }; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + +#[test] +fn mutate_with_reference_in_lambda() { + let src = r#" + fn main() { + let x = &mut 3; + let f = || { + *x += 2; + }; + f(); + assert(*x == 5); + } + "#; + + assert_no_errors(src); +} + +#[test] +fn mutate_with_reference_marked_mutable_in_lambda() { + let src = r#" + fn main() { + let mut x = &mut 3; + let f = || { + *x += 2; + }; + f(); + assert(*x == 5); + } + "#; + assert_no_errors(src); +} + +#[test] +fn deny_capturing_mut_variable_without_reference_in_lambda() { + let src = r#" + fn main() { + let mut x = 3; + let f = || { + x += 2; + ^ Mutable variable x captured in lambda must be a mutable reference + ~ Use '&mut' instead of 'mut' to capture a mutable variable. + }; + f(); + assert(x == 5); + } + "#; + check_errors(src); +} + +#[test] +fn deny_capturing_mut_variable_without_reference_in_nested_lambda() { + let src = r#" + fn main() { + let mut x = 3; + let f = || { + let inner = || { + x += 2; + ^ Mutable variable x captured in lambda must be a mutable reference + ~ Use '&mut' instead of 'mut' to capture a mutable variable. + }; + inner(); + }; + f(); + assert(x == 5); + } + "#; + check_errors(src); +} + +#[test] +fn allow_capturing_mut_variable_only_used_immutably() { + let src = r#" + fn main() { + let mut x = 3; + let f = || x; + let _x2 = f(); + assert(x == 3); + } + "#; + assert_no_errors(src); +} + +#[test] +fn deny_capturing_mut_var_as_param_to_function() { + let src = r#" + fn main() { + let mut x = 3; + let f = || mutate(&mut x); + ^ Mutable variable x captured in lambda must be a mutable reference + ~ Use '&mut' instead of 'mut' to capture a mutable variable. + f(); + assert(x == 3); + } + + fn mutate(x: &mut Field) { + *x = 5; + } + "#; + check_errors(src); +} + +#[test] +fn deny_capturing_mut_var_as_param_to_function_in_nested_lambda() { + let src = r#" + fn main() { + let mut x = 3; + let f = || { + let inner = || mutate(&mut x); + ^ Mutable variable x captured in lambda must be a mutable reference + ~ Use '&mut' instead of 'mut' to capture a mutable variable. + inner(); + }; + f(); + assert(x == 3); + } + + fn mutate(x: &mut Field) { + *x = 5; + } + "#; + check_errors(src); +} + +#[test] +fn deny_capturing_mut_var_as_param_to_impl_method() { + let src = r#" + struct Foo { + value: Field, + } + + impl Foo { + fn mutate(&mut self) { + self.value = 2; + } + } + + fn main() { + let mut foo = Foo { value: 1 }; + let f = || foo.mutate(); + ^^^ Mutable variable foo captured in lambda must be a mutable reference + ~~~ Use '&mut' instead of 'mut' to capture a mutable variable. + f(); + assert(foo.value == 2); + } + "#; + check_errors(src); +} + +#[test] +fn deny_attaching_mut_ref_to_immutable_object() { + let src = r#" + struct Foo { + value: Field, + } + + impl Foo { + fn mutate(&mut self) { + self.value = 2; + } + } + + fn main() { + let foo = Foo { value: 1 }; + let f = || foo.mutate(); + ^^^ Cannot mutate immutable variable `foo` + f(); + assert(foo.value == 2); + } + "#; + check_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/arithmetic_generics.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/arithmetic_generics.rs index 83de9c077ab3..bcbdbdd6211e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/arithmetic_generics.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/arithmetic_generics.rs @@ -2,11 +2,10 @@ use acvm::{AcirField, FieldElement}; -use super::get_program_errors; use crate::hir::type_check::TypeCheckError; use crate::hir_def::types::{BinaryTypeOperator, Type}; use crate::monomorphization::errors::MonomorphizationError; -use crate::tests::get_monomorphization_error; +use crate::tests::{assert_no_errors, get_monomorphization_error}; #[test] fn arithmetic_generics_canonicalization_deduplication_regression() { @@ -23,8 +22,7 @@ fn arithmetic_generics_canonicalization_deduplication_regression() { }; } "#; - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); } #[test] @@ -52,9 +50,7 @@ fn checked_casts_do_not_prevent_canonicalization() { } } "#; - let errors = get_program_errors(source); - println!("{:?}", errors); - assert_eq!(errors.len(), 0); + assert_no_errors(source); } #[test] @@ -76,9 +72,7 @@ fn arithmetic_generics_checked_cast_zeros() { bar(w) } "#; - - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); let monomorphization_error = get_monomorphization_error(source); assert!(monomorphization_error.is_some()); @@ -123,9 +117,7 @@ fn arithmetic_generics_checked_cast_indirect_zeros() { let _ = bar(w); } "#; - - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); let monomorphization_error = get_monomorphization_error(source); assert!(monomorphization_error.is_some()); @@ -166,8 +158,7 @@ fn global_numeric_generic_larger_than_u32() { let _ = foo::(); } "#; - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); } #[test] @@ -196,6 +187,5 @@ fn global_arithmetic_generic_larger_than_u32() { let _ = foo::().size(); } "#; - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/bound_checks.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/bound_checks.rs index 05669bda4111..6a084e68c7e3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/bound_checks.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/bound_checks.rs @@ -1,24 +1,14 @@ -use crate::hir::def_collector::dc_crate::CompilationError; - -use super::get_program_errors; +use crate::tests::check_errors; #[test] fn overflowing_u8() { let src = r#" fn main() { let _: u8 = 256; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `256` cannot fit into `u8` which has range `0..=255`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } + ^^^ The value `256` cannot fit into `u8` which has range `0..=255` + } + "#; + check_errors(src); } #[test] @@ -26,18 +16,10 @@ fn underflowing_u8() { let src = r#" fn main() { let _: u8 = -1; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `-1` cannot fit into `u8` which has range `0..=255`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } + ^^ The value `-1` cannot fit into `u8` which has range `0..=255` + } + "#; + check_errors(src); } #[test] @@ -45,18 +27,10 @@ fn overflowing_i8() { let src = r#" fn main() { let _: i8 = 128; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `128` cannot fit into `i8` which has range `-128..=127`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } + ^^^ The value `128` cannot fit into `i8` which has range `-128..=127` + } + "#; + check_errors(src); } #[test] @@ -64,16 +38,8 @@ fn underflowing_i8() { let src = r#" fn main() { let _: i8 = -129; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `-129` cannot fit into `i8` which has range `-128..=127`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } + ^^^^ The value `-129` cannot fit into `i8` which has range `-128..=127` + } + "#; + check_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/enums.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/enums.rs new file mode 100644 index 000000000000..6163b34003e5 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/enums.rs @@ -0,0 +1,201 @@ +use crate::{ + hir::def_collector::dc_crate::CompilationError, + parser::ParserErrorReason, + tests::{assert_no_errors, get_program_using_features}, +}; + +use super::{check_errors, check_errors_using_features}; + +#[test] +fn error_with_duplicate_enum_variant() { + let src = r#" + pub enum Foo { + Bar(i32), + ~~~ First enum variant found here + Bar(u8), + ^^^ Duplicate definitions of enum variant with name Bar found + ~~~ Second enum variant found here + } + + fn main() {} + "#; + check_errors(src); +} + +#[test] +fn errors_on_unspecified_unstable_enum() { + // Enums are experimental - this will need to be updated when they are stabilized + let src = r#" + enum Foo { Bar } + ^^^ This requires the unstable feature 'enums' which is not enabled + ~~~ Pass -Zenums to nargo to enable this feature at your own risk. + + fn main() { + let _x = Foo::Bar; + } + "#; + let no_features = &[]; + check_errors_using_features(src, no_features); +} + +#[test] +fn errors_on_unspecified_unstable_match() { + // TODO: update this test. Right now it's hard to test because the span happens in the entire + // `match` node but ideally it would be nice if it only happened in the `match` keyword. + // Enums are experimental - this will need to be updated when they are stabilized + let src = r#" + fn main() { + match 3 { + _ => (), + } + } + "#; + + let no_features = &[]; + let errors = get_program_using_features(src, no_features).2; + assert_eq!(errors.len(), 1); + + let CompilationError::ParseError(error) = &errors[0] else { + panic!("Expected a ParseError experimental feature error"); + }; + + assert!(matches!(error.reason(), Some(ParserErrorReason::ExperimentalFeature(_)))); +} + +#[test] +fn errors_on_repeated_match_variables_in_pattern() { + let src = r#" + fn main() { + match (1, 2) { + (_x, _x) => (), + ^^ Variable `_x` was already defined in the same match pattern + ~~ `_x` redefined here + ~~ `_x` was previously defined here + } + } + "#; + check_errors(src); +} + +#[test] +fn duplicate_field_in_match_struct_pattern() { + let src = r#" + fn main() { + let foo = Foo { x: 10, y: 20 }; + match foo { + Foo { x: _, x: _, y: _ } => {} + ^ duplicate field x + } + } + + struct Foo { + x: i32, + y: Field, + } + "#; + check_errors(src); +} + +#[test] +fn missing_field_in_match_struct_pattern() { + let src = r#" + fn main() { + let foo = Foo { x: 10, y: 20 }; + match foo { + Foo { x: _ } => {} + ^^^ missing field y in struct Foo + } + } + + struct Foo { + x: i32, + y: Field, + } + "#; + check_errors(src); +} + +#[test] +fn no_such_field_in_match_struct_pattern() { + let src = r#" + fn main() { + let foo = Foo { x: 10, y: 20 }; + match foo { + Foo { x: _, y: _, z: _ } => {} + ^ no such field z defined in struct Foo + } + } + + struct Foo { + x: i32, + y: Field, + } + "#; + check_errors(src); +} + +#[test] +fn match_integer_type_mismatch_in_pattern() { + let src = r#" + fn main() { + match 2 { + Foo::One(_) => (), + ^^^^^^^^ Expected type Field, found type Foo + } + } + + enum Foo { + One(i32), + } + "#; + check_errors(src); +} + +#[test] +fn match_shadow_global() { + let src = r#" + fn main() { + match 2 { + foo => assert_eq(foo, 2), + } + } + + fn foo() {} + "#; + assert_no_errors(src); +} + +#[test] +fn match_no_shadow_global() { + let src = r#" + fn main() { + match 2 { + crate::foo => (), + ^^^^^^^^^^ Expected a struct, enum, or literal pattern, but found a function + } + } + + fn foo() {} + "#; + check_errors(src); +} + +#[test] +fn constructor_arg_arity_mismatch_in_pattern() { + let src = r#" + fn main() { + match Foo::One(1) { + Foo::One(_, _) => (), + ^^^^^^^^ Expected 1 argument, but found 2 + Foo::Two(_) => (), + ^^^^^^^^ Expected 2 arguments, but found 1 + } + } + + enum Foo { + One(i32), + Two(i32, i32), + } + "#; + check_errors(src); +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/imports.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/imports.rs index 8598d8296e59..ba2dad285d6a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/imports.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/imports.rs @@ -1,9 +1,6 @@ -use crate::hir::{ - def_collector::{dc_crate::CompilationError, errors::DefCollectorErrorKind}, - resolution::{errors::ResolverError, import::PathResolutionError}, -}; +use crate::tests::check_errors; -use super::{assert_no_errors, get_program_errors}; +use super::assert_no_errors; #[test] fn use_super() { @@ -23,19 +20,11 @@ fn use_super() { #[test] fn no_super() { - let src = "use super::some_func;"; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( - PathResolutionError::NoSuper(span), - )) = &errors[0].0 - else { - panic!("Expected a 'no super' error, got {:?}", errors[0].0); - }; - - assert_eq!(span.start(), 4); - assert_eq!(span.end(), 9); + let src = " + use super::some_func; + ^^^^^ There is no super module + "; + check_errors(src); } #[test] @@ -69,18 +58,11 @@ fn warns_on_use_of_private_exported_item() { fn main() { foo::baz(); + ^^^ baz is private and not visible from the current module + ~~~ baz is private } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - &errors[0].0, - CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(..), - )) - )); + check_errors(src); } #[test] @@ -110,22 +92,15 @@ fn warns_on_re_export_of_item_with_less_visibility() { } pub use bar::baz; + ^^^ cannot re-export baz because it has less visibility than this use statement + ~~~ consider marking baz as pub } fn main() { foo::baz(); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - &errors[0].0, - CompilationError::DefinitionError( - DefCollectorErrorKind::CannotReexportItemWithLessVisibility { .. } - ) - )); + check_errors(src); } #[test] @@ -136,21 +111,10 @@ fn errors_if_using_alias_in_import() { } use foo::bar::baz; + ^^^ bar is a type alias, not a module fn main() { } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( - PathResolutionError::NotAModule { ident, kind }, - )) = &errors[0].0 - else { - panic!("Expected a 'not a module' error, got {:?}", errors[0].0); - }; - - assert_eq!(ident.to_string(), "bar"); - assert_eq!(*kind, "type alias"); + check_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/metaprogramming.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/metaprogramming.rs index b42342fa47d3..a19ef17d8353 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/metaprogramming.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/metaprogramming.rs @@ -1,17 +1,15 @@ -use noirc_errors::Spanned; +use noirc_errors::Located; use crate::{ ast::Ident, hir::{ - comptime::InterpreterError, + comptime::ComptimeError, def_collector::{ dc_crate::CompilationError, errors::{DefCollectorErrorKind, DuplicateType}, }, - resolution::errors::ResolverError, - type_check::TypeCheckError, }, - parser::ParserErrorReason, + tests::check_errors, }; use super::{assert_no_errors, get_program_errors}; @@ -23,39 +21,30 @@ fn comptime_let() { comptime let my_var = 2; assert_eq(my_var, 2); }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] fn comptime_code_rejects_dynamic_variable() { - let src = r#"fn main(x: Field) { + let src = r#" + fn main(x: Field) { comptime let my_var = (x - x) + 2; + ^ Non-comptime variable `x` referenced in comptime code + ~ Non-comptime variables can't be used in comptime code assert_eq(my_var, 2); - }"#; - let errors = get_program_errors(src); - - assert_eq!(errors.len(), 1); - match &errors[0].0 { - CompilationError::InterpreterError(InterpreterError::NonComptimeVarReferenced { - name, - .. - }) => { - assert_eq!(name, "x"); - } - _ => panic!("expected an InterpreterError"), } + "#; + check_errors(src); } #[test] fn comptime_type_in_runtime_code() { - let source = "pub fn foo(_f: FunctionDefinition) {}"; - let errors = get_program_errors(source); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::ComptimeTypeInRuntimeCode { .. }) - )); + let source = " + pub fn foo(_f: FunctionDefinition) {} + ^^^^^^^^^^^^^^^^^^ Comptime-only type `FunctionDefinition` cannot be used in runtime code + ~~~~~~~~~~~~~~~~~~ Comptime-only type used here + "; + check_errors(source); } #[test] @@ -64,6 +53,7 @@ fn macro_result_type_mismatch() { fn main() { comptime { let x = unquote!(quote { "test" }); + ^^^^^^^^^^^^^^^^^^^^^^^^^^ Expected type Field, found type str<4> let _: Field = x; } } @@ -72,13 +62,7 @@ fn macro_result_type_mismatch() { q } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) - )); + check_errors(src); } #[test] @@ -130,6 +114,8 @@ fn allows_references_to_structs_generated_by_macros() { #[test] fn errors_if_macros_inject_functions_with_name_collisions() { + // This can't be tested using `check_errors` right now because the two secondary + // errors land on the same span. let src = r#" comptime fn make_colliding_functions(_s: StructDefinition) -> Quoted { quote { @@ -150,14 +136,21 @@ fn errors_if_macros_inject_functions_with_name_collisions() { } "#; - let errors = get_program_errors(src); + let mut errors = get_program_errors(src); assert_eq!(errors.len(), 1); + + let CompilationError::ComptimeError(ComptimeError::ErrorRunningAttribute { error, .. }) = + errors.remove(0) + else { + panic!("Expected a ComptimeError, got {:?}", errors[0]); + }; + assert!(matches!( - &errors[0].0, + *error, CompilationError::DefinitionError( DefCollectorErrorKind::Duplicate { typ: DuplicateType::Function, - first_def: Ident(Spanned { contents, .. }), + first_def: Ident(Located { contents, .. }), .. }, ) if contents == "foo" @@ -194,6 +187,8 @@ fn does_not_fail_to_parse_macro_on_parser_warning() { quote { pub fn bar() { unsafe { + ^^^^^^ Unsafe block must have a safety comment above it + ~~~~~~ The comment must start with the "Safety: " word foo(); } } @@ -204,12 +199,5 @@ fn does_not_fail_to_parse_macro_on_parser_warning() { bar() } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ParseError(parser_error) = &errors[0].0 else { - panic!("Expected a ParseError, got {:?}", errors[0].0); - }; - - assert!(matches!(parser_error.reason(), Some(ParserErrorReason::MissingSafetyComment))); + check_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/references.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/references.rs index 5dbc395eb590..a11cd0eea538 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/references.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/references.rs @@ -1,9 +1,4 @@ -use crate::hir::{ - def_collector::dc_crate::CompilationError, resolution::errors::ResolverError, - type_check::TypeCheckError, -}; - -use super::get_program_errors; +use crate::tests::check_errors; #[test] fn cannot_mutate_immutable_variable() { @@ -11,21 +6,12 @@ fn cannot_mutate_immutable_variable() { fn main() { let array = [1]; mutate(&mut array); + ^^^^^ Cannot mutate immutable variable `array` } fn mutate(_: &mut [Field; 1]) {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = - &errors[0].0 - else { - panic!("Expected a CannotMutateImmutableVariable error"); - }; - - assert_eq!(name, "array"); + check_errors(src); } #[test] @@ -38,23 +24,14 @@ fn cannot_mutate_immutable_variable_on_member_access() { fn main() { let foo = Foo { x: 0 }; mutate(&mut foo.x); + ^^^^^ Cannot mutate immutable variable `foo` } fn mutate(foo: &mut Field) { *foo = 1; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = - &errors[0].0 - else { - panic!("Expected a CannotMutateImmutableVariable error"); - }; - - assert_eq!(name, "foo"); + check_errors(src); } #[test] @@ -62,23 +39,15 @@ fn does_not_crash_when_passing_mutable_undefined_variable() { let src = r#" fn main() { mutate(&mut undefined); + ^^^^^^^^^ cannot find `undefined` in this scope + ~~~~~~~~~ not found in this scope } fn mutate(foo: &mut Field) { *foo = 1; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, .. }) = - &errors[0].0 - else { - panic!("Expected a VariableNotDeclared error"); - }; - - assert_eq!(name, "undefined"); + check_errors(src); } #[test] @@ -90,6 +59,7 @@ fn constrained_reference_to_unconstrained() { // Safety: test context unsafe { mut_ref_input(x_ref, y); + ^^^^^ Cannot pass a mutable reference from a constrained runtime to an unconstrained runtime } } @@ -100,13 +70,5 @@ fn constrained_reference_to_unconstrained() { *x = y; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::ConstrainedReferenceToUnconstrained { .. }) = - &errors[0].0 - else { - panic!("Expected an error about passing a constrained reference to unconstrained"); - }; + check_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/traits.rs index 7f252b556c2c..d2f9d9a96724 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/traits.rs @@ -1,9 +1,6 @@ -use crate::hir::def_collector::dc_crate::CompilationError; -use crate::hir::def_collector::errors::DefCollectorErrorKind; -use crate::hir::resolution::errors::ResolverError; -use crate::hir::resolution::import::PathResolutionError; -use crate::hir::type_check::TypeCheckError; -use crate::tests::{get_program_errors, get_program_with_maybe_parser_errors}; +use crate::elaborator::FrontendOptions; + +use crate::tests::{check_errors, get_program_with_options}; use super::assert_no_errors; @@ -107,16 +104,12 @@ fn trait_inheritance_with_generics_4() { fn trait_inheritance_dependency_cycle() { let src = r#" trait Foo: Bar {} + ^^^ Dependency cycle found + ~~~ 'Foo' recursively depends on itself: Foo -> Bar -> Foo trait Bar: Foo {} fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::DependencyCycle { .. }) - )); + check_errors(src); } #[test] @@ -125,43 +118,31 @@ fn trait_inheritance_missing_parent_implementation() { pub trait Foo {} pub trait Bar: Foo {} + ~~~ required by this bound in `Bar` pub struct Struct {} impl Bar for Struct {} + ^^^^^^ The trait bound `Struct: Foo` is not satisfied + ~~~~~~ The trait `Foo` is not implemented for `Struct` fn main() { let _ = Struct {}; // silence Struct never constructed warning } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::TraitNotImplemented { - impl_trait, - missing_trait: the_trait, - type_missing_trait: typ, - .. - }) = &errors[0].0 - else { - panic!("Expected a TraitNotImplemented error, got {:?}", &errors[0].0); - }; - - assert_eq!(the_trait, "Foo"); - assert_eq!(typ, "Struct"); - assert_eq!(impl_trait, "Bar"); + check_errors(src); } #[test] fn errors_on_unknown_type_in_trait_where_clause() { let src = r#" pub trait Foo where T: Unknown {} + ^^^^^^^ Could not resolve 'Unknown' in path fn main() { } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); + check_errors(src); } #[test] @@ -220,6 +201,7 @@ fn does_not_error_if_impl_trait_constraint_is_satisfied_for_type_variable() { "#; assert_no_errors(src); } + #[test] fn errors_if_impl_trait_constraint_is_not_satisfied() { let src = r#" @@ -230,6 +212,7 @@ fn errors_if_impl_trait_constraint_is_not_satisfied() { pub trait Foo where T: Greeter, + ~~~~~~~ required by this bound in `Foo` { fn greet(object: U) where @@ -244,25 +227,12 @@ fn errors_if_impl_trait_constraint_is_not_satisfied() { pub struct Bar; impl Foo for Bar {} + ^^^ The trait bound `SomeGreeter: Greeter` is not satisfied + ~~~ The trait `Greeter` is not implemented for `SomeGreeter` fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::TraitNotImplemented { - impl_trait, - missing_trait: the_trait, - type_missing_trait: typ, - .. - }) = &errors[0].0 - else { - panic!("Expected a TraitNotImplemented error, got {:?}", &errors[0].0); - }; - - assert_eq!(the_trait, "Greeter"); - assert_eq!(typ, "SomeGreeter"); - assert_eq!(impl_trait, "Foo"); + check_errors(src); } #[test] @@ -441,6 +411,7 @@ fn trait_alias_polymorphic_where_clause() { fn qux(x: T) -> bool where T: Qux { x.foo().bar().baz() + ^^^^^^^^^^^^^^^^^^^ No method named 'baz' found for type 'U' } impl Foo for Field { @@ -463,35 +434,14 @@ fn trait_alias_polymorphic_where_clause() { fn main() { assert(0.foo().bar().baz() == qux(0)); + ^^^ No matching impl found for `T: Baz` + ~~~ No impl for `T: Baz` } "#; // TODO(https://github.com/noir-lang/noir/issues/6467) // assert_no_errors(src); - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - match &errors[0].0 { - CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { - method_name, .. - }) => { - assert_eq!(method_name, "baz"); - } - other => { - panic!("expected UnresolvedMethodCall, but found {:?}", other); - } - } - - match &errors[1].0 { - CompilationError::TypeError(TypeCheckError::NoMatchingImplFound(err)) => { - assert_eq!(err.constraints.len(), 2); - assert_eq!(err.constraints[0].1, "Baz"); - assert_eq!(err.constraints[1].1, "Qux<_>"); - } - other => { - panic!("expected NoMatchingImplFound, but found {:?}", other); - } - } + check_errors(src); } // TODO(https://github.com/noir-lang/noir/issues/6467): currently failing, so @@ -517,10 +467,12 @@ fn trait_alias_with_where_clause_has_equivalent_errors() { pub fn qux(x: T, _: U) -> bool where U: Qux { x.baz() + ^^^^^^^ No method named 'baz' found for type 'T' } fn main() {} "#; + check_errors(src); let alias_src = r#" trait Bar { @@ -535,37 +487,12 @@ fn trait_alias_with_where_clause_has_equivalent_errors() { pub fn qux(x: T, _: U) -> bool where U: Qux { x.baz() + ^^^^^^^ No method named 'baz' found for type 'T' } fn main() {} "#; - - let errors = get_program_errors(src); - let alias_errors = get_program_errors(alias_src); - - assert_eq!(errors.len(), 1); - assert_eq!(alias_errors.len(), 1); - - match (&errors[0].0, &alias_errors[0].0) { - ( - CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { - method_name, - object_type, - .. - }), - CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { - method_name: alias_method_name, - object_type: alias_object_type, - .. - }), - ) => { - assert_eq!(method_name, alias_method_name); - assert_eq!(object_type, alias_object_type); - } - other => { - panic!("expected UnresolvedMethodCall, but found {:?}", other); - } - } + check_errors(alias_src); } #[test] @@ -650,9 +577,9 @@ fn does_not_crash_on_as_trait_path_with_empty_path() { fn main() {} "#; - let (_, _, errors) = get_program_with_maybe_parser_errors( - src, true, // allow parser errors - ); + let allow_parser_errors = true; + let options = FrontendOptions::test_default(); + let (_, _, errors) = get_program_with_options(src, allow_parser_errors, options); assert!(!errors.is_empty()); } @@ -661,6 +588,7 @@ fn warns_if_trait_is_not_in_scope_for_function_call_and_there_is_only_one_trait_ let src = r#" fn main() { let _ = Bar::foo(); + ^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } pub struct Bar { @@ -678,17 +606,7 @@ fn warns_if_trait_is_not_in_scope_for_function_call_and_there_is_only_one_trait_ } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0].0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -790,6 +708,8 @@ fn errors_if_trait_is_not_in_scope_for_function_call_and_there_are_multiple_cand let src = r#" fn main() { let _ = Bar::foo(); + ^^^ Could not resolve 'foo' in path + ~~~ The following traits which provide `foo` are implemented but not in scope: `private_mod::Foo2`, `private_mod::Foo` } pub struct Bar { @@ -817,18 +737,7 @@ fn errors_if_trait_is_not_in_scope_for_function_call_and_there_are_multiple_cand } } "#; - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::UnresolvedWithPossibleTraitsToImport { ident, mut traits }, - )) = errors.remove(0).0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - traits.sort(); - assert_eq!(traits, vec!["private_mod::Foo", "private_mod::Foo2"]); + check_errors(src); } #[test] @@ -839,6 +748,8 @@ fn errors_if_multiple_trait_methods_are_in_scope_for_function_call() { fn main() { let _ = Bar::foo(); + ^^^ Multiple applicable items in scope + ~~~ All these trait which provide `foo` are implemented and in scope: `private_mod::Foo2`, `private_mod::Foo` } pub struct Bar { @@ -866,18 +777,7 @@ fn errors_if_multiple_trait_methods_are_in_scope_for_function_call() { } } "#; - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::MultipleTraitsInScope { ident, mut traits }, - )) = errors.remove(0).0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - traits.sort(); - assert_eq!(traits, vec!["private_mod::Foo", "private_mod::Foo2"]); + check_errors(src); } #[test] @@ -886,6 +786,7 @@ fn warns_if_trait_is_not_in_scope_for_method_call_and_there_is_only_one_trait_me fn main() { let bar = Bar { x: 42 }; let _ = bar.foo(); + ^^^^^^^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } pub struct Bar { @@ -904,17 +805,7 @@ fn warns_if_trait_is_not_in_scope_for_method_call_and_there_is_only_one_trait_me } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0].0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -952,6 +843,8 @@ fn errors_if_trait_is_not_in_scope_for_method_call_and_there_are_multiple_candid fn main() { let bar = Bar { x: 42 }; let _ = bar.foo(); + ^^^^^^^^^ Could not resolve 'foo' in path + ~~~~~~~~~ The following traits which provide `foo` are implemented but not in scope: `private_mod::Foo2`, `private_mod::Foo` } pub struct Bar { @@ -980,18 +873,7 @@ fn errors_if_trait_is_not_in_scope_for_method_call_and_there_are_multiple_candid } } "#; - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::UnresolvedWithPossibleTraitsToImport { ident, mut traits }, - )) = errors.remove(0).0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - traits.sort(); - assert_eq!(traits, vec!["private_mod::Foo", "private_mod::Foo2"]); + check_errors(src); } #[test] @@ -1003,6 +885,8 @@ fn errors_if_multiple_trait_methods_are_in_scope_for_method_call() { fn main() { let bar = Bar { x : 42 }; let _ = bar.foo(); + ^^^^^^^^^ Multiple applicable items in scope + ~~~~~~~~~ All these trait which provide `foo` are implemented and in scope: `private_mod::Foo2`, `private_mod::Foo` } pub struct Bar { @@ -1031,18 +915,7 @@ fn errors_if_multiple_trait_methods_are_in_scope_for_method_call() { } } "#; - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::MultipleTraitsInScope { ident, mut traits }, - )) = errors.remove(0).0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - traits.sort(); - assert_eq!(traits, vec!["private_mod::Foo", "private_mod::Foo2"]); + check_errors(src); } #[test] @@ -1082,28 +955,17 @@ fn type_checks_trait_default_method_and_errors() { let src = r#" pub trait Foo { fn foo(self) -> i32 { + ^^^ expected type i32, found type bool + ~~~ expected i32 because of return type let _ = self; true + ~~~~ bool returned here } } fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { - expected, - actual, - .. - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected.to_string(), "i32"); - assert_eq!(actual.to_string(), "bool"); + check_errors(src); } #[test] @@ -1145,6 +1007,7 @@ fn warns_if_trait_is_not_in_scope_for_primitive_function_call_and_there_is_only_ let src = r#" fn main() { let _ = Field::foo(); + ^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } mod private_mod { @@ -1159,17 +1022,7 @@ fn warns_if_trait_is_not_in_scope_for_primitive_function_call_and_there_is_only_ } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0].0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -1178,6 +1031,7 @@ fn warns_if_trait_is_not_in_scope_for_primitive_method_call_and_there_is_only_on fn main() { let x: Field = 1; let _ = x.foo(); + ^^^^^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } mod private_mod { @@ -1192,17 +1046,7 @@ fn warns_if_trait_is_not_in_scope_for_primitive_method_call_and_there_is_only_on } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0].0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -1211,6 +1055,7 @@ fn warns_if_trait_is_not_in_scope_for_generic_function_call_and_there_is_only_on fn main() { let x: i32 = 1; let _ = x.foo(); + ^^^^^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } mod private_mod { @@ -1225,17 +1070,7 @@ fn warns_if_trait_is_not_in_scope_for_generic_function_call_and_there_is_only_on } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0].0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -1246,25 +1081,19 @@ fn error_on_duplicate_impl_with_associated_type() { } impl Foo for i32 { + ~~~ Previous impl defined here type Bar = u32; } impl Foo for i32 { + ^^^ Impl for type `i32` overlaps with existing impl + ~~~ Overlapping impl type Bar = u8; } fn main() {} "#; - - // Expect "Impl for type `i32` overlaps with existing impl" - // and "Previous impl defined here" - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - use CompilationError::DefinitionError; - use DefCollectorErrorKind::*; - assert!(matches!(&errors[0].0, DefinitionError(OverlappingImpl { .. }))); - assert!(matches!(&errors[1].0, DefinitionError(OverlappingImplNote { .. }))); + check_errors(src); } #[test] @@ -1275,25 +1104,19 @@ fn error_on_duplicate_impl_with_associated_constant() { } impl Foo for i32 { + ~~~ Previous impl defined here let Bar = 5; } impl Foo for i32 { + ^^^ Impl for type `i32` overlaps with existing impl + ~~~ Overlapping impl let Bar = 6; } fn main() {} "#; - - // Expect "Impl for type `i32` overlaps with existing impl" - // and "Previous impl defined here" - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - use CompilationError::DefinitionError; - use DefCollectorErrorKind::*; - assert!(matches!(&errors[0].0, DefinitionError(OverlappingImpl { .. }))); - assert!(matches!(&errors[1].0, DefinitionError(OverlappingImplNote { .. }))); + check_errors(src); } // See https://github.com/noir-lang/noir/issues/6530 @@ -1337,8 +1160,7 @@ fn regression_6530() { let _: Field = foo.into(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -1385,12 +1207,41 @@ fn calls_trait_method_using_struct_name_when_multiple_impls_exist_and_errors_tur } fn main() { let _ = U60Repr::::from2([1, 2, 3]); + ^^^^^^^^^ Expected type Field, found type [Field; 3] } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) - )); + check_errors(src); +} + +#[test] +fn as_trait_path_in_expression() { + let src = r#" + fn main() { + cursed::(); + } + + fn cursed() + where T: Foo + Foo2 + { + ::bar(1); + ::bar(()); + + // Use each function with different generic arguments + ::bar(()); + } + + trait Foo { fn bar(x: U); } + trait Foo2 { fn bar(x: U); } + + pub struct S {} + + impl Foo for S { + fn bar(_x: Z) {} + } + + impl Foo2 for S { + fn bar(_x: Z) {} + } + "#; + assert_no_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/turbofish.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/turbofish.rs index 3e34ea9521b5..d1bf9002ab74 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/turbofish.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/turbofish.rs @@ -1,10 +1,6 @@ -use crate::hir::{ - def_collector::dc_crate::CompilationError, - resolution::{errors::ResolverError, import::PathResolutionError}, - type_check::TypeCheckError, -}; +use crate::tests::check_errors; -use super::{assert_no_errors, get_program_errors}; +use super::assert_no_errors; #[test] fn turbofish_numeric_generic_nested_call() { @@ -66,15 +62,10 @@ fn turbofish_in_constructor_generics_mismatch() { fn main() { let _ = Foo:: { x: 1 }; + ^^^^^^^^^^^^ struct Foo expects 1 generic but 2 were given } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::GenericCountMismatch { .. }), - )); + check_errors(src); } #[test] @@ -87,21 +78,10 @@ fn turbofish_in_constructor() { fn main() { let x: Field = 0; let _ = Foo:: { x: x }; + ^ Expected type i32, found type Field } "#; - - 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"); + check_errors(src); } #[test] @@ -122,6 +102,7 @@ fn turbofish_in_struct_pattern() { #[test] fn turbofish_in_struct_pattern_errors_if_type_mismatch() { + // TODO: maybe the error should be on the expression let src = r#" struct Foo { x: T @@ -130,17 +111,11 @@ fn turbofish_in_struct_pattern_errors_if_type_mismatch() { fn main() { let value: Field = 0; let Foo:: { x } = Foo { x: value }; + ^^^^^^^^^^^^^^^^ Cannot assign an expression of type Foo to a value of type Foo 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); - }; + check_errors(src); } #[test] @@ -153,26 +128,11 @@ fn turbofish_in_struct_pattern_generic_count_mismatch() { fn main() { let value = 0; let Foo:: { x } = Foo { x: value }; + ^^^^^^^^^^^^ struct Foo expects 1 generic but 2 were given 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); + check_errors(src); } #[test] @@ -202,19 +162,10 @@ fn errors_if_turbofish_after_module() { fn main() { moo::::foo(); + ^^^^^^^ turbofish (`::<_>`) not allowed on module `moo` } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TurbofishNotAllowedOnItem { item, .. }, - )) = &errors[0].0 - else { - panic!("Expected a turbofish not allowed on item error, got {:?}", errors[0].0); - }; - assert_eq!(item, "module `moo`"); + check_errors(src); } #[test] @@ -252,22 +203,10 @@ fn turbofish_in_type_before_call_errors() { fn main() { let _ = Foo::::new(true); + ^^^^ Expected type i32, found type bool } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "i32"); - assert_eq!(expr_typ, "bool"); + check_errors(src); } #[test] @@ -312,22 +251,10 @@ fn use_generic_type_alias_with_turbofish_in_method_call_errors() { fn main() { let _ = Bar::::new(true); + ^^^^ Expected type i32, found type bool } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "i32"); - assert_eq!(expr_typ, "bool"); + check_errors(src); } #[test] @@ -371,22 +298,10 @@ fn use_generic_type_alias_with_partial_generics_with_turbofish_in_method_call_er fn main() { let _ = Bar::::new(1, 1); + ^ Expected type bool, found type Field } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "bool"); - assert_eq!(expr_typ, "Field"); + check_errors(src); } #[test] @@ -407,22 +322,10 @@ fn use_generic_type_alias_with_partial_generics_with_turbofish_in_method_call_er fn main() { let _ = Bar::::new(true, true); + ^^^^ Expected type i32, found type bool } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "i32"); - assert_eq!(expr_typ, "bool"); + check_errors(src); } #[test] @@ -440,20 +343,8 @@ fn trait_function_with_turbofish_on_trait_gives_error() { fn main() { let _: i32 = Foo::::foo(1); + ^ Expected type bool, found type Field } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "bool"); - assert_eq!(expr_typ, "Field"); + check_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs index c38e604f2c3b..a3c501142444 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs @@ -1,9 +1,4 @@ -use crate::{ - hir::{def_collector::dc_crate::CompilationError, resolution::errors::ResolverError}, - tests::assert_no_errors, -}; - -use super::get_program_errors; +use crate::tests::{assert_no_errors, check_errors}; #[test] fn errors_on_unused_private_import() { @@ -17,6 +12,8 @@ fn errors_on_unused_private_import() { } use foo::bar; + ^^^ unused import bar + ~~~ unused import use foo::baz; use foo::Foo; @@ -27,17 +24,7 @@ fn errors_on_unused_private_import() { baz(); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "bar"); - assert_eq!(item.item_type(), "import"); + check_errors(src); } #[test] @@ -52,6 +39,8 @@ fn errors_on_unused_pub_crate_import() { } pub(crate) use foo::bar; + ^^^ unused import bar + ~~~ unused import use foo::baz; use foo::Foo; @@ -62,17 +51,7 @@ fn errors_on_unused_pub_crate_import() { baz(); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "bar"); - assert_eq!(item.item_type(), "import"); + check_errors(src); } #[test] @@ -88,51 +67,37 @@ fn errors_on_unused_function() { fn foo() { + ^^^ unused function foo + ~~~ unused function bar(); } fn bar() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "foo"); - assert_eq!(item.item_type(), "function"); + check_errors(src); } #[test] fn errors_on_unused_struct() { let src = r#" struct Foo {} + ^^^ struct `Foo` is never constructed + ~~~ struct is never constructed struct Bar {} fn main() { let _ = Bar {}; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "Foo"); - assert_eq!(item.item_type(), "struct"); + check_errors(src); } #[test] fn errors_on_unused_trait() { let src = r#" trait Foo {} + ^^^ unused trait Foo + ~~~ unused trait trait Bar {} pub struct Baz { @@ -143,17 +108,7 @@ fn errors_on_unused_trait() { fn main() { } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "Foo"); - assert_eq!(item.item_type(), "trait"); + check_errors(src); } #[test] @@ -171,44 +126,28 @@ fn silences_unused_variable_warning() { fn errors_on_unused_type_alias() { let src = r#" type Foo = Field; + ^^^ unused type alias Foo + ~~~ unused type alias type Bar = Field; pub fn bar(_: Bar) {} fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "Foo"); - assert_eq!(item.item_type(), "type alias"); + check_errors(src); } #[test] fn warns_on_unused_global() { let src = r#" global foo: u32 = 1; + ^^^ unused global foo + ~~~ unused global global bar: Field = 1; fn main() { let _ = bar; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item warning"); - }; - - assert_eq!(ident.to_string(), "foo"); - assert_eq!(item.item_type(), "global"); + check_errors(src); } #[test] @@ -262,9 +201,7 @@ fn no_warning_on_inner_struct_when_parent_is_used() { assert_eq(foos[0].a, 10); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs index 917394316cf9..ee53dcbc84ff 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs @@ -1,10 +1,4 @@ -use crate::{ - hir::{ - def_collector::{dc_crate::CompilationError, errors::DefCollectorErrorKind}, - resolution::{errors::ResolverError, import::PathResolutionError}, - }, - tests::{assert_no_errors, get_program_errors}, -}; +use crate::tests::{assert_no_errors, check_errors}; #[test] fn errors_once_on_unused_import_that_is_not_accessible() { @@ -14,39 +8,13 @@ fn errors_once_on_unused_import_that_is_not_accessible() { struct Foo {} } use moo::Foo; + ^^^ Foo is private and not visible from the current module + ~~~ Foo is private fn main() { let _ = Foo {}; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( - PathResolutionError::Private { .. } - )) - )); -} - -fn assert_type_is_more_private_than_item_error(src: &str, private_typ: &str, public_item: &str) { - let errors = get_program_errors(src); - - assert!(!errors.is_empty(), "expected visibility error, got nothing"); - for (error, _) in &errors { - let CompilationError::ResolverError(ResolverError::TypeIsMorePrivateThenItem { - typ, - item, - .. - }) = error - else { - panic!("Expected a type vs item visibility error, got {}", error); - }; - - assert_eq!(typ, private_typ); - assert_eq!(item, public_item); - } - assert_eq!(errors.len(), 1, "only expected one error"); + check_errors(src); } #[test] @@ -54,12 +22,13 @@ fn errors_if_type_alias_aliases_more_private_type() { let src = r#" struct Foo {} pub type Bar = Foo; + ^^^ Type `Foo` is more private than item `Bar` pub fn no_unused_warnings() { let _: Bar = Foo {}; } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Foo", "Bar"); + check_errors(src); } #[test] @@ -68,13 +37,14 @@ fn errors_if_type_alias_aliases_more_private_type_in_generic() { pub struct Generic { value: T } struct Foo {} pub type Bar = Generic; + ^^^^^^^^^^^^ Type `Foo` is more private than item `Bar` pub fn no_unused_warnings() { let _ = Foo {}; let _: Bar = Generic { value: Foo {} }; } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Foo", "Bar"); + check_errors(src); } #[test] @@ -84,6 +54,7 @@ fn errors_if_pub_type_alias_leaks_private_type_in_generic() { struct Bar {} pub struct Foo { pub value: T } pub type FooBar = Foo; + ^^^^^^^^ Type `Bar` is more private than item `FooBar` pub fn no_unused_warnings() { let _: FooBar = Foo { value: Bar {} }; @@ -91,7 +62,7 @@ fn errors_if_pub_type_alias_leaks_private_type_in_generic() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "FooBar"); + check_errors(src); } #[test] @@ -101,6 +72,7 @@ fn errors_if_pub_struct_field_leaks_private_type_in_generic() { struct Bar {} pub struct Foo { pub value: T } pub struct FooBar { pub value: Foo } + ^^^^^ Type `Bar` is more private than item `FooBar::value` pub fn no_unused_warnings() { let _ = FooBar { value: Foo { value: Bar {} } }; @@ -108,7 +80,7 @@ fn errors_if_pub_struct_field_leaks_private_type_in_generic() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "FooBar::value"); + check_errors(src); } #[test] @@ -118,12 +90,13 @@ fn errors_if_pub_function_leaks_private_type_in_return() { struct Bar {} pub fn bar() -> Bar { + ^^^ Type `Bar` is more private than item `bar` Bar {} } } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "bar"); + check_errors(src); } #[test] @@ -132,6 +105,7 @@ fn errors_if_pub_function_leaks_private_type_in_arg() { pub mod moo { struct Bar {} pub fn bar(_bar: Bar) {} + ^^^ Type `Bar` is more private than item `bar` pub fn no_unused_warnings() { let _ = Bar {}; @@ -139,7 +113,7 @@ fn errors_if_pub_function_leaks_private_type_in_arg() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "bar"); + check_errors(src); } #[test] @@ -172,6 +146,7 @@ fn errors_if_pub_function_on_pub_struct_returns_private() { impl Foo { pub fn bar() -> Bar { + ^^^ Type `Bar` is more private than item `bar` Bar {} } } @@ -182,7 +157,7 @@ fn errors_if_pub_function_on_pub_struct_returns_private() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "bar"); + check_errors(src); } #[test] @@ -218,6 +193,7 @@ fn errors_if_pub_trait_returns_private_struct() { pub trait Foo { fn foo() -> Bar; + ^^^ Type `Bar` is more private than item `foo` } pub fn no_unused_warnings() { @@ -226,7 +202,7 @@ fn errors_if_pub_trait_returns_private_struct() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "foo"); + check_errors(src); } #[test] @@ -262,20 +238,11 @@ fn errors_if_trying_to_access_public_function_inside_private_module() { } fn main() { foo::bar::baz() + ^^^ bar is private and not visible from the current module + ~~~ bar is private } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "bar"); + check_errors(src); } #[test] @@ -293,22 +260,13 @@ fn warns_if_calling_private_struct_method() { pub fn method(foo: moo::Foo) { foo.bar() + ^^^ bar is private and not visible from the current module + ~~~ bar is private } fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "bar"); + check_errors(src); } #[test] @@ -385,22 +343,13 @@ fn error_when_accessing_private_struct_field() { fn foo(foo: moo::Foo) -> Field { foo.x + ^ x is private and not visible from the current module + ~ x is private } fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "x"); + check_errors(src); } #[test] @@ -454,20 +403,11 @@ fn error_when_using_private_struct_field_in_constructor() { fn main() { let _ = moo::Foo { x: 1 }; + ^ x is private and not visible from the current module + ~ x is private } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "x"); + check_errors(src); } #[test] @@ -481,24 +421,15 @@ fn error_when_using_private_struct_field_in_struct_pattern() { fn foo(foo: moo::Foo) -> Field { let moo::Foo { x } = foo; + ^ x is private and not visible from the current module + ~ x is private x } fn main() { } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "x"); + check_errors(src); } #[test] @@ -563,21 +494,12 @@ fn errors_if_accessing_private_struct_member_inside_comptime_context() { comptime { let foo = Foo::new(5); let _ = foo.inner; + ^^^^^ inner is private and not visible from the current module + ~~~~~ inner is private }; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "inner"); + check_errors(src); } #[test] @@ -592,6 +514,7 @@ fn errors_if_accessing_private_struct_member_inside_function_generated_at_compti use foo::Foo; #[generate_inner_accessor] + ~~~~~~~~~~~~~~~~~~~~~~~~~~ While running this function attribute struct Bar { bar_inner: Foo, } @@ -600,6 +523,8 @@ fn errors_if_accessing_private_struct_member_inside_function_generated_at_compti quote { fn bar_get_foo_inner(x: Bar) -> Field { x.bar_inner.foo_inner + ^^^^^^^^^ foo_inner is private and not visible from the current module + ~~~~~~~~~ foo_inner is private } } } @@ -608,16 +533,5 @@ fn errors_if_accessing_private_struct_member_inside_function_generated_at_compti let _ = bar_get_foo_inner(x); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "foo_inner"); + check_errors(src); } diff --git a/noir/noir-repo/compiler/wasm/package.json b/noir/noir-repo/compiler/wasm/package.json index e4a0795f0fe2..bb965244a3ed 100644 --- a/noir/noir-repo/compiler/wasm/package.json +++ b/noir/noir-repo/compiler/wasm/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "1.0.0-beta.2", + "version": "1.0.0-beta.3", "license": "(MIT OR Apache-2.0)", "main": "dist/main.js", "types": "./dist/types/src/index.d.cts", diff --git a/noir/noir-repo/compiler/wasm/src/compile.rs b/noir/noir-repo/compiler/wasm/src/compile.rs index e823f90add58..8c0359bbced0 100644 --- a/noir/noir-repo/compiler/wasm/src/compile.rs +++ b/noir/noir-repo/compiler/wasm/src/compile.rs @@ -5,7 +5,7 @@ use js_sys::{JsString, Object}; use nargo::parse_all; use noirc_artifacts::{contract::ContractArtifact, program::ProgramArtifact}; use noirc_driver::{ - add_dep, file_manager_with_stdlib, prepare_crate, prepare_dependency, CompileOptions, + CompileOptions, add_dep, file_manager_with_stdlib, prepare_crate, prepare_dependency, }; use noirc_evaluator::errors::SsaReport; use noirc_frontend::{ @@ -13,7 +13,7 @@ use noirc_frontend::{ hir::Context, }; use serde::Deserialize; -use std::{collections::HashMap, path::Path}; +use std::{collections::BTreeMap, path::Path}; use wasm_bindgen::prelude::*; use crate::errors::{CompileError, JsCompileError}; @@ -128,16 +128,21 @@ impl JsCompileContractResult { #[derive(Deserialize, Default)] pub(crate) struct DependencyGraph { pub(crate) root_dependencies: Vec, - pub(crate) library_dependencies: HashMap>, + pub(crate) library_dependencies: BTreeMap>, } +/// This map contains the paths of all of the files in the entry-point crate and +/// the transitive dependencies of the entry-point crate. +/// +/// This is for all intents and purposes the file system that the compiler will use to resolve/compile +/// files in the crate being compiled and its dependencies. +/// +/// Using a `BTreeMap` to add files to the [FileManager] in a deterministic order, +/// which affects the `FileId` in the `Location`s in the AST on which the `hash` is based. +/// Note that we cannot expect to match the IDs assigned by the `FileManager` used by `nargo`, +/// because there the order is determined by the dependency graph as well as the file name. #[wasm_bindgen] -// This is a map containing the paths of all of the files in the entry-point crate and -// the transitive dependencies of the entry-point crate. -// -// This is for all intents and purposes the file system that the compiler will use to resolve/compile -// files in the crate being compiled and its dependencies. #[derive(Deserialize, Default)] -pub struct PathToFileSourceMap(pub(crate) HashMap); +pub struct PathToFileSourceMap(pub(crate) BTreeMap); #[wasm_bindgen] impl PathToFileSourceMap { @@ -171,7 +176,7 @@ pub fn compile_program( let compiled_program = noirc_driver::compile_main(&mut context, crate_id, &compile_options, None) .map_err(|errs| { - CompileError::with_file_diagnostics( + CompileError::with_custom_diagnostics( "Failed to compile program", errs, &context.file_manager, @@ -181,7 +186,7 @@ pub fn compile_program( let optimized_program = nargo::ops::transform_program(compiled_program, expression_width); nargo::ops::check_program(&optimized_program).map_err(|errs| { - CompileError::with_file_diagnostics( + CompileError::with_custom_diagnostics( "Compiled program is not solvable", errs, &context.file_manager, @@ -207,8 +212,8 @@ pub fn compile_contract( let compiled_contract = noirc_driver::compile_contract(&mut context, crate_id, &compile_options) - .map_err(|errs| { - CompileError::with_file_diagnostics( + .map_err(|errs: Vec| { + CompileError::with_custom_diagnostics( "Failed to compile contract", errs, &context.file_manager, @@ -231,7 +236,7 @@ fn prepare_context( ::into_serde(&JsValue::from(dependency_graph)) .map_err(|err| err.to_string())? } else { - DependencyGraph { root_dependencies: vec![], library_dependencies: HashMap::new() } + DependencyGraph { root_dependencies: vec![], library_dependencies: BTreeMap::new() } }; let fm = file_manager_with_source_map(file_source_map); @@ -272,7 +277,7 @@ pub(crate) fn file_manager_with_source_map(source_map: PathToFileSourceMap) -> F // upon some library `lib1`. Then the packages that `lib1` depend upon will be placed in the // `library_dependencies` list and the `lib1` will be placed in the `root_dependencies` list. fn process_dependency_graph(context: &mut Context, dependency_graph: DependencyGraph) { - let mut crate_names: HashMap<&CrateName, CrateId> = HashMap::new(); + let mut crate_names: BTreeMap<&CrateName, CrateId> = BTreeMap::new(); for lib in &dependency_graph.root_dependencies { let crate_id = add_noir_lib(context, lib); @@ -311,8 +316,8 @@ mod test { use crate::compile::PathToFileSourceMap; - use super::{file_manager_with_source_map, process_dependency_graph, DependencyGraph}; - use std::{collections::HashMap, path::Path}; + use super::{DependencyGraph, file_manager_with_source_map, process_dependency_graph}; + use std::{collections::BTreeMap, path::Path}; fn setup_test_context(source_map: PathToFileSourceMap) -> Context<'static, 'static> { let mut fm = file_manager_with_source_map(source_map); @@ -333,7 +338,7 @@ mod test { #[test] fn test_works_with_empty_dependency_graph() { let dependency_graph = - DependencyGraph { root_dependencies: vec![], library_dependencies: HashMap::new() }; + DependencyGraph { root_dependencies: vec![], library_dependencies: BTreeMap::new() }; let source_map = PathToFileSourceMap::default(); let mut context = setup_test_context(source_map); @@ -348,7 +353,7 @@ mod test { fn test_works_with_root_dependencies() { let dependency_graph = DependencyGraph { root_dependencies: vec![crate_name("lib1")], - library_dependencies: HashMap::new(), + library_dependencies: BTreeMap::new(), }; let source_map = PathToFileSourceMap( @@ -368,7 +373,7 @@ mod test { fn test_works_with_duplicate_root_dependencies() { let dependency_graph = DependencyGraph { root_dependencies: vec![crate_name("lib1"), crate_name("lib1")], - library_dependencies: HashMap::new(), + library_dependencies: BTreeMap::new(), }; let source_map = PathToFileSourceMap( @@ -387,7 +392,7 @@ mod test { fn test_works_with_transitive_dependencies() { let dependency_graph = DependencyGraph { root_dependencies: vec![crate_name("lib1")], - library_dependencies: HashMap::from([ + library_dependencies: BTreeMap::from([ (crate_name("lib1"), vec![crate_name("lib2")]), (crate_name("lib2"), vec![crate_name("lib3")]), ]), @@ -413,7 +418,7 @@ mod test { fn test_works_with_missing_dependencies() { let dependency_graph = DependencyGraph { root_dependencies: vec![crate_name("lib1")], - library_dependencies: HashMap::from([(crate_name("lib2"), vec![crate_name("lib3")])]), + library_dependencies: BTreeMap::from([(crate_name("lib2"), vec![crate_name("lib3")])]), }; let source_map = PathToFileSourceMap( diff --git a/noir/noir-repo/compiler/wasm/src/compile_new.rs b/noir/noir-repo/compiler/wasm/src/compile_new.rs index ac2f79147b3e..37065c8f8255 100644 --- a/noir/noir-repo/compiler/wasm/src/compile_new.rs +++ b/noir/noir-repo/compiler/wasm/src/compile_new.rs @@ -1,12 +1,12 @@ use crate::compile::{ - file_manager_with_source_map, JsCompileContractResult, JsCompileProgramResult, - PathToFileSourceMap, + JsCompileContractResult, JsCompileProgramResult, PathToFileSourceMap, + file_manager_with_source_map, }; use crate::errors::{CompileError, JsCompileError}; use acvm::acir::circuit::ExpressionWidth; use nargo::parse_all; use noirc_driver::{ - add_dep, compile_contract, compile_main, prepare_crate, prepare_dependency, CompileOptions, + CompileOptions, add_dep, compile_contract, compile_main, prepare_crate, prepare_dependency, }; use noirc_frontend::{ graph::{CrateId, CrateName}, @@ -109,7 +109,7 @@ impl CompilerContext { let compiled_program = compile_main(&mut self.context, root_crate_id, &compile_options, None) .map_err(|errs| { - CompileError::with_file_diagnostics( + CompileError::with_custom_diagnostics( "Failed to compile program", errs, &self.context.file_manager, @@ -119,7 +119,7 @@ impl CompilerContext { let optimized_program = nargo::ops::transform_program(compiled_program, expression_width); nargo::ops::check_program(&optimized_program).map_err(|errs| { - CompileError::with_file_diagnostics( + CompileError::with_custom_diagnostics( "Compiled program is not solvable", errs, &self.context.file_manager, @@ -148,7 +148,7 @@ impl CompilerContext { let compiled_contract = compile_contract(&mut self.context, root_crate_id, &compile_options) .map_err(|errs| { - CompileError::with_file_diagnostics( + CompileError::with_custom_diagnostics( "Failed to compile contract", errs, &self.context.file_manager, @@ -280,7 +280,7 @@ mod test { use noirc_driver::prepare_crate; use noirc_frontend::hir::Context; - use crate::compile::{file_manager_with_source_map, PathToFileSourceMap}; + use crate::compile::{PathToFileSourceMap, file_manager_with_source_map}; use std::path::Path; diff --git a/noir/noir-repo/compiler/wasm/src/errors.rs b/noir/noir-repo/compiler/wasm/src/errors.rs index ef56dcfc911c..47927df10562 100644 --- a/noir/noir-repo/compiler/wasm/src/errors.rs +++ b/noir/noir-repo/compiler/wasm/src/errors.rs @@ -4,7 +4,7 @@ use serde::Serialize; use wasm_bindgen::prelude::*; use fm::FileManager; -use noirc_errors::FileDiagnostic; +use noirc_errors::CustomDiagnostic; #[wasm_bindgen(typescript_custom_section)] const DIAGNOSTICS: &'static str = r#" @@ -87,8 +87,7 @@ pub struct Diagnostic { } impl Diagnostic { - fn new(file_diagnostic: &FileDiagnostic, file: String) -> Diagnostic { - let diagnostic = &file_diagnostic.diagnostic; + fn new(diagnostic: &CustomDiagnostic, file: String) -> Diagnostic { let message = diagnostic.message.clone(); let secondaries = diagnostic @@ -96,8 +95,8 @@ impl Diagnostic { .iter() .map(|label| DiagnosticLabel { message: label.message.clone(), - start: label.span.start(), - end: label.span.end(), + start: label.location.span.start(), + end: label.location.span.end(), }) .collect(); @@ -116,16 +115,16 @@ impl CompileError { CompileError { message: message.to_string(), diagnostics: vec![] } } - pub fn with_file_diagnostics( + pub fn with_custom_diagnostics( message: &str, - file_diagnostics: Vec, + custom_diagnostics: Vec, file_manager: &FileManager, ) -> CompileError { - let diagnostics: Vec<_> = file_diagnostics + let diagnostics: Vec<_> = custom_diagnostics .iter() .map(|err| { let file_path = file_manager - .path(err.file_id) + .path(err.file) .expect("File must exist to have caused diagnostics"); Diagnostic::new(err, file_path.to_str().unwrap().to_string()) }) diff --git a/noir/noir-repo/compiler/wasm/src/lib.rs b/noir/noir-repo/compiler/wasm/src/lib.rs index 6753faf20096..d24b6f4ed01a 100644 --- a/noir/noir-repo/compiler/wasm/src/lib.rs +++ b/noir/noir-repo/compiler/wasm/src/lib.rs @@ -10,8 +10,8 @@ use gloo_utils::format::JsValueSerdeExt; use noirc_driver::{GIT_COMMIT, GIT_DIRTY, NOIRC_VERSION}; use serde::{Deserialize, Serialize}; -use tracing_subscriber::prelude::*; use tracing_subscriber::EnvFilter; +use tracing_subscriber::prelude::*; use tracing_web::MakeWebConsoleWriter; mod compile; @@ -21,8 +21,8 @@ mod errors; pub use compile::{compile_contract, compile_program}; // Expose the new Context-Centric API -pub use compile_new::{compile_contract_, compile_program_, CompilerContext, CrateIDWrapper}; -use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; +pub use compile_new::{CompilerContext, CrateIDWrapper, compile_contract_, compile_program_}; +use wasm_bindgen::{JsValue, prelude::wasm_bindgen}; #[derive(Serialize, Deserialize)] pub struct BuildInfo { diff --git a/noir/noir-repo/compiler/wasm/src/types/noir_artifact.ts b/noir/noir-repo/compiler/wasm/src/types/noir_artifact.ts index b7b42186c1a1..2abdea63c516 100644 --- a/noir/noir-repo/compiler/wasm/src/types/noir_artifact.ts +++ b/noir/noir-repo/compiler/wasm/src/types/noir_artifact.ts @@ -58,6 +58,8 @@ export interface ABIVariable { export interface NoirFunctionEntry { /** The name of the function. */ name: string; + /** The hash of the circuit. */ + hash?: string; /** Whether the function is unconstrained. */ is_unconstrained: boolean; /** The custom attributes applied to the function. */ @@ -96,7 +98,7 @@ export interface ProgramArtifact { /** Version of noir used for the build. */ noir_version: string; /** The hash of the circuit. */ - hash?: number; + hash?: string; /** * The ABI of the function. */ abi: Abi; /** The bytecode of the circuit in base64. */ diff --git a/noir/noir-repo/compiler/wasm/test/compiler/shared/compile.test.ts b/noir/noir-repo/compiler/wasm/test/compiler/shared/compile.test.ts index f9e37530cbcd..8c9af58fa0ae 100644 --- a/noir/noir-repo/compiler/wasm/test/compiler/shared/compile.test.ts +++ b/noir/noir-repo/compiler/wasm/test/compiler/shared/compile.test.ts @@ -27,10 +27,11 @@ export function shouldCompileProgramIdentically( // Prepare noir-wasm artifact const noirWasmProgram = noirWasmArtifact.program; expect(noirWasmProgram).not.to.be.undefined; - const [_noirWasmDebugInfos, norWasmFileMap] = deleteProgramDebugMetadata(noirWasmProgram); + const [_noirWasmDebugInfos, noirWasmFileMap] = deleteProgramDebugMetadata(noirWasmProgram); normalizeVersion(noirWasmProgram); - // We first compare both contracts without considering debug info + // We first compare both contracts without considering debug info. + // We can't expect hashes to match `nargo` because of the different order in which dependencies are visited. delete (noirWasmProgram as Partial).hash; delete (nargoArtifact as Partial).hash; expect(nargoArtifact).to.deep.eq(noirWasmProgram); @@ -38,7 +39,7 @@ export function shouldCompileProgramIdentically( // Compare the file maps, ignoring keys, since those depend in the order in which files are visited, // which may change depending on the file manager implementation. Also ignores paths, since the base // path is reported differently between nargo and noir-wasm. - expect(getSources(nargoFileMap)).to.have.members(getSources(norWasmFileMap)); + expect(getSources(nargoFileMap)).to.have.members(getSources(noirWasmFileMap)); // Compare the debug symbol information, ignoring the actual ids used for file identifiers. // Debug symbol info looks like the following, what we need is to ignore the 'file' identifiers @@ -63,16 +64,23 @@ export function shouldCompileContractIdentically( // Prepare noir-wasm artifact const noirWasmContract = noirWasmArtifact.contract; expect(noirWasmContract).not.to.be.undefined; - const [noirWasmDebugInfos, norWasmFileMap] = deleteContractDebugMetadata(noirWasmContract); + const [noirWasmDebugInfos, noirWasmFileMap] = deleteContractDebugMetadata(noirWasmContract); normalizeVersion(noirWasmContract); // We first compare both contracts without considering debug info + // We can't expect hashes to match `nargo` because of the different order in which dependencies are visited. + nargoArtifact.functions.forEach(function (f) { + delete (f as Partial).hash; + }); + noirWasmContract.functions.forEach(function (f) { + delete (f as Partial).hash; + }); expect(nargoArtifact).to.deep.eq(noirWasmContract); // Compare the file maps, ignoring keys, since those depend in the order in which files are visited, // which may change depending on the file manager implementation. Also ignores paths, since the base // path is reported differently between nargo and noir-wasm. - expect(getSources(nargoFileMap)).to.have.members(getSources(norWasmFileMap)); + expect(getSources(nargoFileMap)).to.have.members(getSources(noirWasmFileMap)); // Compare the debug symbol information, ignoring the actual ids used for file identifiers. // Debug symbol info looks like the following, what we need is to ignore the 'file' identifiers diff --git a/noir/noir-repo/cspell.json b/noir/noir-repo/cspell.json index c7ad22afa96c..3bbbede78ccc 100644 --- a/noir/noir-repo/cspell.json +++ b/noir/noir-repo/cspell.json @@ -261,7 +261,10 @@ "whitespaces", "YOLO", "zkhash", - "zshell" + "zshell", + "flamegraph", + "flamegraphs", + "lookback" ], "ignorePaths": [ "./**/node_modules/**", diff --git a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.mdx b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.mdx index 5f6a7b08ec17..c8c7894ff911 100644 --- a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.mdx +++ b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.mdx @@ -66,7 +66,7 @@ This will compile your source code into a Noir build artifact to be stored in th ```sh bb write_vk_ultra_keccak_honk -b ./target/.json -bb contract --scheme ultra_honk +bb contract_ultra_honk ``` @@ -271,11 +271,13 @@ It would be incorrect to say that a Noir proof verification costs any gas at all ::: -## A Note on EVM chains +## Compatibility with different EVM chains -Noir proof verification requires the ecMul, ecAdd and ecPairing precompiles. Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. You can find an incomplete list of which EVM chains support these precompiles [here](https://www.evmdiff.com/features?feature=precompiles). +Barretenberg proof verification requires the `ecMul`, `ecAdd`, `ecPairing`, and `modexp` EVM precompiles. You can deploy and use the verifier contract on all EVM chains that support the precompiles. -For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: +EVM Diff provides a great table of which EVM chains support which precompiles: https://www.evmdiff.com/features?feature=precompiles + +Some EVM chains manually tested to work with the Barretenberg verifier include: - Optimism - Arbitrum @@ -289,7 +291,12 @@ For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently suppo - Linea - Moonbeam -If you test any other chains, please open a PR on this page to update the list. +Meanwhile, some EVM chains chains manually tested that failed to work with the Barretenberg verifier include: + +- zkSync ERA +- Polygon zkEVM + +Pull requests to update this section is welcome and appreciated if you have compatibility updates on existing / new chains to contribute: https://github.com/noir-lang/noir ## What's next diff --git a/noir/noir-repo/docs/docs/noir/concepts/data_types/integers.md b/noir/noir-repo/docs/docs/noir/concepts/data_types/integers.md index b8a5d4980296..ff3fafa1f905 100644 --- a/noir/noir-repo/docs/docs/noir/concepts/data_types/integers.md +++ b/noir/noir-repo/docs/docs/noir/concepts/data_types/integers.md @@ -58,54 +58,6 @@ fn main(x: i16, y: i16) { Modulo operation is defined for negative integers thanks to integer division, so that the equality `x = (x/y)*y + (x%y)` holds. -## 128 bits Unsigned Integers - -The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind: -- You cannot cast between a native integer and `U128` -- There is a higher performance cost when using `U128`, compared to a native type. - -Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. - -```rust -fn main() { - let x = U128::from_integer(23); - let y = U128::from_hex("0x7"); - let z = x + y; - assert(z.to_integer() == 30); -} -``` - -`U128` is implemented with two 64 bits limbs, representing the low and high bits, which explains the performance cost. You should expect `U128` to be twice more costly for addition and four times more costly for multiplication. -You can construct a U128 from its limbs: -```rust -fn main(x: u64, y: u64) { - let z = U128::from_u64s_be(x,y); - assert(z.hi == x as Field); - assert(z.lo == y as Field); -} -``` - -Note that the limbs are stored as Field elements in order to avoid unnecessary conversions. -Apart from this, most operations will work as usual: - -```rust -fn main(x: U128, y: U128) { - // multiplication - let c = x * y; - // addition and subtraction - let c = c - x + y; - // division - let c = x / y; - // bit operation; - let c = x & y | y; - // bit shift - let c = x << y; - // comparisons; - let c = x < y; - let c = x == y; -} -``` - ## Overflows Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: diff --git a/noir/noir-repo/docs/docs/noir/concepts/traits.md b/noir/noir-repo/docs/docs/noir/concepts/traits.md index 17cc04a97518..af5b396bfb82 100644 --- a/noir/noir-repo/docs/docs/noir/concepts/traits.md +++ b/noir/noir-repo/docs/docs/noir/concepts/traits.md @@ -153,6 +153,37 @@ fn main() { } ``` +## As Trait Syntax + +Rarely to call a method it may not be sufficient to use the general method call syntax of `obj.method(args)`. +One case where this may happen is if there are two traits in scope which both define a method with the same name. +For example: + +```rust +trait Foo { fn bar(); } +trait Foo2 { fn bar(); } + +fn example() + where T: Foo + Foo2 +{ + // How to call Foo::bar and Foo2::bar? +} +``` + +In the above example we have both `Foo` and `Foo2` which define a `bar` method. The normal way to resolve +this would be to use the static method syntax `Foo::bar(object)` but there is no object in this case and +`Self` does not appear in the type signature of `bar` at all so we would not know which impl to choose. +For these situations there is the "as trait" syntax: `::method(object, args...)` + +```rust +fn example() + where T: Foo + Foo2 +{ + ::bar(); + ::bar(); +} +``` + ## Generic Implementations You can add generics to a trait implementation by adding the generic list after the `impl` keyword: diff --git a/noir/noir-repo/docs/docs/noir/modules_packages_crates/dependencies.md b/noir/noir-repo/docs/docs/noir/modules_packages_crates/dependencies.md index 22186b225988..cb8765323929 100644 --- a/noir/noir-repo/docs/docs/noir/modules_packages_crates/dependencies.md +++ b/noir/noir-repo/docs/docs/noir/modules_packages_crates/dependencies.md @@ -77,14 +77,14 @@ use lib_a; You can also import only the specific parts of dependency that you want to use, like so: ```rust -use std::hash::sha256; +use std::hash::blake3; use std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, You can import multiple items in the same line by enclosing them in curly braces: ```rust -use std::hash::{keccak256, sha256}; +use std::hash::{blake2s, blake3}; ``` We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. diff --git a/noir/noir-repo/docs/docs/noir/standard_library/black_box_fns.md b/noir/noir-repo/docs/docs/noir/standard_library/black_box_fns.md index e9392b20a92f..ed905ecb5c29 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/black_box_fns.md +++ b/noir/noir-repo/docs/docs/noir/standard_library/black_box_fns.md @@ -23,7 +23,7 @@ Here is a list of the current black box functions: - AND - XOR - RANGE -- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) +- [Keccakf1600](./cryptographic_primitives/hashes.mdx#keccakf1600) - [Recursive proof verification](./recursion.mdx) 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. diff --git a/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx b/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx index b7518fa95c10..334873e68635 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx +++ b/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -1,8 +1,7 @@ --- title: Hash methods description: - Learn about the cryptographic primitives ready to use for any Noir project, including sha256, - blake2s and pedersen + Learn about the cryptographic primitives ready to use for any Noir project keywords: [cryptographic primitives, Noir project, sha256, blake2s, pedersen, hash] sidebar_position: 0 @@ -10,23 +9,11 @@ sidebar_position: 0 import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; -## sha256 +## sha256 compression -Given an array of bytes, returns the resulting sha256 hash. -Specify a message_size to hash only the first `message_size` bytes of the input. - -#include_code sha256 noir_stdlib/src/hash/sha256.nr rust - -example: -#include_code sha256_var test_programs/execution_success/sha256/src/main.nr rust - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::sha256::sha256_var(x, 4); -} -``` +Performs a sha256 compression on an input and initial state, returning the resulting state. +#include_code sha256_compression noir_stdlib/src/hash/mod.nr rust @@ -88,17 +75,11 @@ example: -## keccak256 +## keccakf1600 -Given an array of bytes (`u8`), returns the resulting keccak hash as an array of -32 bytes (`[u8; 32]`). Specify a message_size to hash only the first -`message_size` bytes of the input. - -#include_code keccak256 noir_stdlib/src/hash/mod.nr rust - -example: +Given an initial `[u64; 25]` state, returns the state resulting from applying a keccakf1600 permutation (`[u64; 25]`). -#include_code keccak256 test_programs/execution_success/keccak256/src/main.nr rust +#include_code keccakf1600 noir_stdlib/src/hash/mod.nr rust diff --git a/noir/noir-repo/docs/docs/noir/standard_library/traits.md b/noir/noir-repo/docs/docs/noir/standard_library/traits.md index e6f6f80ff032..ed923c0707a1 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/traits.md +++ b/noir/noir-repo/docs/docs/noir/standard_library/traits.md @@ -71,7 +71,7 @@ As a general rule of thumb, `From` may be implemented in the [situations where i - The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. - The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. - The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. -- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. +- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `u128: From<[u8; 16]>`, the methods `u128::from_le_bytes` and `u128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `u128` from the same byte array. One additional recommendation specific to Noir is: - The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. diff --git a/noir/noir-repo/docs/docs/tooling/profiler.md b/noir/noir-repo/docs/docs/tooling/profiler.md new file mode 100644 index 000000000000..c31271facbb3 --- /dev/null +++ b/noir/noir-repo/docs/docs/tooling/profiler.md @@ -0,0 +1,135 @@ +--- +title: Noir Profiler +description: Learn about the Noir Profiler, how to generate execution flamegraphs, identify bottlenecks, and visualize optimizations. +keywords: [profiling, profiler, flamegraph] +sidebar_position: 0 +--- + +`noir-profiler` is a sampling profiler designed to analyze and visualize Noir programs. It assists developers to identify bottlenecks by mapping execution data back to the original source code. + +### Installation + +`noir-profiler` comes out of the box with [noirup](../getting_started/noir_installation.md). Test that you have the profiler installed by running `noir-profiler --version`. + +### Usage + +Let's start by creating a simple Noir program. All this program aims to do is zero out an array past some dynamic index. + +```rust +fn main(ptr: pub u32, mut array: [u32; 32]) -> pub [u32; 32] { + for i in 0..32 { + if i > ptr { + array[i] = 0; + } + } + array +} +``` +You can use these values for the `Prover.toml`: +```toml +ptr = 1 +array = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] +``` + +Running `nargo info` we can get some information about the opcodes produced by this program, but it doesn't give us a lot of info on its own. Compile and execute this program normally using `nargo compile` and `nargo execute`. + +### Generating an ACIR opcode flamegraph + +The program on its own is quite high-level. Let's get a more granular look at what is happening by using `noir-profiler`. + +After compiling the program, run the following: +```sh +noir-profiler opcodes --artifact-path ./target/program.json --output ./target/ +``` +Below you can see an example flamegraph with a total 387 opcodes (using `nargo` version 1.0.0-beta.2): +![ACIR Flamegraph Unoptimized](@site/static/img/tooling/profiler/acir-flamegraph-unoptimized.png) + +You should now have a flamegraph that maps ACIR opcodes to their corresponding locations in the source code. We strongly recommend generating these graphs yourself as you follow this guide. Opening the flamegraph in a browser provides a more interactive experience, allowing you to click into and examine different regions of the graph. Simply viewing the image file won't offer the same level of insight. + +We can see that the majority of opcodes come from the write to `array[i]`. Now that we have some more information about our program's bottlenecks, let's optimize it. + +#### Transform conditional writes into reads + +We can improve our circuit's efficiency using [unconstrained functions](../noir/concepts/unconstrained.md). + +Let's replace expensive array writes with array gets with the new code below: +```rust +fn main(ptr: pub u32, array: [u32; 32]) -> pub [u32; 32] { + // Safety: Sets all elements after `ptr` in `array` to zero. + let zeroed_array = unsafe { zero_out_array(ptr, array) }; + for i in 0..32 { + if i > ptr { + assert_eq(zeroed_array[i], 0); + } else { + assert_eq(zeroed_array[i], array[i]); + } + } + zeroed_array +} + +unconstrained fn zero_out_array(ptr: u32, mut array: [u32; 32]) -> [u32; 32] { + for i in 0..32 { + if i > ptr { + array[i] = 0; + } + } + array +} +``` +We chose to instead write our array inside of the unconstrained function. Then inside of our circuit we assert on every value in the array returned from the unconstrained function. + +This new program produces the following ACIR opcodes flamegraph with a total of 284 opcodes: +![ACIR Flamegraph Optimized](@site/static/img/tooling/profiler/acir-flamegraph-optimized.png) + +In the above image we searched for the ACIR opcodes due to `i > ptr` in the source code. Trigger a search by clicking on "Search" in the top right corner of the flamegraph. In the bottom right corner of the image above, you will note that the flamegraph displays the percentage of all opcodes associated with that search. Searching for `memory::op` in the optimized flamegraph will result in no matches. This is due to no longer using a dynamic array in our circuit. By dynamic array, we are referring to using a dynamic index (values reliant upon witness inputs) when working with arrays. Most of the memory operations, have now been replaced with arithmetic operations as we are reading two arrays from known constant indices. + +### Generate a backend gates flamegraph + +Unfortunately, ACIR opcodes do not give us a full picture of where the cost of this program lies. +The `gates` command also accepts a backend binary. In the [quick start guide](../getting_started/quick_start.md#proving-backend) you can see how to get started with the [Barretenberg proving backend](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg). + +Run the following command: +```sh +noir-profiler gates --artifact-path ./target/program.json --backend-path bb --output ./target +``` +`--backend-path` accepts a path to the backend binary. In the above command we assume that you have the backend binary path saved in your PATH. If you do not, you will have to pass the binary's absolute path. + +This produces the following flamegraph with 3,737 total backend gates (using `bb` version 0.76.4): +![Gates Flamegraph Unoptimized](@site/static/img/tooling/profiler/gates-flamegraph-unoptimized.png) + +Searching for ACIR `memory::op` opcodes, they look to cause about 18.2% of the backend gates. + +You will notice that the majority of the backend gates come from the ACIR range opcodes. This is due to the way UltraHonk handles range constraints, which is the backend used in this example. UltraHonk uses lookup tables internally for its range gates. These can take up the majority of the gates for a small circuit, but whose impact becomes more meaningful in larger circuits. If our array was much larger, range gates would become a much smaller percentage of our total circuit. +Here is an example backend gates flamegraph for the same program in this guide but with an array of size 2048: +![Gates Flamegraph Unoptimized 2048](@site/static/img/tooling/profiler/gates-flamegraph-unoptimized-2048.png) +Every backend implements ACIR opcodes differently, so it is important to profile both the ACIR and the backend gates to get a full picture. + +Now let's generate a graph for our optimized circuit with an array of size 32. We get the following flamegraph that produces 3,062 total backend gates: +![Gates Flamegraph Optimized](@site/static/img/tooling/profiler/gates-flamegraph-optimized.png) + +In the optimized flamegraph, we searched for the backend gates due to `i > ptr` in the source code. The backend gates associated with this call stack were only 3.8% of the total backend gates. If we look back to the ACIR flamegraph, that same code was the cause of 43.3% ACIR opcodes. This discrepancy reiterates the earlier point about profiling both the ACIR opcodes and backend gates. + +For posterity, here is the flamegraph for the same program with a size 2048 array: +![Gates Flamegraph Optimized 2048](@site/static/img/tooling/profiler/gates-flamegraph-optimized-2048.png) + +### Generate an unconstrained execution trace flamegraph + +The profiler also enables developers to generate a flamegraph of the unconstrained execution trace. For unconstrained functions Noir compiles down to Brillig bytecode, thus we will be seeing a flamegraph of Brillig opcodes, rather than ACIR opcodes. + +Let's take our initial program and simply add an `unconstrained` modifier before main (e.g. `unconstrained fn main`). Then run the following command: +```sh +noir-profiler execution-opcodes --artifact-name ./target/program.json --prover_toml_path Prover.toml --output ./target +``` +This matches the `opcodes` command, except that now we need to accept a `Prover.toml` file to profile execution with a specific set of inputs. + +We will get the following flamegraph with 1,582 opcodes executed: +![Brillig Trace Initial Program](@site/static/img/tooling/profiler/brillig-trace-initial-32.png) + +Circuit programming (ACIR) is an entirely different execution paradigm compared to regular programming. To demonstrate this point further, let's generate an execution trace for our optimized ACIR program once we have modified `main` to be `unconstrained`. + +We then get the following flamegraph with 2,125 opcodes executed: +![Brillig Trace "Optimized"](@site/static/img/tooling/profiler/brillig-trace-opt-32.png) + +In the above graph we are searching for `new_array`, which shows up zero matches in the initial program. In the unconstrained environment, the updated program essentially just adds extra unnecessary checks. Thus, we see a longer execution trace. + +`execution-opcodes` is useful for when you are searching for bottlenecks in unconstrained code. This can be especially meaningful for optimizing witness generation. Even though unconstrained execution helps us skip proving steps, we still need to compute the relevant inputs/outputs outside of the circuit before proving. diff --git a/noir/noir-repo/docs/docs/tooling/security.md b/noir/noir-repo/docs/docs/tooling/security.md index e14481efc317..8a09d231a7da 100644 --- a/noir/noir-repo/docs/docs/tooling/security.md +++ b/noir/noir-repo/docs/docs/tooling/security.md @@ -39,7 +39,7 @@ Here, the results of `factor` are two elements of the returned array. The value This pass checks if the constraint coverage of Brillig calls is sufficient in these terms. -The check is at the moment disabled by default due to performance concerns and can be enabled by passing the `--enable-brillig-constraints-check` option to `nargo`. +The check is enabled by default and can be disabled by passing the `--skip-brillig-constraints-check` option to `nargo`. #### Lookback option diff --git a/noir/noir-repo/docs/static/img/tooling/profiler/acir-flamegraph-optimized.png b/noir/noir-repo/docs/static/img/tooling/profiler/acir-flamegraph-optimized.png new file mode 100644 index 0000000000000000000000000000000000000000..7237d7868fcce6b18de88d652b9d006ea2cd71a0 GIT binary patch literal 90387 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 z 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 z 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 R{SSaId1)o7A_+sk{|~h518)ET literal 0 HcmV?d00001 diff --git a/noir/noir-repo/docs/static/img/tooling/profiler/acir-flamegraph-unoptimized.png b/noir/noir-repo/docs/static/img/tooling/profiler/acir-flamegraph-unoptimized.png new file mode 100644 index 0000000000000000000000000000000000000000..d807539dcc56cf5bf6b9f894e2e47e5e2a443749 GIT binary patch literal 90830 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

4eg{j`CvW~lsetVfBLgO^ zM~T%NQblC)SP&f7gm!>+!*c$og#QI2@t2?{{v&Qw=e9Q}ygAM0>Zer)DC&CA}9tQ^oQB0Cw9rc* z;Nn2%Y`-Sp^M9_Pfc?+ENy+CA0w2ACz^Pibwaq*3&*~o2Ww6({CQ_uZ*F>Us5^fL< zz^5YfV_H)z3PWAIPhd7+|L?!J$Y1v{>IWRo7<6w$mVovI%1^#Wnez@upgca@ zYm3a4Lta3Ao$mjBVZUw@C;kRPqqvIjllmz9-T@QTv^Q^=JdkAUxzNDiYrg)GxPON) zj^{Oj@MD%cDgT_-zG)6MGx&ME>{95smT;?TR%yA$@l8C@^Jm-`)d15aD`+1?); z^%BH&p!^xoF4%G9vY`f1MgF*hYr(Ba&GyBI^{$9EoLfM3*wumpuD)`yDziAma$`Qt z^ZoH#Ov>uXSQc9)EuU`hgZSi$6R~q8dd)nr(fub$)pZN5&n;5mYPq1`u2bf#^z6t~wiX8Li3QLUEs}=>k*i0uQ{=_!m*#_EacrY^VYq3ha#0yjF zj6ah>BN+ez29J>s1xkG

5jXK#i4Cf|Q>?VY$rbkhokqzp`uJ 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 KT)B+#*Z&8&^A!vL literal 0 HcmV?d00001 diff --git a/noir/noir-repo/docs/static/img/tooling/profiler/gates-flamegraph-optimized.png b/noir/noir-repo/docs/static/img/tooling/profiler/gates-flamegraph-optimized.png new file mode 100644 index 0000000000000000000000000000000000000000..ea445d1950fcb1a60ffb3b60d6e02ba4ca9c501f GIT binary patch literal 34504 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 zⅆ1baGo23PE{{rLnO~db6k%q-yl+6V?!1cU&IlLci0&!}u+Y{kPLZp)^a{KYuN( zaNsY-#s&L%k&TGlu73?(^lz(XaI?&L$%;)#OO)_+V$mBmw*D!@pE)I6%wPhI0q5&M zOG~teAY3mIw{l!`{4!cz)A$g#lU)UBEt7d*r2F6rytx(i=$-cNf};k9TIC064nwV) zPazN$&ff@Y^&-vZw1%@`-*4Vsc}?qiX8^gTV+JYOjdbOCM`uVAQeEZhj!)EPCeHLv zeR_JBR#%GRi$nK^GK@F@=$W|9hXV!d%f(CzVXwJcmQk6+eUhdO?gS~Wi^JX?1nJ2m zk_czS#nFYO4fcIA9pT9mq|Y_x^k32=wzNW|8$K3B3VSG8fW3IT2l2cPALEK&sf~u- zCDBgVm>l*1MdSmIBsK(jTp)+@lr26lGn}WJ#avI&%3YiNJAxBG@0NF#0;pK?xE-Ro z8OAA*@PSsh(W%m_o`RAPVw>mkCeV7I)O^4Q7nN?6S2+)GXz00EJt>ZzjA!*4&dAk% z#<p)DHd9+#@LbP?2fdzCWkX zhND>EKK%v+%dUaJ<{^v_$wS0QLFIZ~YcoJJwR07djA)`G3T(H&kA&8Q$c4>bs}c?D zR8J)rGLWYh^M>rMyYv-D@~Zoo`h#DiZ<^1w>U7{du5?4fv{@byvI#DFR1c@W*i|fL zl&0g8!>c~ADky#qMFV&wV85){+V0T;HG1}KqWH+b`P$3!w5_lAN61Wg3d#E18zB~` zwKf+Iw{D3ylYY5zv62%F5;<41;>upWzlspM(1q#&iH-i*u3uW_X+KL3!#!Nr19c#l7KyYC1tR;zes4D=c-%b;HDD8-R8s-`roE@y zKIW$juQmq&EgO=Pxn9qXTj}Q-2in9<{G+>B z=e#7Kct|05bj@efZr(uSPjh{_Dt=-cMw@4YLP(~?YhIO*hi%S@=kEySGXzpnzaX0xaMjebhg$)cAnT~giKnJ}S0pPY>Ieiy`9eRg1M&JC5 z^{D#28E6d;3Qy70N#WXOTlDLXGKy=BnDjYUGNV`GH^x<3NVr}eZrz@vGkiO-haMCH z1%FR?9nbgYN-@F6ou>S--}#)hzJVy1`&@&Q>Vzx zig#3P(S*d`({|@-y=5*4_kvt1I`r_+(uKZY)1-I>)&F0o$Go%Ga$lGyhCa9 zPNB%n5)h(5BvSeRa628E)Rqs}Ol-=S>T61mr(26}5=ZH_l4Iw;BhDg3;}3UmVeoAb zOx5iw6un!pukm!wXdDKV3kJ+qN;1Md0A)HJ^_D&^au<0SM%g#Ukhs>Zk1|XQ0>l(3 zGn)=2e<;x#?DiOdZWCGt^gmP_hPLFp;0wz&!}fn6AndzJ-*^XZu4?=ndkZh5gMH$F zPR&zvk=%Uaxs-=Y=a**vs&U7GT>W0YM}Mtg_*<}QS0&vkf0<`}FHR&`3e{*|#e%@5 z&sHM`p0r;-;cyEfN0?2O(1?8Q+Zbw@HD=Xs-<5B45?gOHq}AK}t)b&et4H#}H;NS} z&G)Q>&>+mVM*6#pTlz)3MnatHE_mTCTOq6S{Y-vqIZ<-1ZoTN4xQ1p-J`{e}un2NH z&qR_?1w=NDQxe|I)!r9N*8rS)D8g0O+f$3UtY&QP$fm+HggDJ?$(Wsfh&o2gcy z&6_DX`SMIgcQG^wuTQgAD&~FIcXwfl%H3CsP3rofLnx}{e~QojXO+61V*&9Ze9BDQ zWCfN~mrZj_k`lR47bn^?*C|n)8l%lC(+`VD`>SBrX^H4`kBS7BiDHg1T;-`2>#ekC zLg$+aV$n$CB_Y=~Kqz=WqcV^?7bF-KK@-#u6E^RMiFC(`BquS{DO7{QH#gS7C-p+t_ih z$`7RjDK&iay08V72Ka01)GAmU%H5>&o2ItxnJ-Baoo$H#y%IXfGtQJ}${w$uyq>}+ zS+vKm+DvUx;|D^YJOqt5qX4=Cc#8<~NN~L3T!$=I+IbeAnM&#n!08SVOc>TD)inXa z%y6$S?mo89(vMM`14YL^0Y zLy&-yCQUuI-SA>zR6kG>Op%X;N0IkZnoS+fwGvOw(?dRM&~oSOI3iZuwCmf?rIMaX z%NXY)lw--aDo!&;l%SeFicyFgdz|`~^#IACcO9FLo#JMlgl!=Qo70Kj&46GMN*PFQ zt#@f2H)SU&iRPL-`8vCPj_&eX(%^MmtJNg4=Sv8Nqkd{!KZ?AW*`{#4Uysq)ABN-v zvgpquVJ^CDVL0vhlHq=*gMcnd^9WFw(gPFt?Dg}T->7_{x@UFJgE_ixEugTr+M;bC z=t$q@xjwvPY6K`&3^47<;t=${qjT(9C-2^G0g~;BM??nGDeuD((@Aqj)}WQ`SbN_#22su`}4RJs+vy zlfh4WpS~}w+T`|ThK=UqD&QS=k!K_c5~*X=yKfer!Q%&0F3R<5K{i_id(!!FnbHPR zN@e)VGUK^i!IGk3H)Brx~DAMYE@zqIZ9e*d8A3e24m& zqKk^85IqyOe=E**W>B(wx&l$N#n=yFZTSi}q{^?Ui)~(_qFjwvKyV-`%XaxbjIyMU z_I^mPrysO3Q=M#J)?9Bh2ox!Yd z@2VcuBxTTfRsR#VIJ`3lBX4XxBcziKRQjdpAM{kz$%PJDO$9V%97%uKnf~Il+MwQ1 zZ;;uC{esRa3#m1Vw;{yedK(7ylG=;6Fiv%rw7g=54&Q!q^~{?%O}l`~e7ViIg^r!3 z-1-5=$Zsi<@=prk@E4q7`+q1+`eOxDy83=4w37Q$mTmdIys%5yd$;;l*_9Z%a3SJG zp88Q#CH&l|uz5K!NUSkqs;(%h_d)Z*MEc@vbxPF~iA#PhZq3>LO4)KLN4L%Vk1kK3 z8#-}gl7nD-jBZVVzN|a@ai#O#ba7-5xtq1`>qmWJL8y<51z33eT%YL)5v7MMElte6 zZQ@fKk=9JBHv#7zK#+}*XKv0w4GaQw;hc!`cz7)*yhHS{T$ z_c1|<_@C^;S}01Cp^Uu?>cK$m3EaCO)xndsMj=F+kV_cZt_$RKKKfa}BTShw;om^J9I%^wA^SB|yF)&v{;1%TyS++eXY1RU z^mh{^w9J9{DeTar5mNNtQXheKtbso&N=KsIiju0*nKVM@f{WpJd%*t1{CC}PVR~=( z2PuBENbE!d{fUE<1obMS_jHUobm~49Ic`XMW9ra-%hu5MpR!E4;YCd})B9(-&9~>O z$L!oft;Bx&0BxPPO@-{Z!N^O<858q3;{IY$R5ec-#>%oX-)1HCR+p$W84(%9dSqeJ zkz|+$x??cm1p6Jz#{hw$EV`Jruas>MXPEH)#}v< zbUhK`yng$G-?;M$a*(g7iQ%aCP2R9IqNWke>qt2|W+L}BI^{jX8WD&`(jc@aL+w1Fe$lM*+oSJ?~ksmz@X4c$`0P^Xi6$ z&Y!kPJ0307%+B@oMvp%^7&+T+nrHAq0(`?7hVC1O5hP*wqZI5HW>P19eZaeMrZUT$aX&1dABg?~HZ!(69Nk`#L8 zf>VxbMkQEMqVKYt$&ypX;a8hs;9(avKfXjYL!kp}LP`}F6B@&RcpKujF*ZxoSY4F4 zhy~yMI=$&`P^t%$I9=G;~Jp=PZQMh8WmUbiqVu#PZI0E!S#}jv4Nau)Icx#%-qi z3)k!BO*rZ*qjo2?8QG$?PTsDskXHX@UFtjf14zVCp_l$qNAIj8cez79E2AV+ACb_W z1BqkY>ActJQ0;3$$l zXjzw0rzG}wy7cq5LR^G3){14WaoH~gSSJajz+=xuH0cvZm4VTo!47GVnF^i5rq{?F zVSOJP)@=4keO?k4)~pwYX%dGXST*$`QAdX%*~ zQ+ai;7V)t0wV<)W=3v0>^dUzr{a})v&QE@VayC!YTu_}SE8FI7k?;6lVi_;O0AAeb#le zgUxGjA#vBE_S3uS68Zs@5_=!PK+I~+8W2pYETgpgMr^a%ZxF*V(CWE`IvFT^D!dlS zN$tNuH;--rA*l8DZCmOq-7|c&tZnh~6^pUF$UNbBYiG!Av3%mxYIUm9?29h5#071` zPxrq|ZMMdnneqA_t3Y(%=d)7gm)3R54Nd2MaLf=)5{wq!MMJ;pupxnzCI4btay2!V zB_DIgz*wJ~za*JkMoT|lm!wy~?#}Ww@Dm01e15Ma*;I{PcjsHt#yI+%78E8=Q7W2o zaS=j2nt~dC>SGwGd)=cpht}_mQQ#Z*K-F}BAUeLOU$%tr5LQ!DzhF=+w<6^_w6%40 zYG6mGv_*Tp^v>FHJB0aestLzTwAU7O(5RSBFhH$EkMV?a0CCYA;^{?uq2yPK&j7xYK&e1N-F9q(OY=tz&y)+u5Rl`Eb56so*VTr zs`6mO%jw>DM9dGz?&kQtSokZ~LEiE_C3fNS>72u$W)ek!x;&)ofS4EFk5zGEo+N1x z;O2{joP3X-ZJC-O<6@q(K`FGTq*$MOjidC|BHRIy1v*HMP=)2`3Ck#I)!pdt&cPmIR&2&4`Zn*B(r+2FzxR2XwRQdX4dH|9W$oqzpPzC;5{jj=hJPWJApMHDl&m4OT2+F+T zfgvhCP-*}Eq)2r>L!d*O1?`cxj@6!>4UE&QsOrks$0-WNF_KmEF5q(!&~$;YtYe2wm#p5L!k!O9p7XxQ@&ggy7A5K<(eRwdYdr=Or3=CB#0K{rc3JDGpXgk4kqxV4-y{ zVjDC*M_o^;2$nw6AunyYD{%b_mB(K4P5MOm<|sBa((AJh>~wBX{8M`0aJl=Kb|oRH zOO((?65qmOJbiGk3UuKv`bsa-bEoN`xaPXf)kFCAX2X6>N~o|ygFMv}l8vpyd+}Zo z1$HB-QU;HCiBeJIoZ>{gollexy#5k_l3_?aFiiZsd;-@4Xz3S3%xW9a8IDCc{FjRtM$GJ&f?*W{F+2mv%%0>ahQ^iK#(Nl4a+*Pb;cl!5Z)(uJQfuip+ha7_gLr)A||V{{<= z3$daX*<_Fz?JIO(=_!LS{1s*!L~}Qj7?GHYI?Px$nB|I`dUyDuL!`5sG}FlmaRnP% z;+e&-aM+lK1@=^WG%!X?_u%COO}g)_e}tnG2vzb;%-{>A{^Bqlw|C^xE+WzdI-T3g zQ)r$FafWF$6VvD``f8%B3gee?SM$u1U}692Lr#sb-d#^6cmeJ3E^Svr$S*%TFCbS} z5_H=Aa4=n}}E@nsLcho9VL!aHHPxz2s{U0-KNitBW$nmQ$b)4HxxEz=Xd-hX`S+KZfUSXk{TwOK}f zm=TqVUG3AzqLEo6k^ zE|Ia=NlU$qDo#=a2)-lUlOE_DY!klPUHoxvnpIiK<){rc*fC+!+;TDR06h)l={|mL z(*44RI7ApAQ5%Sc;HLQzEPWR=#+7-B^MVO-<*};+C>=+}K`l_#6`)x&?7sc}Gt~Lq>$7g3Z&>RzL!A}V2?Rd*UDxw_nIa7Lq zLy*E7NFi1nqg-K$$gEhxQZWXDf*V-!!=>Uo1sC3Bh&*_!m?@6wLCD>!|AkckHKc%7 zCJ229jByR%0p*hQwyj&)o@FO{eseaBQ+Rmfb7CMPtaun%#?_!x!#&OQQMB9!Y2%)K z_A;?$nxlGYaE7c)R>u9!L)1F|@s7HF{ftec-~QCFH>dbn=IgL}hrDk*IB<1+NH$c1 z_EvcQLKsWEy`ZW#2qMM1oC*mq^9KZCT?3SWkYzil^C6rVnW*VY?x)Dr)O9A271Zh@ z&+UD8+Vw02UVFJhPAK4u(9AF|3kCyvOvPQ5x{LGD;$}v$=4FR?sKDGFY%pqsuaw*G z)?mm*yhCc_wno!D4f&O#JR=|{gh+e@!dEJU0>EP)BY)&D~q^D96N}Lh(R6KF6J@ zg0RLk=5l|OXCF8owcJIXAs92}9>(RlZOG`}>6QO1f#_8Tkw~_A#%)tH`F*j=&d;79 z)Erfp)7-u;wS6y-eyw4f!C+%IM{`eiM1bnax^dQ50i*~HYs<~LnCvCxwm$_tZ;5!L zo2t~-SKoAtSu_xeIzas#kS%uKM88ssQ$KKjZ4fL6z( zc0EVUFT1!lv~Ly;RR;w|y@YWnBtL3-B42!ELi$H*D%++-A&G41aO@)U=Zu29+0SRp z3j%gs3x!sKxjrAT%^sF@N=*VbHDc)INkwuI~F|l^f;@<6l~t=zlPJE4L(F$MVsL zmVbN5sL;(02;h67;W9nmX_Q*VIQFm5s@x!h!H<<&APL+DZ{^b7d8{$c*=q9Pla;T1 zL62Y;zv@#F{kD44apA}yiDbxC68$ zDjC;O_q4sgy&vhrrKnZNOXV$|HjTX?g*8?8Hf4Na4ZKz4y0vkfq8&NDjG+}jU%W4r z{XC!542a_SwXV8|LwwQTEE>SH^@s?@J6}BdM+m_4!G>>$b633gVM*<@Og9QVT35 zA|FdF7gfBfjLvkby`oAJ@*V8tF(sB-l%Tb|sGxp2!nE}w`15wXzd#%-?%#dVzwfIa z(7%CVJP!uTH0 z=5?Ck{k@Jo!%W{ytwp7ht%^dll zUk`R_)zR7bY51oTj?xcz3bE&QXydsl%_E1{W?^JaMy8{4VWNjOl7d{^8JC@$vYa!E zklCV5tKoB(@887SWtk~^0=JJ5>kZd2tBy?4QfZ#&f}sGIEYJ~zZ%X*j+IGBV+u0aG z6+e!#$T2h=v~td$A0qF3XwRtT6_(!*WO>Tgae+0LKP z09usS#=v_yye=AphS-KENloz9Fc!E!4EC-p`CXi$&_g;Ez+dqLju(|Qz8*OoQ1xta zzVl0q+`@79J9zj`AxbIVVCD=urK{ZF)wK9cb3$nr^v7OwNskcv$pV8aiww=pOI|!1 z^id(`?mo)vpOzo+<*Xw#3ly$-GX6?yGh9@c?Wg{wh4(~E#3)|7+~tIn#Pxf?G7~<_ zdrxXX*SOC6(kzU*oUIkx-{S^q$%843`1W3lCg{v%_;+T8i@UIERpsX_cE-T_D7@%t zi7HmAr|wod{fc%uddi@q{Pt@sgk9tFkOE-y+sK|ea+WMDdC-d9IbFWG=Qfv0{h;2| z(sCky3^QH6PF$)Sez^i_bfll?eM!EswGI@0ytEq;b9S=go51wca)w!Hn|!SfQ`VY8 z{q+}Vp@NL0YG3UAOv=qNp^Ela<*N$9bYr?#QMs>0e|HRBD)jV7T%em$pIhw>;LNDf zCSJpX9+APdOLmTv&Rrh*q>euJvH1ET@BktCCRxbt{l%ScD*WaoOza?U=Mkkv%Z}a0 zG`_pb-)r@2BKZXZ6>DRCtwRK)_&6qh9eBY2cN(TthFl`)G%%Q3fyPwl_@FL(y2&uF;o+%Hy zYz=ij-1#~;@za3q=o4D`PIheb>`+Rd#6}9Y(GOR4Tw~m9;&UMB9?j?{fp&~_i&>9B z<8#MgZt%Ty6!!Hikx zd8uv12iptr9?2VDE*HeG4?hicJ=_Uc0Kh$IUbHbgnD}=_7V8Qse1;-Fm zimpRKf{7l}4&y}ruB*$QHeE^fNf>#p$T9|_jg=LVQb~)~FmFJ6h}QLe-_wK_)|EpM zU7O;9U6AMS5BdTTI+M%S3n1gZW|VR@5j~W{rU}{LbpaZt5kOpubs^kUSOw;7V||Z= zq7?K!k?||_o9Uch=au(5klQmj&~NJviK6<1yGA5cUqyE?vrZkWe5_Pw7{>XbJW{2-0AIEZ666SMzUS%Kutu zZ7~36_PA2y2w%KAXz+0wq;yyfgrm9|vkCaKFA@hqJGy{71nlH(G5Lw^wdA$Y3{qN$ zidNB>T1vt2^ST&UFqr7|k@Bw%*U8l;Cb9T8BUeRQ&e|-g&g@TyPu2l7yD0G>G#{W` z1f_3j_2^L0Pk8eUvEq*8r>9EP>J#OH(}s)}n)aiT?sbVV8yg=67){A9m_iu+TP0h} z^bzD$Jn@@I%Im^iPsZqhO>4)JYl-hq6Cvc{?bsOhP}R+Uy6%|Bk4;gVzX<70D3tF4D15-AtFy#d^55PQ_y8?Rn!C>UlQqm>MOHJs z*wPjZv$S;RV8_y)mEg`46TYQSfFE}oyRuVaUA$K`j3mH+$dG`CKm5D$A0s0>p#7U^ zL*W>bT!#2ebK}hC$<)Mzq1(rhSd~$;DuoJa>-YC+Bbg5W=a<8ZkiabOWKdmYY2w$; z*`@#e2((YwCzt3O<+YSp^(`Be16bo)=75@xeDIpd);jtRoW2mI0Ai&*2!1KW7Ed~t z{`?BooB-cwjgvnwZX|eLmfMo;!*E`(X*r7ZT8odrOx6SZe9}{irjOn0MgaRo2C7$p!sBbRT-_Jqjkg zzL!|c*t!CqxruLGeczN$g#3B%ODEJj`NxBs_~$<0sN>?wuro$-oHX1C{((;xrbYDU#Lf0aH zW+urysM7saZhCY1wrk>NBl*yfe;aQ6^!)r@|0jRe7iUjLR3Hge)dA_!E!8Q%sEmy4 z>YTSKKLvSZHQhmZs69-am!j)TOEyc7#_qWF=&x)&wZ8hD{nMnY^?Uy{)%Ts%)EK3jiGMf zIb;lESd`g?*P_Hkq!CO$O!ADxch59rS*XvnV&q5dvIi^E?EmjC<|lFSMrb=mww`{F zJdQ#m?h*DrdY{kPmw#C6NhbE-UbxTOg@Ha_5Ka{wS1z4?Fw-~GEGOv zipKF?vlRSr4aU}Nrzy+O7_1iyj+L;uf)97R9T8uVCpPCwh``^&8 zfha)=bDk%_blx#y@HLc@r#4B@Y>!g5i=17(=B%~DNvjR{pCwf+m3VlGb^<@o5|l)S!}<2`y8#Nd;&`G(GmYx3Q}_Y29%SC3!mGIPCI zbl3KWKjztx^#8}2;xC~`%x&C9b6&0IF_Wh&bd*zi?(@gi{u}B}p;LTFWcS${Tc^ox zqG`v((v@ep>4%1w7Z(?+e{f#dej*OUfW7Uf#-re1-tvM z#@J>eAxOMHvyFjMev(b$DIZBnX~(B^!g+cz`ai^_3=Ef)N3iUur$$_op$92{;QCcT z;tl3c#xv>nk1Hr-igRbireG1BZWp3Rg{ys@O^3Aja zMbfqQY*{+7u3Q$|I2n@AYYA#2ue;=-;34E#&44tTU!Vr*WZs`kA56z&d5<4)6Ob2; z6Y}_E>fmJHK+%}OJ_0YVNPuLcyT4sjsaK$4P;)E#8RK`lmhEkSJCpQL(9p&MFj9v^ z<=y=#nc74(iuI>b0lD}rD4zGNcr38zVFH zs1MXYzsBXT_PZDIEKHuaGqSt;h#yUuN&cDalXCkVTtC0l9~&ReFe<(>>4*-{Cyurh z8N?L%-aQ}iKFi_?>9hU}DqNj6KYjI23=;$_k0w3n59nN;n*BPJ9#Lt6Zr%ROCd>wr z;0^piHGk|qcG zdo@p4{k{F?YfP zJS7Avq1;#fD~QKrk6WEj>3mrz__>~Sg-Xxowy2RRRAD>s6Quf*_+cwxs{&$^Vxl^l4xiT`u?M+Tf;T zAt#u{jzQmk@L%Rb`jQYsff294g+M{&8HJ0E4t?m6KIIxdUaJeQrG>?r(}W-TZ5g`s zhWrZ7?d)v+BSP@94^w#ib~F<%XpL;By(i?#L@b=VdpQZzQ5G~=-p%7xL`Vp(di3Lw zl`&k%Tuh8IVPKudyGo^lZxy=@^Dlny74XgRWY+Ce%FS1bv90UBeB}>PqP)*!iR2rG zqm9-`7yhabx!#J@myGx+H}Xcg1=uJgdUu=CY~}1_?2pQAVa__V7}G?(H5Q+xpXv|r z`YpD7Wz<+0zpDk&R7vk2q<4HW&)gM26&4XvX6l4;Qvgouc?ZEf6Z z)a$PYeST~7c!Dw5eST+cCs~B_YD>g)Udy0mo3b|JL403%mxmH9Id%!x2?=D(>7&r_ z`ei+g4(n-Kzi=tgFb_r#h2?y}D6!0AyuJzS5TA33L=&%b5@b={fIJ>m2)hHVWZHS7NgYYcA$d}msX zCCSw0pwJ7w-43GW)87iG3m+r98o3h>MrerIKIDJrz42{$RSOIud9Y0E_R<9dZI*DB z5Ms_}3AF$$n;)2+*Ao9QI^rntKPC@M)J4Lz@@y;eE&0Ct5&ElEAE}Q>ue?IBhIWyq>vdh=m z*rx~{OPa$|c=(Q>lJc$PiBEE>l0~wXAve z$J1vcpPgFmk6^rQmV^R<7at-HO)oI$MQwdHd^ymygk2$cNu$`~bvH}TW<*N_vLv;| zuB;Tu%;!I^<~_~9;NXJW@4h~m@Yi^`y_~DTA2!b=CvR>0Rju;*Lf^+-JckYCk3Ci? zKk{czw0ZS^oWnS> zbamIhi#)KG>&8!^_rjlp+mbkZ8D$62994Jr#8QTm!tzu*c-RsQQOF~UU^E}pV{e(lRsYIymu+p@>a!Wff{R{ zpX%=CV%Zmp``BOcx?+&Ec1hL4#eW06t$env@%q6rKZ*m?!vqg8e_0OeuIaKYkq1HG ixbrm%;T8WPftb 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 "Open with Codespaces". It should take a few seconds to load, and you're ready to go. :::info -If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. +If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.2/noir/concepts/traits.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.2/noir/concepts/traits.md index 13818ecffac7..17cc04a97518 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.2/noir/concepts/traits.md +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.2/noir/concepts/traits.md @@ -111,6 +111,48 @@ fn foo(elements: [T], thing: U) where } ``` +## Invoking trait methods + +As seen in the previous section, the `area` method was invoked on a type `T` that had a where clause `T: Area`. + +To invoke `area` on a type that directly implements the trait `Area`, the trait must be in scope (imported): + +```rust +use geometry::Rectangle; + +fn main() { + let rectangle = Rectangle { width: 1, height: 2}; + let area = rectangle.area(); // Error: the compiler doesn't know which `area` method this is +} +``` + +The above program errors because there might be multiple traits with an `area` method, all implemented +by `Rectangle`, and it's not clear which one should be used. + +To make the above program compile, the trait must be imported: + +```rust +use geometry::Rectangle; +use geometry::Area; // Bring the Area trait into scope + +fn main() { + let rectangle = Rectangle { width: 1, height: 2}; + let area = rectangle.area(); // OK: will use `area` from `geometry::Area` +} +``` + +An error will also be produced if multiple traits with an `area` method are in scope. If both traits +are needed in a file you can use the fully-qualified path to the trait: + +```rust +use geometry::Rectangle; + +fn main() { + let rectangle = Rectangle { width: 1, height: 2}; + let area = geometry::Area::area(rectangle); +} +``` + ## Generic Implementations You can add generics to a trait implementation by adding the generic list after the `impl` keyword: @@ -325,20 +367,6 @@ let x: Field = Default::default(); let result = x + Default::default(); ``` -:::warning - -```rust -let _ = Default::default(); -``` - -If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be -arbitrarily selected. This occurs most often when the result of a trait function call with no parameters -is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type, -always refer to it via the implementation type's namespace - e.g. `MyType::default()`. -This is set to change to an error in future Noir versions. - -::: - ## Default Method Implementations A trait can also have default implementations of its methods by giving a body to the desired functions. diff --git a/noir/noir-repo/docs/docs/tooling/cspell.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/cspell.json similarity index 55% rename from noir/noir-repo/docs/docs/tooling/cspell.json rename to noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/cspell.json index e8126ba0874b..c60b0a597b10 100644 --- a/noir/noir-repo/docs/docs/tooling/cspell.json +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/cspell.json @@ -1,5 +1,5 @@ { "words": [ - "lookback" + "Cryptdoku" ] } diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-oracle.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-oracle.md new file mode 100644 index 000000000000..821e1f95c04e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-oracle.md @@ -0,0 +1,57 @@ +--- +title: Oracles +description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations. +keywords: + - Noir Programming + - Oracles + - JSON-RPC + - Foreign Call Handlers + - Constrained Functions + - Blockchain Programming +sidebar_position: 1 +--- + +If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs. + +![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg) + +A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source? + +Oracles are functions that provide this feature. + +## Use cases + +An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method. + +Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md). + +In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles. + +## Constraining oracles + +Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. + +To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have an oracle call like this: + +```rust +#[oracle(getNoun)] +unconstrained fn get_noun(address: Field) -> Field +``` + +This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract. + +In short, **Oracles don't prove anything. Your Noir program does.** + +:::danger + +If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained! + +::: + +## How to use Oracles + +On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. + +In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they match the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. + +If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-recursion.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-recursion.md new file mode 100644 index 000000000000..df8529ef4e02 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-recursion.md @@ -0,0 +1,176 @@ +--- +title: Recursive proofs +description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency. + +keywords: + [ + "Recursive Proofs", + "Zero-Knowledge Programming", + "Noir", + "EVM Blockchain", + "Smart Contracts", + "Recursion in Noir", + "Alice and Bob Guessing Game", + "Recursive Merkle Tree", + "Reusable Components", + "Optimizing Computational Resources", + "Improving Efficiency", + "Verification Key", + "Aggregation", + "Recursive zkSNARK schemes", + "PLONK", + "Proving and Verification Keys" + ] +sidebar_position: 1 +pagination_next: how_to/how-to-recursion +--- + +In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: + +```js +function factorial(n) { + if (n === 0 || n === 1) { + return 1; + } else { + return n * factorial(n - 1); + } +} +``` + +In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack: + +```md + Is `n` 1? <--------- + /\ / + / \ n = n -1 + / \ / + Yes No -------- +``` + +In Zero-Knowledge, recursion has some similarities. + +It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid. + +This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism. + +## Examples + +Let us look at some of these examples + +### Alice and Bob - Guessing game + +Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins. + +So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed. + +This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. + +As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". + +She can then generate a proof that she verified his proof, and so on. + +```md + Did you fail? <-------------------------- + / \ / + / \ n = n -1 + / \ / + Yes No / + | | / + | | / + | You win / + | / + | / +Generate proof of that / + + / + my own guess ---------------- +``` + +### Charlie - Recursive merkle tree + +Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count! + +If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such: + +```md + abcd + __________|______________ + | | + ab cd + _____|_____ ______|______ + | | | | + alice bob charlie daniel +``` + +Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes. + +### Daniel - Reusable components + +Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit. + +He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup. + +## What params do I need + +As you can see in the [recursion reference](noir/standard_library/recursion.mdx), a simple recursive proof requires: + +- The proof to verify +- The Verification Key of the circuit that generated the proof +- A hash of this verification key, as it's needed for some backends +- The public inputs for the proof + +:::info + +Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. + +So, taking the example of Alice and Bob and their guessing game: + +- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit +- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. + +We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. + +::: + +## Some architecture + +As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples: + +### Adding some logic to a proof verification + +This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections: + +- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify) +- A `guessing` section, which is basically the logic part where the actual guessing happens + +In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on. + +### Aggregating proofs + +In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere. + +To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits: + +- A `main`, non-recursive circuit with some logic +- A `recursive` circuit meant to verify two proofs in one proof + +The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day. + +### Recursively verifying different circuits + +Nothing prevents you from verifying different circuits in a recursive proof, for example: + +- A `circuit1` circuit +- A `circuit2` circuit +- A `recursive` circuit + +In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received) + +## How fast is it + +At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. + +Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. + +## How can I try it + +Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-writing-noir.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-writing-noir.md new file mode 100644 index 000000000000..b4265a14dbf4 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-writing-noir.md @@ -0,0 +1,208 @@ +--- +title: Thinking in Circuits +description: Considerations when writing Noir programs +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 proof size and proving time, so from a product point of view this should be kept as low as possible. + +Whilst writing efficient code for web apps and Solidity have some 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 + +Programs written in anything from pseudo code to C, can be translated into Noir. A Rust program written for execution can be readily ported to Noir thanks to the similarities in syntax. + +:::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: + - Conceptually 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, the `bb` binary has a `gates` option). +::: + +### Numerical types + +As mentioned earlier Noir has many familiar integer types (eg `i8`, `u64`). Ideally after bringing a program into Noir, proving/verifying of its execution just works where needed: client/server side, on an evm, or on the Aztec network. + +A program optimized for execution may leverage the binary representations of integers, reducing the number of clock cycles, and thus time of execution. +The cryptography in a proving backend makes use of a `Field` type, and leveraging this lower level type correctly can reduce gate count, and thus proof size and proving time. + +In some instances simply replacing the integer type with a `Field` could save on some range checks (and hence gates). +Note: when casting a `Field` to an integer type, the value is converted based on the integer binary representation. Eg a Field variable with a value of 260 `as u8` becomes 4 + +### `Field`s for efficiency + +`Field` types have their own underlying representation that is efficient for cryptography, which is different to binary representations efficient for CPUs. So, mathematically speaking, things like bitwise operations do not directly translate to fields. That said, the same outcome can be achieved if wanting to use the Field type as a number with lower overhead. + +For instance shift (`<<`) and or (`|`) work seamlessly with integer types (bit-packing `u8`'s into a `u16`): +``` + high as u16 << 8 | low as u16 +``` + +More efficiently with `Field` types, the equivalent is: +``` + low.assert_max_bit_size::<8>(); // ensure Field values could be represented as 8 bit numbers + high.assert_max_bit_size::<8>(); + (high * 2.pow_32(8) + low) +``` +(Note, the power of two can instead be a constant (256) or global evaluated at compile time) + +The first snippet is good for compatibility when using existing code, converting to the latter can help optimize frequently used functions. + +:::tip +Where possible, use the `Field` type for values. Writing code with smaller value types and bit-packing strategies will result in MORE gates +::: + +### 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. +::: + +### Reduce what is inside loops and conditional logic + +Putting less logic inside an `if` (`else`, etc) paths, or inside a loop, translates to less gates required to represent the program. The compiler should mostly take care of this. + +A loop duplicates the gates for each iteration of the loop, or put another way, "unrolls" the loop. Any calculations/calls that are unchanged in the loop should be calculated once before, and the result used in the loop. + +An `if` statement is "flattened" and gates created for each path even if execution uses only one path. Furthermore, there are additional operations required for each path. Sometimes this can have a multiplying effect on the operations in the `if` and `else` etc. + +:::tip +Only have essential computation inside conditional logic and loops, and calculate anything else once (before, or after, depending). +::: + +### 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-v1.0.0-beta.3/getting_started/noir_installation.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/noir_installation.md new file mode 100644 index 000000000000..05f036d4f6d6 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/noir_installation.md @@ -0,0 +1,106 @@ +--- +title: Standalone Noir Installation +description: There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains how to specify which version to install when using noirup, and using WSL for windows. +keywords: [ + Installation + Nargo + Noirup + Binaries + Compiling from Source + WSL for Windows + macOS + Linux + Nix + Direnv + Uninstalling Nargo + ] +sidebar_position: 2 +--- + +Noirup is the endorsed method for installing Nargo, streamlining the process of fetching binaries or compiling from source. It supports a range of options to cater to your specific needs, from nightly builds and specific versions to compiling from various sources. + +### Installing Noirup + +First, ensure you have `noirup` installed: + +```sh +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +### Fetching Binaries + +With `noirup`, you can easily switch between different Nargo versions, including nightly builds: + +- **Nightly Version**: Install the latest nightly build. + + ```sh + noirup --version nightly + ``` + +- **Specific Version**: Install a specific version of Nargo. + + ```sh + noirup --version + ``` + +### Compiling from Source + +`noirup` also enables compiling Nargo from various sources: + +- **From a Specific Branch**: Install from the latest commit on a branch. + + ```sh + noirup --branch + ``` + +- **From a Fork**: Install from the main branch of a fork. + + ```sh + noirup --repo + ``` + +- **From a Specific Branch in a Fork**: Install from a specific branch in a fork. + + ```sh + noirup --repo --branch + ``` + +- **From a Specific Pull Request**: Install from a specific PR. + + ```sh + noirup --pr + ``` + +- **From a Specific Commit**: Install from a specific commit. + + ```sh + noirup -C + ``` + +- **From Local Source**: Compile and install from a local directory. + + ```sh + noirup --path ./path/to/local/source + ``` + +## Installation on Windows + +The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. However, it is available by using Windows Subsystem for Linux (WSL). + +Step 1: Follow the instructions [here](https://learn.microsoft.com/en-us/windows/wsl/install) to install and run WSL. + +step 2: Follow the [Noirup instructions](#installing-noirup). + +## Setting up shell completions + +Once `nargo` is installed, you can [set up shell completions for it](setting_up_shell_completions.md). + +## Uninstalling Nargo + +If you installed Nargo with `noirup`, you can uninstall Nargo by removing the files in `~/.nargo`, `~/nargo`, and `~/noir_cache`. This ensures that all installed binaries, configurations, and cache related to Nargo are fully removed from your system. + +```bash +rm -r ~/.nargo +rm -r ~/nargo +rm -r ~/noir_cache +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/project_breakdown.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/project_breakdown.md new file mode 100644 index 000000000000..e442e377040f --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/project_breakdown.md @@ -0,0 +1,159 @@ +--- +title: Project Breakdown +description: + Learn about the anatomy of a Nargo project, including the purpose of the Prover TOML + file, and how to prove and verify your program. +keywords: + [Nargo, Nargo project, Prover.toml, proof verification, private asset transfer] +sidebar_position: 1 +--- + +This section breaks down our hello world program from the previous section. + +## Anatomy of a Nargo Project + +Upon creating a new project with `nargo new` and building the in/output files with `nargo check` +commands, you would get a minimal Nargo project of the following structure: + + - src + - Prover.toml + - Nargo.toml + +The source directory _src_ holds the source code for your Noir program. By default only a _main.nr_ +file will be generated within it. + +### Prover.toml + +_Prover.toml_ is used for specifying the input values for executing and proving the program. You can specify `toml` files with different names by using the `--prover-name` or `-p` flags, see the [Prover](#provertoml) section below. Optionally you may specify expected output values for prove-time checking as well. + +### Nargo.toml + +_Nargo.toml_ contains the environmental options of your project. It contains a "package" section and a "dependencies" section. + +Example Nargo.toml: + +```toml +[package] +name = "noir_starter" +type = "bin" +authors = ["Alice"] +compiler_version = "0.9.0" +description = "Getting started with Noir" +entry = "circuit/main.nr" +license = "MIT" + +[dependencies] +ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} +``` + +Nargo.toml for a [workspace](../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +#### Package section + +The package section defines a number of fields including: + +- `name` (**required**) - the name of the package +- `type` (**required**) - can be "bin", "lib", or "contract" to specify whether its a binary, library or Aztec contract +- `authors` (optional) - authors of the project +- `compiler_version` - specifies the version of the compiler to use. This is enforced by the compiler and follow's [Rust's versioning](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field), so a `compiler_version = 0.18.0` will enforce Nargo version 0.18.0, `compiler_version = ^0.18.0` will enforce anything above 0.18.0 but below 0.19.0, etc. For more information, see how [Rust handles these operators](https://docs.rs/semver/latest/semver/enum.Op.html) +- `description` (optional) +- `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 + +This is where you will specify any dependencies for your project. See the [Dependencies page](../noir/modules_packages_crates/dependencies.md) for more info. + +`./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or +verifier contract respectively. + +### main.nr + +The _main.nr_ file contains a `main` method, this method is the entry point into your Noir program. + +In our sample program, _main.nr_ looks like this: + +```rust +fn main(x : Field, y : Field) { + assert(x != y); +} +``` + +The parameters `x` and `y` can be seen as the API for the program and must be supplied by the prover. Since neither `x` nor `y` is marked as public, the verifier does not supply any inputs, when verifying the proof. + +The prover supplies the values for `x` and `y` in the _Prover.toml_ file. + +As for the program body, `assert` ensures that the condition to be satisfied (e.g. `x != y`) is constrained by the proof of the execution of said program (i.e. if the condition was not met, the verifier would reject the proof as an invalid proof). + +### Prover.toml + +The _Prover.toml_ file is a file which the prover uses to supply the inputs to the Noir program (both private and public). + +In our hello world program the _Prover.toml_ file looks like this: + +```toml +x = "1" +y = "2" +``` + +When the command `nargo execute` is executed, nargo will execute the Noir program using the inputs specified in `Prover.toml`, aborting if it finds that these do not satisfy the constraints defined by `main`. In this example, `x` and `y` must satisfy the inequality constraint `assert(x != y)`. + +If an output name is specified such as `nargo execute foo`, the witness generated by this execution will be written to `./target/foo.gz`. This can then be used to generate a proof of the execution. + +#### Arrays of Structs + +The following code shows how to pass an array of structs to a Noir program to generate a proof. + +```rust +// main.nr +struct Foo { + bar: Field, + baz: Field, +} + +fn main(foos: [Foo; 3]) -> pub Field { + foos[2].bar + foos[2].baz +} +``` + +Prover.toml: + +```toml +[[foos]] # foos[0] +bar = 0 +baz = 0 + +[[foos]] # foos[1] +bar = 0 +baz = 0 + +[[foos]] # foos[2] +bar = 1 +baz = 2 +``` + +#### Custom toml files + +You can specify a `toml` file with a different name to use for execution by using the `--prover-name` or `-p` flags. + +This command looks for proof inputs in the default **Prover.toml** and generates the witness and saves it at `./target/foo.gz`: + +```bash +nargo execute foo +``` + +This command looks for proof inputs in the custom **OtherProver.toml** and generates the witness and saves it at `./target/bar.gz`: + +```bash +nargo execute -p OtherProver bar +``` + +Now that you understand the concepts, you'll probably want some editor feedback while you are writing more complex code. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/quick_start.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/quick_start.md new file mode 100644 index 000000000000..7b4524f6b8e4 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/quick_start.md @@ -0,0 +1,127 @@ +--- +title: Quick Start +tags: [] +sidebar_position: 0 +--- + +## Installation + +### Noir + +The easiest way to develop with Noir is using Nargo the CLI tool. It provides you the ability to start new projects, compile, execute and test Noir programs from the terminal. + +You can use `noirup` the installation script to quickly install and update Nargo: + +```bash +curl -L https://raw.githubusercontent.com/noir-lang/noirup/refs/heads/main/install | bash +noirup +``` + +Once installed, you can [set up shell completions for the `nargo` command](setting_up_shell_completions). + +### Proving backend + +After installing Noir, we install a proving backend to work with our Noir programs. + +Proving backends provide you the abilities to generate proofs, verify proofs, generate smart contracts and more for your Noir programs. + +Different proving backends provide different tools for working with Noir programs, here we will use the [Barretenberg proving backend](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg) developed by Aztec Labs as an example. + +You can use the `bbup` installation script to quickly install and update BB, Barretenberg's CLI tool: + +You can find the full list of proving backends compatible with Noir in Awesome Noir. + +```bash +curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/refs/heads/master/barretenberg/bbup/install | bash +bbup +``` + +For the full list of proving backends compatible with Noir, visit [Awesome Noir](https://github.com/noir-lang/awesome-noir/?tab=readme-ov-file#proving-backends). + +## Nargo + +Nargo provides the ability to initiate and execute Noir projects. Let's initialize the traditional `hello_world`: + +```sh +nargo new hello_world +``` + +Two files will be created. + +- `src/main.nr` contains a simple boilerplate circuit +- `Nargo.toml` contains environmental options, such as name, author, dependencies, and others. + +Glancing at _main.nr_ , we can see that inputs in Noir are private by default, but can be labeled public using the keyword `pub`. This means that we will _assert_ that we know a value `x` which is different from `y` without revealing `x`: + +```rust +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` + +To learn more about private and public values, check the [Data Types](../noir/concepts/data_types/index.md) section. + +### Compiling and executing + +We can now use `nargo` to generate a _Prover.toml_ file, where our input values will be specified: + +```sh +cd hello_world +nargo check +``` + +Let's feed some valid values into this file: + +```toml +x = "1" +y = "2" +``` + +We're now ready to compile and execute our Noir program. By default the `nargo execute` command will do both, and generate the `witness` that we need to feed to our proving backend: + +```sh +nargo execute +``` + +The witness corresponding to this execution will then be written to the file _./target/witness-name.gz_. + +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_. + +With circuit compiled and witness generated, we're ready to prove. + +## Proving backend + +Different proving backends may provide different tools and commands to work with Noir programs. Here Barretenberg's `bb` CLI tool is used as an example: + +```sh +bb prove -b ./target/hello_world.json -w ./target/hello_world.gz -o ./target/proof +``` + +:::tip + +Naming can be confusing, specially as you pass them to the `bb` commands. If unsure, it won't hurt to delete the target folder and start fresh to make sure you're using the most recent versions of the compiled circuit and witness. + +::: + +The proof is now generated in the `target` folder. To verify it we first need to compute the verification key from the compiled circuit, and use it to verify: + +```sh +bb write_vk -b ./target/hello_world.json -o ./target/vk +bb verify -k ./target/vk -p ./target/proof +``` + +:::info + +Notice that in order to verify a proof, the verifier knows nothing but the circuit, which is compiled and used to generate the verification key. This is obviously quite important: private inputs remain private. + +As for the public inputs, you may have noticed they haven't been specified. This behavior varies with each particular backend, but barretenberg typically attaches them to the proof. You can see them by parsing and splitting it. For example if your public inputs are 32 bytes: + +```bash +head -c 32 ./target/proof | od -An -v -t x1 | tr -d $' \n' +``` + +::: + +Congratulations, you have now created and verified a proof for your very first Noir program! + +In the [next section](./project_breakdown.md), we will go into more detail on each step performed. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/setting_up_shell_completions.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/setting_up_shell_completions.md new file mode 100644 index 000000000000..0447321cbab0 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/setting_up_shell_completions.md @@ -0,0 +1,87 @@ +--- +title: Setting up shell completions +tags: [] +sidebar_position: 3 +--- + +The `nargo` binary provides a command to generate shell completions: + +```bash +nargo generate-completion-script [shell] +``` + +where `shell` must be one of `bash`, `elvish`, `fish`, `powershell`, and `zsh`. + +Below we explain how to install them in some popular shells. + +## Installing Zsh Completions + +If you have `oh-my-zsh` installed, you might already have a directory of automatically loading completion scripts — `.oh-my-zsh/completions`. +If not, first create it: + +```bash +mkdir -p ~/.oh-my-zsh/completions` +``` + +Then copy the completion script to that directory: + +```bash +nargo generate-completion-script zsh > ~/.oh-my-zsh/completions/_nargo +``` + +Without `oh-my-zsh`, you’ll need to add a path for completion scripts to your function path, and turn on completion script auto-loading. +First, add these lines to `~/.zshrc`: + +```bash +fpath=(~/.zsh/completions $fpath) +autoload -U compinit +compinit +``` + +Next, create a directory at `~/.zsh/completions`: + +```bash +mkdir -p ~/.zsh/completions +``` + +Then copy the completion script to that directory: + +```bash +nargo generate-completion-script zsh > ~/.zsh/completions/_nargo +``` + +## Installing Bash Completions + +If you have [bash-completion](https://github.com/scop/bash-completion) installed, you can just copy the completion script to the `/usr/local/etc/bash_completion.d` directory: + +```bash +nargo generate-completion-script bash > /usr/local/etc/bash_completion.d/nargo +``` + +Without `bash-completion`, you’ll need to source the completion script directly. +First create a directory such as `~/.bash_completions/`: + +```bash +mkdir ~/.bash_completions/ +``` + +Copy the completion script to that directory: + +```bash +nargo generate-completion-script bash > ~/.bash_completions/nargo.bash +``` + +Then add the following line to `~/.bash_profile` or `~/.bashrc`: + + +```bash +source ~/.bash_completions/nargo.bash +``` + +## Installing Fish Completions + +Copy the completion script to any path listed in the environment variable `$fish_completion_path`. For example, a typical location is `~/.config/fish/completions/nargo.fish`: + +```bash +nargo generate-completion-script fish > ~/.config/fish/completions/nargo.fish +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/_category_.json new file mode 100644 index 000000000000..23b560f610b8 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/_category_.json new file mode 100644 index 000000000000..cc2cbb1c2533 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Debugging", + "position": 5, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_the_repl.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_the_repl.md new file mode 100644 index 000000000000..1d64dae3f37a --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_the_repl.md @@ -0,0 +1,164 @@ +--- +title: Using the REPL Debugger +description: + Step-by-step guide on how to debug your Noir circuits with the REPL Debugger. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + REPL, + ] +sidebar_position: 1 +--- + +#### Pre-requisites + +In order to use the REPL debugger, first you need to install recent enough versions of Nargo and vscode-noir. + +## Debugging a simple circuit + +Let's debug a simple circuit: + +```rust +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` + +To start the REPL debugger, using a terminal, go to a Noir circuit's home directory. Then: + +`$ nargo debug` + +You should be seeing this in your terminal: + +``` +[main] Starting debugger +At ~/noir-examples/recursion/circuits/main/src/main.nr:1:9 + 1 -> fn main(x : Field, y : pub Field) { + 2 assert(x != y); + 3 } +> +``` + +The debugger displays the current Noir code location, and it is now waiting for us to drive it. + +Let's first take a look at the available commands. For that we'll use the `help` command. + +``` +> help +Available commands: + + opcodes display ACIR opcodes + into step into to the next opcode + next step until a new source location is reached + out step until a new source location is reached + and the current stack frame is finished + break LOCATION:OpcodeLocation add a breakpoint at an opcode location + over step until a new source location is reached + without diving into function calls + restart restart the debugging session + delete LOCATION:OpcodeLocation delete breakpoint at an opcode location + witness show witness map + witness index:u32 display a single witness from the witness map + witness index:u32 value:String update a witness with the given value + memset index:usize value:String update a memory cell with the given + value + continue continue execution until the end of the + program + vars show variable values available at this point + in execution + stacktrace display the current stack trace + memory show memory (valid when executing unconstrained code) + step step to the next ACIR opcode + +Other commands: + + help Show this help message + quit Quit repl + +``` + +Some commands operate only for unconstrained functions, such as `memory` and `memset`. If you try to use them while execution is paused at an ACIR opcode, the debugger will simply inform you that you are not executing unconstrained code: + +``` +> memory +Unconstrained VM memory not available +> +``` + +Before continuing, we can take a look at the initial witness map: + +``` +> witness +_0 = 1 +_1 = 2 +> +``` + +Cool, since `x==1`, `y==2`, and we want to check that `x != y`, our circuit should succeed. At this point we could intervene and use the witness setter command to change one of the witnesses. Let's set `y=3`, then back to 2, so we don't affect the expected result: + +``` +> witness +_0 = 1 +_1 = 2 +> witness 1 3 +_1 = 3 +> witness +_0 = 1 +_1 = 3 +> witness 1 2 +_1 = 2 +> witness +_0 = 1 +_1 = 2 +> +``` + +Now we can inspect the current state of local variables. For that we use the `vars` command. + +``` +> vars +> +``` + +We currently have no vars in context, since we are at the entry point of the program. Let's use `next` to execute until the next point in the program. + +``` +> vars +> next +At ~/noir-examples/recursion/circuits/main/src/main.nr:1:20 + 1 -> fn main(x : Field, y : pub Field) { + 2 assert(x != y); + 3 } +> vars +x:Field = 0x01 +``` + +As a result of stepping, the variable `x`, whose initial value comes from the witness map, is now in context and returned by `vars`. + +``` +> next + 1 fn main(x : Field, y : pub Field) { + 2 -> assert(x != y); + 3 } +> vars +y:Field = 0x02 +x:Field = 0x01 +``` + +Stepping again we can finally see both variables and their values. And now we can see that the next assertion should succeed. + +Let's continue to the end: + +``` +> continue +(Continuing execution...) +Finished execution +> q +[main] Circuit witness successfully solved +``` + +Upon quitting the debugger after a solved circuit, the resulting circuit witness gets saved, equivalent to what would happen if we had run the same circuit with `nargo execute`. + +We just went through the basics of debugging using Noir REPL debugger. For a comprehensive reference, check out [the reference page](../../reference/debugger/debugger_repl.md). diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_vs_code.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_vs_code.md new file mode 100644 index 000000000000..ecd64fc26536 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_vs_code.md @@ -0,0 +1,68 @@ +--- +title: Using the VS Code Debugger +description: + Step-by-step guide on how to debug your Noir circuits with the VS Code Debugger configuration and features. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + VS Code, + IDE, + ] +sidebar_position: 0 +--- + +This guide will show you how to use VS Code with the vscode-noir extension to debug a Noir project. + +#### Pre-requisites + +- Nargo +- vscode-noir +- A Noir project with a `Nargo.toml`, `Prover.toml` and at least one Noir (`.nr`) containing an entry point function (typically `main`). + +## Running the debugger + +The easiest way to start debugging is to open the file you want to debug, and press `F5`. This will cause the debugger to launch, using your `Prover.toml` file as input. + +You should see something like this: + +![Debugger launched](@site/static/img/debugger/1-started.png) + +Let's inspect the state of the program. For that, we open VS Code's _Debug pane_. Look for this icon: + +![Debug pane icon](@site/static/img/debugger/2-icon.png) + +You will now see two categories of variables: Locals and Witness Map. + +![Debug pane expanded](@site/static/img/debugger/3-debug-pane.png) + +1. **Locals**: variables of your program. At this point in execution this section is empty, but as we step through the code it will get populated by `x`, `result`, `digest`, etc. + +2. **Witness map**: these are initially populated from your project's `Prover.toml` file. In this example, they will be used to populate `x` and `result` at the beginning of the `main` function. + +Most of the time you will probably be focusing mostly on locals, as they represent the high level state of your program. + +You might be interested in inspecting the witness map in case you are trying to solve a really low level issue in the compiler or runtime itself, so this concerns mostly advanced or niche users. + +Let's step through the program, by using the debugger buttons or their corresponding keyboard shortcuts. + +![Debugger buttons](@site/static/img/debugger/4-debugger-buttons.png) + +Now we can see in the variables pane that there's values for `digest`, `result` and `x`. + +![Inspecting locals](@site/static/img/debugger/5-assert.png) + +We can also inspect the values of variables by directly hovering on them on the code. + +![Hover locals](@site/static/img/debugger/6-hover.png) + +Let's set a break point at the `keccak256` function, so we can continue execution up to the point when it's first invoked without having to go one step at a time. + +We just need to click to the right of the line number 18. Once the breakpoint appears, we can click the `continue` button or use its corresponding keyboard shortcut (`F5` by default). + +![Breakpoint](@site/static/img/debugger/7-break.png) + +Now we are debugging the `keccak256` function, notice the _Call Stack pane_ at the lower right. This lets us inspect the current call stack of our process. + +That covers most of the current debugger functionalities. Check out [the reference](../../reference/debugger/debugger_vscode.md) for more details on how to configure the debugger. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-oracles.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-oracles.md new file mode 100644 index 000000000000..845c24c98291 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-oracles.md @@ -0,0 +1,275 @@ +--- +title: How to use Oracles +description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS. +keywords: + - Noir Programming + - Oracles + - Nargo + - NoirJS + - JSON RPC Server + - Foreign Call Handlers +sidebar_position: 1 +--- + +This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that: + +- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept. +- You have a Noir program to add oracles to. You can create one using one of the starters in [awesome-noir](https://github.com/noir-lang/awesome-noir?tab=readme-ov-file#boilerplates). +- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. +- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). + +## Rundown + +This guide has 3 major steps: + +1. How to modify our Noir program to make use of oracle calls as unconstrained functions +2. How to write a JSON RPC Server to resolve these oracle calls with Nargo +3. How to use them in Nargo and how to provide a custom resolver in NoirJS + +## Step 1 - Modify your Noir program + +An oracle is defined in a Noir program by defining two methods: + +- An unconstrained method - This tells the compiler that it is executing an [unconstrained function](../noir/concepts//unconstrained.md). +- A decorated oracle method - This tells the compiler that this method is an RPC call. + +An example of an oracle that returns a `Field` would be: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(number: Field) -> Field { } + +unconstrained fn get_sqrt(number: Field) -> Field { + sqrt(number) +} +``` + +In this example, we're wrapping our oracle function in an unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); +} +``` + +In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use. + +:::danger + +As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); + assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle! +} +``` + +::: + +:::info + +Currently, oracles only work with single params or array params. For example: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt([Field; 2]) -> [Field; 2] { } +``` + +::: + +## Step 2 - Write an RPC server + +Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript. + +Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { } + +unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] { + sqrt(input) +} + +fn main(input: [Field; 2]) { + let sqrt = get_sqrt(input); + assert(sqrt[0].pow_32(2) as u64 == input[0] as u64); + assert(sqrt[1].pow_32(2) as u64 == input[1] as u64); +} + +#[test] +fn test() { + let input = [4, 16]; + main(input); +} +``` + +:::info + +Why square root? + +In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values. + +::: + +Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example): + +```js +import { JSONRPCServer } from "json-rpc-2.0"; +import express from "express"; +import bodyParser from "body-parser"; + +const app = express(); +app.use(bodyParser.json()); + +const server = new JSONRPCServer(); +app.post("/", (req, res) => { + const jsonRPCRequest = req.body; + server.receive(jsonRPCRequest).then((jsonRPCResponse) => { + if (jsonRPCResponse) { + res.json(jsonRPCResponse); + } else { + res.sendStatus(204); + } + }); +}); + +app.listen(5555); +``` + +Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: + +```js +server.addMethod("resolve_foreign_call", async (params) => { + if (params[0].function !== "getSqrt") { + throw Error("Unexpected foreign call") + }; + const values = params[0].inputs[0].map((field) => { + return `${Math.sqrt(parseInt(field, 16))}`; + }); + return { values: [values] }; +}); +``` + +If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: + +```js +export type ForeignCallSingle = string; + +export type ForeignCallArray = string[]; + +export type ForeignCallResult = { + values: (ForeignCallSingle | ForeignCallArray)[]; +}; +``` + +:::info Multidimensional Arrays + +If the Oracle function is returning an array containing other arrays, such as `[['1','2],['3','4']]`, you need to provide the values in JSON as flattened values. In the previous example, it would be `['1', '2', '3', '4']`. In the Noir program, the Oracle signature can use a nested type, the flattened values will be automatically converted to the nested type. + +::: + +## Step 3 - Usage with Nargo + +Using the [`nargo` CLI tool](../reference/nargo_commands.md), you can use oracles in the `nargo test` and `nargo execute` commands by passing a value to `--oracle-resolver`. For example: + +```bash +nargo test --oracle-resolver http://localhost:5555 +``` + +This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator. + +## Step 4 - Usage with NoirJS + +In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything. + +For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app: + +```js +const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc + +await noir.execute(inputs, foreignCallHandler) +``` + +As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. + +:::tip + +Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)? + +You don't technically have to, but then how would you run `nargo test`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server. + +::: + +In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client. + +For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code): + +```js +import { JSONRPCClient } from "json-rpc-2.0"; + +// declaring the JSONRPCClient +const client = new JSONRPCClient((jsonRPCRequest) => { +// hitting the same JSON RPC Server we coded above + return fetch("http://localhost:5555", { + method: "POST", + headers: { + "content-type": "application/json", + }, + body: JSON.stringify(jsonRPCRequest), + }).then((response) => { + if (response.status === 200) { + return response + .json() + .then((jsonRPCResponse) => client.receive(jsonRPCResponse)); + } else if (jsonRPCRequest.id !== undefined) { + return Promise.reject(new Error(response.statusText)); + } + }); +}); + +// declaring a function that takes the name of the foreign call (getSqrt) and the inputs +const foreignCallHandler = async (name, input) => { + const inputs = input[0].map((i) => i.toString("hex")) + // notice that the "inputs" parameter contains *all* the inputs + // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] + const oracleReturn = await client.request("resolve_foreign_call", [ + { + function: name, + inputs: [inputs] + }, + ]); + return [oracleReturn.values[0]]; +}; + +// the rest of your NoirJS code +const input = { input: [4, 16] }; +const { witness } = await noir.execute(input, foreignCallHandler); +``` + +:::tip + +If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem: + +```bash +yarn add cors +``` + +and use it as a middleware: + +```js +import cors from "cors"; + +const app = express(); +app.use(cors()) +``` + +::: + +## Conclusion + +Hopefully by the end of this guide, you should be able to: + +- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands. +- Provide custom foreign call handlers for NoirJS. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-recursion.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-recursion.md new file mode 100644 index 000000000000..399e4d4b38a0 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-recursion.md @@ -0,0 +1,172 @@ +--- +title: How to use recursion on NoirJS +description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `bb.js`. +keywords: + [ + "NoirJS", + "EVM blockchain", + "smart contracts", + "recursion", + "solidity verifiers", + "Barretenberg backend", + "noir_js", + "intermediate proofs", + "final proofs", + "nargo compile", + "json import", + "recursive circuit", + "recursive app" + ] +sidebar_position: 1 +--- + +This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that: + +- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). +- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) +- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.mdx), and understand how it works. + +It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. + +:::info + +As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. + +::: + +In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume the following: + +- `main`: a circuit of type `assert(x != y)`, which we want to embed in another circuit recursively. For example when proving with the `bb` tool, we can use the `--recursive` CLI option to tell the backend that it should generate proofs that are friendly for verification within another circuit. +- `recursive`: a circuit that verifies `main` + +For a full example of how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. + +## Step 1: Setup + +In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface. + +For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object. + +It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency: + +```js +const backend = new UltraPlonkBackend(circuit, { threads: 8 }, { recursive: true }) +``` + +:::tip +You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores +::: + +## Step 2: Generating the witness and the proof for `main` + +After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. + +```js +const noir = new Noir(circuit) +const { witness } = noir.execute(input) +``` + +With this witness, you are now able to generate the intermediate proof for the main circuit: + +```js +const { proof, publicInputs } = await backend.generateProof(witness) +``` + +:::warning + +Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why! + +In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof. + +With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain. + +::: + +## Step 3 - Verification and proof artifacts + +Optionally, you are able to verify the intermediate proof: + +```js +const verified = await backend.verifyProof({ proof, publicInputs }) +``` + +This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate recursive proof artifacts that will be passed to the circuit that is verifying the proof we just generated. Instead of passing the proof and verification key as a byte array, we pass them as fields which makes it cheaper to verify in a circuit: + +```js +const { proofAsFields, vkAsFields, vkHash } = await backend.generateRecursiveProofArtifacts( { publicInputs, proof }, publicInputsCount) +``` + +This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away. + +:::info + +The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. + +::: + +:::warning + +One common mistake is to forget *who* makes this call. + +In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything! + +Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit. + +::: + +## Step 4 - Recursive proof generation + +With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it: + +```js +const recursiveInputs = { + verification_key: vkAsFields, // array of length 114 + proof: proofAsFields, // array of length 93 + size of public inputs + publicInputs: [mainInput.y], // using the example above, where `y` is the only public input + key_hash: vkHash, +} + +const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! +const { proof, publicInputs } = backend.generateProof(witness) +const verified = backend.verifyProof({ proof, publicInputs }) +``` + +You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! + +:::tip + +Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example: + +```js +const circuits = { + main: mainJSON, + recursive: recursiveJSON +} +const backends = { + main: new BarretenbergBackend(circuits.main), + recursive: new BarretenbergBackend(circuits.recursive) +} +const noir_programs = { + main: new Noir(circuits.main), + recursive: new Noir(circuits.recursive) +} +``` + +This allows you to neatly call exactly the method you want without conflicting names: + +```js +// Alice runs this 👇 +const { witness: mainWitness } = await noir_programs.main.execute(input) +const proof = await backends.main.generateProof(mainWitness) + +// Bob runs this 👇 +const verified = await backends.main.verifyProof(proof) +const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateRecursiveProofArtifacts( + proof, + numPublicInputs, +); +const { witness: recursiveWitness } = await noir_programs.recursive.execute(recursiveInputs) +const recursiveProof = await backends.recursive.generateProof(recursiveWitness); +``` + +::: diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-solidity-verifier.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-solidity-verifier.mdx new file mode 100644 index 000000000000..c8c7894ff911 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-solidity-verifier.mdx @@ -0,0 +1,305 @@ +--- +title: Generate a Solidity Verifier +description: + Learn how to run the verifier as a smart contract on the blockchain. Compile a Solidity verifier + contract for your Noir program and deploy it on any EVM blockchain acting as a verifier smart + contract. Read more to find out +keywords: + [ + solidity verifier, + smart contract, + blockchain, + compiler, + plonk_vk.sol, + EVM blockchain, + verifying Noir programs, + proving backend, + Barretenberg, + ] +sidebar_position: 0 +pagination_next: tutorials/noirjs_app +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Noir is universal. The witness and the compiled program can be fed into a proving backend such as Aztec's [Barretenberg](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg), which can then generate a verifier contract for deployment on blockchains. + +This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. + +Although not strictly in the domain of Noir itself, this guide shows how to generate a Solidity Verifier with Barretenberg and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: + +- You will be using Barretenberg as your proving backend +- You will be using an EVM blockchain to verify your proof +- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network +- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/quick_start.md) with Nargo and the example Hello Noir circuit +- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. + +## Rundown + +Generating a Solidity Verifier with Barretenberg contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: + +1. How to generate a solidity smart contract +2. How to compile the smart contract in the RemixIDE +3. How to deploy it to a testnet + +:::info[Which proving system to use?] + +Barretenberg currently provides two provers: `UltraPlonk` and `UltraHonk`. In a nutshell, `UltraHonk` is faster and uses less RAM, but its verifier contract is much more expensive. `UltraPlonk` is optimized for on-chain verification, but proving is more expensive. + +In any case, we provide instructions for both. Choose your poison ☠️ + +::: + +## Step 1 - Generate a contract + +This is by far the most straightforward step. Just run: + +```sh +nargo compile +``` + +This will compile your source code into a Noir build artifact to be stored in the `./target` directory. From here on, it's Barretenberg's work. You can generate the smart contract using the commands: + + + + +```sh +bb write_vk_ultra_keccak_honk -b ./target/.json +bb contract_ultra_honk +``` + + + + +```sh +bb write_vk -b ./target/.json +bb contract +``` + + + + +replacing `` with the name of your Noir project. A `Verifier.sol` contract is now in the target folder and can be deployed to any EVM blockchain acting as a verifier smart contract. + +You can find more information about `bb` and the default Noir proving backend on [this page](../getting_started/quick_start.md#proving-backend). + + +## Step 2 - Compiling + +We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open +
Remix and create a blank workspace. + +![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) + +We will create a new file to contain the contract Nargo generated, and copy-paste its content. + +:::warning + +You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. + +::: + +To compile our the verifier, we can navigate to the compilation tab: + +![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) + +Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely tell you the contract is too big to deploy on mainnet, or complain about a stack too deep: + +![Contract code too big](@site/static/img/how-tos/solidity_verifier_6.png) +![Stack too deep](@site/static/img/how-tos/solidity_verifier_8.png) + +To avoid this, you can just use some optimization. Open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. + +![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) + +## Step 3 - Deploying + +At this point we should have a compiled contract ready to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. + +Looking closely, we will notice that our "Solidity Verifier" is composed on multiple contracts working together. Remix will take care of the dependencies for us so we can simply deploy the Verifier contract by selecting it and hitting "deploy": + + + + +![Deploying HonkVerifier](@site/static/img/how-tos/solidity_verifier_7.png) + + + + +![Deploying PlonkVerifier](@site/static/img/how-tos/solidity_verifier_9.png) + + + + +A contract will show up in the "Deployed Contracts" section. + +## Step 4 - Verifying + +To verify a proof using the Solidity verifier contract, we call the `verify` function: + +```solidity +function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) +``` + +First generate a proof with `bb`. We need a `Prover.toml` file for our inputs. Run: + +```bash +nargo check +``` + +This will generate a `Prover.toml` you can fill with the values you want to prove. We can now execute the circuit with `nargo` and then use the proving backend to prove: + + + + +```bash +nargo execute +bb prove_ultra_keccak_honk -b ./target/.json -w ./target/ -o ./target/proof +``` + +:::tip[Public inputs] +Barretenberg attaches the public inputs to the proof, which in this case it's not very useful. If you're up for some JS, `bb.js` has [a method for it](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/ts/src/proof/index.ts), but in the CLI you can use this ugly snippet: + +```bash +cat ./target/proof | od -An -v -t x1 | tr -d $' \n' | sed 's/^.\{8\}//' | (read hex; echo "${hex:0:192}${hex:256}") +``` + +Beautiful. This assumes a circuit with one public input (32 bytes, for Barretenberg). For more inputs, you can just increment `hex:256` with 32 more bytes for each public input. + +::: + + + + +```bash +nargo execute +bb prove -b ./target/.json -w ./target/ -o ./target/proof +``` + + +:::tip[Public inputs] +Barretenberg attaches the public inputs to the proof, which in this case it's not very useful. If you're up for some JS, `bb.js` has [a method for it](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/ts/src/proof/index.ts), but in the CLI you can use this ugly snippet: + +```bash +tail -c +33 ./target/proof | od -An -v -t x1 | tr -d $' \n' +``` + +Beautiful. This assumes a circuit with one public input (32 bytes, for Barretenberg). For more inputs, you can just add 32 more bytes for each public input to the `tail` command. + +::: + + + + +Remix expects that the public inputs will be split into an array of `bytes32` values so `HEX_PUBLIC_INPUTS` needs to be split up into 32 byte chunks which are prefixed with `0x` accordingly. + +A programmatic example of how the `verify` function is called can be seen in the example zk voting application [here](https://github.com/noir-lang/noir-examples/blob/33e598c257e2402ea3a6b68dd4c5ad492bce1b0a/foundry-voting/src/zkVote.sol#L35): + +```solidity +function castVote(bytes calldata proof, uint proposalId, uint vote, bytes32 nullifierHash) public returns (bool) { + // ... + bytes32[] memory publicInputs = new bytes32[](4); + publicInputs[0] = merkleRoot; + publicInputs[1] = bytes32(proposalId); + publicInputs[2] = bytes32(vote); + publicInputs[3] = nullifierHash; + require(verifier.verify(proof, publicInputs), "Invalid proof"); +``` + +:::info[Return Values] + +A circuit doesn't have the concept of a return value. Return values are just syntactic sugar in Noir. + +Under the hood, the return value is passed as an input to the circuit and is checked at the end of the circuit program. + +For example, if you have Noir program like this: + +```rust +fn main( + // Public inputs + pubkey_x: pub Field, + pubkey_y: pub Field, + // Private inputs + priv_key: Field, +) -> pub Field +``` + +the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. + +Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. + +In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return]`. + +::: + +:::tip[Structs] + +You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. + +For example, consider the following program: + +```rust +struct Type1 { + val1: Field, + val2: Field, +} + +struct Nested { + t1: Type1, + is_true: bool, +} + +fn main(x: pub Field, nested: pub Nested, y: pub Field) { + //... +} +``` + +The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` + +::: + +The other function you can call is our entrypoint `verify` function, as defined above. + +:::tip + +It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. + +This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. + +It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). + +::: + +## Compatibility with different EVM chains + +Barretenberg proof verification requires the `ecMul`, `ecAdd`, `ecPairing`, and `modexp` EVM precompiles. You can deploy and use the verifier contract on all EVM chains that support the precompiles. + +EVM Diff provides a great table of which EVM chains support which precompiles: https://www.evmdiff.com/features?feature=precompiles + +Some EVM chains manually tested to work with the Barretenberg verifier include: + +- Optimism +- Arbitrum +- Polygon PoS +- Scroll +- Celo +- BSC +- Blast L2 +- Avalanche C-Chain +- Mode +- Linea +- Moonbeam + +Meanwhile, some EVM chains chains manually tested that failed to work with the Barretenberg verifier include: + +- zkSync ERA +- Polygon zkEVM + +Pull requests to update this section is welcome and appreciated if you have compatibility updates on existing / new chains to contribute: https://github.com/noir-lang/noir + +## What's next + +Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks. You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. + +You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/merkle-proof.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/merkle-proof.mdx new file mode 100644 index 000000000000..0a128adb2de6 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/merkle-proof.mdx @@ -0,0 +1,48 @@ +--- +title: Prove Merkle Tree Membership +description: + Learn how to use merkle membership proof in Noir to prove that a given leaf is a member of a + merkle tree with a specified root, at a given index. +keywords: + [merkle proof, merkle membership proof, Noir, rust, hash function, Pedersen, sha256, merkle tree] +sidebar_position: 4 +--- + +Let's walk through an example of a merkle membership proof in Noir that proves that a given leaf is +in a merkle tree. + +```rust + +fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { + let leaf = std::hash::hash_to_field(message.as_slice()); + let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); + assert(merkle_root == root); +} + +``` + +The message is hashed using `hash_to_field`. The specific hash function that is being used is chosen +by the backend. The only requirement is that this hash function can heuristically be used as a +random oracle. If only collision resistance is needed, then one can call `std::hash::pedersen_hash` +instead. + +```rust +let leaf = std::hash::hash_to_field(message.as_slice()); +``` + +The leaf is then passed to a compute_merkle_root function with the root, index and hashpath. The returned root can then be asserted to be the same as the provided root. + +```rust +let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); +assert (merkle_root == root); +``` + +> **Note:** It is possible to re-implement the merkle tree implementation without standard library. +> However, for most usecases, it is enough. In general, the standard library will always opt to be +> as conservative as possible, while striking a balance with efficiency. + +An example, the merkle membership proof, only requires a hash function that has collision +resistance, hence a hash function like Pedersen is allowed, which in most cases is more efficient +than the even more conservative sha256. + +[View an example on the starter repo](https://github.com/noir-lang/noir-examples/blob/3ea09545cabfa464124ec2f3ea8e60c608abe6df/stealthdrop/circuits/src/main.nr#L20) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/using-devcontainers.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/using-devcontainers.mdx new file mode 100644 index 000000000000..dcd30d898015 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/using-devcontainers.mdx @@ -0,0 +1,91 @@ +--- +title: Developer Containers and Codespaces +description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally." +keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"] +sidebar_position: 1 +--- + +Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser. + +## What's a devcontainer after all? + +A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box. + +There are many advantages to this: + +- It's platform and architecture agnostic +- You don't need to have an IDE installed, or Nargo, or use a terminal at all +- It's safer for using on a public machine or public network + +One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use. +Enter Codespaces. + +## Codespaces + +If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof? + +Nothing! Except perhaps the 30-40$ per hour it will cost you. + +The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick. + +Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool: + +- You can start coding Noir in less than a minute +- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be +- It makes it easy to share work with your frens +- It's fully reusable, you can stop and restart whenever you need to + +:::info + +Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine. + +::: + +## Tell me it's _actually_ easy + +It is! + +Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide. + + + +8 simple steps: + +#### 1. Create a new repository on GitHub. + +#### 2. Click "Start coding with Codespaces". This will use the default image. + +#### 3. Create a folder called `.devcontainer` in the root of your repository. + +#### 4. Create a file called `devcontainer.json` in the same folder, and paste the following code: + +```json +{ + "name": "Noir on Codespaces", + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "ghcr.io/noir-lang/features/noir:latest": { + "version": "1.0.0-beta.2" + } + } +} +``` +#### 6. Commit and push your changes + +This will pull the new image and build it, so it could take a minute or so + +#### 8. Done! +Just wait for the build to finish, and there's your easy Noir environment. Some examples of how to use it can be found in the [awesome-noir](https://github.com/noir-lang/awesome-noir?tab=readme-ov-file#boilerplates) repository. + +## How do I use it? + +Using the codespace is obviously much easier than setting it up. +Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go. + +:::info + +If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. +Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/index.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/index.mdx new file mode 100644 index 000000000000..5c116a73b3fe --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/index.mdx @@ -0,0 +1,76 @@ +--- +title: Noir Lang +hide_title: true +description: + Learn about the public alpha release of Noir, a domain specific language heavily influenced by Rust that compiles to + an intermediate language which can be compiled to an arithmetic circuit or a rank-1 constraint system. +keywords: + [Noir, + Domain Specific Language, + Rust, + Intermediate Language, + Arithmetic Circuit, + Rank-1 Constraint System, + Ethereum Developers, + Protocol Developers, + Blockchain Developers, + Proving System, + Smart Contract Language] +sidebar_position: 0 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import ThemedImage from '@theme/ThemedImage'; +import useBaseUrl from '@docusaurus/useBaseUrl'; + + + +Noir is an open-source Domain-Specific Language for safe and seamless construction of privacy-preserving Zero-Knowledge programs, requiring no previous knowledge on the underlying mathematics or cryptography. + +ZK programs are programs that can generate short proofs of statements without revealing all inputs to the statements. You can read more about Zero-Knowledge Proofs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). + +## What's new about Noir? + +Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. + +:::info + +Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. + +However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. + +::: + +## Who is Noir for? + +Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: + + + + Noir Logo + + Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. + + + Soliditry Verifier Example + Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) + + + Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. + + + + +## Libraries + +Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. +The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. +Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/migration_notes.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/migration_notes.md new file mode 100644 index 000000000000..6bd740024e5b --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/migration_notes.md @@ -0,0 +1,105 @@ +--- +title: Migration notes +description: Read about migration notes from previous versions, which could solve problems while updating +keywords: [Noir, notes, migration, updating, upgrading] +--- + +Noir is in full-speed development. Things break fast, wild, and often. This page attempts to leave some notes on errors you might encounter when upgrading and how to resolve them until proper patches are built. + +### `backend encountered an error: libc++.so.1` + +Depending on your OS, you may encounter the following error when running `nargo prove` for the first time: + +```text +The backend encountered an error: "/home/codespace/.nargo/backends/acvm-backend-barretenberg/backend_binary: error while loading shared libraries: libc++.so.1: cannot open shared object file: No such file or directory\n" +``` + +Install the `libc++-dev` library with: + +```bash +sudo apt install libc++-dev +``` + +## ≥0.19 + +### Enforcing `compiler_version` + +From this version on, the compiler will check for the `compiler_version` field in `Nargo.toml`, and will error if it doesn't match the current Nargo version in use. + +To update, please make sure this field in `Nargo.toml` matches the output of `nargo --version`. + +## ≥0.14 + +The index of the [for loops](noir/concepts/control_flow.md#loops) is now of type `u64` instead of `Field`. An example refactor would be: + +```rust +for i in 0..10 { + let i = i as Field; +} +``` + +## ≥v0.11.0 and Nargo backend + +From this version onwards, Nargo starts managing backends through the `nargo backend` command. Upgrading to the versions per usual steps might lead to: + +### `backend encountered an error` + +This is likely due to the existing locally installed version of proving backend (e.g. barretenberg) is incompatible with the version of Nargo in use. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo prove +``` + +with your Noir program. + +This will trigger the download and installation of the latest version of barretenberg compatible with your Nargo in use. + +### `backend encountered an error: illegal instruction` + +On certain Intel-based systems, an `illegal instruction` error may arise due to incompatibility of barretenberg with certain CPU instructions. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo backend install acvm-backend-barretenberg https://github.com/noir-lang/barretenberg-js-binary/raw/master/run-bb.tar.gz +``` + +This downloads and installs a specific bb.js based version of barretenberg binary from GitHub. + +The gzipped file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), where we need to gzip it as the Nargo currently expect the backend to be zipped up. + +Then run: + +``` +DESIRED_BINARY_VERSION=0.8.1 nargo info +``` + +This overrides the bb native binary with a bb.js node application instead, which should be compatible with most if not all hardware. This does come with the drawback of being generally slower than native binary. + +0.8.1 indicates bb.js version 0.8.1, so if you change that it will update to a different version or the default version in the script if none was supplied. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/_category_.json new file mode 100644 index 000000000000..7da08f8a8c5d --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Concepts", + "position": 0, + "collapsible": true, + "collapsed": true +} \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/assert.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/assert.md new file mode 100644 index 000000000000..c7bc42fa3228 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/assert.md @@ -0,0 +1,79 @@ +--- +title: Assert Function +description: + Learn about the `assert` and `static_assert` functions in Noir, which can be used to explicitly + constrain the predicate or comparison expression that follows to be true, and what happens if + the expression is false at runtime or compile-time, respectively. +keywords: [Noir programming language, assert statement, predicate expression, comparison expression] +sidebar_position: 4 +--- + +Noir includes a special `assert` function which will explicitly constrain the predicate/comparison +expression that follows to be true. If this expression is false at runtime, the program will fail to +be proven. As of v1.0.0-beta.2, assert statements are expressions and can be used in value contexts. + +Example: + +```rust +fn main(x : Field, y : Field) { + assert(x == y); +} +``` + +> Assertions only work for predicate operations, such as `==`. If there's any ambiguity on the operation, the program will fail to compile. For example, it is unclear if `assert(x + y)` would check for `x + y == 0` or simply would return `true`. + +You can optionally provide a message to be logged when the assertion fails: + +```rust +assert(x == y, "x and y are not equal"); +``` + +Aside string literals, the optional message can be a format string or any other type supported as input for Noir's [print](../standard_library/logging.md) functions. This feature lets you incorporate runtime variables into your failed assertion logs: + +```rust +assert(x == y, f"Expected x == y, but got {x} == {y}"); +``` + +Using a variable as an assertion message directly: + +```rust +struct myStruct { + myField: Field +} + +let s = myStruct { myField: y }; +assert(s.myField == x, s); +``` + +There is also a special `static_assert` function that behaves like `assert`, +but that runs at compile-time. + +```rust +fn main(xs: [Field; 3]) { + let x = 2 + 2; + let y = 4; + static_assert(x == y, "expected 2 + 2 to equal 4"); + + // This passes since the length of `xs` is known at compile-time + static_assert(xs.len() == 3, "expected the input to have 3 elements"); +} +``` + +This function fails when passed a dynamic (run-time) argument: + +```rust +fn main(x : Field, y : Field) { + // this fails because `x` is not known at compile-time + static_assert(x == 2, "expected x to be known at compile-time and equal to 2"); + + let mut example_slice = &[]; + if y == 4 { + example_slice = example_slice.push_back(0); + } + + // This fails because the length of `example_slice` is not known at + // compile-time + let error_message = "expected an empty slice, known at compile-time"; + static_assert(example_slice.len() == 0, error_message); +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comments.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comments.md new file mode 100644 index 000000000000..b51a85f5c949 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comments.md @@ -0,0 +1,33 @@ +--- +title: Comments +description: + Learn how to write comments in Noir programming language. A comment is a line of code that is + ignored by the compiler, but it can be read by programmers. Single-line and multi-line comments + are supported in Noir. +keywords: [Noir programming language, comments, single-line comments, multi-line comments] +sidebar_position: 10 +--- + +A comment is a line in your codebase which the compiler ignores, however it can be read by +programmers. + +Here is a single line comment: + +```rust +// This is a comment and is ignored +``` + +`//` is used to tell the compiler to ignore the rest of the line. + +Noir also supports multi-line block comments. Start a block comment with `/*` and end the block with `*/`. + +Noir does not natively support doc comments. You may be able to use [Rust doc comments](https://doc.rust-lang.org/reference/comments.html) in your code to leverage some Rust documentation build tools with Noir code. + +```rust +/* + This is a block comment describing a complex function. +*/ +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comptime.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comptime.md new file mode 100644 index 000000000000..4cdd2094a162 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comptime.md @@ -0,0 +1,565 @@ +--- +title: Compile-time Code & Metaprogramming +description: Learn how to use metaprogramming in Noir to create macros or derive your own traits +keywords: [Noir, comptime, compile-time, metaprogramming, macros, quote, unquote] +sidebar_position: 15 +--- + +## Overview + +Metaprogramming in Noir is comprised of three parts: +1. `comptime` code +2. Quoting and unquoting +3. The metaprogramming API in `std::meta` + +Each of these are explained in more detail in the next sections but the wide picture is that +`comptime` allows us to write code which runs at compile-time. In this `comptime` code we +can quote and unquote snippets of the program, manipulate them, and insert them in other +parts of the program. Comptime functions which do this are said to be macros. Additionally, +there's a compile-time API of built-in types and functions provided by the compiler which allows +for greater analysis and modification of programs. + +--- + +## Comptime + +`comptime` is a new keyword in Noir which marks an item as executing or existing at compile-time. It can be used in several ways: + +- `comptime fn` to define functions which execute exclusively during compile-time. +- `comptime global` to define a global variable which is evaluated at compile-time. + - Unlike runtime globals, `comptime global`s can be mutable. +- `comptime { ... }` to execute a block of statements during compile-time. +- `comptime let` to define a variable whose value is evaluated at compile-time. +- `comptime for` to run a for loop at compile-time. Syntax sugar for `comptime { for .. }`. + +### Scoping + +Note that while in a `comptime` context, any runtime variables _local to the current function_ are never visible. + +### Evaluating + +Evaluation rules of `comptime` follows the normal unconstrained evaluation rules for other Noir code. There are a few things to note though: + +- Certain built-in functions may not be available, although more may be added over time. +- Evaluation order of `comptime {}` blocks within global items is currently unspecified. For example, given the following two functions we can't guarantee +which `println` will execute first. The ordering of the two printouts will be arbitrary, but should be stable across multiple compilations with the same `nargo` version as long as the program is also unchanged. + +```rust +fn one() { + comptime { println("one"); } +} + +fn two() { + comptime { println("two"); } +} +``` + +- Since evaluation order is unspecified, care should be taken when using mutable globals so that they do not rely on a particular ordering. +For example, using globals to generate unique ids should be fine but relying on certain ids always being produced (especially after edits to the program) should be avoided. +- Although the ordering of comptime code is usually unspecified, there are cases where it is: + - Dependencies of a crate will always be evaluated before the dependent crate. + - Any attributes on a function will be run before the function body is resolved. This is to allow the attribute to modify the function if necessary. Note that if the + function itself was called at compile-time previously, it will already be resolved and cannot be modified. To prevent accidentally calling functions you wish to modify + at compile-time, it may be helpful to sort your `comptime` annotation functions into a different submodule crate along with any dependencies they require. + - Unlike raw `comptime {}` blocks, attributes on top-level items in the program do have a set evaluation order. Attributes within a module are evaluated top-down, and attributes + in different modules are evaluated submodule-first. Sibling modules to the same parent module are evaluated in order of the module declarations (`mod foo; mod bar;`) in their + parent module. + +### Lowering + +When a `comptime` value is used in runtime code it must be lowered into a runtime value. This means replacing the expression with the literal that it evaluated to. For example, the code: + +```rust +struct Foo { array: [Field; 2], len: u32 } + +fn main() { + println(comptime { + let mut foo = std::mem::zeroed::(); + foo.array[0] = 4; + foo.len = 1; + foo + }); +} +``` + +will be converted to the following after `comptime` expressions are evaluated: + +```rust +struct Foo { array: [Field; 2], len: u32 } + +fn main() { + println(Foo { array: [4, 0], len: 1 }); +} +``` + +Not all types of values can be lowered. For example, references, `Type`s, and `TypeDefinition`s (among other types) cannot be lowered at all. + +```rust +fn main() { + // There's nothing we could inline here to create a Type value at runtime + // let _ = get_type!(); +} + +comptime fn get_type() -> Type { ... } +``` + +Values of certain types may also change type when they are lowered. For example, a comptime format string will already be +formatted, and thus lowers into a runtime string instead: + +```rust +fn main() { + let foo = comptime { + let i = 2; + f"i = {i}" + }; + assert_eq(foo, "i = 2"); +} +``` + +--- + +## (Quasi) Quote + +Macros in Noir are `comptime` functions which return code as a value which is inserted into the call site when it is lowered there. +A code value in this case is of type `Quoted` and can be created by a `quote { ... }` expression. +More specifically, the code value `quote` creates is a token stream - a representation of source code as a series of words, numbers, string literals, or operators. +For example, the expression `quote { Hi "there reader"! }` would quote three tokens: the word "hi", the string "there reader", and an exclamation mark. +You'll note that snippets that would otherwise be invalid syntax can still be quoted. + +When a `Quoted` value is used in runtime code, it is lowered into a `quote { ... }` expression. Since this expression is only valid +in compile-time code however, we'd get an error if we tried this. Instead, we can use macro insertion to insert each token into the +program at that point, and parse it as an expression. To do this, we have to add a `!` after the function name returning the `Quoted` value. +If the value was created locally and there is no function returning it, `std::meta::unquote!(_)` can be used instead. +Calling such a function at compile-time without `!` will just return the `Quoted` value to be further manipulated. For example: + +```rust title="quote-example" showLineNumbers +comptime fn quote_one() -> Quoted { + quote { 1 } + } + + #[test] + fn returning_versus_macro_insertion() { + comptime { + // let _a: Quoted = quote { 1 }; + let _a: Quoted = quote_one(); + + // let _b: Field = 1; + let _b: Field = quote_one!(); + + // Since integers default to fields, if we + // want a different type we have to explicitly cast + // let _c: i32 = 1 as i32; + let _c: i32 = quote_one!() as i32; + } + } +``` +> Source code: noir_stdlib/src/meta/mod.nr#L131-L151 + + +For those familiar with quoting from other languages (primarily lisps), Noir's `quote` is actually a _quasiquote_. +This means we can escape the quoting by using the unquote operator to splice values in the middle of quoted code. + +In addition to curly braces, you can also use square braces for the quote operator: + +```rust +comptime { + let q1 = quote { 1 }; + let q2 = quote [ 2 ]; + assert_eq(q1, q2); + + // Square braces can be used to quote mismatched curly braces if needed + let _ = quote[}]; +} +``` + +--- + +## Unquote + +The unquote operator `$` is usable within a `quote` expression. +It takes a variable as an argument, evaluates the variable, and splices the resulting value into the quoted token stream at that point. For example, + +```rust +comptime { + let x = 1 + 2; + let y = quote { $x + 4 }; +} +``` + +The value of `y` above will be the token stream containing `3`, `+`, and `4`. We can also use this to combine `Quoted` values into larger token streams: + +```rust +comptime { + let x = quote { 1 + 2 }; + let y = quote { $x + 4 }; +} +``` + +The value of `y` above is now the token stream containing five tokens: `1 + 2 + 4`. + +Note that to unquote something, a variable name _must_ follow the `$` operator in a token stream. +If it is an expression (even a parenthesized one), it will do nothing. Most likely a parse error will be given when the macro is later unquoted. + +Unquoting can also be avoided by escaping the `$` with a backslash: + +```rust +comptime { + let x = quote { 1 + 2 }; + + // y contains the four tokens: `$x + 4` + let y = quote { \$x + 4 }; +} +``` + +### Combining Tokens + +Note that `Quoted` is internally a series of separate tokens, and that all unquoting does is combine these token vectors. +This means that code which appears to append like a string actually appends like a vector internally: + +```rust +comptime { + let x = 3; + let q = quote { foo$x }; // This is [foo, 3], not [foo3] + + // Spaces are ignored in general, they're never part of a token + assert_eq(q, quote { foo 3 }); +} +``` + +If you do want string semantics, you can use format strings then convert back to a `Quoted` value with `.quoted_contents()`. +Note that formatting a quoted value with multiple tokens will always insert a space between each token. If this is +undesired, you'll need to only operate on quoted values containing a single token. To do this, you can iterate +over each token of a larger quoted value with `.tokens()`: + +```rust title="concatenate-example" showLineNumbers +comptime fn concatenate(q1: Quoted, q2: Quoted) -> Quoted { + assert(q1.tokens().len() <= 1); + assert(q2.tokens().len() <= 1); + + f"{q1}{q2}".quoted_contents() + } + + // The CtString type is also useful for a compile-time string of unbounded size + // so that you can append to it in a loop. + comptime fn double_spaced(q: Quoted) -> CtString { + let mut result = "".as_ctstring(); + + for token in q.tokens() { + if result != "".as_ctstring() { + result = result.append_str(" "); + } + result = result.append_fmtstr(f"{token}"); + } + + result + } + + #[test] + fn concatenate_test() { + comptime { + let result = concatenate(quote {foo}, quote {bar}); + assert_eq(result, quote {foobar}); + + let result = double_spaced(quote {foo bar 3}).as_quoted_str!(); + assert_eq(result, "foo bar 3"); + } + } +``` +> Source code: noir_stdlib/src/meta/mod.nr#L266-L299 + + +--- + +## Attributes + +Attributes provide a way to run a `comptime` function on an item in the program. +When you use an attribute, the function with the same name will be called with that item as an argument: + +```rust +#[my_struct_attribute] +struct Foo {} + +comptime fn my_struct_attribute(s: StructDefinition) { + println("Called my_struct_attribute!"); +} + +#[my_function_attribute] +fn foo() {} + +comptime fn my_function_attribute(f: FunctionDefinition) { + println("Called my_function_attribute!"); +} +``` + +Anything returned from one of these functions will be inserted at top-level along with the original item. +Note that expressions are not valid at top-level so you'll get an error trying to return `3` or similar just as if you tried to write a program containing `3; struct Foo {}`. +You can insert other top-level items such as trait impls, structs, or functions this way though. +For example, this is the mechanism used to insert additional trait implementations into the program when deriving a trait impl from a struct: + +```rust title="derive-field-count-example" showLineNumbers +trait FieldCount { + fn field_count() -> u32; + } + + #[derive_field_count] + struct Bar { + x: Field, + y: [Field; 2], + } + + comptime fn derive_field_count(s: StructDefinition) -> Quoted { + let typ = s.as_type(); + let field_count = s.fields_as_written().len(); + quote { + impl FieldCount for $typ { + fn field_count() -> u32 { + $field_count + } + } + } + } +``` +> Source code: noir_stdlib/src/meta/mod.nr#L153-L175 + + +### Calling annotations with additional arguments + +Arguments may optionally be given to attributes. +When this is done, these additional arguments are passed to the attribute function after the item argument. + +```rust title="annotation-arguments-example" showLineNumbers +#[assert_field_is_type(quote { i32 }.as_type())] + struct MyStruct { + my_field: i32, + } + + comptime fn assert_field_is_type(s: StructDefinition, typ: Type) { + // Assert the first field in `s` has type `typ` + let fields = s.fields([]); + assert_eq(fields[0].1, typ); + } +``` +> Source code: noir_stdlib/src/meta/mod.nr#L177-L188 + + +We can also take any number of arguments by adding the `varargs` attribute: + +```rust title="annotation-varargs-example" showLineNumbers +#[assert_three_args(1, 2, 3)] + struct MyOtherStruct { + my_other_field: u32, + } + + #[varargs] + comptime fn assert_three_args(_s: StructDefinition, args: [Field]) { + assert_eq(args.len(), 3); + } +``` +> Source code: noir_stdlib/src/meta/mod.nr#L190-L200 + + +### Attribute Evaluation Order + +Unlike the evaluation order of stray `comptime {}` blocks within functions, attributes have a well-defined evaluation +order. Within a module, attributes are evaluated top to bottom. Between modules, attributes in child modules are evaluated +first. Attributes in sibling modules are resolved following the `mod foo; mod bar;` declaration order within their parent +modules. + +```rust +mod foo; // attributes in foo are run first +mod bar; // followed by attributes in bar + +// followed by any attributes in the parent module +#[derive(Eq)] +struct Baz {} +``` + +Note that because of this evaluation order, you may get an error trying to derive a trait for a struct whose fields +have not yet had the trait derived already: + +```rust +// Error! `Bar` field of `Foo` does not (yet) implement Eq! +#[derive(Eq)] +struct Foo { + bar: Bar +} + +#[derive(Eq)] +struct Bar {} +``` + +In this case, the issue can be resolved by rearranging the structs. + +--- + +## Comptime API + +Although `comptime`, `quote`, and unquoting provide a flexible base for writing macros, +Noir's true metaprogramming ability comes from being able to interact with the compiler through a compile-time API. +This API can be accessed through built-in functions in `std::meta` as well as on methods of several `comptime` types. + +The following is an incomplete list of some `comptime` types along with some useful methods on them. You can see more in the standard library [Metaprogramming section](../standard_library/meta). + +- `Quoted`: A token stream +- `Type`: The type of a Noir type + - `fn implements(self, constraint: TraitConstraint) -> bool` + - Returns true if `self` implements the given trait constraint +- `Expr`: A syntactically valid expression. Can be used to recur on a program's parse tree to inspect how it is structured. + - Methods: + - `fn as_function_call(self) -> Option<(Expr, [Expr])>` + - If this is a function call expression, return `(function, arguments)` + - `fn as_block(self) -> Option<[Expr]>` + - If this is a block, return each statement in the block +- `FunctionDefinition`: A function definition + - Methods: + - `fn parameters(self) -> [(Quoted, Type)]` + - Returns a slice of `(name, type)` pairs for each parameter +- `StructDefinition`: A struct definition + - Methods: + - `fn as_type(self) -> Type` + - Returns this `StructDefinition` as a `Type`. Any generics are kept as-is + - `fn generics(self) -> [Quoted]` + - Return the name of each generic on this struct + - `fn fields(self) -> [(Quoted, Type)]` + - Return the name and type of each field +- `TraitConstraint`: A trait constraint such as `From` +- `TypedExpr`: A type-checked expression. +- `UnresolvedType`: A syntactic notation that refers to a Noir type that hasn't been resolved yet + +There are many more functions available by exploring the `std::meta` module and its submodules. +Using these methods is the key to writing powerful metaprogramming libraries. + +### `#[use_callers_scope]` + +Since certain functions such as `Quoted::as_type`, `Expression::as_type`, or `Quoted::as_trait_constraint` will attempt +to resolve their contents in a particular scope - it can be useful to change the scope they resolve in. By default +these functions will resolve in the current function's scope which is usually the attribute function they are called in. +If you're working on a library however, this may be a completely different module or crate to the item you're trying to +use the attribute on. If you want to be able to use `Quoted::as_type` to refer to types local to the caller's scope for +example, you can annotate your attribute function with `#[use_callers_scope]`. This will ensure your attribute, and any +closures it uses, can refer to anything in the caller's scope. `#[use_callers_scope]` also works recursively. So if both +your attribute function and a helper function it calls use it, then they can both refer to the same original caller. + +--- + +## Example: Derive + +Using all of the above, we can write a `derive` macro that behaves similarly to Rust's but is not built into the language. +From the user's perspective it will look like this: + +```rust +// Example usage +#[derive(Default, Eq, Ord)] +struct MyStruct { my_field: u32 } +``` + +To implement `derive` we'll have to create a `comptime` function that accepts +a variable amount of traits. + +```rust title="derive_example" showLineNumbers +// These are needed for the unconstrained hashmap we're using to store derive functions +use crate::collections::umap::UHashMap; +use crate::hash::BuildHasherDefault; +use crate::hash::poseidon2::Poseidon2Hasher; + +// A derive function is one that given a struct definition can +// create us a quoted trait impl from it. +pub type DeriveFunction = fn(StructDefinition) -> Quoted; + +// We'll keep a global HANDLERS map to keep track of the derive handler for each trait +comptime mut global HANDLERS: UHashMap> = + UHashMap::default(); + +// Given a struct and a slice of traits to derive, create trait impls for each. +// This function is as simple as iterating over the slice, checking if we have a trait +// handler registered for the given trait, calling it, and appending the result. +#[varargs] +pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted { + let mut result = quote {}; + + for trait_to_derive in traits { + let handler = HANDLERS.get(trait_to_derive); + 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 +} +``` +> Source code: noir_stdlib/src/meta/mod.nr#L33-L66 + + +Registering a derive function could be done as follows: + +```rust title="derive_via" showLineNumbers +// To register a handler for a trait, just add it to our handlers map +pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { + HANDLERS.insert(t, f); +} +``` +> Source code: noir_stdlib/src/meta/mod.nr#L68-L75 + + +```rust title="big-derive-usage-example" showLineNumbers +// Finally, to register a handler we call the above function as an annotation + // with our handler function. + #[derive_via(derive_do_nothing)] + trait DoNothing { + fn do_nothing(self); + } + + comptime fn derive_do_nothing(s: StructDefinition) -> Quoted { + // This is simplified since we don't handle generics or where clauses! + // In a real example we'd likely also need to introduce each of + // `s.generics()` as well as a trait constraint for each generic + // to ensure they also implement the trait. + let typ = s.as_type(); + quote { + impl DoNothing for $typ { + fn do_nothing(self) { + // Traits can't tell us what to do + println("something"); + } + } + } + } + + // Since `DoNothing` is a simple trait which: + // 1. Only has one method + // 2. Does not have any generics on the trait itself + // We can use `std::meta::make_trait_impl` to help us out. + // This helper function will generate our impl for us along with any + // necessary where clauses and still provides a flexible interface + // for us to work on each field on the struct. + comptime fn derive_do_nothing_alt(s: StructDefinition) -> Quoted { + let trait_name = quote { DoNothing }; + let method_signature = quote { fn do_nothing(self) }; + + // Call `do_nothing` recursively on each field in the struct + let for_each_field = |field_name| quote { self.$field_name.do_nothing(); }; + + // Some traits like Eq want to join each field expression with something like `&`. + // We don't need that here + let join_fields_with = quote {}; + + // The body function is a spot to insert any extra setup/teardown needed. + // We'll insert our println here. Since we recur on each field, we should see + // one println for the struct itself, followed by a println for every field (recursively). + let body = |body| quote { + println("something"); + $body + }; + crate::meta::make_trait_impl( + s, + trait_name, + method_signature, + for_each_field, + join_fields_with, + body, + ) + } +``` +> Source code: noir_stdlib/src/meta/mod.nr#L202-L260 + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/control_flow.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/control_flow.md new file mode 100644 index 000000000000..57816c38c575 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/control_flow.md @@ -0,0 +1,111 @@ +--- +title: Control Flow +description: + Learn how to use loops and if expressions in the Noir programming language. Discover the syntax + and examples for for loops and if-else statements. +keywords: [Noir programming language, loops, for loop, if-else statements, Rust syntax] +sidebar_position: 2 +--- + +## If Expressions + +Noir supports `if-else` statements. The syntax is most similar to Rust's where it is not required +for the statement's conditional to be surrounded by parentheses. + +```rust +let a = 0; +let mut x: u32 = 0; + +if a == 0 { + if a != 0 { + x = 6; + } else { + x = 2; + } +} else { + x = 5; + assert(x == 5); +} +assert(x == 2); +``` + +## For loops + +`for` loops allow you to repeat a block of code multiple times. + +The following block of code between the braces is run 10 times. + +```rust +for i in 0..10 { + // do something +} +``` + +Alternatively, `start..=end` can be used for a range that is inclusive on both ends. + +The index for loops is of type `u64`. + +### Break and Continue + +In unconstrained code, `break` and `continue` are also allowed in `for` and `loop` loops. These are only allowed +in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations +a loop may have. `break` and `continue` can be used like so: + +```rust +for i in 0 .. 10 { + println("Iteration start") + + if i == 2 { + continue; + } + + if i == 5 { + break; + } + + println(i); +} +println("Loop end") +``` + +When used, `break` will end the current loop early and jump to the statement after the for loop. In the example +above, the `break` will stop the loop and jump to the `println("Loop end")`. + +`continue` will stop the current iteration of the loop, and jump to the start of the next iteration. In the example +above, `continue` will jump to `println("Iteration start")` when used. Note that the loop continues as normal after this. +The iteration variable `i` is still increased by one as normal when `continue` is used. + +`break` and `continue` cannot currently be used to jump out of more than a single loop at a time. + +## Loops + +In unconstrained code, `loop` is allowed for loops that end with a `break`. +A `loop` must contain at least one `break` statement that is reachable during execution. +This is only allowed in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations +a loop may have. + +```rust +let mut i = 10; +loop { + println(i); + i -= 1; + + if i == 0 { + break; + } +} +``` + +## While loops + +In unconstrained code, `while` is allowed for loops that end when a given condition is met. +This is only allowed in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations +a loop may have. + +```rust +let mut i = 0 +while i < 10 { + println(i); + i += 2; +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_bus.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_bus.mdx new file mode 100644 index 000000000000..e55e58622ce8 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_bus.mdx @@ -0,0 +1,23 @@ +--- +title: Data Bus +sidebar_position: 13 +--- +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +The data bus is an optimization that the backend can use to make recursion more efficient. +In order to use it, you must define some inputs of the program entry points (usually the `main()` +function) with the `call_data` modifier, and the return values with the `return_data` modifier. +These modifiers are incompatible with `pub` and `mut` modifiers. + +## Example + +```rust +fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { + let a = z[x]; + a+y +} +``` + +As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/_category_.json new file mode 100644 index 000000000000..5d694210bbf3 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/arrays.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/arrays.md new file mode 100644 index 000000000000..289145a8c4d1 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/arrays.md @@ -0,0 +1,276 @@ +--- +title: Arrays +description: + Dive into the Array data type in Noir. Grasp its methods, practical examples, and best practices for efficiently using Arrays in your Noir code. +keywords: + [ + noir, + array type, + methods, + examples, + indexing, + ] +sidebar_position: 4 +--- + +An array is one way of grouping together values into one compound type. Array types can be inferred +or explicitly specified via the syntax `[; ]`: + +```rust +fn main(x : Field, y : Field) { + let my_arr = [x, y]; + let your_arr: [Field; 2] = [x, y]; +} +``` + +Here, both `my_arr` and `your_arr` are instantiated as an array containing two `Field` elements. + +Array elements can be accessed using indexing: + +```rust +fn main() { + let a = [1, 2, 3, 4, 5]; + + let first = a[0]; + let second = a[1]; +} +``` + +All elements in an array must be of the same type (i.e. homogeneous). That is, an array cannot group +a `Field` value and a `u8` value together for example. + +You can write mutable arrays, like: + +```rust +fn main() { + let mut arr = [1, 2, 3, 4, 5]; + assert(arr[0] == 1); + + arr[0] = 42; + assert(arr[0] == 42); +} +``` + +You can instantiate a new array of a fixed size with the same value repeated for each element. The following example instantiates an array of length 32 where each element is of type Field and has the value 0. + +```rust +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: + +```rust +let array: [Field; 32] = [0; 32]; +let sl = array.as_slice() +``` + +You can define multidimensional arrays: + +```rust +let array : [[Field; 2]; 2]; +let element = array[0][0]; +``` + +However, multidimensional slices are not supported. For example, the following code will error at compile time: + +```rust +let slice : [[Field]] = &[]; +``` + +## Types + +You can create arrays of primitive types or structs. There is not yet support for nested arrays +(arrays of arrays) or arrays of structs that contain arrays. + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for arrays. +Each of these functions are located within the generic impl `impl [T; N] {`. +So anywhere `self` appears, it refers to the variable `self: [T; N]`. + +### len + +Returns the length of an array + +```rust +fn len(self) -> Field +``` + +example + +```rust +fn main() { + let array = [42, 42]; + assert(array.len() == 2); +} +``` + +### sort + +Returns a new sorted array. The original array remains untouched. Notice that this function will +only work for arrays of fields or integers, not for any arbitrary type. This is because the sorting +logic it uses internally is optimized specifically for these values. If you need a sort function to +sort any type, you should use the function `sort_via` described below. + +```rust +fn sort(self) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32]; + let sorted = arr.sort(); + assert(sorted == [32, 42]); +} +``` + +### sort_via + +Sorts the array with a custom comparison function. The ordering function must return true if the first argument should be sorted to be before the second argument or is equal to the second argument. + +Using this method with an operator like `<` that does not return `true` for equal values will result in an assertion failure for arrays with equal elements. + +```rust +fn sort_via(self, ordering: fn(T, T) -> bool) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32] + let sorted_ascending = arr.sort_via(|a, b| a <= b); + assert(sorted_ascending == [32, 42]); // verifies + + let sorted_descending = arr.sort_via(|a, b| a >= b); + assert(sorted_descending == [32, 42]); // does not verify +} +``` + +### map + +Applies a function to each element of the array, returning a new array containing the mapped elements. + +```rust +fn map(self, f: fn(T) -> U) -> [U; N] +``` + +example + +```rust +let a = [1, 2, 3]; +let b = a.map(|a| a * 2); // b is now [2, 4, 6] +``` + +### fold + +Applies a function to each element of the array, returning the final accumulated value. The first +parameter is the initial value. + +```rust +fn fold(self, mut accumulator: U, f: fn(U, T) -> U) -> U +``` + +This is a left fold, so the given function will be applied to the accumulator and first element of +the array, then the second, and so on. For a given call the expected result would be equivalent to: + +```rust +let a1 = [1]; +let a2 = [1, 2]; +let a3 = [1, 2, 3]; + +let f = |a, b| a - b; +a1.fold(10, f) //=> f(10, 1) +a2.fold(10, f) //=> f(f(10, 1), 2) +a3.fold(10, f) //=> f(f(f(10, 1), 2), 3) +``` + +example: + +```rust + +fn main() { + let arr = [2, 2, 2, 2, 2]; + let folded = arr.fold(0, |a, b| a + b); + assert(folded == 10); +} + +``` + +### reduce + +Same as fold, but uses the first element as the starting element. + +Requires `self` to be non-empty. + +```rust +fn reduce(self, f: fn(T, T) -> T) -> T +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let reduced = arr.reduce(|a, b| a + b); + assert(reduced == 10); +} +``` + +### all + +Returns true if all the elements satisfy the given predicate + +```rust +fn all(self, predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let all = arr.all(|a| a == 2); + assert(all); +} +``` + +### any + +Returns true if any of the elements satisfy the given predicate + +```rust +fn any(self, predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 5]; + let any = arr.any(|a| a == 5); + assert(any); +} +``` + +### as_str_unchecked + +Converts a byte array of type `[u8; N]` to a string. Note that this performs no UTF-8 validation - +the given array is interpreted as-is as a string. + +```rust +impl [u8; N] { + pub fn as_str_unchecked(self) -> str +} +``` + +example: + +```rust +fn main() { + let hi = [104, 105].as_str_unchecked(); + assert_eq(hi, "hi"); +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/booleans.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/booleans.md new file mode 100644 index 000000000000..2507af710e71 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/booleans.md @@ -0,0 +1,28 @@ +--- +title: Booleans +description: + Delve into the Boolean data type in Noir. Understand its methods, practical examples, and best practices for using Booleans in your Noir programs. +keywords: + [ + noir, + boolean type, + methods, + examples, + logical operations, + ] +sidebar_position: 2 +--- + + +The `bool` type in Noir has two possible values: `true` and `false`: + +```rust +fn main() { + let t = true; + let f: bool = false; +} +``` + +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. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/fields.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/fields.md new file mode 100644 index 000000000000..7f424516ab95 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/fields.md @@ -0,0 +1,260 @@ +--- +title: Fields +description: + Dive deep into the Field data type in Noir. Understand its methods, practical examples, and best practices to effectively use Fields in your Noir programs. +keywords: + [ + noir, + field type, + methods, + examples, + best practices, + ] +sidebar_position: 0 +--- + +The field type corresponds to the native field type of the proving backend. + +The size of a Noir field depends on the elliptic curve's finite field for the proving backend +adopted. For example, a field would be a 254-bit integer when paired with the default backend that +spans the Grumpkin curve. + +Fields support integer arithmetic and are often used as the default numeric type in Noir: + +```rust +fn main(x : Field, y : Field) { + let z = x + y; +} +``` + +`x`, `y` and `z` are all private fields in this example. Using the `let` keyword we defined a new +private value `z` constrained to be equal to `x + y`. + +If proving efficiency is of priority, fields should be used as a default for solving problems. +Smaller integer types (e.g. `u64`) incur extra range constraints. + +## Methods + +After declaring a Field, you can use these common methods on it: + +### to_le_bits + +Transforms the field into an array of bits, Little Endian. + +```rust title="to_le_bits" showLineNumbers +pub fn to_le_bits(self: Self) -> [u1; N] { +``` +> Source code: noir_stdlib/src/field/mod.nr#L60-L62 + + +example: + +```rust title="to_le_bits_example" showLineNumbers +fn test_to_le_bits() { + let field = 2; + let bits: [u1; 8] = field.to_le_bits(); + assert_eq(bits, [0, 1, 0, 0, 0, 0, 0, 0]); + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L356-L362 + + + +### to_be_bits + +Transforms the field into an array of bits, Big Endian. + +```rust title="to_be_bits" showLineNumbers +pub fn to_be_bits(self: Self) -> [u1; N] { +``` +> Source code: noir_stdlib/src/field/mod.nr#L92-L94 + + +example: + +```rust title="to_be_bits_example" showLineNumbers +fn test_to_be_bits() { + let field = 2; + let bits: [u1; 8] = field.to_be_bits(); + assert_eq(bits, [0, 0, 0, 0, 0, 0, 1, 0]); + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L347-L353 + + + +### to_le_bytes + +Transforms into an array of bytes, Little Endian + +```rust title="to_le_bytes" showLineNumbers +pub fn to_le_bytes(self: Self) -> [u8; N] { +``` +> Source code: noir_stdlib/src/field/mod.nr#L124-L126 + + +example: + +```rust title="to_le_bytes_example" showLineNumbers +fn test_to_le_bytes() { + let field = 2; + let bytes: [u8; 8] = field.to_le_bytes(); + assert_eq(bytes, [2, 0, 0, 0, 0, 0, 0, 0]); + assert_eq(Field::from_le_bytes::<8>(bytes), field); + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L375-L382 + + +### to_be_bytes + +Transforms into an array of bytes, Big Endian + +```rust title="to_be_bytes" showLineNumbers +pub fn to_be_bytes(self: Self) -> [u8; N] { +``` +> Source code: noir_stdlib/src/field/mod.nr#L161-L163 + + +example: + +```rust title="to_be_bytes_example" showLineNumbers +fn test_to_be_bytes() { + let field = 2; + let bytes: [u8; 8] = field.to_be_bytes(); + assert_eq(bytes, [0, 0, 0, 0, 0, 0, 0, 2]); + assert_eq(Field::from_be_bytes::<8>(bytes), field); + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L365-L372 + + + +### to_le_radix + +Decomposes into an array over the specified base, Little Endian + +```rust title="to_le_radix" showLineNumbers +pub fn to_le_radix(self: Self, radix: u32) -> [u8; N] { + // Brillig does not need an immediate radix + if !crate::runtime::is_unconstrained() { + static_assert(1 < radix, "radix must be greater than 1"); + static_assert(radix <= 256, "radix must be less than or equal to 256"); + static_assert(radix & (radix - 1) == 0, "radix must be a power of 2"); + } + self.__to_le_radix(radix) + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L189-L199 + + + +example: + +```rust title="to_le_radix_example" showLineNumbers +fn test_to_le_radix() { + // 259, in base 256, little endian, is [3, 1]. + // i.e. 3 * 256^0 + 1 * 256^1 + let field = 259; + + // The radix (in this example, 256) must be a power of 2. + // The length of the returned byte array can be specified to be + // >= the amount of space needed. + let bytes: [u8; 8] = field.to_le_radix(256); + assert_eq(bytes, [3, 1, 0, 0, 0, 0, 0, 0]); + assert_eq(Field::from_le_bytes::<8>(bytes), field); + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L401-L414 + + + +### to_be_radix + +Decomposes into an array over the specified base, Big Endian + +```rust title="to_be_radix" showLineNumbers +pub fn to_be_radix(self: Self, radix: u32) -> [u8; N] { + // Brillig does not need an immediate radix + if !crate::runtime::is_unconstrained() { + crate::assert_constant(radix); + } + self.__to_be_radix(radix) + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L201-L209 + + +example: + +```rust title="to_be_radix_example" showLineNumbers +fn test_to_be_radix() { + // 259, in base 256, big endian, is [1, 3]. + // i.e. 3 * 256^0 + 1 * 256^1 + let field = 259; + + // The radix (in this example, 256) must be a power of 2. + // The length of the returned byte array can be specified to be + // >= the amount of space needed. + let bytes: [u8; 8] = field.to_be_radix(256); + assert_eq(bytes, [0, 0, 0, 0, 0, 0, 1, 3]); + assert_eq(Field::from_be_bytes::<8>(bytes), field); + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L385-L398 + + + +### pow_32 + +Returns the value to the power of the specified exponent + +```rust +fn pow_32(self, exponent: Field) -> Field +``` + +example: + +```rust +fn main() { + let field = 2 + let pow = field.pow_32(4); + assert(pow == 16); +} +``` + +### assert_max_bit_size + +Adds a constraint to specify that the field can be represented with `bit_size` number of bits + +```rust title="assert_max_bit_size" showLineNumbers +pub fn assert_max_bit_size(self) { +``` +> Source code: noir_stdlib/src/field/mod.nr#L10-L12 + + +example: + +```rust +fn main() { + let field = 2 + field.assert_max_bit_size::<32>(); +} +``` + +### sgn0 + +Parity of (prime) Field element, i.e. sgn0(x mod p) = 0 if x ∈ \{0, ..., p-1\} is even, otherwise sgn0(x mod p) = 1. + +```rust +fn sgn0(self) -> u1 +``` + + +### lt + +Returns true if the field is less than the other field + +```rust +pub fn lt(self, another: Field) -> bool +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/function_types.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/function_types.md new file mode 100644 index 000000000000..f6121af17e24 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/function_types.md @@ -0,0 +1,26 @@ +--- +title: Function types +sidebar_position: 10 +--- + +Noir supports higher-order functions. The syntax for a function type is as follows: + +```rust +fn(arg1_type, arg2_type, ...) -> return_type +``` + +Example: + +```rust +fn assert_returns_100(f: fn() -> Field) { // f takes no args and returns a Field + assert(f() == 100); +} + +fn main() { + assert_returns_100(|| 100); // ok + assert_returns_100(|| 150); // fails +} +``` + +A function type also has an optional capture environment - this is necessary to support closures. +See [Lambdas](../lambdas.md) for more details. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/index.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/index.md new file mode 100644 index 000000000000..0f2db2b2d753 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/index.md @@ -0,0 +1,126 @@ +--- +title: Data Types +description: + Get a clear understanding of the two categories of Noir data types - primitive types and compound + types. Learn about their characteristics, differences, and how to use them in your Noir + programming. +keywords: + [ + noir, + data types, + primitive types, + compound types, + private types, + public types, + ] +--- + +Every value in Noir has a type, which determines which operations are valid for it. + +All values in Noir are fundamentally composed of `Field` elements. For a more approachable +developing experience, abstractions are added on top to introduce different data types in Noir. + +Noir has two category of data types: primitive types (e.g. `Field`, integers, `bool`) and compound +types that group primitive types (e.g. arrays, tuples, structs). Each value can either be private or +public. + +## Private & Public Types + +A **private value** is known only to the Prover, while a **public value** is known by both the +Prover and Verifier. Mark values as `private` when the value should only be known to the prover. All +primitive types (including individual fields of compound types) in Noir are private by default, and +can be marked public when certain values are intended to be revealed to the Verifier. + +> **Note:** For public values defined in Noir programs paired with smart contract verifiers, once +> the proofs are verified on-chain the values can be considered known to everyone that has access to +> that blockchain. + +Public data types are treated no differently to private types apart from the fact that their values +will be revealed in proofs generated. Simply changing the value of a public type will not change the +circuit (where the same goes for changing values of private types as well). + +_Private values_ are also referred to as _witnesses_ sometimes. + +> **Note:** The terms private and public when applied to a type (e.g. `pub Field`) have a different +> meaning than when applied to a function (e.g. `pub fn foo() {}`). +> +> The former is a visibility modifier for the Prover to interpret if a value should be made known to +> the Verifier, while the latter is a visibility modifier for the compiler to interpret if a +> function should be made accessible to external Noir programs like in other languages. + +### pub Modifier + +All data types in Noir are private by default. Types are explicitly declared as public using the +`pub` modifier: + +```rust +fn main(x : Field, y : pub Field) -> pub Field { + x + y +} +``` + +In this example, `x` is **private** while `y` and `x + y` (the return value) are **public**. Note +that visibility is handled **per variable**, so it is perfectly valid to have one input that is +private and another that is public. + +> **Note:** Public types can only be declared through parameters on `main`. + +## Type Aliases + +A type alias is a new name for an existing type. Type aliases are declared with the keyword `type`: + +```rust +type Id = u8; + +fn main() { + let id: Id = 1; + let zero: u8 = 0; + assert(zero + 1 == id); +} +``` + +Type aliases can also be used with [generics](../generics.md): + +```rust +type Id = Size; + +fn main() { + let id: Id = 1; + let zero: u32 = 0; + assert(zero + 1 == id); +} +``` + +Type aliases can even refer to other aliases. An error will be issued if they form a cycle: + +```rust +// Ok! +type A = B; +type B = Field; + +type Bad1 = Bad2; + +// error: Dependency cycle found +type Bad2 = Bad1; +// ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2 +``` + +By default, like functions, type aliases are private to the module they exist in. You can use `pub` +to make the type alias public or `pub(crate)` to make it public to just its crate: + +```rust +// This type alias is now public +pub type Id = u8; +``` + +## Wildcard Type +Noir can usually infer the type of the variable from the context, so specifying the type of a variable is only required when it cannot be inferred. However, specifying a complex type can be tedious, especially when it has multiple generic arguments. Often some of the generic types can be inferred from the context, and Noir only needs a hint to properly infer the other types. We can partially specify a variable's type by using `_` as a marker, indicating where we still want the compiler to infer the type. + +```rust +let a: [_; 4] = foo(b); +``` + + +### BigInt + +You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/integers.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/integers.md new file mode 100644 index 000000000000..b8a5d4980296 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/integers.md @@ -0,0 +1,178 @@ +--- +title: Integers +description: Explore the Integer data type in Noir. Learn about its methods, see real-world examples, and grasp how to efficiently use Integers in your Noir code. +keywords: [noir, integer types, methods, examples, arithmetic] +sidebar_position: 1 +--- + +An integer type is a range constrained field type. +The Noir frontend supports both unsigned and signed integer types. +The allowed sizes are 1, 8, 16, 32 and 64 bits. + +:::info + +When an integer is defined in Noir without a specific type, it will default to `Field`. + +The one exception is for loop indices which default to `u64` since comparisons on `Field`s are not possible. + +::: + +## Unsigned Integers + +An unsigned integer type is specified first with the letter `u` (indicating its unsigned nature) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: u8 = 1; + let y: u8 = 1; + let z = x + y; + assert (z == 2); +} +``` + +The bit size determines the maximum value the integer type can store. For example, a `u8` variable can store a value in the range of 0 to 255 (i.e. $\\2^{8}-1\\$). + +## Signed Integers + +A signed integer type is specified first with the letter `i` (which stands for integer) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: i8 = -1; + let y: i8 = -1; + let z = x + y; + assert (z == -2); +} +``` + +The bit size determines the maximum and minimum range of value the integer type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$). + + +```rust +fn main(x: i16, y: i16) { + // modulo + let c = x % y; + let c = x % -13; +} +``` + +Modulo operation is defined for negative integers thanks to integer division, so that the equality `x = (x/y)*y + (x%y)` holds. + +## 128 bits Unsigned Integers + +The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind: +- You cannot cast between a native integer and `U128` +- There is a higher performance cost when using `U128`, compared to a native type. + +Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. + +```rust +fn main() { + let x = U128::from_integer(23); + let y = U128::from_hex("0x7"); + let z = x + y; + assert(z.to_integer() == 30); +} +``` + +`U128` is implemented with two 64 bits limbs, representing the low and high bits, which explains the performance cost. You should expect `U128` to be twice more costly for addition and four times more costly for multiplication. +You can construct a U128 from its limbs: +```rust +fn main(x: u64, y: u64) { + let z = U128::from_u64s_be(x,y); + assert(z.hi == x as Field); + assert(z.lo == y as Field); +} +``` + +Note that the limbs are stored as Field elements in order to avoid unnecessary conversions. +Apart from this, most operations will work as usual: + +```rust +fn main(x: U128, y: U128) { + // multiplication + let c = x * y; + // addition and subtraction + let c = c - x + y; + // division + let c = x / y; + // bit operation; + let c = x & y | y; + // bit shift + let c = x << y; + // comparisons; + let c = x < y; + let c = x == y; +} +``` + +## Overflows + +Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: + +```rust +fn main(x: u8, y: u8) -> pub u8 { + let z = x + y; + z +} +``` + +With: + +```toml +x = "255" +y = "1" +``` + +Would result in: + +``` +$ nargo execute +error: Assertion failed: 'attempt to add with overflow' +┌─ ~/src/main.nr:9:13 +│ +│ let z = x + y; +│ ----- +│ += Call stack: + ... +``` + +A similar error would happen with signed integers: + +```rust +fn main() -> i8 { + let x: i8 = -118; + let y: i8 = -11; + let z = x + y; + z +} +``` + +Note that if a computation ends up being unused the compiler might remove it and it won't end up producing an overflow: + +```rust +fn main() { + // "255 + 1" would overflow, but `z` is unused so no computation happens + let z: u8 = 255 + 1; +} +``` + +### Wrapping methods + +Although integer overflow is expected to error, some use-cases rely on wrapping. For these use-cases, the standard library provides `wrapping` variants of certain common operations: + +```rust +fn wrapping_add(x: T, y: T) -> T; +fn wrapping_sub(x: T, y: T) -> T; +fn wrapping_mul(x: T, y: T) -> T; +``` + +Example of how it is used: + +```rust + +fn main(x: u8, y: u8) -> pub u8 { + std::wrapping_add(x, y) +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/references.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/references.md new file mode 100644 index 000000000000..a5293d11cfb9 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/references.md @@ -0,0 +1,23 @@ +--- +title: References +sidebar_position: 9 +--- + +Noir supports first-class references. References are a bit like pointers: they point to a specific address that can be followed to access the data stored at that address. You can use Rust-like syntax to use pointers in Noir: the `&` operator references the variable, the `*` operator dereferences it. + +Example: + +```rust +fn main() { + let mut x = 2; + + // you can reference x as &mut and pass it to multiplyBy2 + multiplyBy2(&mut x); +} + +// you can access &mut here +fn multiplyBy2(x: &mut Field) { + // and dereference it with * + *x = *x * 2; +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/slices.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/slices.mdx new file mode 100644 index 000000000000..e8091c62dd86 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/slices.mdx @@ -0,0 +1,358 @@ +--- +title: Slices +description: Explore the Slice data type in Noir. Understand its methods, see real-world examples, and learn how to effectively use Slices in your Noir programs. +keywords: [noir, slice type, methods, examples, subarrays] +sidebar_position: 5 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A slice is a dynamically-sized view into a sequence of elements. They can be resized at runtime, but because they don't own the data, they cannot be returned from a circuit. You can treat slices as arrays without a constrained size. + +```rust +fn main() -> pub u32 { + let mut slice: [Field] = &[0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +To write a slice literal, use a preceding ampersand as in: `&[0; 2]` or +`&[1, 2, 3]`. + +It is important to note that slices are not references to arrays. In Noir, +`&[..]` is more similar to an immutable, growable vector. + +View the corresponding test file [here][test-file]. + +[test-file]: https://github.com/noir-lang/noir/blob/f387ec1475129732f72ba294877efdf6857135ac/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for slices: + +### push_back + +Pushes a new element to the end of the slice, returning a new slice with a length one greater than the original unmodified slice. + +```rust +fn push_back(_self: [T], _elem: T) -> [T] +``` + +example: + +```rust +fn main() -> pub Field { + let mut slice: [Field] = &[0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +View the corresponding test file [here][test-file]. + +### push_front + +Returns a new slice with the specified element inserted at index 0. The existing elements indexes are incremented by 1. + +```rust +fn push_front(_self: Self, _elem: T) -> Self +``` + +Example: + +```rust +let mut new_slice: [Field] = &[]; +new_slice = new_slice.push_front(20); +assert(new_slice[0] == 20); // returns true +``` + +View the corresponding test file [here][test-file]. + +### pop_front + +Returns a tuple of two items, the first element of the slice and the rest of the slice. + +```rust +fn pop_front(_self: Self) -> (T, Self) +``` + +Example: + +```rust +let (first_elem, rest_of_slice) = slice.pop_front(); +``` + +View the corresponding test file [here][test-file]. + +### pop_back + +Returns a tuple of two items, the beginning of the slice with the last element omitted and the last element. + +```rust +fn pop_back(_self: Self) -> (Self, T) +``` + +Example: + +```rust +let (popped_slice, last_elem) = slice.pop_back(); +``` + +View the corresponding test file [here][test-file]. + +### append + +Loops over a slice and adds it to the end of another. + +```rust +fn append(mut self, other: Self) -> Self +``` + +Example: + +```rust +let append = &[1, 2].append(&[3, 4, 5]); +``` + +### insert + +Inserts an element at a specified index and shifts all following elements by 1. + +```rust +fn insert(_self: Self, _index: Field, _elem: T) -> Self +``` + +Example: + +```rust +new_slice = rest_of_slice.insert(2, 100); +assert(new_slice[2] == 100); +``` + +View the corresponding test file [here][test-file]. + +### remove + +Remove an element at a specified index, shifting all elements after it to the left, returning the altered slice and the removed element. + +```rust +fn remove(_self: Self, _index: Field) -> (Self, T) +``` + +Example: + +```rust +let (remove_slice, removed_elem) = slice.remove(3); +``` + +### len + +Returns the length of a slice + +```rust +fn len(self) -> Field +``` + +Example: + +```rust +fn main() { + let slice = &[42, 42]; + assert(slice.len() == 2); +} +``` + +### as_array + +Converts this slice into an array. + +Make sure to specify the size of the resulting array. +Panics if the resulting array length is different than the slice's length. + +```rust +fn as_array(self) -> [T; N] +``` + +Example: + +```rust +fn main() { + let slice = &[5, 6]; + + // Always specify the length of the resulting array! + let array: [Field; 2] = slice.as_array(); + + assert(array[0] == slice[0]); + assert(array[1] == slice[1]); +} +``` + +### map + +Applies a function to each element of the slice, returning a new slice containing the mapped elements. + +```rust +fn map(self, f: fn[Env](T) -> U) -> [U] +``` + +example + +```rust +let a = &[1, 2, 3]; +let b = a.map(|a| a * 2); // b is now &[2, 4, 6] +``` + +### fold + +Applies a function to each element of the slice, returning the final accumulated value. The first +parameter is the initial value. + +```rust +fn fold(self, mut accumulator: U, f: fn[Env](U, T) -> U) -> U +``` + +This is a left fold, so the given function will be applied to the accumulator and first element of +the slice, then the second, and so on. For a given call the expected result would be equivalent to: + +```rust +let a1 = &[1]; +let a2 = &[1, 2]; +let a3 = &[1, 2, 3]; + +let f = |a, b| a - b; +a1.fold(10, f) //=> f(10, 1) +a2.fold(10, f) //=> f(f(10, 1), 2) +a3.fold(10, f) //=> f(f(f(10, 1), 2), 3) +``` + +example: + +```rust + +fn main() { + let slice = &[2, 2, 2, 2, 2]; + let folded = slice.fold(0, |a, b| a + b); + assert(folded == 10); +} + +``` + +### reduce + +Same as fold, but uses the first element as the starting element. + +```rust +fn reduce(self, f: fn[Env](T, T) -> T) -> T +``` + +example: + +```rust +fn main() { + let slice = &[2, 2, 2, 2, 2]; + let reduced = slice.reduce(|a, b| a + b); + assert(reduced == 10); +} +``` + +### filter + +Returns a new slice containing only elements for which the given predicate returns true. + +```rust +fn filter(self, f: fn[Env](T) -> bool) -> Self +``` + +example: + +```rust +fn main() { + let slice = &[1, 2, 3, 4, 5]; + let odds = slice.filter(|x| x % 2 == 1); + assert_eq(odds, &[1, 3, 5]); +} +``` + +### join + +Flatten each element in the slice into one value, separated by `separator`. + +Note that although slices implement `Append`, `join` cannot be used on slice +elements since nested slices are prohibited. + +```rust +fn join(self, separator: T) -> T where T: Append +``` + +example: + +```rust +struct Accumulator { + total: Field, +} + +// "Append" two accumulators by adding them +impl Append for Accumulator { + fn empty() -> Self { + Self { total: 0 } + } + + fn append(self, other: Self) -> Self { + Self { total: self.total + other.total } + } +} + +fn main() { + let slice = &[1, 2, 3, 4, 5].map(|total| Accumulator { total }); + + let result = slice.join(Accumulator::empty()); + assert_eq(result, Accumulator { total: 15 }); + + // We can use a non-empty separator to insert additional elements to sum: + let separator = Accumulator { total: 10 }; + let result = slice.join(separator); + assert_eq(result, Accumulator { total: 55 }); +} +``` + +### all + +Returns true if all the elements satisfy the given predicate + +```rust +fn all(self, predicate: fn[Env](T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let slice = &[2, 2, 2, 2, 2]; + let all = slice.all(|a| a == 2); + assert(all); +} +``` + +### any + +Returns true if any of the elements satisfy the given predicate + +```rust +fn any(self, predicate: fn[Env](T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let slice = &[2, 2, 2, 2, 5]; + let any = slice.any(|a| a == 5); + assert(any); +} + +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/strings.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/strings.md new file mode 100644 index 000000000000..b2257e8bdbbb --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/strings.md @@ -0,0 +1,114 @@ +--- +title: Strings +description: + Discover the String data type in Noir. Learn about its methods, see real-world examples, and understand how to effectively manipulate and use Strings in Noir. +keywords: + [ + noir, + string type, + methods, + examples, + concatenation, + ] +sidebar_position: 3 +--- + + +The string type is a fixed length value defined with `str`. + +You can use strings in `assert()` functions or print them with +`println()`. See more about [Logging](../../standard_library/logging.md). + +```rust + +fn main(message : pub str<11>, hex_as_string : str<4>) { + println(message); + assert(message == "hello world"); + assert(hex_as_string == "0x41"); +} +``` + +You can convert a `str` to a byte array by calling `as_bytes()` +or a vector by calling `as_bytes_vec()`. + +```rust +fn main() { + let message = "hello world"; + let message_bytes = message.as_bytes(); + let mut message_vec = message.as_bytes_vec(); + assert(message_bytes.len() == 11); + assert(message_bytes[0] == 104); + assert(message_bytes[0] == message_vec.get(0)); +} +``` + +## Escape characters + +You can use escape characters for your strings: + +| Escape Sequence | Description | +|-----------------|-----------------| +| `\r` | Carriage Return | +| `\n` | Newline | +| `\t` | Tab | +| `\0` | Null Character | +| `\"` | Double Quote | +| `\\` | Backslash | + +Example: + +```rust +let s = "Hello \"world" // prints "Hello "world" +let s = "hey \tyou"; // prints "hey you" +``` + +## Raw strings + +A raw string begins with the letter `r` and is optionally delimited by a number of hashes `#`. + +Escape characters are *not* processed within raw strings. All contents are interpreted literally. + +Example: + +```rust +let s = r"Hello world"; +let s = r#"Simon says "hello world""#; + +// Any number of hashes may be used (>= 1) as long as the string also terminates with the same number of hashes +let s = r#####"One "#, Two "##, Three "###, Four "####, Five will end the string."#####; +``` + +## Format strings + +A format string begins with the letter `f` and allows inserting the value of local and global variables in it. + +Example: + +```rust +let four = 2 + 2; +let s = f"Two plus two is: {four}"; +println(s); +``` + +The output of the above program is: + +```text +Two plus two is: 4 +``` + +To insert the value of a local or global variable, put it inside `{...}` in the string. + +If you need to write the `{` or `}` characters, use `{{` and `}}` respectively: + +```rust +let four = 2 + 2; + +// Prints "This is not expanded: {four}" +println(f"This is not expanded: {{four}}"); +``` + +More complex expressions are not allowed inside `{...}`: + +```rust +let s = f"Two plus two is: {2 + 2}" // Error: invalid format string +``` \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/structs.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/structs.md new file mode 100644 index 000000000000..4b0a8001226d --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/structs.md @@ -0,0 +1,96 @@ +--- +title: Structs +description: + Explore the Struct data type in Noir. Learn about its methods, see real-world examples, and grasp how to effectively define and use Structs in your Noir programs. +keywords: + [ + noir, + struct type, + methods, + examples, + data structures, + ] +sidebar_position: 8 +--- + +A struct also allows for grouping multiple values of different types. Unlike tuples, we can also +name each field. + +> **Note:** The usage of _field_ here refers to each element of the struct and is unrelated to the +> field type of Noir. + +Defining a struct requires giving it a name and listing each field within as `: ` pairs: + +```rust +struct Animal { + hands: Field, + legs: Field, + eyes: u8, +} +``` + +An instance of a struct can then be created with actual values in `: ` pairs in any +order. Struct fields are accessible using their given names: + +```rust +fn main() { + let legs = 4; + + let dog = Animal { + eyes: 2, + hands: 0, + legs, + }; + + let zero = dog.hands; +} +``` + +Structs can also be destructured in a pattern, binding each field to a new variable: + +```rust +fn main() { + let Animal { hands, legs: feet, eyes } = get_octopus(); + + let ten = hands + feet + eyes as Field; +} + +fn get_octopus() -> Animal { + let octopus = Animal { + hands: 0, + legs: 8, + eyes: 2, + }; + + octopus +} +``` + +The new variables can be bound with names different from the original struct field names, as +showcased in the `legs --> feet` binding in the example above. + +### Visibility + +By default, like functions, structs are private to the module they exist in. You can use `pub` +to make the struct public or `pub(crate)` to make it public to just its crate: + +```rust +// This struct is now public +pub struct Animal { + hands: Field, + legs: Field, + eyes: u8, +} +``` + +The same applies to struct fields: by default they are private to the module they exist in, +but they can be made `pub` or `pub(crate)`: + +```rust +// This struct is now public +pub struct Animal { + hands: Field, // private to its module + pub(crate) legs: Field, // accessible from the entire crate + pub eyes: u8, // accessible from anywhere +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/tuples.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/tuples.md new file mode 100644 index 000000000000..2ec5c9c41135 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/tuples.md @@ -0,0 +1,48 @@ +--- +title: Tuples +description: + Dive into the Tuple data type in Noir. Understand its methods, practical examples, and best practices for efficiently using Tuples in your Noir code. +keywords: + [ + noir, + tuple type, + methods, + examples, + multi-value containers, + ] +sidebar_position: 7 +--- + +A tuple collects multiple values like an array, but with the added ability to collect values of +different types: + +```rust +fn main() { + let tup: (u8, u64, Field) = (255, 500, 1000); +} +``` + +One way to access tuple elements is via destructuring using pattern matching: + +```rust +fn main() { + let tup = (1, 2); + + let (one, two) = tup; + + let three = one + two; +} +``` + +Another way to access tuple elements is via direct member access, using a period (`.`) followed by +the index of the element we want to access. Index `0` corresponds to the first tuple element, `1` to +the second and so on: + +```rust +fn main() { + let tup = (5, 6, 7, 8); + + let five = tup.0; + let eight = tup.3; +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/functions.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/functions.md new file mode 100644 index 000000000000..f656cdfd97a1 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/functions.md @@ -0,0 +1,226 @@ +--- +title: Functions +description: + Learn how to declare functions and methods in Noir, a programming language with Rust semantics. + This guide covers parameter declaration, return types, call expressions, and more. +keywords: [Noir, Rust, functions, methods, parameter declaration, return types, call expressions] +sidebar_position: 1 +--- + +Functions in Noir follow the same semantics of Rust, though Noir does not support early returns. + +To declare a function the `fn` keyword is used. + +```rust +fn foo() {} +``` + +By default, functions are visible only within the package they are defined. To make them visible outside of that package (for example, as part of a [library](../modules_packages_crates/crates_and_packages.md#libraries)), you should mark them as `pub`: + +```rust +pub fn foo() {} +``` + +You can also restrict the visibility of the function to only the crate it was defined in, by specifying `pub(crate)`: + +```rust +pub(crate) fn foo() {} //foo can only be called within its crate +``` + +All parameters in a function must have a type and all types are known at compile time. The parameter +is pre-pended with a colon and the parameter type. Multiple parameters are separated using a comma. + +```rust +fn foo(x : Field, y : Field){} +``` + +The return type of a function can be stated by using the `->` arrow notation. The function below +states that the foo function must return a `Field`. If the function returns no value, then the arrow +is omitted. + +```rust +fn foo(x : Field, y : Field) -> Field { + x + y +} +``` + +Note that a `return` keyword is unneeded in this case - the last expression in a function's body is +returned. + +## Main function + +If you're writing a binary, the `main` function is the starting point of your program. You can pass all types of expressions to it, as long as they have a fixed size at compile time: + +```rust +fn main(x : Field) // this is fine: passing a Field +fn main(x : [Field; 2]) // this is also fine: passing a Field with known size at compile-time +fn main(x : (Field, bool)) // 👌: passing a (Field, bool) tuple means size 2 +fn main(x : str<5>) // this is fine, as long as you pass a string of size 5 + +fn main(x : Vec) // can't compile, has variable size +fn main(x : [Field]) // can't compile, has variable size +fn main(....// i think you got it by now +``` + +Keep in mind [tests](../../tooling/testing.md) don't differentiate between `main` and any other function. The following snippet passes tests, but won't compile or prove: + +```rust +fn main(x : [Field]) { + assert(x[0] == 1); +} + +#[test] +fn test_one() { + main(&[1, 2]); +} +``` + +```bash +$ nargo test +[testing] Running 1 test functions +[testing] Testing test_one... ok +[testing] All tests passed + +$ nargo check +The application panicked (crashed). +Message: Cannot have variable sized arrays as a parameter to main +``` + +## Call Expressions + +Calling a function in Noir is executed by using the function name and passing in the necessary +arguments. + +Below we show how to call the `foo` function from the `main` function using a call expression: + +```rust +fn main(x : Field, y : Field) { + let z = foo(x); +} + +fn foo(x : Field) -> Field { + x + x +} +``` + +## Methods + +You can define methods in Noir on any struct type in scope. + +```rust +struct MyStruct { + foo: Field, + bar: Field, +} + +impl MyStruct { + fn new(foo: Field) -> MyStruct { + MyStruct { + foo, + bar: 2, + } + } + + fn sum(self) -> Field { + self.foo + self.bar + } +} + +fn main() { + let s = MyStruct::new(40); + assert(s.sum() == 42); +} +``` + +Methods are just syntactic sugar for functions, so if we wanted to we could also call `sum` as +follows: + +```rust +assert(MyStruct::sum(s) == 42); +``` + +It is also possible to specialize which method is chosen depending on the [generic](./generics.md) type that is used. In this example, the `foo` function returns different values depending on its type: + +```rust +struct Foo {} + +impl Foo { + fn foo(self) -> Field { 1 } +} + +impl Foo { + fn foo(self) -> Field { 2 } +} + +fn main() { + let f1: Foo = Foo{}; + let f2: Foo = Foo{}; + assert(f1.foo() + f2.foo() == 3); +} +``` + +Also note that impls with the same method name defined in them cannot overlap. For example, if we already have `foo` defined for `Foo` and `Foo` like we do above, we cannot also define `foo` in an `impl Foo` since it would be ambiguous which version of `foo` to choose. + +```rust +// Including this impl in the same project as the above snippet would +// cause an overlapping impls error +impl Foo { + fn foo(self) -> Field { 3 } +} +``` + +## Lambdas + +Lambdas are anonymous functions. They follow the syntax of Rust - `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +See [Lambdas](./lambdas.md) for more details. + +## Attributes + +Attributes are metadata that can be applied to a function, using the following syntax: `#[attribute(value)]`. + +Supported attributes include: + +- **builtin**: the function is implemented by the compiler, for efficiency purposes. +- **deprecated**: mark the function as _deprecated_. Calling the function will generate a warning: `warning: use of deprecated function` +- **field**: Used to enable conditional compilation of code depending on the field size. See below for more details +- **oracle**: mark the function as _oracle_; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. +- **test**: mark the function as unit tests. See [Tests](../../tooling/testing.md) for more details + +### Field Attribute + +The field attribute defines which field the function is compatible for. The function is conditionally compiled, under the condition that the field attribute matches the Noir native field. +The field can be defined implicitly, by using the name of the elliptic curve usually associated to it - for instance bn254, bls12_381 - or explicitly by using the field (prime) order, in decimal or hexadecimal form. +As a result, it is possible to define multiple versions of a function with each version specialized for a different field attribute. This can be useful when a function requires different parameters depending on the underlying elliptic curve. + +Example: we define the function `foo()` three times below. Once for the default Noir bn254 curve, once for the field $\mathbb F_{23}$, which will normally never be used by Noir, and once again for the bls12_381 curve. + +```rust +#[field(bn254)] +fn foo() -> u32 { + 1 +} + +#[field(23)] +fn foo() -> u32 { + 2 +} + +// This commented code would not compile as foo would be defined twice because it is the same field as bn254 +// #[field(21888242871839275222246405745257275088548364400416034343698204186575808495617)] +// fn foo() -> u32 { +// 2 +// } + +#[field(bls12_381)] +fn foo() -> u32 { + 3 +} +``` + +If the field name is not known to Noir, it will discard the function. Field names are case insensitive. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/generics.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/generics.md new file mode 100644 index 000000000000..7b3906b682e8 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/generics.md @@ -0,0 +1,262 @@ +--- +title: Generics +description: Learn how to use Generics in Noir +keywords: [Noir, Rust, generics, functions, structs] +sidebar_position: 7 +--- + +Generics allow you to use the same functions with multiple different concrete data types. You can +read more about the concept of generics in the Rust documentation +[here](https://doc.rust-lang.org/book/ch10-01-syntax.html). + +Here is a trivial example showing the identity function that supports any type. In Rust, it is +common to refer to the most general type as `T`. We follow the same convention in Noir. + +```rust +fn id(x: T) -> T { + x +} +``` + +## Numeric Generics + +If we want to be generic over array lengths (which are type-level integers), we can use numeric +generics. Using these looks similar to using regular generics, but introducing them into scope +requires declaring them with `let MyGenericName: IntegerType`. This can be done anywhere a normal +generic is declared. Instead of types, these generics resolve to integers at compile-time. +Here's an example of a struct that is generic over the size of the array it contains internally: + +```rust +struct BigInt { + limbs: [u32; N], +} + +impl BigInt { + // `N` is in scope of all methods in the impl + fn first(first: BigInt, second: BigInt) -> Self { + assert(first.limbs != second.limbs); + first + } + + fn second(first: BigInt, second: Self) -> Self { + assert(first.limbs != second.limbs); + second + } +} +``` + +## In Structs + +Generics are useful for specifying types in structs. For example, we can specify that a field in a +struct will be of a certain generic type. In this case `value` is of type `T`. + +```rust +struct RepeatedValue { + value: T, + count: Field, +} + +impl RepeatedValue { + fn print(self) { + for _i in 0 .. self.count { + println(self.value); + } + } +} + +fn main() { + let repeated = RepeatedValue { value: "Hello!", count: 2 }; + repeated.print(); +} +``` + +The `print` function will print `Hello!` an arbitrary number of times, twice in this case. + +## Calling functions on generic parameters + +Since a generic type `T` can represent any type, how can we call functions on the underlying type? +In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" + +This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over +any type `T` that implements the `Eq` trait for equality: + +```rust +fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool + where T: Eq +{ + if (array1.len() == 0) | (array2.len() == 0) { + true + } else { + array1[0] == array2[0] + } +} + +fn main() { + assert(first_element_is_equal([1, 2, 3], [1, 5, 6])); + + // We can use first_element_is_equal for arrays of any type + // as long as we have an Eq impl for the types we pass in + let array = [MyStruct::new(), MyStruct::new()]; + assert(first_element_is_equal(array, array)); +} + +struct MyStruct { + foo: Field +} + +impl MyStruct { + fn new() -> Self { + MyStruct { foo: 0 } + } +} + +impl Eq for MyStruct { + fn eq(self, other: MyStruct) -> bool { + self.foo == other.foo + } +} +``` + +You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). + +## Manually Specifying Generics with the Turbofish Operator + +There are times when the compiler cannot reasonably infer what type should be used for a generic, or when the developer themselves may want to manually distinguish generic type parameters. This is where the `::<>` turbofish operator comes into play. + +The `::<>` operator can follow a variable or path and can be used to manually specify generic arguments within the angle brackets. +The name "turbofish" comes from that `::<>` looks like a little fish. + +Examples: +```rust +fn main() { + let mut slice = []; + slice = slice.push_back(1); + slice = slice.push_back(2); + // Without turbofish a type annotation would be needed on the left hand side + let array = slice.as_array::<2>(); +} +``` + + +```rust +trait MyTrait { + fn ten() -> Self; +} + +impl MyTrait for Field { + fn ten() -> Self { 10 } +} + +struct Foo { + inner: T +} + +impl Foo { + fn generic_method(_self: Self) -> U where U: MyTrait { + U::ten() + } +} + +fn example() { + let foo: Foo = Foo { inner: 1 }; + // Using a type other than `Field` here (e.g. u32) would fail as + // there is no matching impl for `u32: MyTrait`. + // + // Substituting the `10` on the left hand side of this assert + // with `10 as u32` would fail with a type mismatch as we + // are expecting a `Field` from the right hand side. + assert(10 == foo.generic_method::()); +} +``` + +## Arithmetic Generics + +In addition to numeric generics, Noir also allows a limited form of arithmetic on generics. +When you have a numeric generic such as `N`, you can use the following operators on it in a +type position: `+`, `-`, `*`, `/`, and `%`. + +Note that type checking arithmetic generics is a best effort guess from the compiler and there +are many cases of types that are equal that the compiler may not see as such. For example, +we know that `T * (N + M)` should be equal to `T*N + T*M` but the compiler does not currently +apply the distributive law and thus sees these as different types. + +Even with this limitation though, the compiler can handle common cases decently well: + +```rust +trait Serialize { + fn serialize(self) -> [Field; N]; +} + +impl Serialize<1> for Field { + fn serialize(self) -> [Field; 1] { + [self] + } +} + +impl Serialize for [T; N] + where T: Serialize { .. } + +impl Serialize for (T, U) + where T: Serialize, U: Serialize { .. } + +fn main() { + let data = (1, [2, 3, 4]); + assert_eq(data.serialize().len(), 4); +} +``` + +Note that if there is any over or underflow the types will fail to unify: + +```rust title="underflow-example" showLineNumbers +fn pop(array: [Field; N]) -> [Field; N - 1] { + let mut result: [Field; N - 1] = std::mem::zeroed(); + for i in 0..N - 1 { + result[i] = array[i]; + } + result +} + +fn main() { + // error: Could not determine array length `(0 - 1)` + pop([]); +} +``` +> Source code: test_programs/compile_failure/arithmetic_generics_underflow/src/main.nr#L1-L14 + + +This also applies if there is underflow in an intermediate calculation: + +```rust title="intermediate-underflow-example" showLineNumbers +fn main() { + // From main it looks like there's nothing sketchy going on + seems_fine([]); +} + +// Since `seems_fine` says it can receive and return any length N +fn seems_fine(array: [Field; N]) -> [Field; N] { + // But inside `seems_fine` we pop from the array which + // requires the length to be greater than zero. + + // error: Could not determine array length `(0 - 1)` + push_zero(pop(array)) +} + +fn pop(array: [Field; N]) -> [Field; N - 1] { + let mut result: [Field; N - 1] = std::mem::zeroed(); + for i in 0..N - 1 { + result[i] = array[i]; + } + result +} + +fn push_zero(array: [Field; N]) -> [Field; N + 1] { + let mut result: [Field; N + 1] = std::mem::zeroed(); + for i in 0..N { + result[i] = array[i]; + } + // index N is already zeroed + result +} +``` +> Source code: test_programs/compile_failure/arithmetic_generics_intermediate_underflow/src/main.nr#L1-L32 + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/globals.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/globals.md new file mode 100644 index 000000000000..c64b6c53746e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/globals.md @@ -0,0 +1,82 @@ +--- +title: Global Variables +description: + Learn about global variables in Noir. Discover how + to declare, modify, and use them in your programs. +keywords: [noir programming language, globals, global variables, constants] +sidebar_position: 8 +--- + +## Globals + + +Noir supports global variables. The global's type must be specified by the user: + +```rust +global N: Field = 5; + +global TUPLE: (Field, Field) = (3, 2); + +fn main() { + assert(N == 5); + assert(N == TUPLE.0 + TUPLE.1); +} +``` + +:::info + +Globals can be defined as any expression, so long as they don't depend on themselves - otherwise there would be a dependency cycle! For example: + +```rust +global T: u32 = foo(T); // dependency error +``` + +::: + + +If they are initialized to a literal integer, globals can be used to specify an array's length: + +```rust +global N: u32 = 2; + +fn main(y : [Field; N]) { + assert(y[0] == y[1]) +} +``` + +A global from another module can be imported or referenced externally like any other name: + +```rust +global N: Field = 20; + +fn main() { + assert(my_submodule::N != N); +} + +mod my_submodule { + global N: Field = 10; +} +``` + +When a global is used, Noir replaces the name with its definition on each occurrence. +This means globals defined using function calls will repeat the call each time they're used: + +```rust +global RESULT: [Field; 100] = foo(); + +fn foo() -> [Field; 100] { ... } +``` + +This is usually fine since Noir will generally optimize any function call that does not +refer to a program input into a constant. It should be kept in mind however, if the called +function performs side-effects like `println`, as these will still occur on each use. + +### Visibility + +By default, like functions, globals are private to the module they exist in. You can use `pub` +to make the global public or `pub(crate)` to make it public to just its crate: + +```rust +// This global is now public +pub global N: u32 = 5; +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/lambdas.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/lambdas.md new file mode 100644 index 000000000000..be3c7e0b5caa --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/lambdas.md @@ -0,0 +1,81 @@ +--- +title: Lambdas +description: Learn how to use anonymous functions in Noir programming language. +keywords: [Noir programming language, lambda, closure, function, anonymous function] +sidebar_position: 9 +--- + +## Introduction + +Lambdas are anonymous functions. The syntax is `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +A block can be used as the body of a lambda, allowing you to declare local variables inside it: + +```rust +let cool = || { + let x = 100; + let y = 100; + x + y +} + +assert(cool() == 200); +``` + +## Closures + +Inside the body of a lambda, you can use variables defined in the enclosing function. Such lambdas are called **closures**. In this example `x` is defined inside `main` and is accessed from within the lambda: + +```rust +fn main() { + let x = 100; + let closure = || x + 150; + assert(closure() == 250); +} +``` + +## Passing closures to higher-order functions + +It may catch you by surprise that the following code fails to compile: + +```rust +fn foo(f: fn () -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // error :( +} +``` + +The reason is that the closure's capture environment affects its type - we have a closure that captures two Fields and `foo` +expects a regular function as an argument - those are incompatible. +:::note + +Variables contained within the `||` are the closure's parameters, and the expression that follows it is the closure's body. The capture environment is comprised of any variables used in the closure's body that are not parameters. + +E.g. in |x| x + y, y would be a captured variable, but x would not be, since it is a parameter of the closure. + +::: +The syntax for the type of a closure is `fn[env](args) -> ret_type`, where `env` is the capture environment of the closure - +in this example that's `(Field, Field)`. + +The best solution in our case is to make `foo` generic over the environment type of its parameter, so that it can be called +with closures with any environment, as well as with regular functions: + +```rust +fn foo(f: fn[Env]() -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // compiles fine + assert(foo(|| 60) == 60); // compiles fine +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/mutability.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/mutability.md new file mode 100644 index 000000000000..fdeef6a87c53 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/mutability.md @@ -0,0 +1,121 @@ +--- +title: Mutability +description: + Learn about mutable variables in Noir. Discover how + to declare, modify, and use them in your programs. +keywords: [noir programming language, mutability in noir, mutable variables] +sidebar_position: 8 +--- + +Variables in noir can be declared mutable via the `mut` keyword. Mutable variables can be reassigned +to via an assignment expression. + +```rust +let x = 2; +x = 3; // error: x must be mutable to be assigned to + +let mut y = 3; +let y = 4; // OK +``` + +The `mut` modifier can also apply to patterns: + +```rust +let (a, mut b) = (1, 2); +a = 11; // error: a must be mutable to be assigned to +b = 12; // OK + +let mut (c, d) = (3, 4); +c = 13; // OK +d = 14; // OK + +// etc. +let MyStruct { x: mut y } = MyStruct { x: a }; +// y is now in scope +``` + +Note that mutability in noir is local and everything is passed by value, so if a called function +mutates its parameters then the parent function will keep the old value of the parameters. + +```rust +fn main() -> pub Field { + let x = 3; + helper(x); + x // x is still 3 +} + +fn helper(mut x: i32) { + x = 4; +} +``` + +## Non-local mutability + +Non-local mutability can be achieved through the mutable reference type `&mut T`: + +```rust +fn set_to_zero(x: &mut Field) { + *x = 0; +} + +fn main() { + let mut y = 42; + set_to_zero(&mut y); + assert(*y == 0); +} +``` + +When creating a mutable reference, the original variable being referred to (`y` in this +example) must also be mutable. Since mutable references are a reference type, they must +be explicitly dereferenced via `*` to retrieve the underlying value. Note that this yields +a copy of the value, so mutating this copy will not change the original value behind the +reference: + +```rust +fn main() { + let mut x = 1; + let x_ref = &mut x; + + let mut y = *x_ref; + let y_ref = &mut y; + + x = 2; + *x_ref = 3; + + y = 4; + *y_ref = 5; + + assert(x == 3); + assert(*x_ref == 3); + assert(y == 5); + assert(*y_ref == 5); +} +``` + +Note that types in Noir are actually deeply immutable so the copy that occurs when +dereferencing is only a conceptual copy - no additional constraints will occur. + +Mutable references can also be stored within structs. Note that there is also +no lifetime parameter on these unlike rust. This is because the allocated memory +always lasts the entire program - as if it were an array of one element. + +```rust +struct Foo { + x: &mut Field +} + +impl Foo { + fn incr(mut self) { + *self.x += 1; + } +} + +fn main() { + let foo = Foo { x: &mut 0 }; + foo.incr(); + assert(*foo.x == 1); +} +``` + +In general, you should avoid non-local & shared mutability unless it is needed. Sticking +to only local mutability will improve readability and potentially improve compiler optimizations as well. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/ops.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/ops.md new file mode 100644 index 000000000000..c35c36c38a90 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/ops.md @@ -0,0 +1,98 @@ +--- +title: Logical Operations +description: + Learn about the supported arithmetic and logical operations in the Noir programming language. + Discover how to perform operations on private input types, integers, and booleans. +keywords: + [ + Noir programming language, + supported operations, + arithmetic operations, + logical operations, + predicate operators, + bitwise operations, + short-circuiting, + backend, + ] +sidebar_position: 3 +--- + +# Operations + +## Table of Supported Operations + +| Operation | Description | Requirements | +| :-------- | :------------------------------------------------------------: | -------------------------------------: | +| + | Adds two private input types together | Types must be private input | +| - | Subtracts two private input types together | Types must be private input | +| \* | Multiplies two private input types together | Types must be private input | +| / | Divides two private input types together | Types must be private input | +| ^ | XOR two private input types together | Types must be integer | +| & | AND two private input types together | Types must be integer | +| \| | OR two private input types together | Types must be integer | +| \<\< | Left shift an integer by another integer amount | Types must be integer, shift must be u8 | +| >> | Right shift an integer by another integer amount | Types must be integer, shift must be u8 | +| ! | Bitwise not of a value | Type must be integer or boolean | +| \< | returns a bool if one value is less than the other | Upper bound must have a known bit size | +| \<= | returns a bool if one value is less than or equal to the other | Upper bound must have a known bit size | +| > | returns a bool if one value is more than the other | Upper bound must have a known bit size | +| >= | returns a bool if one value is more than or equal to the other | Upper bound must have a known bit size | +| == | returns a bool if one value is equal to the other | Both types must not be constants | +| != | returns a bool if one value is not equal to the other | Both types must not be constants | + +### Predicate Operators + +`<,<=, !=, == , >, >=` are known as predicate/comparison operations because they compare two values. +This differs from the operations such as `+` where the operands are used in _computation_. + +### Bitwise Operations Example + +```rust +fn main(x : Field) { + let y = x as u32; + let z = y & y; +} +``` + +`z` is implicitly constrained to be the result of `y & y`. The `&` operand is used to denote bitwise +`&`. + +> `x & x` would not compile as `x` is a `Field` and not an integer type. + +### Logical Operators + +Noir has no support for the logical operators `||` and `&&`. This is because encoding the +short-circuiting that these operators require can be inefficient for Noir's backend. Instead you can +use the bitwise operators `|` and `&` which operate identically for booleans, just without the +short-circuiting. + +```rust +let my_val = 5; + +let mut flag = 1; +if (my_val > 6) | (my_val == 0) { + flag = 0; +} +assert(flag == 1); + +if (my_val != 10) & (my_val < 50) { + flag = 0; +} +assert(flag == 0); +``` + +### Shorthand operators + +Noir shorthand operators for most of the above operators, namely `+=, -=, *=, /=, %=, &=, |=, ^=, <<=`, and `>>=`. These allow for more concise syntax. For example: + +```rust +let mut i = 0; +i = i + 1; +``` + +could be written as: + +```rust +let mut i = 0; +i += 1; +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/oracles.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/oracles.mdx new file mode 100644 index 000000000000..77a2ac1550ac --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/oracles.mdx @@ -0,0 +1,29 @@ +--- +title: Oracles +description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide. +keywords: + - Noir + - Oracles + - RPC Calls + - Unconstrained Functions + - Programming + - Blockchain +sidebar_position: 6 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation. + +Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md) + +You can declare an Oracle through the `#[oracle()]` flag. Example: + +```rust +#[oracle(get_number_sequence)] +unconstrained fn get_number_sequence(_size: Field) -> [Field] {} +``` + +The timeout for when using an external RPC oracle resolver can be set with the `NARGO_FOREIGN_CALL_TIMEOUT` environment variable. This timeout is in units of milliseconds. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/shadowing.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/shadowing.md new file mode 100644 index 000000000000..5ce6130d2011 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/shadowing.md @@ -0,0 +1,44 @@ +--- +title: Shadowing +sidebar_position: 12 +--- + +Noir allows for inheriting variables' values and re-declaring them with the same name similar to Rust, known as shadowing. + +For example, the following function is valid in Noir: + +```rust +fn main() { + let x = 5; + + { + let x = x * 2; + assert (x == 10); + } + + assert (x == 5); +} +``` + +In this example, a variable x is first defined with the value 5. + +The local scope that follows shadows the original x, i.e. creates a local mutable x based on the value of the original x. It is given a value of 2 times the original x. + +When we return to the main scope, x once again refers to just the original x, which stays at the value of 5. + +## Temporal mutability + +One way that shadowing is useful, in addition to ergonomics across scopes, is for temporarily mutating variables. + +```rust +fn main() { + let age = 30; + // age = age + 5; // Would error as `age` is immutable by default. + + let mut age = age + 5; // Temporarily mutates `age` with a new value. + + let age = age; // Locks `age`'s mutability again. + + assert (age == 35); +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/traits.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/traits.md new file mode 100644 index 000000000000..17cc04a97518 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/traits.md @@ -0,0 +1,611 @@ +--- +title: Traits +description: + Traits in Noir can be used to abstract out a common interface for functions across + several data types. +keywords: [noir programming language, traits, interfaces, generic, protocol] +sidebar_position: 14 +--- + +## Overview + +Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines +the interface of several methods contained within the trait. Types can then implement this trait by providing +implementations for these methods. For example in the program: + +```rust +struct Rectangle { + width: Field, + height: Field, +} + +impl Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +fn log_area(r: Rectangle) { + println(r.area()); +} +``` + +We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this +function to work on `Triangle`s as well?: + +```rust +struct Triangle { + width: Field, + height: Field, +} + +impl Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can +introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`: + +```rust +trait Area { + fn area(self) -> Field; +} + +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing +impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined +by the `Area` trait. + +```rust +impl Area for Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +impl Area for Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Now we have a working program that is generic over any type of Shape that is used! Others can even use this program +as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types. + +## Where Clauses + +As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements +a trait, we can add a where clause to the generic function. + +```rust +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+` +operator. Similarly, we can have multiple trait constraints by separating each with a comma: + +```rust +fn foo(elements: [T], thing: U) where + T: Default + Add + Eq, + U: Bar, +{ + let mut sum = T::default(); + + for element in elements { + sum += element; + } + + if sum == T::default() { + thing.bar(); + } +} +``` + +## Invoking trait methods + +As seen in the previous section, the `area` method was invoked on a type `T` that had a where clause `T: Area`. + +To invoke `area` on a type that directly implements the trait `Area`, the trait must be in scope (imported): + +```rust +use geometry::Rectangle; + +fn main() { + let rectangle = Rectangle { width: 1, height: 2}; + let area = rectangle.area(); // Error: the compiler doesn't know which `area` method this is +} +``` + +The above program errors because there might be multiple traits with an `area` method, all implemented +by `Rectangle`, and it's not clear which one should be used. + +To make the above program compile, the trait must be imported: + +```rust +use geometry::Rectangle; +use geometry::Area; // Bring the Area trait into scope + +fn main() { + let rectangle = Rectangle { width: 1, height: 2}; + let area = rectangle.area(); // OK: will use `area` from `geometry::Area` +} +``` + +An error will also be produced if multiple traits with an `area` method are in scope. If both traits +are needed in a file you can use the fully-qualified path to the trait: + +```rust +use geometry::Rectangle; + +fn main() { + let rectangle = Rectangle { width: 1, height: 2}; + let area = geometry::Area::area(rectangle); +} +``` + +## Generic Implementations + +You can add generics to a trait implementation by adding the generic list after the `impl` keyword: + +```rust +trait Second { + fn second(self) -> Field; +} + +impl Second for (T, Field) { + fn second(self) -> Field { + self.1 + } +} +``` + +You can also implement a trait for every type this way: + +```rust +trait Debug { + fn debug(self); +} + +impl Debug for T { + fn debug(self) { + println(self); + } +} + +fn main() { + 1.debug(); +} +``` + +### Generic Trait Implementations With Where Clauses + +Where clauses can be placed on trait implementations themselves to restrict generics in a similar way. +For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar` +will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types. +For example, here is the implementation for array equality: + +```rust +impl Eq for [T; let N: u32] where T: Eq { + // Test if two arrays have the same elements. + // Because both arrays must have length N, we know their lengths already match. + fn eq(self, other: Self) -> bool { + let mut result = true; + + for i in 0 .. self.len() { + // The T: Eq constraint is needed to call == on the array elements here + result &= self[i] == other[i]; + } + + result + } +} +``` + +Where clauses can also be placed on struct implementations. +For example, here is a method utilizing a generic type that implements the equality trait. + +```rust +struct Foo { + a: u32, + b: T, +} + +impl Foo where T: Eq { + fn eq(self, other: Self) -> bool { + (self.a == other.a) & self.b.eq(other.b) + } +} +``` + +## Generic Traits + +Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in +scope of every item within the trait. + +```rust +trait Into { + // Convert `self` to type `T` + fn into(self) -> T; +} +``` + +When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime +when referencing a generic trait (e.g. in a `where` clause). + +```rust +struct MyStruct { + array: [Field; 2], +} + +impl Into<[Field; 2]> for MyStruct { + fn into(self) -> [Field; 2] { + self.array + } +} + +fn as_array(x: T) -> [Field; 2] + where T: Into<[Field; 2]> +{ + x.into() +} + +fn main() { + let array = [1, 2]; + let my_struct = MyStruct { array }; + + assert_eq(as_array(my_struct), array); +} +``` + +### Associated Types and Constants + +Traits also support associated types and constraints which can be thought of as additional generics that are referred to by name. + +Here's an example of a trait with an associated type `Foo` and a constant `Bar`: + +```rust +trait MyTrait { + type Foo; + + let Bar: u32; +} +``` + +Now when we're implementing `MyTrait` we also have to provide values for `Foo` and `Bar`: + +```rust +impl MyTrait for Field { + type Foo = i32; + + let Bar: u32 = 11; +} +``` + +Since associated constants can also be used in a type position, its values are limited to only other +expression kinds allowed in numeric generics. + +When writing a trait constraint, you can specify all associated types and constants explicitly if +you wish: + +```rust +fn foo(x: T) where T: MyTrait { + ... +} +``` + +Or you can also elide them since there should only be one `Foo` and `Bar` for a given implementation +of `MyTrait` for a type: + +```rust +fn foo(x: T) where T: MyTrait { + ... +} +``` + +If you elide associated types, you can still refer to them via the type as trait syntax ``: + +```rust +fn foo(x: T) where + T: MyTrait, + ::Foo: Default + Eq +{ + let foo_value: ::Foo = Default::default(); + assert_eq(foo_value, foo_value); +} +``` + +## Trait Methods With No `self` + +A trait can contain any number of methods, each of which have access to the `Self` type which represents each type +that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used. +For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type +but doesn't need to take any parameters: + +```rust +trait Default { + fn default() -> Self; +} +``` + +Implementing this trait can be done similarly to any other trait: + +```rust +impl Default for Field { + fn default() -> Field { + 0 + } +} + +struct MyType {} + +impl Default for MyType { + fn default() -> Field { + MyType {} + } +} +``` + +However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`. +Instead, we'll need to refer to the function directly. This can be done either by referring to the +specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later +case, type inference determines the impl that is selected. + +```rust +let my_struct = MyStruct::default(); + +let x: Field = Default::default(); +let result = x + Default::default(); +``` + +## Default Method Implementations + +A trait can also have default implementations of its methods by giving a body to the desired functions. +Note that this body must be valid for all types that may implement the trait. As a result, the only +valid operations on `self` will be operations valid for any type or other operations on the trait itself. + +```rust +trait Numeric { + fn add(self, other: Self) -> Self; + + // Default implementation of double is (self + self) + fn double(self) -> Self { + self.add(self) + } +} +``` + +When implementing a trait with default functions, a type may choose to implement only the required functions: + +```rust +impl Numeric for Field { + fn add(self, other: Field) -> Field { + self + other + } +} +``` + +Or it may implement the optional methods as well: + +```rust +impl Numeric for u32 { + fn add(self, other: u32) -> u32 { + self + other + } + + fn double(self) -> u32 { + self * 2 + } +} +``` + +## Impl Specialization + +When implementing traits for a generic type it is possible to implement the trait for only a certain combination +of generics. This can be either as an optimization or because those specific generics are required to implement the trait. + +```rust +trait Sub { + fn sub(self, other: Self) -> Self; +} + +struct NonZero { + value: T, +} + +impl Sub for NonZero { + fn sub(self, other: Self) -> Self { + let value = self.value - other.value; + assert(value != 0); + NonZero { value } + } +} +``` + +## Overlapping Implementations + +Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous. +This means if a trait `Foo` is already implemented +by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other +type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create +any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given +method call. + +```rust +trait Trait {} + +// Previous impl defined here +impl Trait for (A, B) {} + +// error: Impl for type `(Field, Field)` overlaps with existing impl +impl Trait for (Field, Field) {} +``` + +## Trait Coherence + +Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create +impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same +program. + +The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared +in the crate the impl is in. + +In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does +not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate. +While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its +own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the +library your choices are to either submit a patch to the library or use the newtype pattern. + +### The Newtype Pattern + +The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type +that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create +impls for any trait we need on it. + +```rust +struct Wrapper { + foo: some_library::Foo, +} + +impl Default for Wrapper { + fn default() -> Wrapper { + Wrapper { + foo: some_library::Foo::new(), + } + } +} +``` + +Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated +to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and +unwrapping of values when converting to and from the `Wrapper` and `Foo` types. + +### Trait Inheritance + +Sometimes, you might need one trait to use another trait’s functionality (like "inheritance" in some other languages). In this case, you can specify this relationship by listing any child traits after the parent trait's name and a colon. Now, whenever the parent trait is implemented it will require the child traits to be implemented as well. A parent trait is also called a "super trait." + +```rust +trait Person { + fn name(self) -> String; +} + +// Person is a supertrait of Student. +// Implementing Student requires you to also impl Person. +trait Student: Person { + fn university(self) -> String; +} + +trait Programmer { + fn fav_language(self) -> String; +} + +// CompSciStudent (computer science student) is a subtrait of both Programmer +// and Student. Implementing CompSciStudent requires you to impl both supertraits. +trait CompSciStudent: Programmer + Student { + fn git_username(self) -> String; +} +``` + +### Trait Aliases + +Similar to the proposed Rust feature for [trait aliases](https://github.com/rust-lang/rust/blob/4d215e2426d52ca8d1af166d5f6b5e172afbff67/src/doc/unstable-book/src/language-features/trait-alias.md), +Noir supports aliasing one or more traits and using those aliases wherever +traits would normally be used. + +```rust +trait Foo { + fn foo(self) -> Self; +} + +trait Bar { + fn bar(self) -> Self; +} + +// Equivalent to: +// trait Baz: Foo + Bar {} +// +// impl Baz for T where T: Foo + Bar {} +trait Baz = Foo + Bar; + +// We can use `Baz` to refer to `Foo + Bar` +fn baz(x: T) -> T where T: Baz { + x.foo().bar() +} +``` + +#### Generic Trait Aliases + +Trait aliases can also be generic by placing the generic arguments after the +trait name. These generics are in scope of every item within the trait alias. + +```rust +trait Foo { + fn foo(self) -> Self; +} + +trait Bar { + fn bar(self) -> T; +} + +// Equivalent to: +// trait Baz: Foo + Bar {} +// +// impl Baz for U where U: Foo + Bar {} +trait Baz = Foo + Bar; +``` + +#### Trait Alias Where Clauses + +Trait aliases support where clauses to add trait constraints to any of their +generic arguments, e.g. ensuring `T: Baz` for a trait alias `Qux`. + +```rust +trait Foo { + fn foo(self) -> Self; +} + +trait Bar { + fn bar(self) -> T; +} + +trait Baz { + fn baz(self) -> bool; +} + +// Equivalent to: +// trait Qux: Foo + Bar where T: Baz {} +// +// impl Qux for U where +// U: Foo + Bar, +// T: Baz, +// {} +trait Qux = Foo + Bar where T: Baz; +``` + +Note that while trait aliases support where clauses, +the equivalent traits can fail due to [#6467](https://github.com/noir-lang/noir/issues/6467) + +### Visibility + +By default, like functions, traits and trait aliases are private to the module +they exist in. You can use `pub` to make the trait public or `pub(crate)` to make +it public to just its crate: + +```rust +// This trait is now public +pub trait Trait {} + +// This trait alias is now public +pub trait Baz = Foo + Bar; +``` + +Trait methods have the same visibility as the trait they are in. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/unconstrained.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/unconstrained.md new file mode 100644 index 000000000000..467253bed0d2 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/unconstrained.md @@ -0,0 +1,104 @@ +--- +title: Unconstrained Functions +description: "Learn about what unconstrained functions in Noir are, how to use them and when you'd want to." + +keywords: [Noir programming language, unconstrained, open] +sidebar_position: 5 +--- + +Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic computation. + +## Why? + +Zero-knowledge (ZK) domain-specific languages (DSL) enable developers to generate ZK proofs from their programs by compiling code down to the constraints of an NP complete language (such as R1CS or PLONKish languages). However, the hard bounds of a constraint system can be very limiting to the functionality of a ZK DSL. + +Enabling a circuit language to perform unconstrained execution is a powerful tool. Said another way, unconstrained execution lets developers generate witnesses from code that does not generate any constraints. Being able to execute logic outside of a circuit is critical for both circuit performance and constructing proofs on information that is external to a circuit. + +Fetching information from somewhere external to a circuit can also be used to enable developers to improve circuit efficiency. + +A ZK DSL does not just prove computation, but proves that some computation was handled correctly. Thus, it is necessary that when we switch from performing some operation directly inside of a circuit to inside of an unconstrained environment that the appropriate constraints are still laid down elsewhere in the circuit. + +## Example + +An in depth example might help drive the point home. This example comes from the excellent [post](https://discord.com/channels/1113924620781883405/1124022445054111926/1128747641853972590) by Tom in the Noir Discord. + +Let's look at how we can optimize a function to turn a `u72` into an array of `u8`s. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u72 & 0xff) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 91 +Backend circuit size: 3619 +``` + +A lot of the operations in this function are optimized away by the compiler (all the bit-shifts turn into divisions by constants). However we can save a bunch of gates by casting to u8 a bit earlier. This automatically truncates the bit-shifted value to fit in a u8 which allows us to remove the AND against 0xff. This saves us ~480 gates in total. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 75 +Backend circuit size: 3143 +``` + +Those are some nice savings already but we can do better. This code is all constrained so we're proving every step of calculating out using num, but we don't actually care about how we calculate this, just that it's correct. This is where brillig comes in. + +It turns out that truncating a u72 into a u8 is hard to do inside a snark, each time we do as u8 we lay down 4 ACIR opcodes which get converted into multiple gates. It's actually much easier to calculate num from out than the other way around. All we need to do is multiply each element of out by a constant and add them all together, both relatively easy operations inside a snark. + +We can then run `u72_to_u8` as unconstrained brillig code in order to calculate out, then use that result in our constrained function and assert that if we were to do the reverse calculation we'd get back num. This looks a little like the below: + +```rust +fn main(num: u72) -> pub [u8; 8] { + // Safety: 'out' is properly constrained below in 'assert(num == reconstructed_num);' + let out = unsafe { u72_to_u8(num) }; + + let mut reconstructed_num: u72 = 0; + for i in 0..8 { + reconstructed_num += (out[i] as u72 << (56 - (8 * i))); + } + assert(num == reconstructed_num); + out +} + +unconstrained fn u72_to_u8(num: u72) -> [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8))) as u8; + } + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 78 +Backend circuit size: 2902 +``` + +This ends up taking off another ~250 gates from our circuit! We've ended up with more ACIR opcodes than before but they're easier for the backend to prove (resulting in fewer gates). + +Note that in order to invoke unconstrained functions we need to wrap them in an `unsafe` block, +to make it clear that the call is unconstrained. +Furthermore, a warning is emitted unless the `unsafe` block is commented with a `// Safety: ...` comment explaining why it is fine to call the unconstrained function. Note that either the `unsafe` block can be commented this way or the statement it exists in (like in the `let` example above). + +Generally we want to use brillig whenever there's something that's easy to verify but hard to compute within the circuit. For example, if you wanted to calculate a square root of a number it'll be a much better idea to calculate this in brillig and then assert that if you square the result you get back your number. + +## Break and Continue + +In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow.md#break-and-continue) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/_category_.json new file mode 100644 index 000000000000..1debcfe76753 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Modules, Packages and Crates", + "position": 2, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/crates_and_packages.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/crates_and_packages.md new file mode 100644 index 000000000000..8881130be634 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/crates_and_packages.md @@ -0,0 +1,43 @@ +--- +title: Crates and Packages +description: Learn how to use Crates and Packages in your Noir project +keywords: [Nargo, dependencies, package management, crates, package] +sidebar_position: 0 +--- + +## Crates + +A crate is the smallest amount of code that the Noir compiler considers at a time. +Crates can contain modules, and the modules may be defined in other files that get compiled with the crate, as we’ll see in the coming sections. + +### Crate Types + +A Noir crate can come in several forms: binaries, libraries or contracts. + +#### Binaries + +_Binary crates_ are programs which you can compile to an ACIR circuit which you can then create proofs against. Each must have a function called `main` that defines the ACIR circuit which is to be proved. + +#### Libraries + +_Library crates_ don't have a `main` function and they don't compile down to ACIR. Instead they define functionality intended to be shared with multiple projects, and eventually included in a binary crate. + +#### Contracts + +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts). + +### Crate Root + +Every crate has a root, which is the source file that the compiler starts, this is also known as the root module. The Noir compiler does not enforce any conditions on the name of the file which is the crate root, however if you are compiling via Nargo the crate root must be called `lib.nr` or `main.nr` for library or binary crates respectively. + +## Packages + +A Nargo _package_ is a collection of one or more crates that provides a set of functionality. A package must include a Nargo.toml file. + +A package _must_ contain either a library or a binary crate, but not both. + +### Differences from Cargo Packages + +One notable difference between Rust's Cargo and Noir's Nargo is that while Cargo allows a package to contain an unlimited number of binary crates and a single library crate, Nargo currently only allows a package to contain a single crate. + +In future this restriction may be lifted to allow a Nargo package to contain both a binary and library crate or multiple binary crates. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/dependencies.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/dependencies.md new file mode 100644 index 000000000000..22186b225988 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/dependencies.md @@ -0,0 +1,122 @@ +--- +title: Dependencies +description: + Learn how to specify and manage dependencies in Nargo, allowing you to upload packages to GitHub + and use them easily in your project. +keywords: [Nargo, dependencies, GitHub, package management, versioning] +sidebar_position: 1 +--- + +Nargo allows you to upload packages to GitHub and use them as dependencies. + +## Specifying a dependency + +Specifying a dependency requires a tag to a specific commit and the git url to the url containing +the package. + +Currently, there are no requirements on the tag contents. If requirements are added, it would follow +semver 2.0 guidelines. + +> Note: Without a `tag` , there would be no versioning and dependencies would change each time you +> compile your project. + +For example, to add the [ecrecover-noir library](https://github.com/colinnielsen/ecrecover-noir) to your project, add it to `Nargo.toml`: + +```toml +# Nargo.toml + +[dependencies] +ecrecover = {tag = "v0.8.0", git = "https://github.com/colinnielsen/ecrecover-noir"} +``` + +If the module is in a subdirectory, you can define a subdirectory in your git repository, for example: + +```toml +# Nargo.toml + +[dependencies] +easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "noir-contracts/contracts/easy_private_token_contract"} +``` + +## Specifying a local dependency + +You can also specify dependencies that are local to your machine. + +For example, this file structure has a library and binary crate + +```tree +├── binary_crate +│   ├── Nargo.toml +│   └── src +│   └── main.nr +└── lib_a + ├── Nargo.toml + └── src + └── lib.nr +``` + +Inside of the binary crate, you can specify: + +```toml +# Nargo.toml + +[dependencies] +lib_a = { path = "../lib_a" } +``` + +## Importing dependencies + +You can import a dependency to a Noir file using the following syntax. For example, to import the +ecrecover-noir library and local lib_a referenced above: + +```rust +use ecrecover; +use lib_a; +``` + +You can also import only the specific parts of dependency that you want to use, like so: + +```rust +use std::hash::sha256; +use std::scalar_mul::fixed_base_embedded_curve; +``` + +Lastly, You can import multiple items in the same line by enclosing them in curly braces: + +```rust +use std::hash::{keccak256, sha256}; +``` + +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +## Dependencies of Dependencies + +Note that when you import a dependency, you also get access to all of the dependencies of that package. + +For example, the [phy_vector](https://github.com/resurgencelabs/phy_vector) library imports an [fraction](https://github.com/resurgencelabs/fraction) library. If you're importing the phy_vector library, then you can access the functions in fractions library like so: + +```rust +use phy_vector; + +fn main(x : Field, y : pub Field) { + //... + let f = phy_vector::fraction::toFraction(true, 2, 1); + //... +} +``` + +## Available Libraries + +Noir does not currently have an official package manager. You can find a list of available Noir libraries in the [awesome-noir repo here](https://github.com/noir-lang/awesome-noir#libraries). + +Some libraries that are available today include: + +- [Standard Library](https://github.com/noir-lang/noir/tree/master/noir_stdlib) - the Noir Standard Library +- [Ethereum Storage Proof Verification](https://github.com/aragonzkresearch/noir-trie-proofs) - a library that contains the primitives necessary for RLP decoding (in the form of look-up table construction) and Ethereum state and storage proof verification (or verification of any trie proof involving 32-byte long keys) +- [BigInt](https://github.com/shuklaayush/noir-bigint) - a library that provides a custom BigUint56 data type, allowing for computations on large unsigned integers +- [ECrecover](https://github.com/colinnielsen/ecrecover-noir/tree/main) - a library to verify an ECDSA signature and return the source Ethereum address +- [Sparse Merkle Tree Verifier](https://github.com/vocdoni/smtverifier-noir/tree/main) - a library for verification of sparse Merkle trees +- [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir +- [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/modules.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/modules.md new file mode 100644 index 000000000000..14aa1f0579ac --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/modules.md @@ -0,0 +1,221 @@ +--- +title: Modules +description: + Learn how to organize your files using modules in Noir, following the same convention as Rust's + module system. Examples included. +keywords: [Noir, Rust, modules, organizing files, sub-modules] +sidebar_position: 2 +--- + +Noir's module system follows the same convention as the _newer_ version of Rust's module system. + +## Purpose of Modules + +Modules are used to organize files. Without modules all of your code would need to live in a single +file. In Noir, the compiler does not automatically scan all of your files to detect modules. This +must be done explicitly by the developer. + +## Examples + +### Importing a module in the crate root + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::hello_world(); +} +``` + +Filename : `src/foo.nr` + +```rust +fn from_foo() {} +``` + +In the above snippet, the crate root is the `src/main.nr` file. The compiler sees the module +declaration `mod foo` which prompts it to look for a foo.nr file. + +Visually this module hierarchy looks like the following : + +``` +crate + ├── main + │ + └── foo + └── from_foo + +``` + +The module filename may also be the name of the module as a directory with the contents in a +file named `mod.nr` within that directory. The above example can alternatively be expressed like this: + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::hello_world(); +} +``` + +Filename : `src/foo/mod.nr` + +```rust +fn from_foo() {} +``` + +Note that it's an error to have both files `src/foo.nr` and `src/foo/mod.nr` in the filesystem. + +### Importing a module throughout the tree + +All modules are accessible from the `crate::` namespace. + +``` +crate + ├── bar + ├── foo + └── main + +``` + +In the above snippet, if `bar` would like to use functions in `foo`, it can do so by `use crate::foo::function_name`. + +### Sub-modules + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::from_foo(); +} +``` + +Filename : `src/foo.nr` + +```rust +mod bar; +fn from_foo() {} +``` + +Filename : `src/foo/bar.nr` + +```rust +fn from_bar() {} +``` + +In the above snippet, we have added an extra module to the module tree; `bar`. `bar` is a submodule +of `foo` hence we declare bar in `foo.nr` with `mod bar`. Since `foo` is not the crate root, the +compiler looks for the file associated with the `bar` module in `src/foo/bar.nr` + +Visually the module hierarchy looks as follows: + +``` +crate + ├── main + │ + └── foo + ├── from_foo + └── bar + └── from_bar +``` + +Similar to importing a module in the crate root, modules can be placed in a `mod.nr` file, like this: + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::from_foo(); +} +``` + +Filename : `src/foo/mod.nr` + +```rust +mod bar; +fn from_foo() {} +``` + +Filename : `src/foo/bar/mod.nr` + +```rust +fn from_bar() {} +``` + +### Referencing a parent module + +Given a submodule, you can refer to its parent module using the `super` keyword. + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::from_foo(); +} +``` + +Filename : `src/foo.nr` + +```rust +mod bar; + +fn from_foo() {} +``` + +Filename : `src/foo/bar.nr` + +```rust +// Same as bar::from_foo +use super::from_foo; + +fn from_bar() { + from_foo(); // invokes super::from_foo(), which is bar::from_foo() + super::from_foo(); // also invokes bar::from_foo() +} +``` + +### `use` visibility + +`use` declarations are private to the containing module, by default. However, like functions, +they can be marked as `pub` or `pub(crate)`. Such a use declaration serves to _re-export_ a name. +A public `use` declaration can therefore redirect some public name to a different target definition: +even a definition with a private canonical path, inside a different module. + +An example of re-exporting: + +```rust +mod some_module { + pub use foo::{bar, baz}; + mod foo { + pub fn bar() {} + pub fn baz() {} + } +} + +fn main() { + some_module::bar(); + some_module::baz(); +} +``` + +In this example, the module `some_module` re-exports two public names defined in `foo`. + +### Visibility + +By default, like functions, modules are private to the module (or crate) they exist in. You can use `pub` +to make the module public or `pub(crate)` to make it public to just its crate: + +```rust +// This module is now public and can be seen by other crates. +pub mod foo; +``` \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/workspaces.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/workspaces.md new file mode 100644 index 000000000000..05213be0222e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/workspaces.md @@ -0,0 +1,46 @@ +--- +title: Workspaces +sidebar_position: 3 +--- + +Workspaces are a feature of nargo that allow you to manage multiple related Noir packages in a single repository. A workspace is essentially a group of related projects that share common build output directories and configurations. + +Each Noir project (with its own Nargo.toml file) can be thought of as a package. Each package is expected to contain exactly one "named circuit", being the "name" defined in Nargo.toml with the program logic defined in `./src/main.nr`. + +For a project with the following structure: + +```tree +├── crates +│ ├── a +│ │ ├── Nargo.toml +│ │ └── Prover.toml +│ │ └── src +│ │ └── main.nr +│ └── b +│ ├── Nargo.toml +│ └── Prover.toml +│ └── src +│ └── main.nr +│ +└── Nargo.toml +``` + +You can define a workspace in Nargo.toml like so: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +`members` indicates which packages are included in the workspace. As such, all member packages of a workspace will be processed when the `--workspace` flag is used with various commands or if a `default-member` is not specified. The `--package` option can be used to limit +the scope of some commands to a specific member of the workspace; otherwise these commands run on the package nearest on the path to the +current directory where `nargo` was invoked. + +`default-member` indicates which package various commands process by default. + +Libraries can be defined in a workspace. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +Please note that nesting regular packages is not supported: certain commands work on the workspace level and will use the topmost Nargo.toml file they can find on the path; unless this is a workspace configuration with `members`, the command might run on some unintended package. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/_category_.json new file mode 100644 index 000000000000..af04c0933fdb --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Standard Library", + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/black_box_fns.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/black_box_fns.md new file mode 100644 index 000000000000..e9392b20a92f --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/black_box_fns.md @@ -0,0 +1,31 @@ +--- +title: Black Box Functions +description: Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. +keywords: [noir, black box functions] +--- + +Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. This makes certain zk-snark unfriendly computations cheaper than if they were implemented in Noir. + +The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not. + +## Function list + +Here is a list of the current black box functions: + +- [AES128](./cryptographic_primitives/ciphers.mdx#aes128) +- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) +- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) +- [Blake3](./cryptographic_primitives/hashes.mdx#blake3) +- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) +- [Embedded curve operations (MSM, addition, ...)](./cryptographic_primitives/embedded_curve_ops.mdx) +- AND +- XOR +- RANGE +- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) +- [Recursive proof verification](./recursion.mdx) + +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. + +You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/bn254.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/bn254.md new file mode 100644 index 000000000000..3294f005dbb4 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/bn254.md @@ -0,0 +1,46 @@ +--- +title: Bn254 Field Library +--- + +Noir provides a module in standard library with some optimized functions for bn254 Fr in `std::field::bn254`. + +## decompose + +```rust +fn decompose(x: Field) -> (Field, Field) {} +``` + +Decomposes a single field into two fields, low and high. The low field contains the lower 16 bytes of the input field and the high field contains the upper 16 bytes of the input field. Both field results are range checked to 128 bits. + + +## assert_gt + +```rust +fn assert_gt(a: Field, b: Field) {} +``` + +Asserts that a > b. This will generate less constraints than using `assert(gt(a, b))`. + +## assert_lt + +```rust +fn assert_lt(a: Field, b: Field) {} +``` + +Asserts that a < b. This will generate less constraints than using `assert(lt(a, b))`. + +## gt + +```rust +fn gt(a: Field, b: Field) -> bool {} +``` + +Returns true if a > b. + +## lt + +```rust +fn lt(a: Field, b: Field) -> bool {} +``` + +Returns true if a < b. \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/boundedvec.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/boundedvec.md new file mode 100644 index 000000000000..ba75e45564ab --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/boundedvec.md @@ -0,0 +1,479 @@ +--- +title: Bounded Vectors +keywords: [noir, vector, bounded vector, slice] +sidebar_position: 1 +--- + +A `BoundedVec` is a growable storage similar to a `Vec` except that it +is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented +via slices and thus is not subject to the same restrictions slices are (notably, nested +slices - and thus nested vectors as well - are disallowed). + +Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by +pushing an additional element is also more efficient - the length only needs to be increased +by one. + +For these reasons `BoundedVec` should generally be preferred over `Vec` when there +is a reasonable maximum bound that can be placed on the vector. + +Example: + +```rust +let mut vector: BoundedVec = BoundedVec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +assert(vector.max_len() == 10); +``` + +## Methods + +### new + +```rust +pub fn new() -> Self +``` + +Creates a new, empty vector of length zero. + +Since this container is backed by an array internally, it still needs an initial value +to give each element. To resolve this, each element is zeroed internally. This value +is guaranteed to be inaccessible unless `get_unchecked` is used. + +Example: + +```rust +let empty_vector: BoundedVec = BoundedVec::new(); +assert(empty_vector.len() == 0); +``` + +Note that whenever calling `new` the maximum length of the vector should always be specified +via a type signature: + +```rust title="new_example" showLineNumbers +fn good() -> BoundedVec { + // Ok! MaxLen is specified with a type annotation + let v1: BoundedVec = BoundedVec::new(); + let v2 = BoundedVec::new(); + + // Ok! MaxLen is known from the type of `good`'s return value + v2 +} + +fn bad() { + // Error: Type annotation needed + // The compiler can't infer `MaxLen` from this code. + let mut v3 = BoundedVec::new(); + v3.push(5); +} +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L11-L27 + + +This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions +but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a constraint failure at runtime when the vec is pushed to. + +### get + +```rust +pub fn get(self, index: u64) -> T { +``` + +Retrieves an element from the vector at the given index, starting from zero. + +If the given index is equal to or greater than the length of the vector, this +will issue a constraint failure. + +Example: + +```rust +fn foo(v: BoundedVec) { + let first = v.get(0); + let last = v.get(v.len() - 1); + assert(first != last); +} +``` + +### get_unchecked + +```rust +pub fn get_unchecked(self, index: u64) -> T { +``` + +Retrieves an element from the vector at the given index, starting from zero, without +performing a bounds check. + +Since this function does not perform a bounds check on length before accessing the element, +it is unsafe! Use at your own risk! + +Example: + +```rust title="get_unchecked_example" showLineNumbers +fn sum_of_first_three(v: BoundedVec) -> u32 { + // Always ensure the length is larger than the largest + // index passed to get_unchecked + assert(v.len() > 2); + let first = v.get_unchecked(0); + let second = v.get_unchecked(1); + let third = v.get_unchecked(2); + first + second + third +} +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L54-L64 + + +### set + +```rust +pub fn set(&mut self: Self, index: u64, value: T) { +``` + +Writes an element to the vector at the given index, starting from zero. + +If the given index is equal to or greater than the length of the vector, this will issue a constraint failure. + +Example: + +```rust +fn foo(v: BoundedVec) { + let first = v.get(0); + assert(first != 42); + v.set(0, 42); + let new_first = v.get(0); + assert(new_first == 42); +} +``` + +### set_unchecked + +```rust +pub fn set_unchecked(&mut self: Self, index: u64, value: T) -> T { +``` + +Writes an element to the vector at the given index, starting from zero, without performing a bounds check. + +Since this function does not perform a bounds check on length before accessing the element, it is unsafe! Use at your own risk! + +Example: + +```rust title="set_unchecked_example" showLineNumbers +fn set_unchecked_example() { + let mut vec: BoundedVec = BoundedVec::new(); + vec.extend_from_array([1, 2]); + + // Here we're safely writing within the valid range of `vec` + // `vec` now has the value [42, 2] + vec.set_unchecked(0, 42); + + // We can then safely read this value back out of `vec`. + // Notice that we use the checked version of `get` which would prevent reading unsafe values. + assert_eq(vec.get(0), 42); + + // We've now written past the end of `vec`. + // As this index is still within the maximum potential length of `v`, + // it won't cause a constraint failure. + vec.set_unchecked(2, 42); + println(vec); + + // This will write past the end of the maximum potential length of `vec`, + // it will then trigger a constraint failure. + vec.set_unchecked(5, 42); + println(vec); +} +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L67-L91 + + + +### push + +```rust +pub fn push(&mut self, elem: T) { +``` + +Pushes an element to the end of the vector. This increases the length +of the vector by one. + +Panics if the new length of the vector will be greater than the max length. + +Example: + +```rust title="bounded-vec-push-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + v.push(1); + v.push(2); + + // Panics with failed assertion "push out of bounds" + v.push(3); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L95-L103 + + +### pop + +```rust +pub fn pop(&mut self) -> T +``` + +Pops the element at the end of the vector. This will decrease the length +of the vector by one. + +Panics if the vector is empty. + +Example: + +```rust title="bounded-vec-pop-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + v.push(1); + v.push(2); + + let two = v.pop(); + let one = v.pop(); + + assert(two == 2); + assert(one == 1); + // error: cannot pop from an empty vector + // let _ = v.pop(); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L108-L120 + + +### len + +```rust +pub fn len(self) -> u64 { +``` + +Returns the current length of this vector + +Example: + +```rust title="bounded-vec-len-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + assert(v.len() == 0); + + v.push(100); + assert(v.len() == 1); + + v.push(200); + v.push(300); + v.push(400); + assert(v.len() == 4); + + let _ = v.pop(); + let _ = v.pop(); + assert(v.len() == 2); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L125-L140 + + +### max_len + +```rust +pub fn max_len(_self: BoundedVec) -> u64 { +``` + +Returns the maximum length of this vector. This is always +equal to the `MaxLen` parameter this vector was initialized with. + +Example: + +```rust title="bounded-vec-max-len-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + assert(v.max_len() == 5); + v.push(10); + assert(v.max_len() == 5); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L145-L151 + + +### storage + +```rust +pub fn storage(self) -> [T; MaxLen] { +``` + +Returns the internal array within this vector. +Since arrays in Noir are immutable, mutating the returned storage array will not mutate +the storage held internally by this vector. + +Note that uninitialized elements may be zeroed out! + +Example: + +```rust title="bounded-vec-storage-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + assert(v.storage() == [0, 0, 0, 0, 0]); + + v.push(57); + assert(v.storage() == [57, 0, 0, 0, 0]); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L156-L163 + + +### extend_from_array + +```rust +pub fn extend_from_array(&mut self, array: [T; Len]) +``` + +Pushes each element from the given array to this vector. + +Panics if pushing each element would cause the length of this vector +to exceed the maximum length. + +Example: + +```rust title="bounded-vec-extend-from-array-example" showLineNumbers +let mut vec: BoundedVec = BoundedVec::new(); + vec.extend_from_array([2, 4]); + + assert(vec.len == 2); + assert(vec.get(0) == 2); + assert(vec.get(1) == 4); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L168-L175 + + +### extend_from_bounded_vec + +```rust +pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) +``` + +Pushes each element from the other vector to this vector. The length of +the other vector is left unchanged. + +Panics if pushing each element would cause the length of this vector +to exceed the maximum length. + +Example: + +```rust title="bounded-vec-extend-from-bounded-vec-example" showLineNumbers +let mut v1: BoundedVec = BoundedVec::new(); + let mut v2: BoundedVec = BoundedVec::new(); + + v2.extend_from_array([1, 2, 3]); + v1.extend_from_bounded_vec(v2); + + assert(v1.storage() == [1, 2, 3, 0, 0]); + assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L180-L189 + + +### from_array + +```rust +pub fn from_array(array: [T; Len]) -> Self +``` + +Creates a new vector, populating it with values derived from an array input. +The maximum length of the vector is determined based on the type signature. + +Example: +```rust +let bounded_vec: BoundedVec = BoundedVec::from_array([1, 2, 3]) +``` + +### from_parts + +```rust +pub fn from_parts(mut array: [T; MaxLen], len: u32) -> Self +``` + +Creates a new BoundedVec from the given array and length. +The given length must be less than or equal to the length of the array. + +This function will zero out any elements at or past index `len` of `array`. +This incurs an extra runtime cost of O(MaxLen). If you are sure your array is +zeroed after that index, you can use `from_parts_unchecked` to remove the extra loop. + +Example: + +```rust title="from-parts" showLineNumbers +let vec: BoundedVec = BoundedVec::from_parts([1, 2, 3, 0], 3); + assert_eq(vec.len(), 3); + + // Any elements past the given length are zeroed out, so these + // two BoundedVecs will be completely equal + let vec1: BoundedVec = BoundedVec::from_parts([1, 2, 3, 1], 3); + let vec2: BoundedVec = BoundedVec::from_parts([1, 2, 3, 2], 3); + assert_eq(vec1, vec2); +``` +> Source code: noir_stdlib/src/collections/bounded_vec.nr#L693-L702 + + +### from_parts_unchecked + +```rust +pub fn from_parts_unchecked(array: [T; MaxLen], len: u32) -> Self +``` + +Creates a new BoundedVec from the given array and length. +The given length must be less than or equal to the length of the array. + +This function is unsafe because it expects all elements past the `len` index +of `array` to be zeroed, but does not check for this internally. Use `from_parts` +for a safe version of this function which does zero out any indices past the +given length. Invalidating this assumption can notably cause `BoundedVec::eq` +to give incorrect results since it will check even elements past `len`. + +Example: + +```rust title="from-parts-unchecked" showLineNumbers +let vec: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 0], 3); + assert_eq(vec.len(), 3); + + // invalid use! + let vec1: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 1], 3); + let vec2: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 2], 3); + + // both vecs have length 3 so we'd expect them to be equal, but this + // fails because elements past the length are still checked in eq + assert(vec1 != vec2); +``` +> Source code: noir_stdlib/src/collections/bounded_vec.nr#L707-L718 + + +### map + +```rust +pub fn map(self, f: fn[Env](T) -> U) -> BoundedVec +``` + +Creates a new vector of equal size by calling a closure on each element in this vector. + +Example: + +```rust title="bounded-vec-map-example" showLineNumbers +let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]); + let result = vec.map(|value| value * 2); +``` +> Source code: noir_stdlib/src/collections/bounded_vec.nr#L580-L583 + + +### any + +```rust +pub fn any(self, predicate: fn[Env](T) -> bool) -> bool +``` + +Returns true if the given predicate returns true for any element +in this vector. + +Example: + +```rust title="bounded-vec-any-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + v.extend_from_array([2, 4, 6]); + + let all_even = !v.any(|elem: u32| elem % 2 != 0); + assert(all_even); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L256-L262 + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/hashmap.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/hashmap.md new file mode 100644 index 000000000000..810baad16ba0 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/hashmap.md @@ -0,0 +1,587 @@ +--- +title: HashMap +keywords: [noir, map, hash, hashmap] +sidebar_position: 1 +--- + +`HashMap` is used to efficiently store and look up key-value pairs. + +`HashMap` is a bounded type which can store anywhere from zero to `MaxLen` total elements. +Note that due to hash collisions, the actual maximum number of elements stored by any particular +hashmap is likely lower than `MaxLen`. This is true even with cryptographic hash functions since +every hash value will be performed modulo `MaxLen`. + +Example: + +```rust +// Create a mapping from Fields to u32s with a maximum length of 12 +// using a poseidon2 hasher +use std::hash::poseidon2::Poseidon2Hasher; +let mut map: HashMap> = HashMap::default(); + +map.insert(1, 2); +map.insert(3, 4); + +let two = map.get(1).unwrap(); +``` + +## Methods + +### default + +```rust title="default" showLineNumbers +impl Default for HashMap +where + B: BuildHasher + Default, + H: Hasher + Default, +{ + /// Constructs an empty HashMap. + /// + /// Example: + /// + /// ```noir + /// let hashmap: HashMap> = HashMap::default(); + /// assert(hashmap.is_empty()); + /// ``` + fn default() -> Self { +``` +> Source code: noir_stdlib/src/collections/map.nr#L688-L703 + + +Creates a fresh, empty HashMap. + +When using this function, always make sure to specify the maximum size of the hash map. + +This is the same `default` from the `Default` implementation given further below. It is +repeated here for convenience since it is the recommended way to create a hashmap. + +Example: + +```rust title="default_example" showLineNumbers +let hashmap: HashMap> = HashMap::default(); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L208-L211 + + +Because `HashMap` has so many generic arguments that are likely to be the same throughout +your program, it may be helpful to create a type alias: + +```rust title="type_alias" showLineNumbers +type MyMap = HashMap>; +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L202-L204 + + +### with_hasher + +```rust title="with_hasher" showLineNumbers +pub fn with_hasher(_build_hasher: B) -> Self + where + B: BuildHasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L103-L108 + + +Creates a hashmap with an existing `BuildHasher`. This can be used to ensure multiple +hashmaps are created with the same hasher instance. + +Example: + +```rust title="with_hasher_example" showLineNumbers +let my_hasher: BuildHasherDefault = Default::default(); + let hashmap: HashMap> = + HashMap::with_hasher(my_hasher); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L212-L217 + + +### get + +```rust title="get" showLineNumbers +pub fn get(self, key: K) -> Option + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L472-L479 + + +Retrieves a value from the hashmap, returning `Option::none()` if it was not found. + +Example: + +```rust title="get_example" showLineNumbers +fn get_example(map: HashMap>) { + let x = map.get(12); + + if x.is_some() { + assert(x.unwrap() == 42); + } +} +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L297-L305 + + +### insert + +```rust title="insert" showLineNumbers +pub fn insert(&mut self, key: K, value: V) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L514-L521 + + +Inserts a new key-value pair into the map. If the key was already in the map, its +previous value will be overridden with the newly provided one. + +Example: + +```rust title="insert_example" showLineNumbers +let mut map: HashMap> = HashMap::default(); + map.insert(12, 42); + assert(map.len() == 1); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L218-L222 + + +### remove + +```rust title="remove" showLineNumbers +pub fn remove(&mut self, key: K) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L570-L577 + + +Removes the given key-value pair from the map. If the key was not already present +in the map, this does nothing. + +Example: + +```rust title="remove_example" showLineNumbers +map.remove(12); + assert(map.is_empty()); + + // If a key was not present in the map, remove does nothing + map.remove(12); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L225-L232 + + +### is_empty + +```rust title="is_empty" showLineNumbers +pub fn is_empty(self) -> bool { +``` +> Source code: noir_stdlib/src/collections/map.nr#L167-L169 + + +True if the length of the hash map is empty. + +Example: + +```rust title="is_empty_example" showLineNumbers +assert(map.is_empty()); + + map.insert(1, 2); + assert(!map.is_empty()); + + map.remove(1); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L233-L241 + + +### len + +```rust title="len" showLineNumbers +pub fn len(self) -> u32 { +``` +> Source code: noir_stdlib/src/collections/map.nr#L431-L433 + + +Returns the current length of this hash map. + +Example: + +```rust title="len_example" showLineNumbers +// This is equivalent to checking map.is_empty() + assert(map.len() == 0); + + map.insert(1, 2); + map.insert(3, 4); + map.insert(5, 6); + assert(map.len() == 3); + + // 3 was already present as a key in the hash map, so the length is unchanged + map.insert(3, 7); + assert(map.len() == 3); + + map.remove(1); + assert(map.len() == 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L242-L257 + + +### capacity + +```rust title="capacity" showLineNumbers +pub fn capacity(_self: Self) -> u32 { +``` +> Source code: noir_stdlib/src/collections/map.nr#L453-L455 + + +Returns the maximum capacity of this hashmap. This is always equal to the capacity +specified in the hashmap's type. + +Unlike hashmaps in general purpose programming languages, hashmaps in Noir have a +static capacity that does not increase as the map grows larger. Thus, this capacity +is also the maximum possible element count that can be inserted into the hashmap. +Due to hash collisions (modulo the hashmap length), it is likely the actual maximum +element count will be lower than the full capacity. + +Example: + +```rust title="capacity_example" showLineNumbers +let empty_map: HashMap> = + HashMap::default(); + assert(empty_map.len() == 0); + assert(empty_map.capacity() == 42); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L258-L263 + + +### clear + +```rust title="clear" showLineNumbers +pub fn clear(&mut self) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L123-L125 + + +Clears the hashmap, removing all key-value pairs from it. + +Example: + +```rust title="clear_example" showLineNumbers +assert(!map.is_empty()); + map.clear(); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L264-L268 + + +### contains_key + +```rust title="contains_key" showLineNumbers +pub fn contains_key(self, key: K) -> bool + where + K: Hash + Eq, + B: BuildHasher, + H: Hasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L143-L150 + + +True if the hashmap contains the given key. Unlike `get`, this will not also return +the value associated with the key. + +Example: + +```rust title="contains_key_example" showLineNumbers +if map.contains_key(7) { + let value = map.get(7); + assert(value.is_some()); + } else { + println("No value for key 7!"); + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L269-L276 + + +### entries + +```rust title="entries" showLineNumbers +pub fn entries(self) -> BoundedVec<(K, V), N> { +``` +> Source code: noir_stdlib/src/collections/map.nr#L191-L193 + + +Returns a vector of each key-value pair present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="entries_example" showLineNumbers +let entries = map.entries(); + + // The length of a hashmap may not be compile-time known, so we + // need to loop over its capacity instead + for i in 0..map.capacity() { + if i < entries.len() { + let (key, value) = entries.get(i); + println(f"{key} -> {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L308-L319 + + +### keys + +```rust title="keys" showLineNumbers +pub fn keys(self) -> BoundedVec { +``` +> Source code: noir_stdlib/src/collections/map.nr#L230-L232 + + +Returns a vector of each key present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="keys_example" showLineNumbers +let keys = map.keys(); + + for i in 0..keys.max_len() { + if i < keys.len() { + let key = keys.get_unchecked(i); + let value = map.get(key).unwrap_unchecked(); + println(f"{key} -> {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L320-L330 + + +### values + +```rust title="values" showLineNumbers +pub fn values(self) -> BoundedVec { +``` +> Source code: noir_stdlib/src/collections/map.nr#L267-L269 + + +Returns a vector of each value present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="values_example" showLineNumbers +let values = map.values(); + + for i in 0..values.max_len() { + if i < values.len() { + let value = values.get_unchecked(i); + println(f"Found value {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L331-L340 + + +### iter_mut + +```rust title="iter_mut" showLineNumbers +pub fn iter_mut(&mut self, f: fn(K, V) -> (K, V)) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L304-L311 + + +Iterates through each key-value pair of the HashMap, setting each key-value pair to the +result returned from the given function. + +Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated +through. If this is not desired, use `iter_values_mut` if only values need to be mutated, +or `entries` if neither keys nor values need to be mutated. + +The iteration order is left unspecified. As a result, if two keys are mutated to become +equal, which of the two values that will be present for the key in the resulting map is also unspecified. + +Example: + +```rust title="iter_mut_example" showLineNumbers +// Add 1 to each key in the map, and double the value associated with that key. + map.iter_mut(|k, v| (k + 1, v * 2)); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L344-L347 + + +### iter_keys_mut + +```rust title="iter_keys_mut" showLineNumbers +pub fn iter_keys_mut(&mut self, f: fn(K) -> K) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L342-L349 + + +Iterates through the HashMap, mutating each key to the result returned from +the given function. + +Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated +through. If only iteration is desired and the keys are not intended to be mutated, +prefer using `entries` instead. + +The iteration order is left unspecified. As a result, if two keys are mutated to become +equal, which of the two values that will be present for the key in the resulting map is also unspecified. + +Example: + +```rust title="iter_keys_mut_example" showLineNumbers +// Double each key, leaving the value associated with that key untouched + map.iter_keys_mut(|k| k * 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L348-L351 + + +### iter_values_mut + +```rust title="iter_values_mut" showLineNumbers +pub fn iter_values_mut(&mut self, f: fn(V) -> V) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L374-L376 + + +Iterates through the HashMap, applying the given function to each value and mutating the +value to equal the result. This function is more efficient than `iter_mut` and `iter_keys_mut` +because the keys are untouched and the underlying hashmap thus does not need to be reordered. + +Example: + +```rust title="iter_values_mut_example" showLineNumbers +// Halve each value + map.iter_values_mut(|v| v / 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L352-L355 + + +### retain + +```rust title="retain" showLineNumbers +pub fn retain(&mut self, f: fn(K, V) -> bool) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L395-L397 + + +Retains only the key-value pairs for which the given function returns true. +Any key-value pairs for which the function returns false will be removed from the map. + +Example: + +```rust title="retain_example" showLineNumbers +map.retain(|k, v| (k != 0) & (v != 0)); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L280-L282 + + +## Trait Implementations + +### default + +```rust title="default" showLineNumbers +impl Default for HashMap +where + B: BuildHasher + Default, + H: Hasher + Default, +{ + /// Constructs an empty HashMap. + /// + /// Example: + /// + /// ```noir + /// let hashmap: HashMap> = HashMap::default(); + /// assert(hashmap.is_empty()); + /// ``` + fn default() -> Self { +``` +> Source code: noir_stdlib/src/collections/map.nr#L688-L703 + + +Constructs an empty HashMap. + +Example: + +```rust title="default_example" showLineNumbers +let hashmap: HashMap> = HashMap::default(); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L208-L211 + + +### eq + +```rust title="eq" showLineNumbers +impl Eq for HashMap +where + K: Eq + Hash, + V: Eq, + B: BuildHasher, + H: Hasher, +{ + /// Checks if two HashMaps are equal. + /// + /// Example: + /// + /// ```noir + /// let mut map1: HashMap> = HashMap::default(); + /// let mut map2: HashMap> = HashMap::default(); + /// + /// map1.insert(1, 2); + /// map1.insert(3, 4); + /// + /// map2.insert(3, 4); + /// map2.insert(1, 2); + /// + /// assert(map1 == map2); + /// ``` + fn eq(self, other: HashMap) -> bool { +``` +> Source code: noir_stdlib/src/collections/map.nr#L636-L661 + + +Checks if two HashMaps are equal. + +Example: + +```rust title="eq_example" showLineNumbers +let mut map1: HashMap> = HashMap::default(); + let mut map2: HashMap> = HashMap::default(); + + map1.insert(1, 2); + map1.insert(3, 4); + + map2.insert(3, 4); + map2.insert(1, 2); + + assert(map1 == map2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L283-L294 + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/index.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/index.md new file mode 100644 index 000000000000..ea84c6d5c21e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/index.md @@ -0,0 +1,5 @@ +--- +title: Containers +description: Container types provided by Noir's standard library for storing and retrieving data +keywords: [containers, data types, vec, hashmap] +--- diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/vec.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/vec.mdx new file mode 100644 index 000000000000..475011922f81 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/vec.mdx @@ -0,0 +1,170 @@ +--- +title: Vectors +description: Delve into the Vec data type in Noir. Learn about its methods, practical examples, and best practices for using Vectors in your Noir code. +keywords: [noir, vector type, methods, examples, dynamic arrays] +sidebar_position: 6 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A vector is a collection type similar to Rust's `Vec` type. In Noir, it is a convenient way to use slices as mutable arrays. + +Example: + +```rust +let mut vector: Vec = Vec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +``` + +## Methods + +### new + +Creates a new, empty vector. + +```rust +pub fn new() -> Self +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` + +### from_slice + +Creates a vector containing each element from a given slice. Mutations to the resulting vector will not affect the original slice. + +```rust +pub fn from_slice(slice: [T]) -> Self +``` + +Example: + +```rust +let slice: [Field] = &[1, 2, 3]; +let vector_from_slice = Vec::from_slice(slice); +assert(vector_from_slice.len() == 3); +``` + +### len + +Returns the number of elements in the vector. + +```rust +pub fn len(self) -> Field +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` + +### get + +Retrieves an element from the vector at a given index. Panics if the index points beyond the vector's end. + +```rust +pub fn get(self, index: Field) -> T +``` + +Example: + +```rust +let vector: Vec = Vec::from_slice(&[10, 20, 30]); +assert(vector.get(1) == 20); +``` + +### set + +```rust +pub fn set(&mut self: Self, index: u64, value: T) { +``` + +Writes an element to the vector at the given index, starting from zero. + +Panics if the index points beyond the vector's end. + +Example: + +```rust +let vector: Vec = Vec::from_slice(&[10, 20, 30]); +assert(vector.get(1) == 20); +vector.set(1, 42); +assert(vector.get(1) == 42); +``` + +### push + +Adds a new element to the vector's end, returning a new vector with a length one greater than the original unmodified vector. + +```rust +pub fn push(&mut self, elem: T) +``` + +Example: + +```rust +let mut vector: Vec = Vec::new(); +vector.push(10); +assert(vector.len() == 1); +``` + +### pop + +Removes an element from the vector's end, returning a new vector with a length one less than the original vector, along with the removed element. Panics if the vector's length is zero. + +```rust +pub fn pop(&mut self) -> T +``` + +Example: + +```rust +let mut vector = Vec::from_slice(&[10, 20]); +let popped_elem = vector.pop(); +assert(popped_elem == 20); +assert(vector.len() == 1); +``` + +### insert + +Inserts an element at a specified index, shifting subsequent elements to the right. + +```rust +pub fn insert(&mut self, index: Field, elem: T) +``` + +Example: + +```rust +let mut vector = Vec::from_slice(&[10, 30]); +vector.insert(1, 20); +assert(vector.get(1) == 20); +``` + +### remove + +Removes an element at a specified index, shifting subsequent elements to the left, and returns the removed element. + +```rust +pub fn remove(&mut self, index: Field) -> T +``` + +Example: + +```rust +let mut vector = Vec::from_slice(&[10, 20, 30]); +let removed_elem = vector.remove(1); +assert(removed_elem == 20); +assert(vector.len() == 2); +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/_category_.json new file mode 100644 index 000000000000..5d694210bbf3 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ciphers.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ciphers.mdx new file mode 100644 index 000000000000..9f82890a92a2 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ciphers.mdx @@ -0,0 +1,36 @@ +--- +title: Ciphers +description: + Learn about the implemented ciphers ready to use for any Noir project +keywords: + [ciphers, Noir project, aes128, encrypt] +sidebar_position: 0 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; + +## aes128 + +Given a plaintext as an array of bytes, returns the corresponding aes128 ciphertext (CBC mode). Input padding is automatically performed using PKCS#7, so that the output length is `input.len() + (16 - input.len() % 16)`. + +```rust title="aes128" showLineNumbers +pub fn aes128_encrypt( + input: [u8; N], + iv: [u8; 16], + key: [u8; 16], +) -> [u8; N + 16 - N % 16] {} +``` +> Source code: noir_stdlib/src/aes128.nr#L2-L8 + + +```rust +fn main() { + let input: [u8; 4] = [0, 12, 3, 15] // Random bytes, will be padded to 16 bytes. + let iv: [u8; 16] = [0; 16]; // Initialisation vector + let key: [u8; 16] = [0; 16] // AES key + let ciphertext = std::aes128::aes128_encrypt(inputs.as_bytes(), iv.as_bytes(), key.as_bytes()); // In this case, the output length will be 16 bytes. +} +``` + + + \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx new file mode 100644 index 000000000000..8d96027b42cb --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx @@ -0,0 +1,98 @@ +--- +title: ECDSA Signature Verification +description: Learn about the cryptographic primitives regarding ECDSA over the secp256k1 and secp256r1 curves +keywords: [cryptographic primitives, Noir project, ecdsa, secp256k1, secp256r1, signatures] +sidebar_position: 3 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; + +Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. + +## ecdsa_secp256k1::verify_signature + +Verifier for ECDSA Secp256k1 signatures. +See ecdsa_secp256k1::verify_signature_slice for a version that accepts slices directly. + +```rust title="ecdsa_secp256k1" showLineNumbers +pub fn verify_signature( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N], +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 + + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + + + +## ecdsa_secp256k1::verify_signature_slice + +Verifier for ECDSA Secp256k1 signatures where the message is a slice. + +```rust title="ecdsa_secp256k1_slice" showLineNumbers +pub fn verify_signature_slice( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8], +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L13-L20 + + + + +## ecdsa_secp256r1::verify_signature + +Verifier for ECDSA Secp256r1 signatures. +See ecdsa_secp256r1::verify_signature_slice for a version that accepts slices directly. + +```rust title="ecdsa_secp256r1" showLineNumbers +pub fn verify_signature( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N], +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 + + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256r1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + + + +## ecdsa_secp256r1::verify_signature + +Verifier for ECDSA Secp256r1 signatures where the message is a slice. + +```rust title="ecdsa_secp256r1_slice" showLineNumbers +pub fn verify_signature_slice( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8], +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L13-L20 + + + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx new file mode 100644 index 000000000000..482a36932b94 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx @@ -0,0 +1,95 @@ +--- +title: Scalar multiplication +description: See how you can perform scalar multiplication in Noir +keywords: [cryptographic primitives, Noir project, scalar multiplication] +sidebar_position: 1 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; + +The following functions perform operations over the embedded curve whose coordinates are defined by the configured noir field. +For the BN254 scalar field, this is BabyJubJub or Grumpkin. + +:::note +Suffixes `_low` and `_high` denote low and high limbs of a scalar. +::: + +## embedded_curve_ops::multi_scalar_mul + +Performs multi scalar multiplication over the embedded curve. +The function accepts arbitrary amount of point-scalar pairs on the input, it multiplies the individual pairs over +the curve and returns a sum of the resulting points. + +Points represented as x and y coordinates [x1, y1, x2, y2, ...], scalars as low and high limbs [low1, high1, low2, high2, ...]. + +```rust title="multi_scalar_mul" showLineNumbers +pub fn multi_scalar_mul( + points: [EmbeddedCurvePoint; N], + scalars: [EmbeddedCurveScalar; N], +) -> EmbeddedCurvePoint +``` +> Source code: noir_stdlib/src/embedded_curve_ops.nr#L103-L108 + + +example + +```rust +fn main(point_x: Field, point_y: Field, scalar_low: Field, scalar_high: Field) { + let point = std::embedded_curve_ops::multi_scalar_mul([point_x, point_y], [scalar_low, scalar_high]); + println(point); +} +``` + +## embedded_curve_ops::fixed_base_scalar_mul + +Performs fixed base scalar multiplication over the embedded curve (multiplies input scalar with a generator point). +The function accepts a single scalar on the input represented as 2 fields. + +```rust title="fixed_base_scalar_mul" showLineNumbers +pub fn fixed_base_scalar_mul(scalar: EmbeddedCurveScalar) -> EmbeddedCurvePoint +``` +> Source code: noir_stdlib/src/embedded_curve_ops.nr#L120-L122 + + +example + +```rust +fn main(scalar_low: Field, scalar_high: Field) { + let point = std::embedded_curve_ops::fixed_base_scalar_mul(scalar_low, scalar_high); + println(point); +} +``` + +## embedded_curve_ops::embedded_curve_add + +Adds two points on the embedded curve. +This function takes two `EmbeddedCurvePoint` structures as parameters, representing points on the curve, and returns a new `EmbeddedCurvePoint` structure that represents their sum. + +### Parameters: +- `point1` (`EmbeddedCurvePoint`): The first point to add. +- `point2` (`EmbeddedCurvePoint`): The second point to add. + +### Returns: +- `EmbeddedCurvePoint`: The resulting point after the addition of `point1` and `point2`. + +```rust title="embedded_curve_add" showLineNumbers +pub fn embedded_curve_add( + point1: EmbeddedCurvePoint, + point2: EmbeddedCurvePoint, +) -> EmbeddedCurvePoint { +``` +> Source code: noir_stdlib/src/embedded_curve_ops.nr#L136-L141 + + +example + +```rust +fn main() { + let point1 = EmbeddedCurvePoint { x: 1, y: 2 }; + let point2 = EmbeddedCurvePoint { x: 3, y: 4 }; + let result = std::embedded_curve_ops::embedded_curve_add(point1, point2); + println!("Resulting Point: ({}, {})", result.x, result.y); +} +``` + + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/hashes.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/hashes.mdx new file mode 100644 index 000000000000..039ca6b3d494 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -0,0 +1,228 @@ +--- +title: Hash methods +description: + Learn about the cryptographic primitives ready to use for any Noir project, including sha256, + blake2s and pedersen +keywords: + [cryptographic primitives, Noir project, sha256, blake2s, pedersen, hash] +sidebar_position: 0 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; + +## sha256 + +Given an array of bytes, returns the resulting sha256 hash. +Specify a message_size to hash only the first `message_size` bytes of the input. + +```rust title="sha256" showLineNumbers +#[deprecated("sha256 is being deprecated from the stdlib, use https://github.com/noir-lang/sha256 instead")] +pub fn sha256(input: [u8; N]) -> HASH +``` +> Source code: noir_stdlib/src/hash/sha256.nr#L46-L49 + + +example: +```rust title="sha256_var" showLineNumbers +let digest = std::hash::sha256_var([x as u8], 1); +``` +> Source code: test_programs/execution_success/sha256/src/main.nr#L15-L17 + + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::sha256::sha256_var(x, 4); +} +``` + + + + +## blake2s + +Given an array of bytes, returns an array with the Blake2 hash + +```rust title="blake2s" showLineNumbers +pub fn blake2s(input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash/mod.nr#L18-L20 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake2s(x); +} +``` + + + +## blake3 + +Given an array of bytes, returns an array with the Blake3 hash + +```rust title="blake3" showLineNumbers +pub fn blake3(input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash/mod.nr#L24-L26 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake3(x); +} +``` + + + +## pedersen_hash + +Given an array of Fields, returns the Pedersen hash. + +```rust title="pedersen_hash" showLineNumbers +pub fn pedersen_hash(input: [Field; N]) -> Field +``` +> Source code: noir_stdlib/src/hash/mod.nr#L49-L51 + + +example: + +```rust title="pedersen-hash" showLineNumbers +fn main(x: Field, y: Field, expected_hash: Field) { + let hash = std::hash::pedersen_hash([x, y]); + assert_eq(hash, expected_hash); +} +``` +> Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L6 + + + + +## pedersen_commitment + +Given an array of Fields, returns the Pedersen commitment. + +```rust title="pedersen_commitment" showLineNumbers +pub fn pedersen_commitment(input: [Field; N]) -> EmbeddedCurvePoint { +``` +> Source code: noir_stdlib/src/hash/mod.nr#L29-L31 + + +example: + +```rust title="pedersen-commitment" showLineNumbers +fn main(x: Field, y: Field, expected_commitment: std::embedded_curve_ops::EmbeddedCurvePoint) { + let commitment = std::hash::pedersen_commitment([x, y]); + assert_eq(commitment.x, expected_commitment.x); + assert_eq(commitment.y, expected_commitment.y); +} +``` +> Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L7 + + + + +## keccak256 + +Given an array of bytes (`u8`), returns the resulting keccak hash as an array of +32 bytes (`[u8; 32]`). Specify a message_size to hash only the first +`message_size` bytes of the input. + +```rust title="keccak256" showLineNumbers +pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash/mod.nr#L117-L119 + + +example: + +```rust title="keccak256" showLineNumbers +fn main(x: Field, result: [u8; 32]) { + // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field + // The padding is taken care of by the program + let digest = std::hash::keccak256([x as u8], 1); + assert(digest == result); + + //#1399: variable message size + let message_size = 4; + let hash_a = std::hash::keccak256([1, 2, 3, 4], message_size); + let hash_b = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); + + assert(hash_a == hash_b); + + let message_size_big = 8; + let hash_c = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); + + assert(hash_a != hash_c); +} +``` +> Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L20 + + + + +## poseidon + +Given an array of Fields, returns a new Field with the Poseidon Hash. Mind that you need to specify +how many inputs are there to your Poseidon function. + +```rust +// example for hash_1, hash_2 accepts an array of length 2, etc +fn hash_1(input: [Field; 1]) -> Field +``` + +example: + +```rust title="poseidon" showLineNumbers +use std::hash::poseidon; + +fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field) { + let hash1 = poseidon::bn254::hash_2(x1); + assert(hash1 == y1); + + let hash2 = poseidon::bn254::hash_4(x2); + assert(hash2 == y2); +} +``` +> Source code: test_programs/execution_success/poseidon_bn254_hash/src/main.nr#L1-L11 + + +## poseidon 2 + +Given an array of Fields, returns a new Field with the Poseidon2 Hash. Contrary to the Poseidon +function, there is only one hash and you can specify a message_size to hash only the first +`message_size` bytes of the input, + +```rust +// example for hashing the first three elements of the input +Poseidon2::hash(input, 3); +``` + +example: + +```rust title="poseidon2" showLineNumbers +use std::hash::poseidon2; + +fn main(inputs: [Field; 4], expected_hash: Field) { + let hash = poseidon2::Poseidon2::hash(inputs, inputs.len()); + assert_eq(hash, expected_hash); +} +``` +> Source code: test_programs/execution_success/poseidon2/src/main.nr#L1-L8 + + +## hash_to_field + +```rust +fn hash_to_field(_input : [Field]) -> Field {} +``` + +Calculates the `blake2s` hash of the inputs and returns the hash modulo the field modulus to return +a value which can be represented as a `Field`. + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/index.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/index.md new file mode 100644 index 000000000000..650f30165d56 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/index.md @@ -0,0 +1,14 @@ +--- +title: Cryptographic Primitives +description: + Learn about the cryptographic primitives ready to use for any Noir project +keywords: + [ + cryptographic primitives, + Noir project, + ] +--- + +The Noir team is progressively adding new cryptographic primitives to the standard library. Reach out for news or if you would be interested in adding more of these calculations in Noir. + +Some methods are available thanks to the Aztec backend, not being performed using Noir. When using other backends, these methods may or may not be supplied. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/fmtstr.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/fmtstr.md new file mode 100644 index 000000000000..19809d60261d --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/fmtstr.md @@ -0,0 +1,17 @@ +--- +title: fmtstr +--- + +`fmtstr` is the type resulting from using format string (`f"..."`). + +## Methods + +### quoted_contents + +```rust title="quoted_contents" showLineNumbers +pub comptime fn quoted_contents(self) -> Quoted {} +``` +> Source code: noir_stdlib/src/meta/format_string.nr#L3-L5 + + +Returns the format string contents (that is, without the leading and trailing double quotes) as a `Quoted` value. \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/is_unconstrained.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/is_unconstrained.md new file mode 100644 index 000000000000..34b0698552b7 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/is_unconstrained.md @@ -0,0 +1,69 @@ +--- +title: Is Unconstrained Function +description: + The is_unconstrained function returns whether the context at that point of the program is unconstrained or not. +keywords: + [ + unconstrained + ] +--- + +It's very common for functions in circuits to take unconstrained hints of an expensive computation and then verify it. This is done by running the hint in an unconstrained context and then verifying the result in a constrained context. + +When a function is marked as unconstrained, any subsequent functions that it calls will also be run in an unconstrained context. However, if we are implementing a library function, other users might call it within an unconstrained context or a constrained one. Generally, in an unconstrained context we prefer just computing the result instead of taking a hint of it and verifying it, since that'd mean doing the same computation twice: + +```rust + +fn my_expensive_computation(){ + ... +} + +unconstrained fn my_expensive_computation_hint(){ + my_expensive_computation() +} + +pub fn external_interface(){ + my_expensive_computation_hint(); + // verify my_expensive_computation: If external_interface is called from unconstrained, this is redundant + ... +} + +``` + +In order to improve the performance in an unconstrained context you can use the function at `std::runtime::is_unconstrained() -> bool`: + + +```rust +use dep::std::runtime::is_unconstrained; + +fn my_expensive_computation(){ + ... +} + +unconstrained fn my_expensive_computation_hint(){ + my_expensive_computation() +} + +pub fn external_interface(){ + if is_unconstrained() { + my_expensive_computation(); + } else { + my_expensive_computation_hint(); + // verify my_expensive_computation + ... + } +} + +``` + +The is_unconstrained result is resolved at compile time, so in unconstrained contexts the compiler removes the else branch, and in constrained contexts the compiler removes the if branch, reducing the amount of compute necessary to run external_interface. + +Note that using `is_unconstrained` in a `comptime` context will also return `true`: + +``` +fn main() { + comptime { + assert(is_unconstrained()); + } +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/logging.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/logging.md new file mode 100644 index 000000000000..db75ef9f86fa --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/logging.md @@ -0,0 +1,78 @@ +--- +title: Logging +description: + Learn how to use the println statement for debugging in Noir with this tutorial. Understand the + basics of logging in Noir and how to implement it in your code. +keywords: + [ + noir logging, + println statement, + print statement, + debugging in noir, + noir std library, + logging tutorial, + basic logging in noir, + noir logging implementation, + noir debugging techniques, + rust, + ] +--- + +The standard library provides two familiar statements you can use: `println` and `print`. Despite being a limited implementation of rust's `println!` and `print!` macros, these constructs can be useful for debugging. + +You can print the output of both statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are print statements in your tests). + +It is recommended to use `nargo execute` if you want to debug failing constraints with `println` or `print` statements. This is due to every input in a test being a constant rather than a witness, so we issue an error during compilation while we only print during execution (which comes after compilation). Neither `println`, nor `print` are callable for failed constraints caught at compile time. + +Both `print` and `println` are generic functions which can work on integers, fields, strings, and even structs or expressions. Note however, that slices are currently unsupported. For example: + +```rust +struct Person { + age: Field, + height: Field, +} + +fn main(age: Field, height: Field) { + let person = Person { + age: age, + height: height, + }; + println(person); + println(age + height); + println("Hello world!"); +} +``` + +You can print different types in the same statement (including strings) with a type called `fmtstr`. It can be specified in the same way as a normal string, just prepended with an "f" character: + +```rust + let fmt_str = f"i: {i}, j: {j}"; + println(fmt_str); + + let s = myStruct { y: x, x: y }; + println(s); + + println(f"i: {i}, s: {s}"); + + println(x); + println([x, y]); + + let foo = fooStruct { my_struct: s, foo: 15 }; + println(f"s: {s}, foo: {foo}"); + + println(15); // prints 0x0f, implicit Field + println(-1 as u8); // prints 255 + println(-1 as i8); // prints -1 +``` + +Examples shown above are interchangeable between the two `print` statements: + +```rust +let person = Person { age : age, height : height }; + +println(person); +print(person); + +println("Hello world!"); // Prints with a newline at the end of the input +print("Hello world!"); // Prints the input and keeps cursor on the same line +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/mem.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/mem.md new file mode 100644 index 000000000000..1e9102b32dc3 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/mem.md @@ -0,0 +1,82 @@ +--- +title: Memory Module +description: + This module contains functions which manipulate memory in a low-level way +keywords: + [ + mem, memory, zeroed, transmute, checked_transmute + ] +--- + +# `std::mem::zeroed` + +```rust +fn zeroed() -> T +``` + +Returns a zeroed value of any type. +This function is generally unsafe to use as the zeroed bit pattern is not guaranteed to be valid for all types. +It can however, be useful in cases when the value is guaranteed not to be used such as in a BoundedVec library implementing a growable vector, up to a certain length, backed by an array. +The array can be initialized with zeroed values which are guaranteed to be inaccessible until the vector is pushed to. +Similarly, enumerations in noir can be implemented using this method by providing zeroed values for the unused variants. + +This function currently supports the following types: + +- Field +- Bool +- Uint +- Array +- Slice +- String +- Tuple +- Functions + +Using it on other types could result in unexpected behavior. + +# `std::mem::checked_transmute` + +```rust +fn checked_transmute(value: T) -> U +``` + +Transmutes a value of one type into the same value but with a new type `U`. + +This function is safe to use since both types are asserted to be equal later during compilation after the concrete values for generic types become known. +This function is useful for cases where the compiler may fail a type check that is expected to pass where +a user knows the two types to be equal. For example, when using arithmetic generics there are cases the compiler +does not see as equal, such as `[Field; N*(A + B)]` and `[Field; N*A + N*B]`, which users may know to be equal. +In these cases, `checked_transmute` can be used to cast the value to the desired type while also preserving safety +by checking this equality once `N`, `A`, `B` are fully resolved. + +Note that since this safety check is performed after type checking rather than during, no error is issued if the function +containing `checked_transmute` is never called. + +# `std::mem::array_refcount` + +```rust +fn array_refcount(array: [T; N]) -> u32 {} +``` + +Returns the internal reference count of an array value in unconstrained code. + +Arrays only have reference count in unconstrained code - using this anywhere +else will return zero. + +This function is mostly intended for debugging compiler optimizations but can also be used +to find where array copies may be happening in unconstrained code by placing it before array +mutations. + +# `std::mem::slice_refcount` + +```rust +fn slice_refcount(slice: [T]) -> u32 {} +``` + +Returns the internal reference count of a slice value in unconstrained code. + +Slices only have reference count in unconstrained code - using this anywhere +else will return zero. + +This function is mostly intended for debugging compiler optimizations but can also be used +to find where slice copies may be happening in unconstrained code by placing it before slice +mutations. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/merkle_trees.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/merkle_trees.md new file mode 100644 index 000000000000..6a9ebf72ada0 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/merkle_trees.md @@ -0,0 +1,58 @@ +--- +title: Merkle Trees +description: Learn about Merkle Trees in Noir with this tutorial. Explore the basics of computing a merkle root using a proof, with examples. +keywords: + [ + Merkle trees in Noir, + Noir programming language, + check membership, + computing root from leaf, + Noir Merkle tree implementation, + Merkle tree tutorial, + Merkle tree code examples, + Noir libraries, + pedersen hash., + ] +--- + +## compute_merkle_root + +Returns the root of the tree from the provided leaf and its hash path, using a [Pedersen hash](./cryptographic_primitives/hashes.mdx#pedersen_hash). + +```rust +fn compute_merkle_root(leaf : Field, index : Field, hash_path: [Field]) -> Field +``` + +example: + +```rust +/** + // these values are for this example only + index = "0" + priv_key = "0x000000000000000000000000000000000000000000000000000000616c696365" + secret = "0x1929ea3ab8d9106a899386883d9428f8256cfedb3c4f6b66bf4aa4d28a79988f" + note_hash_path = [ + "0x1e61bdae0f027b1b2159e1f9d3f8d00fa668a952dddd822fda80dc745d6f65cc", + "0x0e4223f3925f98934393c74975142bd73079ab0621f4ee133cee050a3c194f1a", + "0x2fd7bb412155bf8693a3bd2a3e7581a679c95c68a052f835dddca85fa1569a40" + ] + */ +fn main(index: Field, priv_key: Field, secret: Field, note_hash_path: [Field; 3]) { + + let pubkey = std::scalar_mul::fixed_base_embedded_curve(priv_key); + let pubkey_x = pubkey[0]; + let pubkey_y = pubkey[1]; + let note_commitment = std::hash::pedersen(&[pubkey_x, pubkey_y, secret]); + + let root = std::merkle::compute_merkle_root(note_commitment[0], index, note_hash_path.as_slice()); + println(root); +} +``` + +To check merkle tree membership: + +1. Include a merkle root as a program input. +2. Compute the merkle root of a given leaf, index and hash path. +3. Assert the merkle roots are equal. + +For more info about merkle trees, see the Wikipedia [page](https://en.wikipedia.org/wiki/Merkle_tree). diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/ctstring.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/ctstring.md new file mode 100644 index 000000000000..fe33486487ef --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/ctstring.md @@ -0,0 +1,100 @@ +--- +title: CtString +--- + +`std::meta::ctstring` contains methods on the built-in `CtString` type which is +a compile-time, dynamically-sized string type. Compared to `str` and `fmtstr`, +`CtString` is useful because its size does not need to be specified in its type. This +can be used for formatting items at compile-time or general string handling in `comptime` +code. + +Since `fmtstr`s can be converted into `CtString`s, you can make use of their formatting +abilities in CtStrings by formatting in `fmtstr`s then converting the result to a CtString +afterward. + +## Traits + +### AsCtString + +```rust title="as-ctstring" showLineNumbers +pub trait AsCtString { + comptime fn as_ctstring(self) -> CtString; +} +``` +> Source code: noir_stdlib/src/meta/ctstring.nr#L44-L48 + + +Converts an object into a compile-time string. + +Implementations: + +```rust +impl AsCtString for str { ... } +impl AsCtString for fmtstr { ... } +``` + +## Methods + +### new + +```rust title="new" showLineNumbers +pub comptime fn new() -> Self { +``` +> Source code: noir_stdlib/src/meta/ctstring.nr#L4-L6 + + +Creates an empty `CtString`. + +### append_str + +```rust title="append_str" showLineNumbers +pub comptime fn append_str(self, s: str) -> Self { +``` +> Source code: noir_stdlib/src/meta/ctstring.nr#L12-L14 + + +Returns a new CtString with the given str appended onto the end. + +### append_fmtstr + +```rust title="append_fmtstr" showLineNumbers +pub comptime fn append_fmtstr(self, s: fmtstr) -> Self { +``` +> Source code: noir_stdlib/src/meta/ctstring.nr#L18-L20 + + +Returns a new CtString with the given fmtstr appended onto the end. + +### as_quoted_str + +```rust title="as_quoted_str" showLineNumbers +pub comptime fn as_quoted_str(self) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/ctstring.nr#L27-L29 + + +Returns a quoted string literal from this string's contents. + +There is no direct conversion from a `CtString` to a `str` since +the size would not be known. To get around this, this function can +be used in combination with macro insertion (`!`) to insert this string +literal at this function's call site. + +Example: + +```rust title="as_quoted_str_example" showLineNumbers +let my_ctstring = "foo bar".as_ctstring(); + let my_str = my_ctstring.as_quoted_str!(); + + assert_eq(crate::meta::type_of(my_str), quote { str<7> }.as_type()); +``` +> Source code: noir_stdlib/src/meta/ctstring.nr#L95-L100 + + +## Trait Implementations + +```rust +impl Eq for CtString +impl Hash for CtString +impl Append for CtString +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/expr.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/expr.md new file mode 100644 index 000000000000..b6d395c6700c --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/expr.md @@ -0,0 +1,380 @@ +--- +title: Expr +--- + +`std::meta::expr` contains methods on the built-in `Expr` type for quoted, syntactically valid expressions. + +## Methods + +### as_array + +```rust title="as_array" showLineNumbers +pub comptime fn as_array(self) -> Option<[Expr]> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L10-L12 + + +If this expression is an array, this returns a slice of each element in the array. + +### as_assert + +```rust title="as_assert" showLineNumbers +pub comptime fn as_assert(self) -> Option<(Expr, Option)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L16-L18 + + +If this expression is an assert, this returns the assert expression and the optional message. + +### as_assert_eq + +```rust title="as_assert_eq" showLineNumbers +pub comptime fn as_assert_eq(self) -> Option<(Expr, Expr, Option)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L23-L25 + + +If this expression is an assert_eq, this returns the left-hand-side and right-hand-side +expressions, together with the optional message. + +### as_assign + +```rust title="as_assign" showLineNumbers +pub comptime fn as_assign(self) -> Option<(Expr, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L30-L32 + + +If this expression is an assignment, this returns a tuple with the left hand side +and right hand side in order. + +### as_binary_op + +```rust title="as_binary_op" showLineNumbers +pub comptime fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L37-L39 + + +If this expression is a binary operator operation ` `, +return the left-hand side, operator, and the right-hand side of the operation. + +### as_block + +```rust title="as_block" showLineNumbers +pub comptime fn as_block(self) -> Option<[Expr]> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L44-L46 + + +If this expression is a block `{ stmt1; stmt2; ...; stmtN }`, return +a slice containing each statement. + +### as_bool + +```rust title="as_bool" showLineNumbers +pub comptime fn as_bool(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L50-L52 + + +If this expression is a boolean literal, return that literal. + +### as_cast + +```rust title="as_cast" showLineNumbers +#[builtin(expr_as_cast)] + pub comptime fn as_cast(self) -> Option<(Expr, UnresolvedType)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L56-L59 + + +If this expression is a cast expression (`expr as type`), returns the casted +expression and the type to cast to. + +### as_comptime + +```rust title="as_comptime" showLineNumbers +pub comptime fn as_comptime(self) -> Option<[Expr]> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L64-L66 + + +If this expression is a `comptime { stmt1; stmt2; ...; stmtN }` block, +return each statement in the block. + +### as_constructor + +```rust title="as_constructor" showLineNumbers +pub comptime fn as_constructor(self) -> Option<(UnresolvedType, [(Quoted, Expr)])> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L71-L73 + + +If this expression is a constructor `Type { field1: expr1, ..., fieldN: exprN }`, +return the type and the fields. + +### as_for + +```rust title="as_for" showLineNumbers +pub comptime fn as_for(self) -> Option<(Quoted, Expr, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L78-L80 + + +If this expression is a for statement over a single expression, return the identifier, +the expression and the for loop body. + +### as_for_range + +```rust title="as_for" showLineNumbers +pub comptime fn as_for(self) -> Option<(Quoted, Expr, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L78-L80 + + +If this expression is a for statement over a range, return the identifier, +the range start, the range end and the for loop body. + +### as_function_call + +```rust title="as_function_call" showLineNumbers +pub comptime fn as_function_call(self) -> Option<(Expr, [Expr])> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L92-L94 + + +If this expression is a function call `foo(arg1, ..., argN)`, return +the function and a slice of each argument. + +### as_if + +```rust title="as_if" showLineNumbers +pub comptime fn as_if(self) -> Option<(Expr, Expr, Option)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L100-L102 + + +If this expression is an `if condition { then_branch } else { else_branch }`, +return the condition, then branch, and else branch. If there is no else branch, +`None` is returned for that branch instead. + +### as_index + +```rust title="as_index" showLineNumbers +pub comptime fn as_index(self) -> Option<(Expr, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L107-L109 + + +If this expression is an index into an array `array[index]`, return the +array and the index. + +### as_integer + +```rust title="as_integer" showLineNumbers +pub comptime fn as_integer(self) -> Option<(Field, bool)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L114-L116 + + +If this expression is an integer literal, return the integer as a field +as well as whether the integer is negative (true) or not (false). + +### as_lambda + +```rust title="as_lambda" showLineNumbers +pub comptime fn as_lambda( + self, + ) -> Option<([(Expr, Option)], Option, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L120-L124 + + +If this expression is a lambda, returns the parameters, return type and body. + +### as_let + +```rust title="as_let" showLineNumbers +pub comptime fn as_let(self) -> Option<(Expr, Option, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L129-L131 + + +If this expression is a let statement, returns the let pattern as an `Expr`, +the optional type annotation, and the assigned expression. + +### as_member_access + +```rust title="as_member_access" showLineNumbers +pub comptime fn as_member_access(self) -> Option<(Expr, Quoted)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L136-L138 + + +If this expression is a member access `foo.bar`, return the struct/tuple +expression and the field. The field will be represented as a quoted value. + +### as_method_call + +```rust title="as_method_call" showLineNumbers +pub comptime fn as_method_call(self) -> Option<(Expr, Quoted, [UnresolvedType], [Expr])> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L143-L145 + + +If this expression is a method call `foo.bar::(arg1, ..., argN)`, return +the receiver, method name, a slice of each generic argument, and a slice of each argument. + +### as_repeated_element_array + +```rust title="as_repeated_element_array" showLineNumbers +pub comptime fn as_repeated_element_array(self) -> Option<(Expr, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L150-L152 + + +If this expression is a repeated element array `[elem; length]`, return +the repeated element and the length expressions. + +### as_repeated_element_slice + +```rust title="as_repeated_element_slice" showLineNumbers +pub comptime fn as_repeated_element_slice(self) -> Option<(Expr, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L157-L159 + + +If this expression is a repeated element slice `[elem; length]`, return +the repeated element and the length expressions. + +### as_slice + +```rust title="as_slice" showLineNumbers +pub comptime fn as_slice(self) -> Option<[Expr]> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L164-L166 + + +If this expression is a slice literal `&[elem1, ..., elemN]`, +return each element of the slice. + +### as_tuple + +```rust title="as_tuple" showLineNumbers +pub comptime fn as_tuple(self) -> Option<[Expr]> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L171-L173 + + +If this expression is a tuple `(field1, ..., fieldN)`, +return each element of the tuple. + +### as_unary_op + +```rust title="as_unary_op" showLineNumbers +pub comptime fn as_unary_op(self) -> Option<(UnaryOp, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L178-L180 + + +If this expression is a unary operation ` `, +return the unary operator as well as the right-hand side expression. + +### as_unsafe + +```rust title="as_unsafe" showLineNumbers +pub comptime fn as_unsafe(self) -> Option<[Expr]> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L185-L187 + + +If this expression is an `unsafe { stmt1; ...; stmtN }` block, +return each statement inside in a slice. + +### has_semicolon + +```rust title="has_semicolon" showLineNumbers +pub comptime fn has_semicolon(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L206-L208 + + +`true` if this expression is trailed by a semicolon. E.g. + +``` +comptime { + let expr1 = quote { 1 + 2 }.as_expr().unwrap(); + let expr2 = quote { 1 + 2; }.as_expr().unwrap(); + + assert(expr1.as_binary_op().is_some()); + assert(expr2.as_binary_op().is_some()); + + assert(!expr1.has_semicolon()); + assert(expr2.has_semicolon()); +} +``` + +### is_break + +```rust title="is_break" showLineNumbers +pub comptime fn is_break(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L212-L214 + + +`true` if this expression is `break`. + +### is_continue + +```rust title="is_continue" showLineNumbers +pub comptime fn is_continue(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L218-L220 + + +`true` if this expression is `continue`. + +### modify + +```rust title="modify" showLineNumbers +pub comptime fn modify(self, f: fn[Env](Expr) -> Option) -> Expr { +``` +> Source code: noir_stdlib/src/meta/expr.nr#L229-L231 + + +Applies a mapping function to this expression and to all of its sub-expressions. +`f` will be applied to each sub-expression first, then applied to the expression itself. + +This happens recursively for every expression within `self`. + +For example, calling `modify` on `(&[1], &[2, 3])` with an `f` that returns `Option::some` +for expressions that are integers, doubling them, would return `(&[2], &[4, 6])`. + +### quoted + +```rust title="quoted" showLineNumbers +pub comptime fn quoted(self) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/expr.nr#L266-L268 + + +Returns this expression as a `Quoted` value. It's the same as `quote { $self }`. + +### resolve + +```rust title="resolve" showLineNumbers +pub comptime fn resolve(self, in_function: Option) -> TypedExpr {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L282-L284 + + +Resolves and type-checks this expression and returns the result as a `TypedExpr`. + +The `in_function` argument specifies where the expression is resolved: +- If it's `none`, the expression is resolved in the function where `resolve` was called +- If it's `some`, the expression is resolved in the given function + +If any names used by this expression are not in scope or if there are any type errors, +this will give compiler errors as if the expression was written directly into +the current `comptime` function. \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/function_def.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/function_def.md new file mode 100644 index 000000000000..7c9615e6ab55 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/function_def.md @@ -0,0 +1,181 @@ +--- +title: FunctionDefinition +--- + +`std::meta::function_def` contains methods on the built-in `FunctionDefinition` type representing +a function definition in the source program. + +## Methods + +### add_attribute + +```rust title="add_attribute" showLineNumbers +pub comptime fn add_attribute(self, attribute: str) {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L3-L5 + + +Adds an attribute to the function. This is only valid +on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +### as_typed_expr + +```rust title="as_typed_expr" showLineNumbers +pub comptime fn as_typed_expr(self) -> TypedExpr {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L8-L10 + + +Returns this function as a `TypedExpr`, which can be unquoted. For example: + +```rust +let typed_expr = some_function.as_typed_expr(); +let _ = quote { $typed_expr(1, 2, 3); }; +``` + +### body + +```rust title="body" showLineNumbers +pub comptime fn body(self) -> Expr {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L13-L15 + + +Returns the body of the function as an expression. This is only valid +on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +### has_named_attribute + +```rust title="has_named_attribute" showLineNumbers +pub comptime fn has_named_attribute(self, name: str) -> bool {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L18-L20 + + +Returns true if this function has a custom attribute with the given name. + +### is_unconstrained + +```rust title="is_unconstrained" showLineNumbers +pub comptime fn is_unconstrained(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L23-L25 + + +Returns true if this function is unconstrained. + +### module + +```rust title="module" showLineNumbers +pub comptime fn module(self) -> Module {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L28-L30 + + +Returns the module where the function is defined. + +### name + +```rust title="name" showLineNumbers +pub comptime fn name(self) -> Quoted {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L33-L35 + + +Returns the name of the function. + +### parameters + +```rust title="parameters" showLineNumbers +pub comptime fn parameters(self) -> [(Quoted, Type)] {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L38-L40 + + +Returns each parameter of the function as a tuple of (parameter pattern, parameter type). + +### return_type + +```rust title="return_type" showLineNumbers +pub comptime fn return_type(self) -> Type {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L43-L45 + + +The return type of the function. + +### set_body + +```rust title="set_body" showLineNumbers +pub comptime fn set_body(self, body: Expr) {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L48-L50 + + +Mutate the function body to a new expression. This is only valid +on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +### set_parameters + +```rust title="set_parameters" showLineNumbers +pub comptime fn set_parameters(self, parameters: [(Quoted, Type)]) {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L53-L55 + + +Mutates the function's parameters to a new set of parameters. This is only valid +on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +Expects a slice of (parameter pattern, parameter type) for each parameter. Requires +each parameter pattern to be a syntactically valid parameter. + +### set_return_type + +```rust title="set_return_type" showLineNumbers +pub comptime fn set_return_type(self, return_type: Type) {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L58-L60 + + +Mutates the function's return type to a new type. This is only valid +on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +### set_return_public + +```rust title="set_return_public" showLineNumbers +pub comptime fn set_return_public(self, public: bool) {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L63-L65 + + +Mutates the function's return visibility to public (if `true` is given) or private (if `false` is given). +This is only valid on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +### set_unconstrained + +```rust title="set_unconstrained" showLineNumbers +pub comptime fn set_unconstrained(self, value: bool) {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L71-L73 + + +Mutates the function to be unconstrained (if `true` is given) or not (if `false` is given). +This is only valid on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +## Trait Implementations + +```rust +impl Eq for FunctionDefinition +impl Hash for FunctionDefinition +``` + +Note that each function is assigned a unique ID internally and this is what is used for +equality and hashing. So even functions with identical signatures and bodies may not +be equal in this sense if they were originally different items in the source program. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/index.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/index.md new file mode 100644 index 000000000000..1d94d4a0374e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/index.md @@ -0,0 +1,224 @@ +--- +title: Metaprogramming +description: Noir's Metaprogramming API +keywords: [metaprogramming, comptime, macros, macro, quote, unquote] +--- + +`std::meta` is the entry point for Noir's metaprogramming API. This consists of `comptime` functions +and types used for inspecting and modifying Noir programs. + +## Functions + +### type_of + +```rust title="type_of" showLineNumbers +pub comptime fn type_of(x: T) -> Type {} +``` +> Source code: noir_stdlib/src/meta/mod.nr#L29-L31 + + +Returns the type of a variable at compile-time. + +Example: +```rust +comptime { + let x: i32 = 1; + let x_type: Type = std::meta::type_of(x); + + assert_eq(x_type, quote { i32 }.as_type()); +} +``` + +### unquote + +```rust title="unquote" showLineNumbers +pub comptime fn unquote(code: Quoted) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/mod.nr#L21-L23 + + +Unquotes the passed-in token stream where this function was called. + +Example: +```rust +comptime { + let code = quote { 1 + 2 }; + + // let x = 1 + 2; + let x = unquote!(code); +} +``` + +### derive + +```rust title="derive" showLineNumbers +#[varargs] +pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/mod.nr#L50-L53 + + +Attribute placed on struct definitions. + +Creates a trait impl for each trait passed in as an argument. +To do this, the trait must have a derive handler registered +with `derive_via` beforehand. The traits in the stdlib that +can be derived this way are `Eq`, `Ord`, `Default`, and `Hash`. + +Example: +```rust +#[derive(Eq, Default)] +struct Foo { + x: i32, + y: T, +} + +fn main() { + let foo1 = Foo::default(); + let foo2 = Foo { x: 0, y: &[0] }; + assert_eq(foo1, foo2); +} +``` + +### derive_via + +```rust title="derive_via_signature" showLineNumbers +pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { +``` +> Source code: noir_stdlib/src/meta/mod.nr#L70-L72 + + +Attribute placed on trait definitions. + +Registers a function to create impls for the given trait +when the trait is used in a `derive` call. Users may use +this to register their own functions to enable their traits +to be derived by `derive`. + +Because this function requires a function as an argument which +should produce a trait impl for any given struct, users may find +it helpful to use a function like `std::meta::make_trait_impl` to +help creating these impls. + +Example: +```rust +#[derive_via(derive_do_nothing)] +trait DoNothing { + fn do_nothing(self); +} + +comptime fn derive_do_nothing(s: StructDefinition) -> Quoted { + let typ = s.as_type(); + quote { + impl DoNothing for $typ { + fn do_nothing(self) { + println("Nothing"); + } + } + } +} +``` + +As another example, `derive_eq` in the stdlib is used to derive the `Eq` +trait for any struct. It makes use of `make_trait_impl` to do this: + +```rust title="derive_eq" showLineNumbers +comptime fn derive_eq(s: StructDefinition) -> Quoted { + let signature = quote { fn eq(_self: Self, _other: Self) -> bool }; + let for_each_field = |name| quote { (_self.$name == _other.$name) }; + let body = |fields| { + if s.fields_as_written().len() == 0 { + quote { true } + } else { + fields + } + }; + crate::meta::make_trait_impl( + s, + quote { Eq }, + signature, + for_each_field, + quote { & }, + body, + ) +} +``` +> Source code: noir_stdlib/src/cmp.nr#L10-L30 + + +### make_trait_impl + +```rust title="make_trait_impl" showLineNumbers +pub comptime fn make_trait_impl( + s: StructDefinition, + trait_name: Quoted, + function_signature: Quoted, + for_each_field: fn[Env1](Quoted) -> Quoted, + join_fields_with: Quoted, + body: fn[Env2](Quoted) -> Quoted, +) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/mod.nr#L89-L98 + + +A helper function to more easily create trait impls while deriving traits. + +Note that this function only works for traits which: +1. Have only one method +2. Have no generics on the trait itself. + - E.g. Using this on a trait such as `trait Foo { ... }` will result in the + generated impl incorrectly missing the `T` generic. + +If your trait fits these criteria then `make_trait_impl` is likely the easiest +way to write your derive handler. The arguments are as follows: + +- `s`: The struct to make the impl for +- `trait_name`: The name of the trait to derive. E.g. `quote { Eq }`. +- `function_signature`: The signature of the trait method to derive. E.g. `fn eq(self, other: Self) -> bool`. +- `for_each_field`: An operation to be performed on each field. E.g. `|name| quote { (self.$name == other.$name) }`. +- `join_fields_with`: A separator to join each result of `for_each_field` with. + E.g. `quote { & }`. You can also use an empty `quote {}` for no separator. +- `body`: The result of the field operations is passed into this function for any final processing. + This is the place to insert any setup/teardown code the trait requires. If the trait doesn't require + any such code, you can return the body as-is: `|body| body`. + +Example deriving `Hash`: + +```rust title="derive_hash" showLineNumbers +comptime fn derive_hash(s: StructDefinition) -> Quoted { + let name = quote { Hash }; + let signature = quote { fn hash(_self: Self, _state: &mut H) where H: std::hash::Hasher }; + let for_each_field = |name| quote { _self.$name.hash(_state); }; + crate::meta::make_trait_impl( + s, + name, + signature, + for_each_field, + quote {}, + |fields| fields, + ) +} +``` +> Source code: noir_stdlib/src/hash/mod.nr#L138-L152 + + +Example deriving `Ord`: + +```rust title="derive_ord" showLineNumbers +comptime fn derive_ord(s: StructDefinition) -> Quoted { + let signature = quote { fn cmp(_self: Self, _other: Self) -> std::cmp::Ordering }; + let for_each_field = |name| quote { + if result == std::cmp::Ordering::equal() { + result = _self.$name.cmp(_other.$name); + } + }; + let body = |fields| quote { + let mut result = std::cmp::Ordering::equal(); + $fields + result + }; + crate::meta::make_trait_impl(s, quote { Ord }, signature, for_each_field, quote {}, body) +} +``` +> Source code: noir_stdlib/src/cmp.nr#L221-L236 + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/module.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/module.md new file mode 100644 index 000000000000..f47231972b7f --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/module.md @@ -0,0 +1,82 @@ +--- +title: Module +--- + +`std::meta::module` contains methods on the built-in `Module` type which represents a module in the source program. +Note that this type represents a module generally, it isn't limited to only `mod my_submodule { ... }` +declarations in the source program. + +## Methods + +### add_item + +```rust title="add_item" showLineNumbers +pub comptime fn add_item(self, item: Quoted) {} +``` +> Source code: noir_stdlib/src/meta/module.nr#L3-L5 + + +Adds a top-level item (a function, a struct, a global, etc.) to the module. +Adding multiple items in one go is also valid if the `Quoted` value has multiple items in it. +Note that the items are type-checked as if they are inside the module they are being added to. + +### functions + +```rust title="functions" showLineNumbers +pub comptime fn functions(self) -> [FunctionDefinition] {} +``` +> Source code: noir_stdlib/src/meta/module.nr#L18-L20 + + +Returns each function defined in the module. + +### has_named_attribute + +```rust title="has_named_attribute" showLineNumbers +pub comptime fn has_named_attribute(self, name: str) -> bool {} +``` +> Source code: noir_stdlib/src/meta/module.nr#L8-L10 + + +Returns true if this module has a custom attribute with the given name. + +### is_contract + +```rust title="is_contract" showLineNumbers +pub comptime fn is_contract(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/module.nr#L13-L15 + + +`true` if this module is a contract module (was declared via `contract foo { ... }`). + +### name + +```rust title="name" showLineNumbers +pub comptime fn name(self) -> Quoted {} +``` +> Source code: noir_stdlib/src/meta/module.nr#L28-L30 + + +Returns the name of the module. + +### structs + +```rust title="structs" showLineNumbers +pub comptime fn structs(self) -> [StructDefinition] {} +``` +> Source code: noir_stdlib/src/meta/module.nr#L23-L25 + + +Returns each struct defined in the module. + +## Trait Implementations + +```rust +impl Eq for Module +impl Hash for Module +``` + +Note that each module is assigned a unique ID internally and this is what is used for +equality and hashing. So even modules with identical names and contents may not +be equal in this sense if they were originally different items in the source program. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/op.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/op.md new file mode 100644 index 000000000000..5e3632890aee --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/op.md @@ -0,0 +1,244 @@ +--- +title: UnaryOp and BinaryOp +--- + +`std::meta::op` contains the `UnaryOp` and `BinaryOp` types as well as methods on them. +These types are used to represent a unary or binary operator respectively in Noir source code. + +## Types + +### UnaryOp + +Represents a unary operator. One of `-`, `!`, `&mut`, or `*`. + +### Methods + +#### is_minus + +```rust title="is_minus" showLineNumbers +pub fn is_minus(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L26-L28 + + +Returns `true` if this operator is `-`. + +#### is_not + +```rust title="is_not" showLineNumbers +pub fn is_not(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L32-L34 + + +`true` if this operator is `!` + +#### is_mutable_reference + +```rust title="is_mutable_reference" showLineNumbers +pub fn is_mutable_reference(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L38-L40 + + +`true` if this operator is `&mut` + +#### is_dereference + +```rust title="is_dereference" showLineNumbers +pub fn is_dereference(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L44-L46 + + +`true` if this operator is `*` + +#### quoted + +```rust title="unary_quoted" showLineNumbers +pub comptime fn quoted(self) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/op.nr#L50-L52 + + +Returns this operator as a `Quoted` value. + +### Trait Implementations + +```rust +impl Eq for UnaryOp +impl Hash for UnaryOp +``` + +### BinaryOp + +Represents a binary operator. One of `+`, `-`, `*`, `/`, `%`, `==`, `!=`, `<`, `<=`, `>`, `>=`, `&`, `|`, `^`, `>>`, or `<<`. + +### Methods + +#### is_add + +```rust title="is_add" showLineNumbers +pub fn is_add(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L88-L90 + + +`true` if this operator is `+` + +#### is_subtract + +```rust title="is_subtract" showLineNumbers +pub fn is_subtract(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L94-L96 + + +`true` if this operator is `-` + +#### is_multiply + +```rust title="is_multiply" showLineNumbers +pub fn is_multiply(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L100-L102 + + +`true` if this operator is `*` + +#### is_divide + +```rust title="is_divide" showLineNumbers +pub fn is_divide(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L106-L108 + + +`true` if this operator is `/` + +#### is_modulo + +```rust title="is_modulo" showLineNumbers +pub fn is_modulo(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L178-L180 + + +`true` if this operator is `%` + +#### is_equal + +```rust title="is_equal" showLineNumbers +pub fn is_equal(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L112-L114 + + +`true` if this operator is `==` + +#### is_not_equal + +```rust title="is_not_equal" showLineNumbers +pub fn is_not_equal(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L118-L120 + + +`true` if this operator is `!=` + +#### is_less_than + +```rust title="is_less_than" showLineNumbers +pub fn is_less_than(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L124-L126 + + +`true` if this operator is `<` + +#### is_less_than_or_equal + +```rust title="is_less_than_or_equal" showLineNumbers +pub fn is_less_than_or_equal(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L130-L132 + + +`true` if this operator is `<=` + +#### is_greater_than + +```rust title="is_greater_than" showLineNumbers +pub fn is_greater_than(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L136-L138 + + +`true` if this operator is `>` + +#### is_greater_than_or_equal + +```rust title="is_greater_than_or_equal" showLineNumbers +pub fn is_greater_than_or_equal(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L142-L144 + + +`true` if this operator is `>=` + +#### is_and + +```rust title="is_and" showLineNumbers +pub fn is_and(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L148-L150 + + +`true` if this operator is `&` + +#### is_or + +```rust title="is_or" showLineNumbers +pub fn is_or(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L154-L156 + + +`true` if this operator is `|` + +#### is_shift_right + +```rust title="is_shift_right" showLineNumbers +pub fn is_shift_right(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L166-L168 + + +`true` if this operator is `>>` + +#### is_shift_left + +```rust title="is_shift_left" showLineNumbers +pub fn is_shift_left(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L172-L174 + + +`true` if this operator is `<<` + +#### quoted + +```rust title="binary_quoted" showLineNumbers +pub comptime fn quoted(self) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/op.nr#L184-L186 + + +Returns this operator as a `Quoted` value. + +### Trait Implementations + +```rust +impl Eq for BinaryOp +impl Hash for BinaryOp +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/quoted.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/quoted.md new file mode 100644 index 000000000000..1914f71f4e7d --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/quoted.md @@ -0,0 +1,140 @@ +--- +title: Quoted +--- + +`std::meta::quoted` contains methods on the built-in `Quoted` type which represents +quoted token streams and is the result of the `quote { ... }` expression. + +## Methods + +### as_expr + +```rust title="as_expr" showLineNumbers +pub comptime fn as_expr(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/quoted.nr#L6-L8 + + +Parses the quoted token stream as an expression. Returns `Option::none()` if +the expression failed to parse. + +Example: + +```rust title="as_expr_example" showLineNumbers +#[test] + fn test_expr_as_function_call() { + comptime { + let expr = quote { foo(42) }.as_expr().unwrap(); + let (_function, args) = expr.as_function_call().unwrap(); + assert_eq(args.len(), 1); + assert_eq(args[0].as_integer().unwrap(), (42, false)); + } + } +``` +> Source code: test_programs/noir_test_success/comptime_expr/src/main.nr#L336-L346 + + +### as_module + +```rust title="as_module" showLineNumbers +pub comptime fn as_module(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/quoted.nr#L11-L13 + + +Interprets this token stream as a module path leading to the name of a module. +Returns `Option::none()` if the module isn't found or this token stream cannot be parsed as a path. + +Example: + +```rust title="as_module_example" showLineNumbers +mod baz { + pub mod qux {} +} + +#[test] +fn as_module_test() { + comptime { + let my_mod = quote { baz::qux }.as_module().unwrap(); + assert_eq(my_mod.name(), quote { qux }); + } +} +``` +> Source code: test_programs/compile_success_empty/comptime_module/src/main.nr#L115-L127 + + +### as_trait_constraint + +```rust title="as_trait_constraint" showLineNumbers +pub comptime fn as_trait_constraint(self) -> TraitConstraint {} +``` +> Source code: noir_stdlib/src/meta/quoted.nr#L16-L18 + + +Interprets this token stream as a trait constraint (without an object type). +Note that this function panics instead of returning `Option::none()` if the token +stream does not parse and resolve to a valid trait constraint. + +Example: + +```rust title="implements_example" showLineNumbers +pub fn function_with_where(_x: T) +where + T: SomeTrait, +{ + comptime { + let t = quote { T }.as_type(); + let some_trait_i32 = quote { SomeTrait }.as_trait_constraint(); + assert(t.implements(some_trait_i32)); + + assert(t.get_trait_impl(some_trait_i32).is_none()); + } +} +``` +> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L160-L173 + + +### as_type + +```rust title="as_type" showLineNumbers +pub comptime fn as_type(self) -> Type {} +``` +> Source code: noir_stdlib/src/meta/quoted.nr#L21-L23 + + +Interprets this token stream as a resolved type. Panics if the token +stream doesn't parse to a type or if the type isn't a valid type in scope. + +```rust title="implements_example" showLineNumbers +pub fn function_with_where(_x: T) +where + T: SomeTrait, +{ + comptime { + let t = quote { T }.as_type(); + let some_trait_i32 = quote { SomeTrait }.as_trait_constraint(); + assert(t.implements(some_trait_i32)); + + assert(t.get_trait_impl(some_trait_i32).is_none()); + } +} +``` +> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L160-L173 + + +### tokens + +```rust title="tokens" showLineNumbers +pub comptime fn tokens(self) -> [Quoted] {} +``` +> Source code: noir_stdlib/src/meta/quoted.nr#L26-L28 + + +Returns a slice of the individual tokens that form this token stream. + +## Trait Implementations + +```rust +impl Eq for Quoted +impl Hash for Quoted +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/struct_def.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/struct_def.md new file mode 100644 index 000000000000..5aa557871683 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/struct_def.md @@ -0,0 +1,199 @@ +--- +title: StructDefinition +--- + +`std::meta::struct_def` contains methods on the built-in `StructDefinition` type. +This type corresponds to `struct Name { field1: Type1, ... }` items in the source program. + +## Methods + +### add_attribute + +```rust title="add_attribute" showLineNumbers +pub comptime fn add_attribute(self, attribute: str) {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L5-L7 + + +Adds an attribute to the struct. + +### add_generic + +```rust title="add_generic" showLineNumbers +pub comptime fn add_generic(self, generic_name: str) -> Type {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L10-L12 + + +Adds an generic to the struct. Returns the new generic type. +Errors if the given generic name isn't a single identifier or if +the struct already has a generic with the same name. + +This method should be used carefully, if there is existing code referring +to the struct type it may be checked before this function is called and +see the struct with the original number of generics. This method should +thus be preferred to use on code generated from other macros and structs +that are not used in function signatures. + +Example: + +```rust title="add-generic-example" showLineNumbers +comptime fn add_generic(s: StructDefinition) { + assert_eq(s.generics().len(), 0); + let new_generic = s.add_generic("T"); + + let generics = s.generics(); + assert_eq(generics.len(), 1); + let (typ, numeric) = generics[0]; + assert_eq(typ, new_generic); + assert(numeric.is_none()); + } +``` +> Source code: test_programs/compile_success_empty/comptime_struct_definition/src/main.nr#L35-L46 + + +### as_type + +```rust title="as_type" showLineNumbers +pub comptime fn as_type(self) -> Type {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L17-L19 + + +Returns this struct as a type in the source program. If this struct has +any generics, the generics are also included as-is. + +### generics + +```rust title="generics" showLineNumbers +pub comptime fn generics(self) -> [(Type, Option)] {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L29-L31 + + +Returns each generic on this struct. Each generic is represented as a tuple containing the type, +and an optional containing the numeric type if it's a numeric generic. + +Example: + +``` +#[example] +struct Foo { + bar: [T; K], + baz: Baz, +} + +comptime fn example(foo: StructDefinition) { + assert_eq(foo.generics().len(), 3); + + // Fails because `T` isn't in scope + // let t = quote { T }.as_type(); + // assert_eq(foo.generics()[0].0, t); + assert(foo.generics()[0].1.is_none()); + + // Last generic is numeric, so we have the numeric type available to us + assert(foo.generics()[2].1.is_some()); +} +``` + +### fields + +```rust title="fields" showLineNumbers +pub comptime fn fields(self, generic_args: [Type]) -> [(Quoted, Type)] {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L37-L39 + + +Returns (name, type) pairs of each field in this struct. +Any generic types used in each field type is automatically substituted with the +provided generic arguments. + +### fields_as_written + +```rust title="fields_as_written" showLineNumbers +pub comptime fn fields_as_written(self) -> [(Quoted, Type)] {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L46-L48 + + +Returns (name, type) pairs of each field in this struct. Each type is as-is +with any generic arguments unchanged. Unless the field types are not needed, +users should generally prefer to use `StructDefinition::fields` over this +function if possible. + +### has_named_attribute + +```rust title="has_named_attribute" showLineNumbers +pub comptime fn has_named_attribute(self, name: str) -> bool {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L22-L24 + + +Returns true if this struct has a custom attribute with the given name. + +### module + +```rust title="module" showLineNumbers +pub comptime fn module(self) -> Module {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L51-L53 + + +Returns the module where the struct is defined. + +### name + +```rust title="name" showLineNumbers +pub comptime fn name(self) -> Quoted {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L56-L58 + + +Returns the name of this struct + +Note that the returned quoted value will be just the struct name, it will +not be the full path to the struct, nor will it include any generics. + +### set_fields + +```rust title="set_fields" showLineNumbers +pub comptime fn set_fields(self, new_fields: [(Quoted, Type)]) {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L65-L67 + + +Sets the fields of this struct to the given fields list where each element +is a pair of the field's name and the field's type. Expects each field name +to be a single identifier. Note that this will override any previous fields +on this struct. If those should be preserved, use `.fields()` to retrieve the +current fields on the struct type and append the new fields from there. + +Example: + +```rust +// Change this struct to: +// struct Foo { +// a: u32, +// b: i8, +// } +#[mangle_fields] +struct Foo { x: Field } + +comptime fn mangle_fields(s: StructDefinition) { + s.set_fields(&[ + (quote { a }, quote { u32 }.as_type()), + (quote { b }, quote { i8 }.as_type()), + ]); +} +``` + +## Trait Implementations + +```rust +impl Eq for StructDefinition +impl Hash for StructDefinition +``` + +Note that each struct is assigned a unique ID internally and this is what is used for +equality and hashing. So even structs with identical generics and fields may not +be equal in this sense if they were originally different items in the source program. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_constraint.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_constraint.md new file mode 100644 index 000000000000..3106f732b5a6 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_constraint.md @@ -0,0 +1,17 @@ +--- +title: TraitConstraint +--- + +`std::meta::trait_constraint` contains methods on the built-in `TraitConstraint` type which represents +a trait constraint that can be used to search for a trait implementation. This is similar +syntactically to just the trait itself, but can also contain generic arguments. E.g. `Eq`, `Default`, +`BuildHasher`. + +This type currently has no public methods but it can be used alongside `Type` in `implements` or `get_trait_impl`. + +## Trait Implementations + +```rust +impl Eq for TraitConstraint +impl Hash for TraitConstraint +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_def.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_def.md new file mode 100644 index 000000000000..e661d3af7f1a --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_def.md @@ -0,0 +1,26 @@ +--- +title: TraitDefinition +--- + +`std::meta::trait_def` contains methods on the built-in `TraitDefinition` type. This type +represents trait definitions such as `trait Foo { .. }` at the top-level of a program. + +## Methods + +### as_trait_constraint + +```rust title="as_trait_constraint" showLineNumbers +pub comptime fn as_trait_constraint(_self: Self) -> TraitConstraint {} +``` +> Source code: noir_stdlib/src/meta/trait_def.nr#L6-L8 + + +Converts this trait into a trait constraint. If there are any generics on this +trait, they will be kept as-is without instantiating or replacing them. + +## Trait Implementations + +```rust +impl Eq for TraitDefinition +impl Hash for TraitDefinition +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_impl.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_impl.md new file mode 100644 index 000000000000..355213507b41 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_impl.md @@ -0,0 +1,62 @@ +--- +title: TraitImpl +--- + +`std::meta::trait_impl` contains methods on the built-in `TraitImpl` type which represents a trait +implementation such as `impl Foo for Bar { ... }`. + +## Methods + +### trait_generic_args + +```rust title="trait_generic_args" showLineNumbers +pub comptime fn trait_generic_args(self) -> [Type] {} +``` +> Source code: noir_stdlib/src/meta/trait_impl.nr#L3-L5 + + +Returns any generic arguments on the trait of this trait implementation, if any. + +```rs +impl Foo for Bar { ... } + +comptime { + let bar_type = quote { Bar }.as_type(); + let foo = quote { Foo }.as_trait_constraint(); + + let my_impl: TraitImpl = bar_type.get_trait_impl(foo).unwrap(); + + let generics = my_impl.trait_generic_args(); + assert_eq(generics.len(), 2); + + assert_eq(generics[0].0, quote { i32 }.as_type()); + assert(generics[0].1.is_none()); + assert_eq(generics[1].0, quote { Field }.as_type()); + assert(generics[1].1.is_none()); +} +``` + +### methods + +```rust title="methods" showLineNumbers +pub comptime fn methods(self) -> [FunctionDefinition] {} +``` +> Source code: noir_stdlib/src/meta/trait_impl.nr#L8-L10 + + +Returns each method in this trait impl. + +Example: + +```rs +comptime { + let i32_type = quote { i32 }.as_type(); + let eq = quote { Eq }.as_trait_constraint(); + + let impl_eq_for_i32: TraitImpl = i32_type.get_trait_impl(eq).unwrap(); + let methods = impl_eq_for_i32.methods(); + + assert_eq(methods.len(), 1); + assert_eq(methods[0].name(), quote { eq }); +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typ.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typ.md new file mode 100644 index 000000000000..3a85a739a4c8 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typ.md @@ -0,0 +1,264 @@ +--- +title: Type +--- + +`std::meta::typ` contains methods on the built-in `Type` type used for representing +a type in the source program. + +## Functions + +```rust title="fresh_type_variable" showLineNumbers +pub comptime fn fresh_type_variable() -> Type {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L57-L59 + + +Creates and returns an unbound type variable. This is a special kind of type internal +to type checking which will type check with any other type. When it is type checked +against another type it will also be set to that type. For example, if `a` is a type +variable and we have the type equality `(a, i32) = (u8, i32)`, the compiler will set +`a` equal to `u8`. + +Unbound type variables will often be rendered as `_` while printing them. Bound type +variables will appear as the type they are bound to. + +This can be used in conjunction with functions which internally perform type checks +such as `Type::implements` or `Type::get_trait_impl` to potentially grab some of the types used. + +Note that calling `Type::implements` or `Type::get_trait_impl` on a type variable will always +fail. + +Example: + +```rust title="serialize-setup" showLineNumbers +trait Serialize {} + +impl Serialize<1> for Field {} + +impl Serialize for [T; N] +where + T: Serialize, +{} + +impl Serialize for (T, U) +where + T: Serialize, + U: Serialize, +{} +``` +> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L14-L29 + +```rust title="fresh-type-variable-example" showLineNumbers +let typevar1 = std::meta::typ::fresh_type_variable(); + let constraint = quote { Serialize<$typevar1> }.as_trait_constraint(); + let field_type = quote { Field }.as_type(); + + // Search for a trait impl (binding typevar1 to 1 when the impl is found): + assert(field_type.implements(constraint)); + + // typevar1 should be bound to the "1" generic now: + assert_eq(typevar1.as_constant().unwrap(), 1); + + // If we want to do the same with a different type, we need to + // create a new type variable now that `typevar1` is bound + let typevar2 = std::meta::typ::fresh_type_variable(); + let constraint = quote { Serialize<$typevar2> }.as_trait_constraint(); + let array_type = quote { [(Field, Field); 5] }.as_type(); + assert(array_type.implements(constraint)); + + // Now typevar2 should be bound to the serialized pair size 2 times the array length 5 + assert_eq(typevar2.as_constant().unwrap(), 10); +``` +> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L129-L149 + + +## Methods + +### as_array + +```rust title="as_array" showLineNumbers +pub comptime fn as_array(self) -> Option<(Type, Type)> {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L76-L78 + + +If this type is an array, return a pair of (element type, size type). + +Example: + +```rust +comptime { + let array_type = quote { [Field; 3] }.as_type(); + let (field_type, three_type) = array_type.as_array().unwrap(); + + assert(field_type.is_field()); + assert_eq(three_type.as_constant().unwrap(), 3); +} +``` + +### as_constant + +```rust title="as_constant" showLineNumbers +pub comptime fn as_constant(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L83-L85 + + +If this type is a constant integer (such as the `3` in the array type `[Field; 3]`), +return the numeric constant. + +### as_integer + +```rust title="as_integer" showLineNumbers +pub comptime fn as_integer(self) -> Option<(bool, u8)> {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L90-L92 + + +If this is an integer type, return a boolean which is `true` +if the type is signed, as well as the number of bits of this integer type. + +### as_mutable_reference + +```rust title="as_mutable_reference" showLineNumbers +comptime fn as_mutable_reference(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L96-L98 + + +If this is a mutable reference type `&mut T`, returns the mutable type `T`. + +### as_slice + +```rust title="as_slice" showLineNumbers +pub comptime fn as_slice(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L102-L104 + + +If this is a slice type, return the element type of the slice. + +### as_str + +```rust title="as_str" showLineNumbers +pub comptime fn as_str(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L108-L110 + + +If this is a `str` type, returns the length `N` as a type. + +### as_struct + +```rust title="as_struct" showLineNumbers +pub comptime fn as_struct(self) -> Option<(StructDefinition, [Type])> {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L114-L116 + + +If this is a struct type, returns the struct in addition to +any generic arguments on this type. + +### as_tuple + +```rust title="as_tuple" showLineNumbers +pub comptime fn as_tuple(self) -> Option<[Type]> {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L120-L122 + + +If this is a tuple type, returns each element type of the tuple. + +### get_trait_impl + +```rust title="get_trait_impl" showLineNumbers +pub comptime fn get_trait_impl(self, constraint: TraitConstraint) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L143-L145 + + +Retrieves the trait implementation that implements the given +trait constraint for this type. If the trait constraint is not +found, `None` is returned. Note that since the concrete trait implementation +for a trait constraint specified in a `where` clause is unknown, +this function will return `None` in these cases. If you only want to know +whether a type implements a trait, use `implements` instead. + +Example: + +```rust +comptime { + let field_type = quote { Field }.as_type(); + let default = quote { Default }.as_trait_constraint(); + + let the_impl: TraitImpl = field_type.get_trait_impl(default).unwrap(); + assert(the_impl.methods().len(), 1); +} +``` + +### implements + +```rust title="implements" showLineNumbers +pub comptime fn implements(self, constraint: TraitConstraint) -> bool {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L166-L168 + + +`true` if this type implements the given trait. Note that unlike +`get_trait_impl` this will also return true for any `where` constraints +in scope. + +Example: + +```rust +fn foo() where T: Default { + comptime { + let field_type = quote { Field }.as_type(); + let default = quote { Default }.as_trait_constraint(); + assert(field_type.implements(default)); + + let t = quote { T }.as_type(); + assert(t.implements(default)); + } +} +``` + +### is_bool + +```rust title="is_bool" showLineNumbers +pub comptime fn is_bool(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L172-L174 + + +`true` if this type is `bool`. + +### is_field + +```rust title="is_field" showLineNumbers +pub comptime fn is_field(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L178-L180 + + +`true` if this type is `Field`. + +### is_unit + +```rust title="is_unit" showLineNumbers +comptime fn is_unit(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L184-L186 + + +`true` if this type is the unit `()` type. + +## Trait Implementations + +```rust +impl Eq for Type +impl Hash for Type +``` +Note that this is syntactic equality, this is not the same as whether two types will type check +to be the same type. Unless type inference or generics are being used however, users should not +typically have to worry about this distinction unless `std::meta::typ::fresh_type_variable` is used. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typed_expr.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typed_expr.md new file mode 100644 index 000000000000..0db7dbfef610 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typed_expr.md @@ -0,0 +1,27 @@ +--- +title: TypedExpr +--- + +`std::meta::typed_expr` contains methods on the built-in `TypedExpr` type for resolved and type-checked expressions. + +## Methods + +### get_type + +```rust title="as_function_definition" showLineNumbers +pub comptime fn as_function_definition(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typed_expr.nr#L7-L9 + + +If this expression refers to a function definitions, returns it. Otherwise returns `Option::none()`. + +### get_type + +```rust title="get_type" showLineNumbers +pub comptime fn get_type(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typed_expr.nr#L13-L15 + + +Returns the type of the expression, or `Option::none()` if there were errors when the expression was previously resolved. \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/unresolved_type.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/unresolved_type.md new file mode 100644 index 000000000000..2826ec5ec0f0 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/unresolved_type.md @@ -0,0 +1,57 @@ +--- +title: UnresolvedType +--- + +`std::meta::unresolved_type` contains methods on the built-in `UnresolvedType` type for the syntax of types. + +## Methods + +### as_mutable_reference + +```rust title="as_mutable_reference" showLineNumbers +comptime fn as_mutable_reference(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/unresolved_type.nr#L8-L10 + + +If this is a mutable reference type `&mut T`, returns the mutable type `T`. + +### as_slice + +```rust title="as_slice" showLineNumbers +comptime fn as_slice(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/unresolved_type.nr#L14-L16 + + +If this is a slice `&[T]`, returns the element type `T`. + +### is_bool + +```rust title="is_bool" showLineNumbers +comptime fn is_bool(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/unresolved_type.nr#L20-L22 + + +Returns `true` if this type is `bool`. + +### is_field + +```rust title="is_field" showLineNumbers +pub comptime fn is_field(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/unresolved_type.nr#L26-L28 + + +Returns true if this type refers to the Field type. + +### is_unit + +```rust title="is_unit" showLineNumbers +comptime fn is_unit(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/unresolved_type.nr#L32-L34 + + +Returns true if this type is the unit `()` type. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/options.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/options.md new file mode 100644 index 000000000000..a1bd4e1de5fd --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/options.md @@ -0,0 +1,101 @@ +--- +title: Option Type +--- + +The `Option` type is a way to express that a value might be present (`Some(T))` or absent (`None`). It's a safer way to handle potential absence of values, compared to using nulls in many other languages. + +```rust +struct Option { + None, + Some(T), +} +``` + +The `Option` type, already imported into your Noir program, can be used directly: + +```rust +fn main() { + let none = Option::none(); + let some = Option::some(3); +} +``` + +See [this test](https://github.com/noir-lang/noir/blob/5cbfb9c4a06c8865c98ff2b594464b037d821a5c/crates/nargo_cli/tests/test_data/option/src/main.nr) for a more comprehensive set of examples of each of the methods described below. + +## Methods + +### none + +Constructs a none value. + +### some + +Constructs a some wrapper around a given value. + +### is_none + +Returns true if the Option is None. + +### is_some + +Returns true of the Option is Some. + +### unwrap + +Asserts `self.is_some()` and returns the wrapped value. + +### unwrap_unchecked + +Returns the inner value without asserting `self.is_some()`. This method can be useful within an if condition when we already know that `option.is_some()`. If the option is None, there is no guarantee what value will be returned, only that it will be of type T for an `Option`. + +### unwrap_or + +Returns the wrapped value if `self.is_some()`. Otherwise, returns the given default value. + +### unwrap_or_else + +Returns the wrapped value if `self.is_some()`. Otherwise, calls the given function to return a default value. + +### expect + +Asserts `self.is_some()` with a provided custom message and returns the contained `Some` value. The custom message is expected to be a format string. + +### map + +If self is `Some(x)`, this returns `Some(f(x))`. Otherwise, this returns `None`. + +### map_or + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns the given default value. + +### map_or_else + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns `default()`. + +### and + +Returns None if self is None. Otherwise, this returns `other`. + +### and_then + +If self is None, this returns None. Otherwise, this calls the given function with the Some value contained within self, and returns the result of that call. In some languages this function is called `flat_map` or `bind`. + +### or + +If self is Some, return self. Otherwise, return `other`. + +### or_else + +If self is Some, return self. Otherwise, return `default()`. + +### xor + +If only one of the two Options is Some, return that option. Otherwise, if both options are Some or both are None, None is returned. + +### filter + +Returns `Some(x)` if self is `Some(x)` and `predicate(x)` is true. Otherwise, this returns `None`. + +### flatten + +Flattens an `Option>` into a `Option`. This returns `None` if the outer Option is None. Otherwise, this returns the inner Option. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/recursion.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/recursion.mdx new file mode 100644 index 000000000000..fcb362780606 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/recursion.mdx @@ -0,0 +1,67 @@ +--- +title: Recursive Proofs +description: Learn about how to write recursive proofs in Noir. +keywords: [recursion, recursive proofs, verification_key, verify_proof] +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; + +Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. + +Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) + +## Verifying Recursive Proofs + +```rust +#[foreign(recursive_aggregation)] +pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} +``` + + + +## Example usage + +```rust + +fn main( + verification_key : [Field; 114], + proof : [Field; 93], + public_inputs : [Field; 1], + key_hash : Field, + proof_b : [Field; 93], +) { + std::verify_proof( + verification_key, + proof, + public_inputs, + key_hash + ); + + std::verify_proof( + verification_key, + proof_b, + public_inputs, + key_hash + ); +} +``` + +You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). + +## Parameters + +### `verification_key` + +The verification key for the zk program that is being verified. + +### `proof` + +The proof for the zk program that is being verified. + +### `public_inputs` + +These represent the public inputs of the proof we are verifying. + +### `key_hash` + +A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/traits.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/traits.md new file mode 100644 index 000000000000..9f447ad82a83 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/traits.md @@ -0,0 +1,655 @@ +--- +title: Traits +description: Noir's stdlib provides a few commonly used traits. +keywords: [traits, trait, interface, protocol, default, add, eq] +--- + +## `std::default` + +### `std::default::Default` + +```rust title="default-trait" showLineNumbers +pub trait Default { + fn default() -> Self; +} +``` +> Source code: noir_stdlib/src/default.nr#L4-L8 + + +Constructs a default value of a type. + +Implementations: +```rust +impl Default for Field { .. } + +impl Default for i8 { .. } +impl Default for i16 { .. } +impl Default for i32 { .. } +impl Default for i64 { .. } + +impl Default for u8 { .. } +impl Default for u16 { .. } +impl Default for u32 { .. } +impl Default for u64 { .. } + +impl Default for () { .. } +impl Default for bool { .. } + +impl Default for [T; N] + where T: Default { .. } + +impl Default for [T] { .. } + +impl Default for (A, B) + where A: Default, B: Default { .. } + +impl Default for (A, B, C) + where A: Default, B: Default, C: Default { .. } + +impl Default for (A, B, C, D) + where A: Default, B: Default, C: Default, D: Default { .. } + +impl Default for (A, B, C, D, E) + where A: Default, B: Default, C: Default, D: Default, E: Default { .. } +``` + +For primitive integer types, the return value of `default` is `0`. Container +types such as arrays are filled with default values of their element type, +except slices whose length is unknown and thus defaulted to zero. + +--- + +## `std::convert` + +### `std::convert::From` + +```rust title="from-trait" showLineNumbers +pub trait From { + fn from(input: T) -> Self; +} +``` +> Source code: noir_stdlib/src/convert.nr#L1-L5 + + +The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented. + +The Noir standard library provides a number of implementations of `From` between primitive types. +```rust title="from-impls" showLineNumbers +// Unsigned integers + +impl From for u32 { + fn from(value: u8) -> u32 { + value as u32 + } +} + +impl From for u64 { + fn from(value: u8) -> u64 { + value as u64 + } +} +impl From for u64 { + fn from(value: u32) -> u64 { + value as u64 + } +} + +impl From for u128 { + fn from(value: u8) -> u128 { + value as u128 + } +} +impl From for u128 { + fn from(value: u32) -> u128 { + value as u128 + } +} +impl From for u128 { + fn from(value: u64) -> u128 { + value as u128 + } +} + +impl From for Field { + fn from(value: u8) -> Field { + value as Field + } +} +impl From for Field { + fn from(value: u32) -> Field { + value as Field + } +} +impl From for Field { + fn from(value: u64) -> Field { + value as Field + } +} + +impl From for Field { + fn from(value: u128) -> Field { + value as Field + } +} + +// Signed integers + +impl From for i32 { + fn from(value: i8) -> i32 { + value as i32 + } +} + +impl From for i64 { + fn from(value: i8) -> i64 { + value as i64 + } +} +impl From for i64 { + fn from(value: i32) -> i64 { + value as i64 + } +} + +// Booleans +impl From for u8 { + fn from(value: bool) -> u8 { + value as u8 + } +} +impl From for u32 { + fn from(value: bool) -> u32 { + value as u32 + } +} +impl From for u64 { + fn from(value: bool) -> u64 { + value as u64 + } +} +impl From for i8 { + fn from(value: bool) -> i8 { + value as i8 + } +} +impl From for i32 { + fn from(value: bool) -> i32 { + value as i32 + } +} +impl From for i64 { + fn from(value: bool) -> i64 { + value as i64 + } +} +impl From for Field { + fn from(value: bool) -> Field { + value as Field + } +} +``` +> Source code: noir_stdlib/src/convert.nr#L28-L141 + + +#### When to implement `From` + +As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from): + +- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. +- The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. +- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. +- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. + +One additional recommendation specific to Noir is: +- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. + +### `std::convert::Into` + +The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`. + +For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation. + +```rust title="into-trait" showLineNumbers +pub trait Into { + fn into(self) -> T; +} + +impl Into for U +where + T: From, +{ + fn into(self) -> T { + T::from(self) + } +} +``` +> Source code: noir_stdlib/src/convert.nr#L13-L26 + + +`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. + +--- + +## `std::cmp` + +### `std::cmp::Eq` + +```rust title="eq-trait" showLineNumbers +pub trait Eq { + fn eq(self, other: Self) -> bool; +} +``` +> Source code: noir_stdlib/src/cmp.nr#L4-L8 + + +Returns `true` if `self` is equal to `other`. Implementing this trait on a type +allows the type to be used with `==` and `!=`. + +Implementations: +```rust +impl Eq for Field { .. } + +impl Eq for i8 { .. } +impl Eq for i16 { .. } +impl Eq for i32 { .. } +impl Eq for i64 { .. } + +impl Eq for u8 { .. } +impl Eq for u16 { .. } +impl Eq for u32 { .. } +impl Eq for u64 { .. } + +impl Eq for () { .. } +impl Eq for bool { .. } + +impl Eq for [T; N] + where T: Eq { .. } + +impl Eq for [T] + where T: Eq { .. } + +impl Eq for (A, B) + where A: Eq, B: Eq { .. } + +impl Eq for (A, B, C) + where A: Eq, B: Eq, C: Eq { .. } + +impl Eq for (A, B, C, D) + where A: Eq, B: Eq, C: Eq, D: Eq { .. } + +impl Eq for (A, B, C, D, E) + where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } +``` + +### `std::cmp::Ord` + +```rust title="ord-trait" showLineNumbers +pub trait Ord { + fn cmp(self, other: Self) -> Ordering; +} +``` +> Source code: noir_stdlib/src/cmp.nr#L215-L219 + + +`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, +`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. +Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be +used on values of the type. + +`std::cmp` also provides `max` and `min` functions for any type which implements the `Ord` trait. + +Implementations: + +```rust +impl Ord for u8 { .. } +impl Ord for u16 { .. } +impl Ord for u32 { .. } +impl Ord for u64 { .. } + +impl Ord for i8 { .. } +impl Ord for i16 { .. } +impl Ord for i32 { .. } + +impl Ord for i64 { .. } + +impl Ord for () { .. } +impl Ord for bool { .. } + +impl Ord for [T; N] + where T: Ord { .. } + +impl Ord for [T] + where T: Ord { .. } + +impl Ord for (A, B) + where A: Ord, B: Ord { .. } + +impl Ord for (A, B, C) + where A: Ord, B: Ord, C: Ord { .. } + +impl Ord for (A, B, C, D) + where A: Ord, B: Ord, C: Ord, D: Ord { .. } + +impl Ord for (A, B, C, D, E) + where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } +``` + +--- + +## `std::ops` + +### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` + +These traits abstract over addition, subtraction, multiplication, and division respectively. +Implementing these traits for a given type will also allow that type to be used with the corresponding operator +for that trait (`+` for Add, etc) in addition to the normal method names. + +```rust title="add-trait" showLineNumbers +pub trait Add { + fn add(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L1-L5 + +```rust title="sub-trait" showLineNumbers +pub trait Sub { + fn sub(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L65-L69 + +```rust title="mul-trait" showLineNumbers +pub trait Mul { + fn mul(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L129-L133 + +```rust title="div-trait" showLineNumbers +pub trait Div { + fn div(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L193-L197 + + +The implementations block below is given for the `Add` trait, but the same types that implement +`Add` also implement `Sub`, `Mul`, and `Div`. + +Implementations: +```rust +impl Add for Field { .. } + +impl Add for i8 { .. } +impl Add for i16 { .. } +impl Add for i32 { .. } +impl Add for i64 { .. } + +impl Add for u8 { .. } +impl Add for u16 { .. } +impl Add for u32 { .. } +impl Add for u64 { .. } +``` + +### `std::ops::Rem` + +```rust title="rem-trait" showLineNumbers +pub trait Rem { + fn rem(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L257-L261 + + +`Rem::rem(a, b)` is the remainder function returning the result of what is +left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator +to be used with the implementation type. + +Unlike other numeric traits, `Rem` is not implemented for `Field`. + +Implementations: +```rust +impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } +impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } +impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } +impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } + +impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } +impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } +impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } +impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } +``` + +### `std::ops::Neg` + +```rust title="neg-trait" showLineNumbers +pub trait Neg { + fn neg(self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L315-L319 + + +`Neg::neg` is equivalent to the unary negation operator `-`. + +Implementations: +```rust title="neg-trait-impls" showLineNumbers +impl Neg for Field { + fn neg(self) -> Field { + -self + } +} + +impl Neg for i8 { + fn neg(self) -> i8 { + -self + } +} +impl Neg for i16 { + fn neg(self) -> i16 { + -self + } +} +impl Neg for i32 { + fn neg(self) -> i32 { + -self + } +} +impl Neg for i64 { + fn neg(self) -> i64 { + -self + } +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L321-L348 + + +### `std::ops::Not` + +```rust title="not-trait" showLineNumbers +pub trait Not { + fn not(self: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L1-L5 + + +`Not::not` is equivalent to the unary bitwise NOT operator `!`. + +Implementations: +```rust title="not-trait-impls" showLineNumbers +impl Not for bool { + fn not(self) -> bool { + !self + } +} + +impl Not for u128 { + fn not(self) -> u128 { + !self + } +} +impl Not for u64 { + fn not(self) -> u64 { + !self + } +} +impl Not for u32 { + fn not(self) -> u32 { + !self + } +} +impl Not for u16 { + fn not(self) -> u16 { + !self + } +} +impl Not for u8 { + fn not(self) -> u8 { + !self + } +} +impl Not for u1 { + fn not(self) -> u1 { + !self + } +} + +impl Not for i8 { + fn not(self) -> i8 { + !self + } +} +impl Not for i16 { + fn not(self) -> i16 { + !self + } +} +impl Not for i32 { + fn not(self) -> i32 { + !self + } +} +impl Not for i64 { + fn not(self) -> i64 { + !self + } +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L7-L65 + + +### `std::ops::{ BitOr, BitAnd, BitXor }` + +```rust title="bitor-trait" showLineNumbers +pub trait BitOr { + fn bitor(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L67-L71 + +```rust title="bitand-trait" showLineNumbers +pub trait BitAnd { + fn bitand(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L131-L135 + +```rust title="bitxor-trait" showLineNumbers +pub trait BitXor { + fn bitxor(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L195-L199 + + +Traits for the bitwise operations `|`, `&`, and `^`. + +Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively +to be used with the type. + +The implementations block below is given for the `BitOr` trait, but the same types that implement +`BitOr` also implement `BitAnd` and `BitXor`. + +Implementations: +```rust +impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } + +impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } +impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } +impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } +impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } + +impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } +impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } +impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } +impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } +``` + +### `std::ops::{ Shl, Shr }` + +```rust title="shl-trait" showLineNumbers +pub trait Shl { + fn shl(self, other: u8) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L259-L263 + +```rust title="shr-trait" showLineNumbers +pub trait Shr { + fn shr(self, other: u8) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L317-L321 + + +Traits for a bit shift left and bit shift right. + +Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. +Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. + +Note that bit shifting is not currently implemented for signed types. + +The implementations block below is given for the `Shl` trait, but the same types that implement +`Shl` also implement `Shr`. + +Implementations: +```rust +impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } +impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } +impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } +impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } +``` + +--- + +## `std::append` + +### `std::append::Append` + +`Append` can abstract over types that can be appended to - usually container types: + +```rust title="append-trait" showLineNumbers +pub trait Append { + fn empty() -> Self; + fn append(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/append.nr#L9-L14 + + +`Append` requires two methods: + +- `empty`: Constructs an empty value of `Self`. +- `append`: Append two values together, returning the result. + +Additionally, it is expected that for any implementation: + +- `T::empty().append(x) == x` +- `x.append(T::empty()) == x` + +Implementations: +```rust +impl Append for [T] +impl Append for Quoted +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/.nojekyll b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/.nojekyll new file mode 100644 index 000000000000..e2ac6616addc --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/classes/Noir.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/classes/Noir.md new file mode 100644 index 000000000000..ead255bc5047 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/classes/Noir.md @@ -0,0 +1,52 @@ +# Noir + +## Constructors + +### new Noir(circuit) + +```ts +new Noir(circuit): Noir +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `circuit` | `CompiledCircuit` | + +#### Returns + +[`Noir`](Noir.md) + +## Methods + +### execute() + +```ts +execute(inputs, foreignCallHandler?): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | `InputMap` | +| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Allows to execute a circuit to get its witness and return value. + +#### Example + +```typescript +async execute(inputs) +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/and.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/and.md new file mode 100644 index 000000000000..c783283e3965 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/and.md @@ -0,0 +1,22 @@ +# and() + +```ts +and(lhs, rhs): string +``` + +Performs a bitwise AND operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/blake2s256.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/blake2s256.md new file mode 100644 index 000000000000..7882d0da8d50 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/blake2s256.md @@ -0,0 +1,21 @@ +# blake2s256() + +```ts +blake2s256(inputs): Uint8Array +``` + +Calculates the Blake2s256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md new file mode 100644 index 000000000000..5e3cd53e9d36 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256k1\_verify() + +```ts +ecdsa_secp256k1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256k1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md new file mode 100644 index 000000000000..0b20ff689575 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256r1\_verify() + +```ts +ecdsa_secp256r1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256r1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/xor.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/xor.md new file mode 100644 index 000000000000..8d762b895d30 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/xor.md @@ -0,0 +1,22 @@ +# xor() + +```ts +xor(lhs, rhs): string +``` + +Performs a bitwise XOR operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/index.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/index.md new file mode 100644 index 000000000000..4de7a6969910 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/index.md @@ -0,0 +1,47 @@ +# noir_js + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [Noir](classes/Noir.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [ErrorWithPayload](type-aliases/ErrorWithPayload.md) | - | +| [ForeignCallHandler](type-aliases/ForeignCallHandler.md) | A callback which performs an foreign call and returns the response. | +| [ForeignCallInput](type-aliases/ForeignCallInput.md) | - | +| [ForeignCallOutput](type-aliases/ForeignCallOutput.md) | - | +| [WitnessMap](type-aliases/WitnessMap.md) | - | + +### Functions + +| Function | Description | +| :------ | :------ | +| [and](functions/and.md) | Performs a bitwise AND operation between `lhs` and `rhs` | +| [blake2s256](functions/blake2s256.md) | Calculates the Blake2s256 hash of the input bytes | +| [ecdsa\_secp256k1\_verify](functions/ecdsa_secp256k1_verify.md) | Verifies a ECDSA signature over the secp256k1 curve. | +| [ecdsa\_secp256r1\_verify](functions/ecdsa_secp256r1_verify.md) | Verifies a ECDSA signature over the secp256r1 curve. | +| [xor](functions/xor.md) | Performs a bitwise XOR operation between `lhs` and `rhs` | + +## References + +### CompiledCircuit + +Renames and re-exports [InputMap](index.md#inputmap) + +## Variables + +### InputMap + +```ts +InputMap: any; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md new file mode 100644 index 000000000000..e8c2f4aef3d5 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md @@ -0,0 +1,15 @@ +# ErrorWithPayload + +```ts +type ErrorWithPayload: ExecutionError & object; +``` + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `decodedAssertionPayload` | `any` | - | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md new file mode 100644 index 000000000000..812b8b164818 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md @@ -0,0 +1,24 @@ +# ForeignCallHandler + +```ts +type ForeignCallHandler: (name, inputs) => Promise; +``` + +A callback which performs an foreign call and returns the response. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | The identifier for the type of foreign call being performed. | +| `inputs` | [`ForeignCallInput`](ForeignCallInput.md)[] | An array of hex encoded inputs to the foreign call. | + +## Returns + +`Promise`\<[`ForeignCallOutput`](ForeignCallOutput.md)[]\> + +outputs - An array of hex encoded outputs containing the results of the foreign call. + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md new file mode 100644 index 000000000000..dd95809186a2 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md @@ -0,0 +1,9 @@ +# ForeignCallInput + +```ts +type ForeignCallInput: string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md new file mode 100644 index 000000000000..b71fb78a9469 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md @@ -0,0 +1,9 @@ +# ForeignCallOutput + +```ts +type ForeignCallOutput: string | string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/WitnessMap.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/WitnessMap.md new file mode 100644 index 000000000000..258c46f9d0c9 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/WitnessMap.md @@ -0,0 +1,9 @@ +# WitnessMap + +```ts +type WitnessMap: Map; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/typedoc-sidebar.cjs b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/typedoc-sidebar.cjs new file mode 100644 index 000000000000..4796b5abaa86 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ErrorWithPayload","label":"ErrorWithPayload"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_js/functions/and","label":"and"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/xor","label":"xor"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/.nojekyll b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/.nojekyll new file mode 100644 index 000000000000..e2ac6616addc --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile.md new file mode 100644 index 000000000000..6faf763b37f7 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile.md @@ -0,0 +1,51 @@ +# compile() + +```ts +compile( + fileManager, + projectPath?, + logFn?, +debugLogFn?): Promise +``` + +Compiles a Noir project + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `fileManager` | `FileManager` | The file manager to use | +| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | +| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | +| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | + +## Returns + +`Promise`\<[`ProgramCompilationArtifacts`](../index.md#programcompilationartifacts)\> + +## Example + +```typescript +// Node.js + +import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager(myProjectPath); +const myCompiledCode = await compile_program(fm); +``` + +```typescript +// Browser + +import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager('/'); +for (const path of files) { + await fm.writeFile(path, await getFileAsStream(path)); +} +const myCompiledCode = await compile_program(fm); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile_contract.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile_contract.md new file mode 100644 index 000000000000..7d0b39a43ef8 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile_contract.md @@ -0,0 +1,51 @@ +# compile\_contract() + +```ts +compile_contract( + fileManager, + projectPath?, + logFn?, +debugLogFn?): Promise +``` + +Compiles a Noir project + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `fileManager` | `FileManager` | The file manager to use | +| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | +| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | +| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | + +## Returns + +`Promise`\<[`ContractCompilationArtifacts`](../index.md#contractcompilationartifacts)\> + +## Example + +```typescript +// Node.js + +import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager(myProjectPath); +const myCompiledCode = await compile_contract(fm); +``` + +```typescript +// Browser + +import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager('/'); +for (const path of files) { + await fm.writeFile(path, await getFileAsStream(path)); +} +const myCompiledCode = await compile_contract(fm); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/createFileManager.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/createFileManager.md new file mode 100644 index 000000000000..7e65c1d69c7e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/createFileManager.md @@ -0,0 +1,21 @@ +# createFileManager() + +```ts +createFileManager(dataDir): FileManager +``` + +Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `dataDir` | `string` | root of the file system | + +## Returns + +`FileManager` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md new file mode 100644 index 000000000000..fcea92753412 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md @@ -0,0 +1,21 @@ +# inflateDebugSymbols() + +```ts +inflateDebugSymbols(debugSymbols): any +``` + +Decompresses and decodes the debug symbols + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `debugSymbols` | `string` | The base64 encoded debug symbols | + +## Returns + +`any` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/index.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/index.md new file mode 100644 index 000000000000..b6e0f9d1bc0e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/index.md @@ -0,0 +1,49 @@ +# noir_wasm + +## Exports + +### Functions + +| Function | Description | +| :------ | :------ | +| [compile](functions/compile.md) | Compiles a Noir project | +| [compile\_contract](functions/compile_contract.md) | Compiles a Noir project | +| [createFileManager](functions/createFileManager.md) | Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) | +| [inflateDebugSymbols](functions/inflateDebugSymbols.md) | Decompresses and decodes the debug symbols | + +## References + +### compile\_program + +Renames and re-exports [compile](functions/compile.md) + +## Interfaces + +### ContractCompilationArtifacts + +The compilation artifacts of a given contract. + +#### Properties + +| Property | Type | Description | +| :------ | :------ | :------ | +| `contract` | `ContractArtifact` | The compiled contract. | +| `warnings` | `unknown`[] | Compilation warnings. | + +*** + +### ProgramCompilationArtifacts + +The compilation artifacts of a given program. + +#### Properties + +| Property | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | not part of the compilation output, injected later | +| `program` | `ProgramArtifact` | The compiled contract. | +| `warnings` | `unknown`[] | Compilation warnings. | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs new file mode 100644 index 000000000000..e0870710349c --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"doc","id":"reference/NoirJS/noir_wasm/index","label":"API"},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile","label":"compile"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile_contract","label":"compile_contract"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/createFileManager","label":"createFileManager"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/inflateDebugSymbols","label":"inflateDebugSymbols"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/_category_.json new file mode 100644 index 000000000000..5b6a20a609af --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 4, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/_category_.json new file mode 100644 index 000000000000..27869205ad3c --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Debugger", + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_known_limitations.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_known_limitations.md new file mode 100644 index 000000000000..936d416ac4bc --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_known_limitations.md @@ -0,0 +1,59 @@ +--- +title: Known limitations +description: + An overview of known limitations of the current version of the Noir debugger +keywords: + [ + Nargo, + Noir Debugger, + VS Code, + ] +sidebar_position: 2 +--- + +# Debugger Known Limitations + +There are currently some limits to what the debugger can observe. + +## Mutable references + +The debugger is currently blind to any state mutated via a mutable reference. For example, in: + +``` +let mut x = 1; +let y = &mut x; +*y = 2; +``` + +The update on `x` will not be observed by the debugger. That means, when running `vars` from the debugger REPL, or inspecting the _local variables_ pane in the VS Code debugger, `x` will appear with value 1 despite having executed `*y = 2;`. + +## Variables of type function or mutable references are opaque + +When inspecting variables, any variable of type `Function` or `MutableReference` will render its value as `<>` or `<>`. + +## Debugger instrumentation affects resulting ACIR + +In order to make the state of local variables observable, the debugger compiles Noir circuits interleaving foreign calls that track any mutations to them. While this works (except in the cases described above) and doesn't introduce any behavior changes, it does as a side effect produce bigger bytecode. In particular, when running the command `opcodes` on the REPL debugger, you will notice Unconstrained VM blocks that look like this: + +``` +... +5 BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [], q_c: 2 }), Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(2))], q_c: 0 })] + | outputs=[] + 5.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 5.1 | Mov { destination: RegisterIndex(3), source: RegisterIndex(1) } + 5.2 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 5.3 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 5.4 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 5.5 | Mov { destination: RegisterIndex(3), source: RegisterIndex(3) } + 5.6 | Call { location: 8 } + 5.7 | Stop + 5.8 | ForeignCall { function: "__debug_var_assign", destinations: [], inputs: [RegisterIndex(RegisterIndex(2)), RegisterIndex(RegisterIndex(3))] } +... +``` + +If you are interested in debugging/inspecting compiled ACIR without these synthetic changes, you can invoke the REPL debugger with the `--skip-instrumentation` flag or launch the VS Code debugger with the `skipConfiguration` property set to true in its launch configuration. You can find more details about those in the [Debugger REPL reference](debugger_repl.md) and the [VS Code Debugger reference](debugger_vscode.md). + +:::note +Skipping debugger instrumentation means you won't be able to inspect values of local variables. +::: + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_repl.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_repl.md new file mode 100644 index 000000000000..46e2011304e5 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_repl.md @@ -0,0 +1,360 @@ +--- +title: REPL Debugger +description: + Noir Debugger REPL options and commands. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + REPL, + ] +sidebar_position: 1 +--- + +## Running the REPL debugger + +`nargo debug [OPTIONS] [WITNESS_NAME]` + +Runs the Noir REPL debugger. If a `WITNESS_NAME` is provided the debugger writes the resulting execution witness to a `WITNESS_NAME` file. + +### Options + +| Option | Description | +| --------------------- | ------------------------------------------------------------ | +| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover]| +| `--package ` | The name of the package to debug | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +None of these options are required. + +:::note +Since the debugger starts by compiling the target package, all Noir compiler options are also available. Check out the [compiler reference](../nargo_commands.md#nargo-compile) to learn more about the compiler options. +::: + +## REPL commands + +Once the debugger is running, it accepts the following commands. + +#### `help` (h) + +Displays the menu of available commands. + +``` +> help +Available commands: + + opcodes display ACIR opcodes + into step into to the next opcode + next step until a new source location is reached + out step until a new source location is reached + and the current stack frame is finished + break LOCATION:OpcodeLocation add a breakpoint at an opcode location + over step until a new source location is reached + without diving into function calls + restart restart the debugging session + delete LOCATION:OpcodeLocation delete breakpoint at an opcode location + witness show witness map + witness index:u32 display a single witness from the witness map + witness index:u32 value:String update a witness with the given value + memset index:usize value:String update a memory cell with the given + value + continue continue execution until the end of the + program + vars show variable values available at this point + in execution + stacktrace display the current stack trace + memory show memory (valid when executing unconstrained code) value + step step to the next ACIR opcode + +Other commands: + + help Show this help message + quit Quit repl + +``` + +### Stepping through programs + +#### `next` (n) + +Step until the next Noir source code location. While other commands, such as [`into`](#into-i) and [`step`](#step-s), allow for finer grained control of the program's execution at the opcode level, `next` is source code centric. For example: + +``` +3 ... +4 fn main(x: u32) { +5 assert(entry_point(x) == 2); +6 swap_entry_point(x, x + 1); +7 -> assert(deep_entry_point(x) == 4); +8 multiple_values_entry_point(x); +9 } +``` + + +Using `next` here would cause the debugger to jump to the definition of `deep_entry_point` (if available). + +If you want to step over `deep_entry_point` and go straight to line 8, use [the `over` command](#over) instead. + +#### `over` + +Step until the next source code location, without diving into function calls. For example: + +``` +3 ... +4 fn main(x: u32) { +5 assert(entry_point(x) == 2); +6 swap_entry_point(x, x + 1); +7 -> assert(deep_entry_point(x) == 4); +8 multiple_values_entry_point(x); +9 } +``` + + +Using `over` here would cause the debugger to execute until line 8 (`multiple_values_entry_point(x);`). + +If you want to step into `deep_entry_point` instead, use [the `next` command](#next-n). + +#### `out` + +Step until the end of the current function call. For example: + +``` + 3 ... + 4 fn main(x: u32) { + 5 assert(entry_point(x) == 2); + 6 swap_entry_point(x, x + 1); + 7 -> assert(deep_entry_point(x) == 4); + 8 multiple_values_entry_point(x); + 9 } + 10 + 11 unconstrained fn returns_multiple_values(x: u32) -> (u32, u32, u32, u32) { + 12 ... + ... + 55 + 56 unconstrained fn deep_entry_point(x: u32) -> u32 { + 57 -> level_1(x + 1) + 58 } + +``` + +Running `out` here will resume execution until line 8. + +#### `step` (s) + +Skips to the next ACIR code. A compiled Noir program is a sequence of ACIR opcodes. However, an unconstrained VM opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. + +Using the `step` command at this point would result in the debugger stopping at ACIR opcode 2, `EXPR`, skipping unconstrained computation steps. + +Use [the `into` command](#into-i) instead if you want to follow unconstrained computation step by step. + +#### `into` (i) + +Steps into the next opcode. A compiled Noir program is a sequence of ACIR opcodes. However, a BRILLIG opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. + +Using the `into` command at this point would result in the debugger stopping at opcode 1.0, `Mov ...`, allowing the debugger user to follow unconstrained computation step by step. + +Use [the `step` command](#step-s) instead if you want to skip to the next ACIR code directly. + +#### `continue` (c) + +Continues execution until the next breakpoint, or the end of the program. + +#### `restart` (res) + +Interrupts execution, and restarts a new debugging session from scratch. + +#### `opcodes` (o) + +Display the program's ACIR opcode sequence. For example: + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +### Breakpoints + +#### `break [Opcode]` (or shorthand `b [Opcode]`) + +Sets a breakpoint on the specified opcode index. To get a list of the program opcode numbers, see [the `opcode` command](#opcodes-o). For example: + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +In this example, issuing a `break 1.2` command adds break on opcode 1.2, as denoted by the `*` character: + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | * Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +Running [the `continue` command](#continue-c) at this point would cause the debugger to execute the program until opcode 1.2. + +#### `delete [Opcode]` (or shorthand `d [Opcode]`) + +Deletes a breakpoint at an opcode location. Usage is analogous to [the `break` command](#). + +### Variable inspection + +#### vars + +Show variable values available at this point in execution. + +:::note +The ability to inspect variable values from the debugger depends on compilation to be run in a special debug instrumentation mode. This instrumentation weaves variable tracing code with the original source code. + +So variable value inspection comes at the expense of making the resulting ACIR bytecode bigger and harder to understand and optimize. + +If you find this compromise unacceptable, you can run the debugger with the flag `--skip-debug-instrumentation`. This will compile your circuit without any additional debug information, so the resulting ACIR bytecode will be identical to the one produced by standard Noir compilation. However, if you opt for this, the `vars` command will not be available while debugging. +::: + + +### Stacktrace + +#### `stacktrace` + +Displays the current stack trace. + + +### Witness map + +#### `witness` (w) + +Show witness map. For example: + +``` +_0 = 0 +_1 = 2 +_2 = 1 +``` + +#### `witness [Witness Index]` + +Display a single witness from the witness map. For example: + +``` +> witness 1 +_1 = 2 +``` + +#### `witness [Witness Index] [New value]` + +Overwrite the given index with a new value. For example: + +``` +> witness 1 3 +_1 = 3 +``` + + +### Unconstrained VM memory + +#### `memory` + +Show unconstrained VM memory state. For example: + +``` +> memory +At opcode 1.13: Store { destination_pointer: RegisterIndex(0), source: RegisterIndex(3) } +... +> registers +0 = 0 +1 = 10 +2 = 0 +3 = 1 +4 = 1 +5 = 2³² +6 = 1 +> into +At opcode 1.14: Const { destination: RegisterIndex(5), value: Value { inner: 1 } } +... +> memory +0 = 1 +> +``` + +In the example above: we start with clean memory, then step through a `Store` opcode which stores the value of register 3 (1) into the memory address stored in register 0 (0). Thus now `memory` shows memory address 0 contains value 1. + +:::note +This command is only functional while the debugger is executing unconstrained code. +::: + +#### `memset [Memory address] [New value]` + +Update a memory cell with the given value. For example: + +``` +> memory +0 = 1 +> memset 0 2 +> memory +0 = 2 +> memset 1 4 +> memory +0 = 2 +1 = 4 +> +``` + +:::note +This command is only functional while the debugger is executing unconstrained code. +::: \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_vscode.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_vscode.md new file mode 100644 index 000000000000..c027332b3b04 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_vscode.md @@ -0,0 +1,82 @@ +--- +title: VS Code Debugger +description: + VS Code Debugger configuration and features. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + VS Code, + IDE, + ] +sidebar_position: 0 +--- + +# VS Code Noir Debugger Reference + +The Noir debugger enabled by the vscode-noir extension ships with default settings such that the most common scenario should run without any additional configuration steps. + +These defaults can nevertheless be overridden by defining a launch configuration file. This page provides a reference for the properties you can override via a launch configuration file, as well as documenting the Nargo `dap` command, which is a dependency of the VS Code Noir debugger. + + +## Creating and editing launch configuration files + +To create a launch configuration file from VS Code, open the _debug pane_, and click on _create a launch.json file_. + +![Creating a launch configuration file](@site/static/img/debugger/ref1-create-launch.png) + +A `launch.json` file will be created, populated with basic defaults. + +### Noir Debugger launch.json properties + +#### projectFolder + +_String, optional._ + +Absolute path to the Nargo project to debug. By default, it is dynamically determined by looking for the nearest `Nargo.toml` file to the active file at the moment of launching the debugger. + +#### proverName + +_String, optional._ + +Name of the prover input to use. Defaults to `Prover`, which looks for a file named `Prover.toml` at the `projectFolder`. + +#### generateAcir + +_Boolean, optional._ + +If true, generate ACIR opcodes instead of unconstrained opcodes which will be closer to release binaries but less convenient for debugging. Defaults to `false`. + +#### skipInstrumentation + +_Boolean, optional._ + +Skips variables debugging instrumentation of code, making debugging less convenient but the resulting binary smaller and closer to production. Defaults to `false`. + +:::note +Skipping instrumentation causes the debugger to be unable to inspect local variables. +::: + +## `nargo dap [OPTIONS]` + +When run without any option flags, it starts the Nargo Debug Adapter Protocol server, which acts as the debugging backend for the VS Code Noir Debugger. + +All option flags are related to preflight checks. The Debug Adapter Protocol specifies how errors are to be informed from a running DAP server, but it doesn't specify mechanisms to communicate server initialization errors between the DAP server and its client IDE. + +Thus `nargo dap` ships with a _preflight check_ mode. If flag `--preflight-check` and the rest of the `--preflight-*` flags are provided, Nargo will run the same initialization routine except it will not start the DAP server. + +`vscode-noir` will then run `nargo dap` in preflight check mode first before a debugging session starts. If the preflight check ends in error, vscode-noir will present stderr and stdout output from this process through its own Output pane in VS Code. This makes it possible for users to diagnose what pieces of configuration might be wrong or missing in case of initialization errors. + +If the preflight check succeeds, `vscode-noir` proceeds to start the DAP server normally but running `nargo dap` without any additional flags. + +### Options + +| Option | Description | +| --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | +| `--preflight-check` | If present, dap runs in preflight check mode. | +| `--preflight-project-folder ` | Absolute path to the project to debug for preflight check. | +| `--preflight-prover-name ` | Name of prover file to use for preflight check | +| `--preflight-generate-acir` | Optional. If present, compile in ACIR mode while running preflight check. | +| `--preflight-skip-instrumentation` | Optional. If present, compile without introducing debug instrumentation while running preflight check. | +| `-h, --help` | Print help. | diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/nargo_commands.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/nargo_commands.md new file mode 100644 index 000000000000..537363599664 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/nargo_commands.md @@ -0,0 +1,580 @@ +--- +title: Nargo +description: + Noir CLI Commands for Noir Prover and Verifier to create, execute, prove and verify programs, + generate Solidity verifier smart contract and compile into JSON file containing ACIR + representation and ABI of circuit. +keywords: + [ + Nargo, + Noir CLI, + Noir Prover, + Noir Verifier, + generate Solidity verifier, + compile JSON file, + ACIR representation, + ABI of circuit, + TypeScript, + ] +sidebar_position: 0 +--- + +# Command-Line Help for `nargo` + +This document contains the help content for the `nargo` command-line program. + +**Command Overview:** + +* [`nargo`↴](#nargo) +* [`nargo check`↴](#nargo-check) +* [`nargo fmt`↴](#nargo-fmt) +* [`nargo compile`↴](#nargo-compile) +* [`nargo new`↴](#nargo-new) +* [`nargo init`↴](#nargo-init) +* [`nargo execute`↴](#nargo-execute) +* [`nargo debug`↴](#nargo-debug) +* [`nargo test`↴](#nargo-test) +* [`nargo info`↴](#nargo-info) +* [`nargo lsp`↴](#nargo-lsp) +* [`nargo generate-completion-script`↴](#nargo-generate-completion-script) + +## `nargo` + +Noir's package manager + +**Usage:** `nargo ` + +###### **Subcommands:** + +* `check` — Check a local package and all of its dependencies for errors +* `fmt` — Format the Noir files in a workspace +* `compile` — Compile the program and its secret execution trace into ACIR format +* `new` — Create a Noir project in a new directory +* `init` — Create a Noir project in the current directory +* `execute` — Executes a circuit to calculate its return value +* `debug` — Executes a circuit in debug mode +* `test` — Run the tests for this program +* `info` — Provides detailed information on each of a program's function (represented by a single circuit) +* `lsp` — Starts the Noir LSP server +* `generate-completion-script` — Generates a shell completion script for your favorite shell + +###### **Options:** + + + + +## `nargo check` + +Check a local package and all of its dependencies for errors + +**Usage:** `nargo check [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to run the command on. By default run on the first one found moving up along the ancestors of the current directory +* `--workspace` — Run on all packages in the workspace + + Possible values: `true`, `false` + +* `--overwrite` — Force overwrite of existing files + + Possible values: `true`, `false` + +* `--expression-width ` — Specify the backend expression width that should be targeted +* `--bounded-codegen` — 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 + + Default value: `false` + + Possible values: `true`, `false` + +* `--force` — Force a full recompilation + + Possible values: `true`, `false` + +* `--print-acir` — Display the ACIR for compiled circuit + + Possible values: `true`, `false` + +* `--deny-warnings` — Treat all warnings as errors + + Possible values: `true`, `false` + +* `--silence-warnings` — Suppress warnings + + Possible values: `true`, `false` + +* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" +* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check` — Flag to turn on the compiler check for missing Brillig call constraints. Warning: This can degrade compilation speed but will also find some correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check-lookback` — Flag to turn on the lookback feature of the Brillig call constraints check, allowing tracking argument values before the call happens preventing certain rare false positives (leads to a slowdown on large rollout functions) + + Possible values: `true`, `false` + +* `--pedantic-solving` — Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. This is disabled by default + + Default value: `false` + + Possible values: `true`, `false` + + + + +## `nargo fmt` + +Format the Noir files in a workspace + +**Usage:** `nargo fmt [OPTIONS]` + +###### **Options:** + +* `--check` — Run noirfmt in check mode + + Possible values: `true`, `false` + +* `--package ` — The name of the package to run the command on. By default run on the first one found moving up along the ancestors of the current directory +* `--workspace` — Run on all packages in the workspace + + Possible values: `true`, `false` + + + + +## `nargo compile` + +Compile the program and its secret execution trace into ACIR format + +**Usage:** `nargo compile [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to run the command on. By default run on the first one found moving up along the ancestors of the current directory +* `--workspace` — Run on all packages in the workspace + + Possible values: `true`, `false` + +* `--expression-width ` — Specify the backend expression width that should be targeted +* `--bounded-codegen` — 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 + + Default value: `false` + + Possible values: `true`, `false` + +* `--force` — Force a full recompilation + + Possible values: `true`, `false` + +* `--print-acir` — Display the ACIR for compiled circuit + + Possible values: `true`, `false` + +* `--deny-warnings` — Treat all warnings as errors + + Possible values: `true`, `false` + +* `--silence-warnings` — Suppress warnings + + Possible values: `true`, `false` + +* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" +* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check` — Flag to turn on the compiler check for missing Brillig call constraints. Warning: This can degrade compilation speed but will also find some correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check-lookback` — Flag to turn on the lookback feature of the Brillig call constraints check, allowing tracking argument values before the call happens preventing certain rare false positives (leads to a slowdown on large rollout functions) + + Possible values: `true`, `false` + +* `--pedantic-solving` — Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. This is disabled by default + + Default value: `false` + + Possible values: `true`, `false` + + + + +## `nargo new` + +Create a Noir project in a new directory + +**Usage:** `nargo new [OPTIONS] ` + +###### **Arguments:** + +* `` — The path to save the new project + +###### **Options:** + +* `--name ` — Name of the package [default: package directory name] +* `--lib` — Use a library template + + Possible values: `true`, `false` + +* `--bin` — Use a binary template [default] + + Possible values: `true`, `false` + +* `--contract` — Use a contract template + + Possible values: `true`, `false` + + + + +## `nargo init` + +Create a Noir project in the current directory + +**Usage:** `nargo init [OPTIONS]` + +###### **Options:** + +* `--name ` — Name of the package [default: current directory name] +* `--lib` — Use a library template + + Possible values: `true`, `false` + +* `--bin` — Use a binary template [default] + + Possible values: `true`, `false` + +* `--contract` — Use a contract template + + Possible values: `true`, `false` + + + + +## `nargo execute` + +Executes a circuit to calculate its return value + +**Usage:** `nargo execute [OPTIONS] [WITNESS_NAME]` + +###### **Arguments:** + +* `` — Write the execution witness to named file + +Defaults to the name of the package being executed. + +###### **Options:** + +* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover + + Default value: `Prover` +* `--package ` — The name of the package to run the command on. By default run on the first one found moving up along the ancestors of the current directory +* `--workspace` — Run on all packages in the workspace + + Possible values: `true`, `false` + +* `--expression-width ` — Specify the backend expression width that should be targeted +* `--bounded-codegen` — 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 + + Default value: `false` + + Possible values: `true`, `false` + +* `--force` — Force a full recompilation + + Possible values: `true`, `false` + +* `--print-acir` — Display the ACIR for compiled circuit + + Possible values: `true`, `false` + +* `--deny-warnings` — Treat all warnings as errors + + Possible values: `true`, `false` + +* `--silence-warnings` — Suppress warnings + + Possible values: `true`, `false` + +* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" +* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check` — Flag to turn on the compiler check for missing Brillig call constraints. Warning: This can degrade compilation speed but will also find some correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check-lookback` — Flag to turn on the lookback feature of the Brillig call constraints check, allowing tracking argument values before the call happens preventing certain rare false positives (leads to a slowdown on large rollout functions) + + Possible values: `true`, `false` + +* `--pedantic-solving` — Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. This is disabled by default + + Default value: `false` + + Possible values: `true`, `false` + +* `--oracle-resolver ` — JSON RPC url to solve oracle calls + + + +## `nargo debug` + +Executes a circuit in debug mode + +**Usage:** `nargo debug [OPTIONS] [WITNESS_NAME]` + +###### **Arguments:** + +* `` — Write the execution witness to named file + +###### **Options:** + +* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover + + Default value: `Prover` +* `--package ` — The name of the package to execute +* `--expression-width ` — Specify the backend expression width that should be targeted +* `--bounded-codegen` — 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 + + Default value: `false` + + Possible values: `true`, `false` + +* `--force` — Force a full recompilation + + Possible values: `true`, `false` + +* `--print-acir` — Display the ACIR for compiled circuit + + Possible values: `true`, `false` + +* `--deny-warnings` — Treat all warnings as errors + + Possible values: `true`, `false` + +* `--silence-warnings` — Suppress warnings + + Possible values: `true`, `false` + +* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" +* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check` — Flag to turn on the compiler check for missing Brillig call constraints. Warning: This can degrade compilation speed but will also find some correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check-lookback` — Flag to turn on the lookback feature of the Brillig call constraints check, allowing tracking argument values before the call happens preventing certain rare false positives (leads to a slowdown on large rollout functions) + + Possible values: `true`, `false` + +* `--pedantic-solving` — Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. This is disabled by default + + Default value: `false` + + Possible values: `true`, `false` + +* `--acir-mode` — Force ACIR output (disabling instrumentation) + + Possible values: `true`, `false` + +* `--skip-instrumentation ` — Disable vars debug instrumentation (enabled by default) + + Possible values: `true`, `false` + + + + +## `nargo test` + +Run the tests for this program + +**Usage:** `nargo test [OPTIONS] [TEST_NAMES]...` + +###### **Arguments:** + +* `` — If given, only tests with names containing this string will be run + +###### **Options:** + +* `--show-output` — Display output of `println` statements + + Possible values: `true`, `false` + +* `--exact` — Only run tests that match exactly + + Possible values: `true`, `false` + +* `--package ` — The name of the package to run the command on. By default run on the first one found moving up along the ancestors of the current directory +* `--workspace` — Run on all packages in the workspace + + Possible values: `true`, `false` + +* `--expression-width ` — Specify the backend expression width that should be targeted +* `--bounded-codegen` — 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 + + Default value: `false` + + Possible values: `true`, `false` + +* `--force` — Force a full recompilation + + Possible values: `true`, `false` + +* `--print-acir` — Display the ACIR for compiled circuit + + Possible values: `true`, `false` + +* `--deny-warnings` — Treat all warnings as errors + + Possible values: `true`, `false` + +* `--silence-warnings` — Suppress warnings + + Possible values: `true`, `false` + +* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" +* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check` — Flag to turn on the compiler check for missing Brillig call constraints. Warning: This can degrade compilation speed but will also find some correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check-lookback` — Flag to turn on the lookback feature of the Brillig call constraints check, allowing tracking argument values before the call happens preventing certain rare false positives (leads to a slowdown on large rollout functions) + + Possible values: `true`, `false` + +* `--pedantic-solving` — Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. This is disabled by default + + Default value: `false` + + Possible values: `true`, `false` + +* `--oracle-resolver ` — JSON RPC url to solve oracle calls +* `--test-threads ` — Number of threads used for running tests in parallel + + Default value: `4` +* `--format ` — Configure formatting of output + + Possible values: + - `pretty`: + Print verbose output + - `terse`: + Display one character per test + - `json`: + Output a JSON Lines document + +* `-q`, `--quiet` — Display one character per test instead of one line + + Possible values: `true`, `false` + + + + +## `nargo info` + +Provides detailed information on each of a program's function (represented by a single circuit) + +Current information provided per circuit: 1. The number of ACIR opcodes 2. Counts the final number gates in the circuit used by a backend + +**Usage:** `nargo info [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to run the command on. By default run on the first one found moving up along the ancestors of the current directory +* `--workspace` — Run on all packages in the workspace + + Possible values: `true`, `false` + +* `--profile-execution` + + Possible values: `true`, `false` + +* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover + + Default value: `Prover` +* `--expression-width ` — Specify the backend expression width that should be targeted +* `--bounded-codegen` — 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 + + Default value: `false` + + Possible values: `true`, `false` + +* `--force` — Force a full recompilation + + Possible values: `true`, `false` + +* `--print-acir` — Display the ACIR for compiled circuit + + Possible values: `true`, `false` + +* `--deny-warnings` — Treat all warnings as errors + + Possible values: `true`, `false` + +* `--silence-warnings` — Suppress warnings + + Possible values: `true`, `false` + +* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" +* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check` — Flag to turn on the compiler check for missing Brillig call constraints. Warning: This can degrade compilation speed but will also find some correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check-lookback` — Flag to turn on the lookback feature of the Brillig call constraints check, allowing tracking argument values before the call happens preventing certain rare false positives (leads to a slowdown on large rollout functions) + + Possible values: `true`, `false` + +* `--pedantic-solving` — Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. This is disabled by default + + Default value: `false` + + Possible values: `true`, `false` + + + + +## `nargo lsp` + +Starts the Noir LSP server + +Starts an LSP server which allows IDEs such as VS Code to display diagnostics in Noir source. + +VS Code Noir Language Support: https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir + +**Usage:** `nargo lsp` + + + +## `nargo generate-completion-script` + +Generates a shell completion script for your favorite shell + +**Usage:** `nargo generate-completion-script ` + +###### **Arguments:** + +* `` — The shell to generate completions for. One of: bash, elvish, fish, powershell, zsh + + + +
+ + + This document was generated automatically by + clap-markdown. + + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/noir_codegen.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/noir_codegen.md new file mode 100644 index 000000000000..e4c362f96100 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/noir_codegen.md @@ -0,0 +1,116 @@ +--- +title: Noir Codegen for TypeScript +description: Learn how to use Noir codegen to generate TypeScript bindings +keywords: [Nargo, Noir, compile, TypeScript] +sidebar_position: 3 +--- + +When using TypeScript, it is extra work to interpret Noir program outputs in a type-safe way. Third party libraries may exist for popular Noir programs, but they are either hard to find or unmaintained. + +Now you can generate TypeScript bindings for your Noir programs in two steps: + +1. Exporting Noir functions using `nargo export` +2. Using the TypeScript module `noir_codegen` to generate TypeScript binding + +**Note:** you can only export functions from a Noir *library* (not binary or contract program types). + +## Installation + +### Your TypeScript project + +If you don't already have a TypeScript project you can add the module with `yarn` (or `npm`), then initialize it: + +```bash +yarn add typescript -D +npx tsc --init +``` + +### Add TypeScript module - `noir_codegen` + +The following command will add the module to your project's devDependencies: + +```bash +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](../getting_started/noir_installation.md). + +If you're in a new project, make a `circuits` folder and create a new Noir library: + +```bash +mkdir circuits && cd circuits +nargo new --lib myNoirLib +``` + +## Usage + +### Export ABI of specified functions + +First go to the `.nr` files in your Noir library, and add the `#[export]` macro to each function that you want to use in TypeScript. + +```rust +#[export] +fn your_function(... +``` + +From your Noir library (where `Nargo.toml` is), run the following command: + +```bash +nargo export +``` + +You will now have an `export` directory with a .json file per exported function. + +You can also specify the directory of Noir programs using `--program-dir`, for example: + +```bash +nargo export --program-dir=./circuits/myNoirLib +``` + +### Generate TypeScript bindings from exported functions + +To use the `noir-codegen` package we added to the TypeScript project: + +```bash +yarn noir-codegen ./export/your_function.json +``` + +This creates an `exports` directory with an `index.ts` file containing all exported functions. + +**Note:** adding `--out-dir` allows you to specify an output dir for your TypeScript bindings to go. Eg: + +```bash +yarn noir-codegen ./export/*.json --out-dir ./path/to/output/dir +``` + +## Example .nr function to .ts output + +Consider a Noir library with this function: + +```rust +#[export] +fn not_equal(x: Field, y: Field) -> bool { + x != y +} +``` + +After the export and codegen steps, you should have an `index.ts` like: + +```typescript +export type Field = string; + + +export const is_equal_circuit: CompiledCircuit = +{"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"private"}],"return_type":{"abi_type":{"kind":"boolean"},"visibility":"private"}},"bytecode":"H4sIAAAAAAAA/7WUMQ7DIAxFQ0Krrr2JjSGYLVcpKrn/CaqqDQN12WK+hPBgmWd/wEyHbF1SS923uhOs3pfoChI+wKXMAXzIKyNj4PB0TFTYc0w5RUjoqeAeEu1wqK0F54RGkWvW44LPzExnlkbMEs4JNZmN8PxS42uHv82T8a3Jeyn2Ks+VLPcO558HmyLMCDOXAXXtpPt4R/Rt9T36ss6dS9HGPx/eG17nGegKBQAA"}; + +export async function is_equal(x: Field, y: Field, foreignCallHandler?: ForeignCallHandler): Promise { + const program = new Noir(is_equal_circuit); + const args: InputMap = { x, y }; + const { returnValue } = await program.execute(args, foreignCallHandler); + return returnValue as boolean; +} +``` + +Now the `is_equal()` function and relevant types are readily available for use in TypeScript. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/cspell.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/cspell.json new file mode 100644 index 000000000000..6791ea20f27e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/cspell.json @@ -0,0 +1,8 @@ +{ + "words": [ + "ACIR", + "flamegraph", + "flamegraphs", + "lookback" + ] +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/debugger.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/debugger.md new file mode 100644 index 000000000000..200b5fc423ac --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/debugger.md @@ -0,0 +1,26 @@ +--- +title: Debugger +description: Learn about the Noir Debugger, in its REPL or VS Code versions. +keywords: [Nargo, VSCode, Visual Studio Code, REPL, Debugger] +sidebar_position: 2 +--- + +# Noir Debugger + +There are currently two ways of debugging Noir programs: + +1. From VS Code, via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). +2. Via the REPL debugger, which ships with Nargo. + +In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/noir_installation.md) and vscode-noir: + +- Noir & Nargo ≥0.28.0 +- Noir's VS Code extension ≥0.0.11 + +:::info +At the moment, the debugger supports debugging binary projects, but not contracts. +::: + +We cover the VS Code Noir debugger more in depth in [its VS Code debugger how-to guide](../how_to/debugger/debugging_with_vs_code.md) and [the reference](../reference/debugger/debugger_vscode.md). + +The REPL debugger is discussed at length in [the REPL debugger how-to guide](../how_to/debugger/debugging_with_the_repl.md) and [the reference](../reference/debugger/debugger_repl.md). diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/language_server.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/language_server.md new file mode 100644 index 000000000000..81e0356ef8a1 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/language_server.md @@ -0,0 +1,43 @@ +--- +title: Language Server +description: Learn about the Noir Language Server, how to install the components, and configuration that may be required. +keywords: [Nargo, Language Server, LSP, VSCode, Visual Studio Code] +sidebar_position: 0 +--- + +This section helps you install and configure the Noir Language Server. + +The Language Server Protocol (LSP) has two components, the [Server](#language-server) and the [Client](#language-client). Below we describe each in the context of Noir. + +## Language Server + +The Server component is provided by the Nargo command line tool that you installed at the beginning of this guide. +As long as Nargo is installed and you've used it to run other commands in this guide, it should be good to go! + +If you'd like to verify that the `nargo lsp` command is available, you can run `nargo --help` and look for `lsp` in the list of commands. If you see it, you're using a version of Noir with LSP support. + +## Language Client + +The Client component is usually an editor plugin that launches the Server. It communicates LSP messages between the editor and the Server. For example, when you save a file, the Client will alert the Server, so it can try to compile the project and report any errors. + +Currently, Noir provides a Language Client for Visual Studio Code via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). + +> **Note:** Noir's Language Server Protocol support currently assumes users' VSCode workspace root to be the same as users' Noir project root (i.e. where Nargo.toml lies). +> +> If LSP features seem to be missing / malfunctioning, make sure you are opening your Noir project directly (instead of as a sub-folder) in your VSCode instance. + +When your language server is running correctly and the VSCode plugin is installed, you should see handy codelens buttons for compilation, measuring circuit size, execution, and tests: + +![Compile and Execute](@site/static/img/codelens_compile_execute.png) +![Run test](@site/static/img/codelens_run_test.png) + +You should also see your tests in the `testing` panel: + +![Testing panel](@site/static/img/codelens_testing_panel.png) + +### Configuration + +- **Noir: Enable LSP** - If checked, the extension will launch the Language Server via `nargo lsp` and communicate with it. +- **Noir: Nargo Flags** - Additional flags may be specified if you require them to be added when the extension calls `nargo lsp`. +- **Noir: Nargo Path** - An absolute path to a Nargo binary with the `lsp` command. This may be useful if Nargo is not within the `PATH` of your editor. +- **Noir > Trace: Server** - Setting this to `"messages"` or `"verbose"` will log LSP messages between the Client and Server. Useful for debugging. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/profiler.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/profiler.md new file mode 100644 index 000000000000..1f906226f8ce --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/profiler.md @@ -0,0 +1,115 @@ +--- +title: Noir Profiler +description: Learn about the Noir Profiler, how to generate execution flamegraphs, identify bottlenecks, and visualize optimizations. +keywords: [profiling, profiler, flamegraph] +sidebar_position: 0 +--- + +## Noir Profiler + +`noir-profiler` is a sampling profiler designed to analyze and visualize Noir programs. It assists developers to identify bottlenecks by mapping execution data back to the original source code. + +### Installation + +`noir-profiler` comes out of the box with [noirup](../getting_started/noir_installation.md). Test that you have the profiler installed by running `noir-profiler --version`. + +### Usage + +Let's start by creating a simple Noir program. All this program aims to do is zero out an array past some dynamic index. + +```rust +fn main(ptr: pub u32, mut array: [u32; 32]) -> pub [u32; 32] { + for i in 0..32 { + if i > ptr { + array[i] = 0; + } + } + array +} +``` +You can use these values for the `Prover.toml`: +```toml +ptr = 1 +array = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] +``` + +Running `nargo info` we can get some information about the opcodes produced by this program, but it doesn't give us a lot of info on its own. Compile and execute this program normally using `nargo compile` and `nargo execute`. + +### Generating an ACIR opcode flamegraph + +The program on its own is quite high-level. Let's get a more granular look at what is happening by using `noir-profiler`. + +After compiling the program, run the following: +```sh +noir-profiler opcodes --artifact-path ./target/program.json --output ./target/ +``` +Below you can see an example flamegraph with a total 387 opcodes (using `nargo` version 1.0.0-beta.2): +![ACIR Flamegraph Unoptimized](@site/static/img/tooling/profiler/acir-flamegraph-unoptimized.png) + +You should now have a flamegraph that maps ACIR opcodes to their corresponding locations in the source code. We strongly recommend generating these graphs yourself as you follow this guide. Opening the flamegraph in a browser provides a more interactive experience, allowing you to click into and examine different regions of the graph. Simply viewing the image file won't offer the same level of insight. + +We can see that the majority of opcodes come from the write to `array[i]`. Now that we have some more information about our program's bottlenecks, let's optimize it. + +#### Transform conditional writes into reads + +We can improve our circuit's efficiency using [unconstrained functions](../noir/concepts/unconstrained.md). + +Let's replace expensive array writes with array gets with the new code below: +```rust +fn main(ptr: pub u32, array: [u32; 32]) -> pub [u32; 32] { + // Safety: Sets all elements after `ptr` in `array` to zero. + let zeroed_array = unsafe { zero_out_array(ptr, array) }; + for i in 0..32 { + if i > ptr { + assert_eq(zeroed_array[i], 0); + } else { + assert_eq(zeroed_array[i], array[i]); + } + } + zeroed_array +} + +unconstrained fn zero_out_array(ptr: u32, mut array: [u32; 32]) -> [u32; 32] { + for i in 0..32 { + if i > ptr { + array[i] = 0; + } + } + array +} +``` +We chose to instead write our array inside of the unconstrained function. Then inside of our circuit we assert on every value in the array returned from the unconstrained function. + +This new program produces the following ACIR opcodes flamegraph with a total of 284 opcodes: +![ACIR Flamegraph Optimized](@site/static/img/tooling/profiler/acir-flamegraph-optimized.png) + +In the above image we searched for the ACIR opcodes due to `i > ptr` in the source code. Trigger a search by clicking on "Search" in the top right corner of the flamegraph. In the bottom right corner of the image above, you will note that the flamegraph displays the percentage of all opcodes associated with that search. Searching for `memory::op` in the optimized flamegraph will result in no matches. This is due to no longer using a dynamic array in our circuit. By dynamic array, we are referring to using a dynamic index (values reliant upon witness inputs) when working with arrays. Most of the memory operations, have now been replaced with arithmetic operations as we are reading two arrays from known constant indices. + +### Generate a backend gates flamegraph + +Unfortunately, ACIR opcodes do not give us a full picture of where the cost of this program lies. +The `gates` command also accepts a backend binary. In the [quick start guide](../getting_started/quick_start.md#proving-backend) you can see how to get started with the [Barretenberg proving backend](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg). + +Run the following command: +```sh +noir-profiler gates --artifact-path ./target/program.json --backend-path bb --output ./target +``` +`--backend-path` accepts a path to the backend binary. In the above command we assume that you have the backend binary path saved in your PATH. If you do not, you will have to pass the binary's absolute path. + +This produces the following flamegraph with 3,737 total backend gates (using `bb` version 0.76.4): +![Gates Flamegraph Unoptimized](@site/static/img/tooling/profiler/gates-flamegraph-unoptimized.png) + +Searching for ACIR `memory::op` opcodes, they look to cause about 18.2% of the backend gates. + +You will notice that the majority of the backend gates come from the ACIR range opcodes. This is due to the way UltraHonk handles range constraints, which is the backend used in this example. UltraHonk uses lookup tables internally for its range gates. These can take up the majority of the gates for a small circuit, but whose impact becomes more meaningful in larger circuits. If our array was much larger, range gates would become a much smaller percentage of our total circuit. +Here is an example backend gates flamegraph for the same program in this guide but with an array of size 2048: +![Gates Flamegraph Unoptimized 2048](@site/static/img/tooling/profiler/gates-flamegraph-unoptimized-2048.png) +Every backend implements ACIR opcodes differently, so it is important to profile both the ACIR and the backend gates to get a full picture. + +Now let's generate a graph for our optimized circuit with an array of size 32. We get the following flamegraph that produces 3,062 total backend gates: +![Gates Flamegraph Optimized](@site/static/img/tooling/profiler/gates-flamegraph-optimized.png) + +In the optimized flamegraph, we searched for the backend gates due to `i > ptr` in the source code. The backend gates associated with this call stack were only 3.8% of the total backend gates. If we look back to the ACIR flamegraph, that same code was the cause of 43.3% ACIR opcodes. This discrepancy reiterates the earlier point about profiling both the ACIR opcodes and backend gates. + +For posterity, here is the flamegraph for the same program with a size 2048 array: +![Gates Flamegraph Optimized 2048](@site/static/img/tooling/profiler/gates-flamegraph-optimized-2048.png) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/security.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/security.md new file mode 100644 index 000000000000..e14481efc317 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/security.md @@ -0,0 +1,61 @@ +--- +title: Security checks +description: Security checks currently provided by the compiler +keywords: [Nargo, Security, Brillig, Unconstrained] +sidebar_position: 2 +--- + +# Security checks + +Two compilation security passes exist currently to ensure soundness of compiled code. Problems they catch are reported as "bugs" (as opposed to errors) in the compiler output. For example: + +``` +**bug**: Brillig function call isn't properly covered by a manual constraint +``` + +### Independent subgraph detection + +This pass examines the instruction flow graph to see if the final function would involve values that don't come from any provided inputs and don't result in the outputs. That would mean there are no constraints ensuring the required continuity. + +This check is enabled by default and can be disabled by passing the `--skip-underconstrained-check` option to `nargo`. + +### Brillig manual constraint coverage + +The results of a Brillig function call must be constrained to ensure security, adhering to these rules: every resulting value (including every array element of a resulting array) has to be involved in a later constraint (i.e. assert, range check) against either one of the arguments of the call, or a constant. In this context, involvement means that a descendant value (e.g. a result of a chain of operations over the value) of a result has to be checked against a descendant value of an argument. For example: + +```rust +unconstrained fn factor(v0: Field) -> [Field; 2] { + ... +} + +fn main f0 (foo: Field) -> [Field; 2] { + let factored = unsafe { factor(foo) }; + assert(factored[0] * factored[1] == foo); + return factored +} +``` + +Here, the results of `factor` are two elements of the returned array. The value `factored[0] * factored[1]` is a descendant of both of them, so both are involved in a constraint against the argument value in the `assert`. Hence, the call to an unconstrained function is properly covered. + +This pass checks if the constraint coverage of Brillig calls is sufficient in these terms. + +The check is at the moment disabled by default due to performance concerns and can be enabled by passing the `--enable-brillig-constraints-check` option to `nargo`. + +#### Lookback option + +Certain false positives of this check can be avoided by providing the `--enable-brillig-constraints-check-lookback` option to `nargo`, which can be slower at compile-time but additionally ensures that descendants of call argument values coming from operations *preceding* the call itself would be followed. For example, consider this case: + +```rust +unconstrained fn unconstrained_add(v0: Field, v1: Field) -> Field { + v0 + v1 +} + +fn main f0 (v0: Field, v1: Field) { + let foo = v0 + v1; + let bar = unsafe { unconstrained_add(v0, v1) }; + assert(foo == bar); + return bar +} +``` + +Normally, the addition operation over `v0` and `v1` happening before the call itself would prevent the call from being (correctly) considered properly constrained. With this option enabled, the false positive goes away at the cost of the check becoming somewhat less performant on large unrolled loops. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/testing.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/testing.md new file mode 100644 index 000000000000..866677da5679 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/testing.md @@ -0,0 +1,79 @@ +--- +title: Testing in Noir +description: Learn how to use Nargo to test your Noir program in a quick and easy way +keywords: [Nargo, testing, Noir, compile, test] +sidebar_position: 1 +--- + +You can test your Noir programs using Noir circuits. + +Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if +you run `nargo test`. + +For example if you have a program like: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test] +fn test_add() { + assert(add(2,2) == 4); + assert(add(0,1) == 1); + assert(add(1,0) == 1); +} +``` + +Running `nargo test` will test that the `test_add` function can be executed while satisfying all +the constraints which allows you to test that add returns the expected values. Test functions can't +have any arguments currently. + +### Test fail + +You can write tests that are expected to fail by using the decorator `#[test(should_fail)]`. For example: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test(should_fail)] +fn test_add() { + assert(add(2,2) == 5); +} +``` + +You can be more specific and make it fail with a specific reason by using `should_fail_with = ""`: + +```rust +fn main(african_swallow_avg_speed : Field) { + assert(african_swallow_avg_speed == 65, "What is the airspeed velocity of an unladen swallow"); +} + +#[test] +fn test_king_arthur() { + main(65); +} + +#[test(should_fail_with = "What is the airspeed velocity of an unladen swallow")] +fn test_bridgekeeper() { + main(32); +} +``` + +The string given to `should_fail_with` doesn't need to exactly match the failure reason, it just needs to be a substring of it: + +```rust +fn main(african_swallow_avg_speed : Field) { + assert(african_swallow_avg_speed == 65, "What is the airspeed velocity of an unladen swallow"); +} + +#[test] +fn test_king_arthur() { + main(65); +} + +#[test(should_fail_with = "airspeed velocity")] +fn test_bridgekeeper() { + main(32); +} +``` \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tutorials/noirjs_app.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tutorials/noirjs_app.md new file mode 100644 index 000000000000..40e5d94180de --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tutorials/noirjs_app.md @@ -0,0 +1,304 @@ +--- +title: Building a web app with Noir and Barretenberg +description: Learn how to setup a new app that uses Noir to generate and verify zero-knowledge SNARK proofs in a typescript or javascript environment. +keywords: [how to, guide, javascript, typescript, noir, barretenberg, zero-knowledge, proofs, app] +sidebar_position: 0 +pagination_next: noir/concepts/data_types/index +--- + +NoirJS is a Typescript package meant to work both in a browser and a server environment. + +In this tutorial, we will combine NoirJS with Aztec's Barretenberg backend to build a simple web app. From here, you should get an idea on how to proceed with your own Noir projects! + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). + +## Dependencies + +Before we start, we want to make sure we have Node installed. For convenience (and speed), we can just install [Bun](https://bun.sh) as our package manager, and Node will work out-of-the-box: + +```bash +curl -fsSL https://bun.sh/install | bash +``` + +Let's go barebones. Doing the bare minimum is not only simple, but also allows you to easily adapt it to almost any frontend framework. + +Barebones means we can immediately start with the dependencies even on an empty folder 😈: + +```bash +bun i @noir-lang/noir_wasm@1.0.0-beta.2 @noir-lang/noir_js@1.0.0-beta.2 @aztec/bb.js@0.72.1 +``` + +Wait, what are these dependencies? + +- `noir_wasm` is the `wasm` version of the Noir compiler. Although most developers prefer to use `nargo` for compiling, there's nothing wrong with `noir_wasm`. We like `noir_wasm`. +- `noir_js` is the main Noir package. It will execute our program, and generate the witness that will be sent to the backend. +- `bb.js` is the Typescript interface for Aztec's Barretenberg proving backend. It also uses the `wasm` version in order to run on the browser. + +:::info + +In this guide, we will install versions pinned to 1.0.0-beta.2. These work with Barretenberg version 0.72.1, so we are using that one version too. Feel free to try with older or later versions, though! + +::: + +## Setting up our Noir program + +ZK is a powerful technology. An app that reveals computational correctness but doesn't reveal some of its inputs is almost unbelievable, yet Noir makes it as easy as a single line of code. + +:::tip + +It's not just you. We also enjoy syntax highlighting. [Check out the Language Server](../tooling/language_server.md) + +::: + +All you need is a `main.nr` and a `Nargo.toml` file. You can follow the [noirup](../getting_started/noir_installation.md) installation and just run `noirup -v 1.0.0-beta.2`, or just create them by hand: + +```bash +mkdir -p circuit/src +touch circuit/src/main.nr circuit/Nargo.toml +``` + +To make our program interesting, let's give it a real use-case scenario: Bob wants to prove he is older than 18, without disclosing his age. Open `main.nr` and write: + +```rust +fn main(age: u8) { + assert(age >= 18); +} +``` + +This program accepts a private input called age, and simply proves this number is higher than 18. But to run this code, we need to give the compiler a `Nargo.toml` with at least a name and a type: + +```toml +[package] +name = "circuit" +type = "bin" +``` + +This is all that we need to get started with Noir. + +![my heart is ready for you, noir.js](@site/static/img/memes/titanic.jpeg) + +## Setting up our app + +Remember when apps only had one `html` and one `js` file? Well, that's enough for Noir webapps. Let's create them: + +```bash +touch index.html index.js +``` + +And add something useful to our HTML file: + +```html + + + + + + +

Noir app

+
+ + +
+
+

Logs

+

Proof

+
+ + +``` + +It _could_ be a beautiful UI... Depending on which universe you live in. In any case, we're using some scary CSS to make two boxes that will show cool things on the screen. + +As for the JS, real madmen could just `console.log` everything, but let's say we want to see things happening (the true initial purpose of JS... right?). Here's some boilerplate for that. Just paste it in `index.js`: + +```js +const show = (id, content) => { + const container = document.getElementById(id); + container.appendChild(document.createTextNode(content)); + container.appendChild(document.createElement("br")); +}; + +document.getElementById("submit").addEventListener("click", async () => { + try { + // noir goes here + } catch { + show("logs", "Oh 💔"); + } +}); + +``` + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit + └── src + └── main.nr + Nargo.toml + index.js + package.json + index.html + ...etc +``` + +::: + +## Compile compile compile + +Finally we're up for something cool. But before we can execute a Noir program, we need to compile it into ACIR: an abstract representation. Here's where `noir_wasm` comes in. + +`noir_wasm` expects a filesystem so it can resolve dependencies. While we could use the `public` folder, let's just import those using the nice `?url` syntax provided by vite. At the top of the file: + +```js +import { compile, createFileManager } from "@noir-lang/noir_wasm" + +import main from "./circuit/src/main.nr?url"; +import nargoToml from "./circuit/Nargo.toml?url"; +``` + +Compiling on the browser is common enough that `createFileManager` already gives us a nice in-memory filesystem we can use. So all we need to compile is fetching these files, writing them to our filesystem, and compile. Add this function: + +```js +export async function getCircuit() { + const fm = createFileManager("/"); + const { body } = await fetch(main); + const { body: nargoTomlBody } = await fetch(nargoToml); + + fm.writeFile("./src/main.nr", body); + fm.writeFile("./Nargo.toml", nargoTomlBody); + return await compile(fm); +} +``` + +:::tip + +As you can imagine, with `node` it's all conveniently easier since you get native access to `fs`... + +::: + +## Some more JS + +We're starting with the good stuff now. We want to execute our circuit to get the witness, and then feed that witness to Barretenberg. Luckily, both packages are quite easy to work with. Let's import them at the top of the file: + +```js +import { UltraHonkBackend } from '@aztec/bb.js'; +import { Noir } from '@noir-lang/noir_js'; +``` + +And instantiate them inside our try-catch block: + +```ts +// try { +const { program } = await getCircuit(); +const noir = new Noir(program); +const backend = new UltraHonkBackend(program.bytecode); +// } +``` + +:::warning + +WASMs are not always easy to work with. In our case, `vite` likes serving them with the wrong MIME type. There are different fixes but we found the easiest one is just YOLO instantiating the WASMs manually. Paste this at the top of the file, just below the other imports, and it will work just fine: + +```js +import initNoirC from "@noir-lang/noirc_abi"; +import initACVM from "@noir-lang/acvm_js"; +import acvm from "@noir-lang/acvm_js/web/acvm_js_bg.wasm?url"; +import noirc from "@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm?url"; +await Promise.all([initACVM(fetch(acvm)), initNoirC(fetch(noirc))]); +``` + +::: + +## Executing and proving + +Now for the app itself. We're capturing whatever is in the input when people press the submit button. Inside our `try` block, let's just grab that input and get its value. Noir will gladly execute it, and give us a witness: + +```js +const age = document.getElementById("age").value; +show("logs", "Generating witness... ⏳"); +const { witness } = await noir.execute({ age }); +show("logs", "Generated witness... ✅"); + +``` + +:::note + +For the remainder of the tutorial, everything will be happening inside the `try` block + +::: + +Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: + +```js +show("logs", "Generating proof... ⏳"); +const proof = await backend.generateProof(witness); +show("logs", "Generated proof... ✅"); +show("results", proof.proof); +``` + +Our program is technically **done** . You're probably eager to see stuff happening! To serve this in a convenient way, we can use a bundler like `vite` by creating a `vite.config.js` file: + +```bash +touch vite.config.js +``` + +`vite` helps us with a little catch: `bb.js` in particular uses top-level awaits which aren't supported everywhere. So we can add this to the `vite.config.js` to make the bundler optimize them: + +```js +export default { optimizeDeps: { esbuildOptions: { target: "esnext" } } }; +``` + +This should be enough for vite. We don't even need to install it, just run: + +```bash +bunx vite +``` + +If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. + +![Noir Webapp UI](@site/static/img/tutorials/noirjs_webapp/webapp1.png) + +Now, our circuit requires a private input `fn main(age: u8)`, and fails if it is less than 18. Let's see if it works. Submit any number above 18 (as long as it fits in 8 bits) and you should get a valid proof. Otherwise the proof won't even generate correctly. + +By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. + +## Verifying + +Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: + +```js +show('logs', 'Verifying proof... ⌛'); +const isValid = await backend.verifyProof(proof); +show("logs", `Proof is ${isValid ? "valid" : "invalid"}... ✅`); +``` + +You have successfully generated a client-side Noir web app! + +![coded app without math knowledge](@site/static/img/memes/flextape.jpeg) + +## Next steps + +At this point, you have a working ZK app that works on the browser. Actually, it works on a mobile phone too! + +If you want to continue learning by doing, here are some challenges for you: + +- Install [nargo](https://noir-lang.org/docs/getting_started/noir_installation) and write [Noir tests](../tooling/testing) +- Change the circuit to accept a [public input](../noir/concepts/data_types/#private--public-types) as the cutoff age. It could be different depending on the purpose, for example! +- Enjoy Noir's Rust-like syntax and write a struct `Country` that implements a trait `MinAge` with a method `get_min_age`. Then, make a struct `Person` have an `u8` as its age and a country of type `Country`. You can pass a `person` in JS just like a JSON object `person: { age, country: { min_age: 18 }}` + +The world is your stage, just have fun with ZK! You can see how noirjs is used in some common frameworks in the [awesome-noir repo](https://github.com/noir-lang/awesome-noir?tab=readme-ov-file#boilerplates). diff --git a/noir/noir-repo/docs/versioned_sidebars/version-v1.0.0-beta.3-sidebars.json b/noir/noir-repo/docs/versioned_sidebars/version-v1.0.0-beta.3-sidebars.json new file mode 100644 index 000000000000..b9ad026f69ff --- /dev/null +++ b/noir/noir-repo/docs/versioned_sidebars/version-v1.0.0-beta.3-sidebars.json @@ -0,0 +1,93 @@ +{ + "sidebar": [ + { + "type": "doc", + "id": "index" + }, + { + "type": "category", + "label": "Getting Started", + "items": [ + { + "type": "autogenerated", + "dirName": "getting_started" + } + ] + }, + { + "type": "category", + "label": "The Noir Language", + "items": [ + { + "type": "autogenerated", + "dirName": "noir" + } + ] + }, + { + "type": "html", + "value": "
", + "defaultStyle": true + }, + { + "type": "category", + "label": "How To Guides", + "items": [ + { + "type": "autogenerated", + "dirName": "how_to" + } + ] + }, + { + "type": "category", + "label": "Explainers", + "items": [ + { + "type": "autogenerated", + "dirName": "explainers" + } + ] + }, + { + "type": "category", + "label": "Tutorials", + "items": [ + { + "type": "autogenerated", + "dirName": "tutorials" + } + ] + }, + { + "type": "category", + "label": "Reference", + "items": [ + { + "type": "autogenerated", + "dirName": "reference" + } + ] + }, + { + "type": "category", + "label": "Tooling", + "items": [ + { + "type": "autogenerated", + "dirName": "tooling" + } + ] + }, + { + "type": "html", + "value": "
", + "defaultStyle": true + }, + { + "type": "doc", + "id": "migration_notes", + "label": "Migration notes" + } + ] +} diff --git a/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh b/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh index b227f82914e2..c792b4183119 100755 --- a/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh +++ b/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh @@ -6,18 +6,18 @@ BACKEND=${BACKEND:-bb} nargo compile # TODO: backend should automatically generate vk if necessary. -$BACKEND OLD_API write_vk -b ./target/hello_world.json -$BACKEND OLD_API contract -o ./src/contract.sol +$BACKEND write_vk -b ./target/hello_world.json +$BACKEND contract -o ./src/contract.sol # We now generate a proof and check whether the verifier contract will verify it. nargo execute --pedantic-solving witness PROOF_PATH=./target/proof -$BACKEND OLD_API prove -b ./target/hello_world.json -w ./target/witness.gz -o $PROOF_PATH +$BACKEND prove -b ./target/hello_world.json -w ./target/witness.gz -o $PROOF_PATH # Sanity check that proof is valid. -$BACKEND OLD_API verify -k ./target/vk -p ./target/proof +$BACKEND verify -k ./target/vk -p ./target/proof NUM_PUBLIC_INPUTS=2 PUBLIC_INPUT_BYTES=$((32 * $NUM_PUBLIC_INPUTS)) diff --git a/noir/noir-repo/examples/prove_and_verify/prove_and_verify.sh b/noir/noir-repo/examples/prove_and_verify/prove_and_verify.sh index 92288bc92e02..411f5258caf4 100755 --- a/noir/noir-repo/examples/prove_and_verify/prove_and_verify.sh +++ b/noir/noir-repo/examples/prove_and_verify/prove_and_verify.sh @@ -7,8 +7,8 @@ nargo execute --pedantic-solving witness # TODO: `bb` should create `proofs` directory if it doesn't exist. mkdir -p proofs -$BACKEND OLD_API prove -b ./target/hello_world.json -w ./target/witness.gz +$BACKEND prove -b ./target/hello_world.json -w ./target/witness.gz # TODO: backend should automatically generate vk if necessary. -$BACKEND OLD_API write_vk -b ./target/hello_world.json -$BACKEND OLD_API verify -k ./target/vk -p ./proofs/proof +$BACKEND write_vk -b ./target/hello_world.json +$BACKEND verify -k ./target/vk -p ./proofs/proof diff --git a/noir/noir-repo/examples/recursion/generate_recursive_proof.sh b/noir/noir-repo/examples/recursion/generate_recursive_proof.sh index e05dd854d69f..09b01d547b68 100755 --- a/noir/noir-repo/examples/recursion/generate_recursive_proof.sh +++ b/noir/noir-repo/examples/recursion/generate_recursive_proof.sh @@ -4,16 +4,16 @@ set -eu BACKEND=${BACKEND:-bb} nargo execute sum_witness --package sum -$BACKEND OLD_API prove -b ./target/sum.json -w ./target/sum_witness.gz -o ./target/sum_proof --recursive +$BACKEND prove -b ./target/sum.json -w ./target/sum_witness.gz -o ./target/sum_proof --recursive # Once we have generated our inner proof, we must use this to generate inputs to `recurse_leaf`` -$BACKEND OLD_API write_vk -b ./target/sum.json -o ./target/sum_key --recursive -$BACKEND OLD_API vk_as_fields -k ./target/sum_key -o ./target/sum_vk_as_fields +$BACKEND write_vk -b ./target/sum.json -o ./target/sum_key --recursive +$BACKEND vk_as_fields -k ./target/sum_key -o ./target/sum_vk_as_fields VK_HASH=$(jq -r '.[0]' ./target/sum_vk_as_fields) VK_AS_FIELDS=$(jq -r '.[1:]' ./target/sum_vk_as_fields) -FULL_PROOF_AS_FIELDS="$($BACKEND OLD_API proof_as_fields -p ./target/sum_proof -k ./target/sum_key -o -)" +FULL_PROOF_AS_FIELDS="$($BACKEND proof_as_fields -p ./target/sum_proof -k ./target/sum_key -o -)" # sum has 3 public inputs PUBLIC_INPUTS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[:3]') PROOF_AS_FIELDS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[3:]') @@ -28,19 +28,19 @@ echo "public_inputs = $PUBLIC_INPUTS" >> $RECURSE_LEAF_PROVER_TOML # We can now execute and prove `recurse_leaf` nargo execute recurse_leaf_witness --package recurse_leaf -$BACKEND OLD_API prove -b ./target/recurse_leaf.json -w ./target/recurse_leaf_witness.gz -o ./target/recurse_leaf_proof --recursive +$BACKEND prove -b ./target/recurse_leaf.json -w ./target/recurse_leaf_witness.gz -o ./target/recurse_leaf_proof --recursive # Let's do a sanity check that the proof we've generated so far is valid. -$BACKEND OLD_API write_vk -b ./target/recurse_leaf.json -o ./target/recurse_leaf_key --recursive -$BACKEND OLD_API verify -p ./target/recurse_leaf_proof -k ./target/recurse_leaf_key +$BACKEND write_vk -b ./target/recurse_leaf.json -o ./target/recurse_leaf_key --recursive +$BACKEND verify -p ./target/recurse_leaf_proof -k ./target/recurse_leaf_key # Now we generate the final `recurse_node` proof similarly to how we did for `recurse_leaf`. -$BACKEND OLD_API vk_as_fields -k ./target/recurse_leaf_key -o ./target/recurse_leaf_vk_as_fields +$BACKEND vk_as_fields -k ./target/recurse_leaf_key -o ./target/recurse_leaf_vk_as_fields VK_HASH=$(jq -r '.[0]' ./target/recurse_leaf_vk_as_fields) VK_AS_FIELDS=$(jq -r '.[1:]' ./target/recurse_leaf_vk_as_fields) -FULL_PROOF_AS_FIELDS="$($BACKEND OLD_API proof_as_fields -p ./target/recurse_leaf_proof -k ./target/recurse_leaf_key -o -)" +FULL_PROOF_AS_FIELDS="$($BACKEND proof_as_fields -p ./target/recurse_leaf_proof -k ./target/recurse_leaf_key -o -)" # recurse_leaf has 4 public inputs (excluding aggregation object) PUBLIC_INPUTS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[:4]') PROOF_AS_FIELDS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[4:]') @@ -54,8 +54,8 @@ echo "public_inputs = $PUBLIC_INPUTS" >> $RECURSE_NODE_PROVER_TOML # We can now execute and prove `recurse_node` nargo execute recurse_node_witness --package recurse_node -$BACKEND OLD_API prove -b ./target/recurse_node.json -w ./target/recurse_node_witness.gz -o ./target/recurse_node_proof +$BACKEND prove -b ./target/recurse_node.json -w ./target/recurse_node_witness.gz -o ./target/recurse_node_proof # We finally verify that the generated recursive proof is valid. -$BACKEND OLD_API write_vk -b ./target/recurse_node.json -o ./target/recurse_node_key -$BACKEND OLD_API verify -p ./target/recurse_node_proof -k ./target/recurse_node_key +$BACKEND write_vk -b ./target/recurse_node.json -o ./target/recurse_node_key +$BACKEND verify -p ./target/recurse_node_proof -k ./target/recurse_node_key diff --git a/noir/noir-repo/noir_stdlib/src/cmp.nr b/noir/noir-repo/noir_stdlib/src/cmp.nr index 16bcd4390f78..24621bb37934 100644 --- a/noir/noir-repo/noir_stdlib/src/cmp.nr +++ b/noir/noir-repo/noir_stdlib/src/cmp.nr @@ -35,6 +35,11 @@ impl Eq for Field { } } +impl Eq for u128 { + fn eq(self, other: u128) -> bool { + self == other + } +} impl Eq for u64 { fn eq(self, other: u64) -> bool { self == other @@ -232,6 +237,17 @@ comptime fn derive_ord(s: StructDefinition) -> Quoted { // Note: Field deliberately does not implement Ord +impl Ord for u128 { + fn cmp(self, other: u128) -> Ordering { + if self < other { + Ordering::less() + } else if self > other { + Ordering::greater() + } else { + Ordering::equal() + } + } +} impl Ord for u64 { fn cmp(self, other: u64) -> Ordering { if self < other { diff --git a/noir/noir-repo/noir_stdlib/src/convert.nr b/noir/noir-repo/noir_stdlib/src/convert.nr index 1e5c1484b5fe..7e7348c2b37d 100644 --- a/noir/noir-repo/noir_stdlib/src/convert.nr +++ b/noir/noir-repo/noir_stdlib/src/convert.nr @@ -45,6 +45,22 @@ impl From for u64 { } } +impl From for u128 { + fn from(value: u8) -> u128 { + value as u128 + } +} +impl From for u128 { + fn from(value: u32) -> u128 { + value as u128 + } +} +impl From for u128 { + fn from(value: u64) -> u128 { + value as u128 + } +} + impl From for Field { fn from(value: u8) -> Field { value as Field @@ -61,6 +77,12 @@ impl From for Field { } } +impl From for Field { + fn from(value: u128) -> Field { + value as Field + } +} + // Signed integers impl From for i32 { @@ -142,6 +164,7 @@ comptime fn generate_as_primitive_impls(_: FunctionDefinition) -> Quoted { quote { u16 }, quote { u32 }, quote { u64 }, + quote { u128 }, quote { i8 }, quote { i16 }, quote { i32 }, diff --git a/noir/noir-repo/noir_stdlib/src/default.nr b/noir/noir-repo/noir_stdlib/src/default.nr index 01f10a368b14..229e5e92003f 100644 --- a/noir/noir-repo/noir_stdlib/src/default.nr +++ b/noir/noir-repo/noir_stdlib/src/default.nr @@ -47,6 +47,12 @@ impl Default for u64 { } } +impl Default for u128 { + fn default() -> u128 { + 0 + } +} + impl Default for i8 { fn default() -> i8 { 0 diff --git a/noir/noir-repo/noir_stdlib/src/ecdsa_secp256k1.nr b/noir/noir-repo/noir_stdlib/src/ecdsa_secp256k1.nr index ec39e9997152..1b0f32343b98 100644 --- a/noir/noir-repo/noir_stdlib/src/ecdsa_secp256k1.nr +++ b/noir/noir-repo/noir_stdlib/src/ecdsa_secp256k1.nr @@ -19,4 +19,3 @@ pub fn verify_signature_slice( ) -> bool // docs:end:ecdsa_secp256k1_slice {} - diff --git a/noir/noir-repo/noir_stdlib/src/hash/keccak.nr b/noir/noir-repo/noir_stdlib/src/hash/keccak.nr deleted file mode 100644 index 75be7982e667..000000000000 --- a/noir/noir-repo/noir_stdlib/src/hash/keccak.nr +++ /dev/null @@ -1,155 +0,0 @@ -use crate::runtime::is_unconstrained; - -global BLOCK_SIZE_IN_BYTES: u32 = 136; //(1600 - BITS * 2) / WORD_SIZE; -global WORD_SIZE: u32 = 8; // Limbs are made up of u64s so 8 bytes each. -global LIMBS_PER_BLOCK: u32 = BLOCK_SIZE_IN_BYTES / WORD_SIZE; -global NUM_KECCAK_LANES: u32 = 25; - -#[foreign(keccakf1600)] -pub fn keccakf1600(input: [u64; 25]) -> [u64; 25] {} - -#[no_predicates] -#[deprecated("keccak256 is being deprecated from the stdlib, use https://github.com/noir-lang/keccak256 instead")] -pub(crate) fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] { - assert(N >= message_size); - - // Copy input to block bytes. For that we'll need at least input bytes (N) - // but we want it to be padded to a multiple of BLOCK_SIZE_IN_BYTES. - let mut block_bytes = [0; ((N / BLOCK_SIZE_IN_BYTES) + 1) * BLOCK_SIZE_IN_BYTES]; - if is_unconstrained() { - for i in 0..message_size { - block_bytes[i] = input[i]; - } - } else { - for i in 0..N { - if i < message_size { - block_bytes[i] = input[i]; - } - } - } - - //1. format_input_lanes - let max_blocks = (N + BLOCK_SIZE_IN_BYTES) / BLOCK_SIZE_IN_BYTES; - //maximum number of bytes to hash - let real_max_blocks = (message_size + BLOCK_SIZE_IN_BYTES) / BLOCK_SIZE_IN_BYTES; - let real_blocks_bytes = real_max_blocks * BLOCK_SIZE_IN_BYTES; - - block_bytes[message_size] = 1; - block_bytes[real_blocks_bytes - 1] = 0x80; - - // populate a vector of 64-bit limbs from our byte array - let mut sliced_buffer = - [0; (((N / BLOCK_SIZE_IN_BYTES) + 1) * BLOCK_SIZE_IN_BYTES) / WORD_SIZE]; - for i in 0..sliced_buffer.len() { - let limb_start = WORD_SIZE * i; - - let mut sliced = 0; - let mut v = 1; - for k in 0..WORD_SIZE { - sliced += v * (block_bytes[limb_start + k] as Field); - v *= 256; - } - - sliced_buffer[i] = sliced as u64; - } - - //2. sponge_absorb - let mut state: [u64; NUM_KECCAK_LANES] = [0; NUM_KECCAK_LANES]; - // When in an unconstrained runtime we can take advantage of runtime loop bounds, - // thus allowing us to simplify the loop body. - if is_unconstrained() { - for i in 0..real_max_blocks { - if (i == 0) { - for j in 0..LIMBS_PER_BLOCK { - state[j] = sliced_buffer[j]; - } - } else { - for j in 0..LIMBS_PER_BLOCK { - state[j] = state[j] ^ sliced_buffer[i * LIMBS_PER_BLOCK + j]; - } - } - state = keccakf1600(state); - } - } else { - // `real_max_blocks` is guaranteed to at least be `1` - // We peel out the first block as to avoid a conditional inside of the loop. - // Otherwise, a dynamic predicate can cause a blowup in a constrained runtime. - for j in 0..LIMBS_PER_BLOCK { - state[j] = sliced_buffer[j]; - } - state = keccakf1600(state); - for i in 1..max_blocks { - if i < real_max_blocks { - for j in 0..LIMBS_PER_BLOCK { - state[j] = state[j] ^ sliced_buffer[i * LIMBS_PER_BLOCK + j]; - } - state = keccakf1600(state); - } - } - } - - //3. sponge_squeeze - let mut result = [0; 32]; - for i in 0..4 { - let lane = state[i] as Field; - let lane_le: [u8; 8] = lane.to_le_bytes(); - for j in 0..8 { - result[8 * i + j] = lane_le[j]; - } - } - result -} - -mod tests { - use super::keccak256; - - #[test] - fn smoke_test() { - let input = [0xbd]; - let result = [ - 0x5a, 0x50, 0x2f, 0x9f, 0xca, 0x46, 0x7b, 0x26, 0x6d, 0x5b, 0x78, 0x33, 0x65, 0x19, - 0x37, 0xe8, 0x05, 0x27, 0x0c, 0xa3, 0xf3, 0xaf, 0x1c, 0x0d, 0xd2, 0x46, 0x2d, 0xca, - 0x4b, 0x3b, 0x1a, 0xbf, - ]; - assert_eq(keccak256(input, input.len()), result); - } - - #[test] - fn hash_hello_world() { - let input = "Hello world!".as_bytes(); - let result = [ - 0xec, 0xd0, 0xe1, 0x8, 0xa9, 0x8e, 0x19, 0x2a, 0xf1, 0xd2, 0xc2, 0x50, 0x55, 0xf4, 0xe3, - 0xbe, 0xd7, 0x84, 0xb5, 0xc8, 0x77, 0x20, 0x4e, 0x73, 0x21, 0x9a, 0x52, 0x3, 0x25, 0x1f, - 0xea, 0xab, - ]; - assert_eq(keccak256(input, input.len()), result); - } - - #[test] - fn var_size_hash() { - let input = [ - 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, - 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, - 223, - ]; - let result = [ - 226, 37, 115, 94, 94, 196, 72, 116, 194, 105, 79, 233, 65, 12, 30, 94, 181, 131, 170, - 219, 171, 166, 236, 88, 143, 67, 255, 160, 248, 214, 39, 129, - ]; - assert_eq(keccak256(input, 13), result); - } - - #[test] - fn hash_longer_than_136_bytes() { - let input = "123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789" - .as_bytes(); - assert(input.len() > 136); - - let result = [ - 0x1d, 0xca, 0xeb, 0xdf, 0xd9, 0xd6, 0x24, 0x67, 0x1c, 0x18, 0x16, 0xda, 0xd, 0x8a, 0xeb, - 0xa8, 0x75, 0x71, 0x2c, 0xc, 0x89, 0xe0, 0x25, 0x2, 0xe8, 0xb6, 0x5e, 0x16, 0x5, 0x55, - 0xe4, 0x40, - ]; - assert_eq(keccak256(input, input.len()), result); - } -} diff --git a/noir/noir-repo/noir_stdlib/src/hash/mod.nr b/noir/noir-repo/noir_stdlib/src/hash/mod.nr index ee78d04c91b6..7a492d373cc9 100644 --- a/noir/noir-repo/noir_stdlib/src/hash/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/hash/mod.nr @@ -1,18 +1,28 @@ pub mod poseidon; pub mod poseidon2; -pub mod keccak; -pub mod sha256; -pub mod sha512; use crate::default::Default; use crate::embedded_curve_ops::{ EmbeddedCurvePoint, EmbeddedCurveScalar, multi_scalar_mul, multi_scalar_mul_array_return, }; use crate::meta::derive_via; -use crate::uint128::U128; -// Kept for backwards compatibility -pub use sha256::{digest, sha256, sha256_compression, sha256_var}; +#[foreign(sha256_compression)] +// docs:start:sha256_compression +pub fn sha256_compression(input: [u32; 16], state: [u32; 8]) -> [u32; 8] {} +// docs:end:sha256_compression + +#[foreign(keccakf1600)] +// docs:start:keccakf1600 +pub fn keccakf1600(input: [u64; 25]) -> [u64; 25] {} +// docs:end:keccakf1600 + +pub mod keccak { + #[deprecated("This function has been moved to std::hash::keccakf1600")] + pub fn keccakf1600(input: [u64; 25]) -> [u64; 25] { + super::keccakf1600(input) + } +} #[foreign(blake2s)] // docs:start:blake2s @@ -114,13 +124,6 @@ pub fn hash_to_field(inputs: [Field]) -> Field { sum } -// docs:start:keccak256 -pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] -// docs:end:keccak256 -{ - crate::hash::keccak::keccak256(input, message_size) -} - #[foreign(poseidon2_permutation)] pub fn poseidon2_permutation(_input: [Field; N], _state_length: u32) -> [Field; N] {} @@ -241,6 +244,15 @@ impl Hash for u64 { } } +impl Hash for u128 { + fn hash(self, state: &mut H) + where + H: Hasher, + { + H::write(state, self as Field); + } +} + impl Hash for i8 { fn hash(self, state: &mut H) where @@ -293,16 +305,6 @@ impl Hash for () { {} } -impl Hash for U128 { - fn hash(self, state: &mut H) - where - H: Hasher, - { - H::write(state, self.lo as Field); - H::write(state, self.hi as Field); - } -} - impl Hash for [T; N] where T: Hash, diff --git a/noir/noir-repo/noir_stdlib/src/hash/sha256.nr b/noir/noir-repo/noir_stdlib/src/hash/sha256.nr deleted file mode 100644 index a8bd71a21111..000000000000 --- a/noir/noir-repo/noir_stdlib/src/hash/sha256.nr +++ /dev/null @@ -1,845 +0,0 @@ -use crate::runtime::is_unconstrained; - -// Implementation of SHA-256 mapping a byte array of variable length to -// 32 bytes. - -// A message block is up to 64 bytes taken from the input. -global BLOCK_SIZE: u32 = 64; - -// The first index in the block where the 8 byte message size will be written. -global MSG_SIZE_PTR: u32 = 56; - -// Size of the message block when packed as 4-byte integer array. -global INT_BLOCK_SIZE: u32 = 16; - -// A `u32` integer consists of 4 bytes. -global INT_SIZE: u32 = 4; - -// Index of the integer in the `INT_BLOCK` where the length is written. -global INT_SIZE_PTR: u32 = MSG_SIZE_PTR / INT_SIZE; - -// Magic numbers for bit shifting. -// Works with actual bit shifting as well as the compiler turns them into * and / -// but circuit execution appears to be 10% faster this way. -global TWO_POW_8: u32 = 256; -global TWO_POW_16: u32 = TWO_POW_8 * 256; -global TWO_POW_24: u32 = TWO_POW_16 * 256; -global TWO_POW_32: u64 = TWO_POW_24 as u64 * 256; - -// Index of a byte in a 64 byte block; ie. 0..=63 -type BLOCK_BYTE_PTR = u32; - -// The foreign function to compress blocks works on 16 pieces of 4-byte integers, instead of 64 bytes. -type INT_BLOCK = [u32; INT_BLOCK_SIZE]; - -// A message block is a slice of the original message of a fixed size, -// potentially padded with zeros, with neighbouring 4 bytes packed into integers. -type MSG_BLOCK = INT_BLOCK; - -// The hash is 32 bytes. -type HASH = [u8; 32]; - -// The state accumulates the blocks. -// Its overall size is the same as the `HASH`. -type STATE = [u32; 8]; - -// docs:start:sha256 -#[deprecated("sha256 is being deprecated from the stdlib, use https://github.com/noir-lang/sha256 instead")] -pub fn sha256(input: [u8; N]) -> HASH -// docs:end:sha256 -{ - digest(input) -} - -#[foreign(sha256_compression)] -pub fn sha256_compression(_input: INT_BLOCK, _state: STATE) -> STATE {} - -// SHA-256 hash function -#[no_predicates] -#[deprecated("sha256 is being deprecated from the stdlib, use https://github.com/noir-lang/sha256 instead")] -pub fn digest(msg: [u8; N]) -> HASH { - sha256_var(msg, N as u64) -} - -// Variable size SHA-256 hash -#[deprecated("sha256 is being deprecated from the stdlib, use https://github.com/noir-lang/sha256 instead")] -pub fn sha256_var(msg: [u8; N], message_size: u64) -> HASH { - let message_size = message_size as u32; - let num_blocks = N / BLOCK_SIZE; - let mut msg_block: MSG_BLOCK = [0; INT_BLOCK_SIZE]; - // Intermediate hash, starting with the canonical initial value - let mut h: STATE = [ - 1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, - 1541459225, - ]; - // Pointer into msg_block on a 64 byte scale - let mut msg_byte_ptr = 0; - for i in 0..num_blocks { - let msg_start = BLOCK_SIZE * i; - // Safety: the msg_block is checked below in verify_msg_block - let (new_msg_block, new_msg_byte_ptr) = - unsafe { build_msg_block(msg, message_size, msg_start) }; - - if msg_start < message_size { - msg_block = new_msg_block; - } - - if !is_unconstrained() { - // Verify the block we are compressing was appropriately constructed - let new_msg_byte_ptr = verify_msg_block(msg, message_size, msg_block, msg_start); - if msg_start < message_size { - msg_byte_ptr = new_msg_byte_ptr; - } - } else if msg_start < message_size { - msg_byte_ptr = new_msg_byte_ptr; - } - - // If the block is filled, compress it. - // An un-filled block is handled after this loop. - if (msg_start < message_size) & (msg_byte_ptr == BLOCK_SIZE) { - h = sha256_compression(msg_block, h); - } - } - - let modulo = N % BLOCK_SIZE; - // Handle setup of the final msg block. - // This case is only hit if the msg is less than the block size, - // or our message cannot be evenly split into blocks. - if modulo != 0 { - let msg_start = BLOCK_SIZE * num_blocks; - // Safety: the msg_block is checked below in verify_msg_block - let (new_msg_block, new_msg_byte_ptr) = - unsafe { build_msg_block(msg, message_size, msg_start) }; - - if msg_start < message_size { - msg_block = new_msg_block; - } - - if !is_unconstrained() { - let new_msg_byte_ptr = verify_msg_block(msg, message_size, msg_block, msg_start); - if msg_start < message_size { - msg_byte_ptr = new_msg_byte_ptr; - verify_msg_block_padding(msg_block, msg_byte_ptr); - } - } else if msg_start < message_size { - msg_byte_ptr = new_msg_byte_ptr; - } - } - - // If we had modulo == 0 then it means the last block was full, - // and we can reset the pointer to zero to overwrite it. - if msg_byte_ptr == BLOCK_SIZE { - msg_byte_ptr = 0; - } - - // Pad the rest such that we have a [u32; 2] block at the end representing the length - // of the message, and a block of 1 0 ... 0 following the message (i.e. [1 << 7, 0, ..., 0]). - // Here we rely on the fact that everything beyond the available input is set to 0. - msg_block = update_block_item( - msg_block, - msg_byte_ptr, - |msg_item| set_item_byte_then_zeros(msg_item, msg_byte_ptr, 1 << 7), - ); - msg_byte_ptr = msg_byte_ptr + 1; - let last_block = msg_block; - - // If we don't have room to write the size, compress the block and reset it. - if msg_byte_ptr > MSG_SIZE_PTR { - h = sha256_compression(msg_block, h); - // `attach_len_to_msg_block` will zero out everything after the `msg_byte_ptr`. - msg_byte_ptr = 0; - } - - // Safety: the msg_len is checked below in verify_msg_len - msg_block = unsafe { attach_len_to_msg_block(msg_block, msg_byte_ptr, message_size) }; - - if !is_unconstrained() { - verify_msg_len(msg_block, last_block, msg_byte_ptr, message_size); - } - - hash_final_block(msg_block, h) -} - -// Take `BLOCK_SIZE` number of bytes from `msg` starting at `msg_start`. -// Returns the block and the length that has been copied rather than padded with zeros. -unconstrained fn build_msg_block( - msg: [u8; N], - message_size: u32, - msg_start: u32, -) -> (MSG_BLOCK, BLOCK_BYTE_PTR) { - let mut msg_block: MSG_BLOCK = [0; INT_BLOCK_SIZE]; - - // We insert `BLOCK_SIZE` bytes (or up to the end of the message) - let block_input = if msg_start + BLOCK_SIZE > message_size { - if message_size < msg_start { - // This function is sometimes called with `msg_start` past the end of the message. - // In this case we return an empty block and zero pointer to signal that the result should be ignored. - 0 - } else { - message_size - msg_start - } - } else { - BLOCK_SIZE - }; - - // Figure out the number of items in the int array that we have to pack. - // e.g. if the input is [0,1,2,3,4,5] then we need to pack it as 2 items: [0123, 4500] - let mut int_input = block_input / INT_SIZE; - if block_input % INT_SIZE != 0 { - int_input = int_input + 1; - }; - - for i in 0..int_input { - let mut msg_item: u32 = 0; - // Always construct the integer as 4 bytes, even if it means going beyond the input. - for j in 0..INT_SIZE { - let k = i * INT_SIZE + j; - let msg_byte = if k < block_input { - msg[msg_start + k] - } else { - 0 - }; - msg_item = lshift8(msg_item, 1) + msg_byte as u32; - } - msg_block[i] = msg_item; - } - - // Returning the index as if it was a 64 byte array. - // We have to project it down to 16 items and bit shifting to get a byte back if we need it. - (msg_block, block_input) -} - -// Verify the block we are compressing was appropriately constructed by `build_msg_block` -// and matches the input data. Returns the index of the first unset item. -// If `message_size` is less than `msg_start` then this is called with the old non-empty block; -// in that case we can skip verification, ie. no need to check that everything is zero. -fn verify_msg_block( - msg: [u8; N], - message_size: u32, - msg_block: MSG_BLOCK, - msg_start: u32, -) -> BLOCK_BYTE_PTR { - let mut msg_byte_ptr = 0; - let mut msg_end = msg_start + BLOCK_SIZE; - if msg_end > N { - msg_end = N; - } - // We might have to go beyond the input to pad the fields. - if msg_end % INT_SIZE != 0 { - msg_end = msg_end + INT_SIZE - msg_end % INT_SIZE; - } - - // Reconstructed packed item. - let mut msg_item: u32 = 0; - - // Inclusive at the end so that we can compare the last item. - let mut i: u32 = 0; - for k in msg_start..=msg_end { - if k % INT_SIZE == 0 { - // If we consumed some input we can compare against the block. - if (msg_start < message_size) & (k > msg_start) { - assert_eq(msg_block[i], msg_item as u32); - i = i + 1; - msg_item = 0; - } - } - // Shift the accumulator - msg_item = lshift8(msg_item, 1); - // If we have input to consume, add it at the rightmost position. - if k < message_size & k < msg_end { - msg_item = msg_item + msg[k] as u32; - msg_byte_ptr = msg_byte_ptr + 1; - } - } - - msg_byte_ptr -} - -// Verify the block we are compressing was appropriately padded with zeros by `build_msg_block`. -// This is only relevant for the last, potentially partially filled block. -fn verify_msg_block_padding(msg_block: MSG_BLOCK, msg_byte_ptr: BLOCK_BYTE_PTR) { - // Check all the way to the end of the block. - verify_msg_block_zeros(msg_block, msg_byte_ptr, INT_BLOCK_SIZE); -} - -// Verify that a region of ints in the message block are (partially) zeroed, -// up to an (exclusive) maximum which can either be the end of the block -// or just where the size is to be written. -fn verify_msg_block_zeros( - msg_block: MSG_BLOCK, - mut msg_byte_ptr: BLOCK_BYTE_PTR, - max_int_byte_ptr: u32, -) { - // This variable is used to get around the compiler under-constrained check giving a warning. - // We want to check against a constant zero, but if it does not come from the circuit inputs - // or return values the compiler check will issue a warning. - let zero = msg_block[0] - msg_block[0]; - - // First integer which is supposed to be (partially) zero. - let mut int_byte_ptr = msg_byte_ptr / INT_SIZE; - - // Check partial zeros. - let modulo = msg_byte_ptr % INT_SIZE; - if modulo != 0 { - let zeros = INT_SIZE - modulo; - let mask = if zeros == 3 { - TWO_POW_24 - } else if zeros == 2 { - TWO_POW_16 - } else { - TWO_POW_8 - }; - assert_eq(msg_block[int_byte_ptr] % mask, zero); - int_byte_ptr = int_byte_ptr + 1; - } - - // Check the rest of the items. - for i in 0..max_int_byte_ptr { - if i >= int_byte_ptr { - assert_eq(msg_block[i], zero); - } - } -} - -// Verify that up to the byte pointer the two blocks are equal. -// At the byte pointer the new block can be partially zeroed. -fn verify_msg_block_equals_last( - msg_block: MSG_BLOCK, - last_block: MSG_BLOCK, - mut msg_byte_ptr: BLOCK_BYTE_PTR, -) { - // msg_byte_ptr is the position at which they are no longer have to be the same. - // First integer which is supposed to be (partially) zero contains that pointer. - let mut int_byte_ptr = msg_byte_ptr / INT_SIZE; - - // Check partial zeros. - let modulo = msg_byte_ptr % INT_SIZE; - if modulo != 0 { - // Reconstruct the partially zero item from the last block. - let last_field = last_block[int_byte_ptr]; - let mut msg_item: u32 = 0; - // Reset to where they are still equal. - msg_byte_ptr = msg_byte_ptr - modulo; - for i in 0..INT_SIZE { - msg_item = lshift8(msg_item, 1); - if i < modulo { - msg_item = msg_item + get_item_byte(last_field, msg_byte_ptr) as u32; - msg_byte_ptr = msg_byte_ptr + 1; - } - } - assert_eq(msg_block[int_byte_ptr], msg_item); - } - - for i in 0..INT_SIZE_PTR { - if i < int_byte_ptr { - assert_eq(msg_block[i], last_block[i]); - } - } -} - -// Apply a function on the block item which the pointer indicates. -fn update_block_item( - mut msg_block: MSG_BLOCK, - msg_byte_ptr: BLOCK_BYTE_PTR, - f: fn[Env](u32) -> u32, -) -> MSG_BLOCK { - let i = msg_byte_ptr / INT_SIZE; - msg_block[i] = f(msg_block[i]); - msg_block -} - -// Set the rightmost `zeros` number of bytes to 0. -fn set_item_zeros(item: u32, zeros: u8) -> u32 { - lshift8(rshift8(item, zeros), zeros) -} - -// Replace one byte in the item with a value, and set everything after it to zero. -fn set_item_byte_then_zeros(msg_item: u32, msg_byte_ptr: BLOCK_BYTE_PTR, msg_byte: u8) -> u32 { - let zeros = INT_SIZE - msg_byte_ptr % INT_SIZE; - let zeroed_item = set_item_zeros(msg_item, zeros as u8); - let new_item = byte_into_item(msg_byte, msg_byte_ptr); - zeroed_item + new_item -} - -// Get a byte of a message item according to its overall position in the `BLOCK_SIZE` space. -fn get_item_byte(mut msg_item: u32, msg_byte_ptr: BLOCK_BYTE_PTR) -> u8 { - // How many times do we have to shift to the right to get to the position we want? - let max_shifts = INT_SIZE - 1; - let shifts = max_shifts - msg_byte_ptr % INT_SIZE; - msg_item = rshift8(msg_item, shifts as u8); - // At this point the byte we want is in the rightmost position. - msg_item as u8 -} - -// Project a byte into a position in a field based on the overall block pointer. -// For example putting 1 into pointer 5 would be 100, because overall we would -// have [____, 0100] with indexes [0123,4567]. -fn byte_into_item(msg_byte: u8, msg_byte_ptr: BLOCK_BYTE_PTR) -> u32 { - let mut msg_item = msg_byte as u32; - // How many times do we have to shift to the left to get to the position we want? - let max_shifts = INT_SIZE - 1; - let shifts = max_shifts - msg_byte_ptr % INT_SIZE; - lshift8(msg_item, shifts as u8) -} - -// Construct a field out of 4 bytes. -fn make_item(b0: u8, b1: u8, b2: u8, b3: u8) -> u32 { - let mut item = b0 as u32; - item = lshift8(item, 1) + b1 as u32; - item = lshift8(item, 1) + b2 as u32; - item = lshift8(item, 1) + b3 as u32; - item -} - -// Shift by 8 bits to the left between 0 and 4 times. -// Checks `is_unconstrained()` to just use a bitshift if we're running in an unconstrained context, -// otherwise multiplies by 256. -fn lshift8(item: u32, shifts: u8) -> u32 { - if is_unconstrained() { - if item == 0 { - 0 - } else { - // Brillig wouldn't shift 0<<4 without overflow. - item << (8 * shifts) - } - } else { - // We can do a for loop up to INT_SIZE or an if-else. - if shifts == 0 { - item - } else if shifts == 1 { - item * TWO_POW_8 - } else if shifts == 2 { - item * TWO_POW_16 - } else if shifts == 3 { - item * TWO_POW_24 - } else { - // Doesn't make sense, but it's most likely called on 0 anyway. - 0 - } - } -} - -// Shift by 8 bits to the right between 0 and 4 times. -// Checks `is_unconstrained()` to just use a bitshift if we're running in an unconstrained context, -// otherwise divides by 256. -fn rshift8(item: u32, shifts: u8) -> u32 { - if is_unconstrained() { - item >> (8 * shifts) - } else { - // Division wouldn't work on `Field`. - if shifts == 0 { - item - } else if shifts == 1 { - item / TWO_POW_8 - } else if shifts == 2 { - item / TWO_POW_16 - } else if shifts == 3 { - item / TWO_POW_24 - } else { - 0 - } - } -} - -// Zero out all bytes between the end of the message and where the length is appended, -// then write the length into the last 8 bytes of the block. -unconstrained fn attach_len_to_msg_block( - mut msg_block: MSG_BLOCK, - mut msg_byte_ptr: BLOCK_BYTE_PTR, - message_size: u32, -) -> MSG_BLOCK { - // We assume that `msg_byte_ptr` is less than 57 because if not then it is reset to zero before calling this function. - // In any case, fill blocks up with zeros until the last 64 bits (i.e. until msg_byte_ptr = 56). - // There can be one item which has to be partially zeroed. - let modulo = msg_byte_ptr % INT_SIZE; - if modulo != 0 { - // Index of the block in which we find the item we need to partially zero. - let i = msg_byte_ptr / INT_SIZE; - let zeros = INT_SIZE - modulo; - msg_block[i] = set_item_zeros(msg_block[i], zeros as u8); - msg_byte_ptr = msg_byte_ptr + zeros; - } - - // The rest can be zeroed without bit shifting anything. - for i in (msg_byte_ptr / INT_SIZE)..INT_SIZE_PTR { - msg_block[i] = 0; - } - - // Set the last two 4 byte ints as the first/second half of the 8 bytes of the length. - let len = 8 * message_size; - let len_bytes: [u8; 8] = (len as Field).to_be_bytes(); - for i in 0..=1 { - let shift = i * 4; - msg_block[INT_SIZE_PTR + i] = make_item( - len_bytes[shift], - len_bytes[shift + 1], - len_bytes[shift + 2], - len_bytes[shift + 3], - ); - } - msg_block -} - -// Verify that the message length was correctly written by `attach_len_to_msg_block`, -// and that everything between the byte pointer and the size pointer was zeroed, -// and that everything before the byte pointer was untouched. -fn verify_msg_len( - msg_block: MSG_BLOCK, - last_block: MSG_BLOCK, - msg_byte_ptr: BLOCK_BYTE_PTR, - message_size: u32, -) { - // Check zeros up to the size pointer. - verify_msg_block_zeros(msg_block, msg_byte_ptr, INT_SIZE_PTR); - - // Check that up to the pointer we match the last block. - verify_msg_block_equals_last(msg_block, last_block, msg_byte_ptr); - - // We verify the message length was inserted correctly by reversing the byte decomposition. - let mut reconstructed_len: u64 = 0; - for i in INT_SIZE_PTR..INT_BLOCK_SIZE { - reconstructed_len = reconstructed_len * TWO_POW_32; - reconstructed_len = reconstructed_len + msg_block[i] as u64; - } - let len = 8 * message_size as u64; - assert_eq(reconstructed_len, len); -} - -// Perform the final compression, then transform the `STATE` into `HASH`. -fn hash_final_block(msg_block: MSG_BLOCK, mut state: STATE) -> HASH { - let mut out_h: HASH = [0; 32]; // Digest as sequence of bytes - // Hash final padded block - state = sha256_compression(msg_block, state); - - // Return final hash as byte array - for j in 0..8 { - let h_bytes: [u8; 4] = (state[j] as Field).to_be_bytes(); - for k in 0..4 { - out_h[4 * j + k] = h_bytes[k]; - } - } - - out_h -} - -mod tests { - use super::{ - attach_len_to_msg_block, build_msg_block, byte_into_item, get_item_byte, make_item, - set_item_byte_then_zeros, set_item_zeros, - }; - use super::INT_BLOCK; - use super::sha256_var; - - #[test] - fn smoke_test() { - let input = [0xbd]; - let result = [ - 0x68, 0x32, 0x57, 0x20, 0xaa, 0xbd, 0x7c, 0x82, 0xf3, 0x0f, 0x55, 0x4b, 0x31, 0x3d, - 0x05, 0x70, 0xc9, 0x5a, 0xcc, 0xbb, 0x7d, 0xc4, 0xb5, 0xaa, 0xe1, 0x12, 0x04, 0xc0, - 0x8f, 0xfe, 0x73, 0x2b, - ]; - assert_eq(sha256_var(input, input.len() as u64), result); - } - - #[test] - fn msg_just_over_block() { - let input = [ - 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, - 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, - 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, - 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, - ]; - let result = [ - 91, 122, 146, 93, 52, 109, 133, 148, 171, 61, 156, 70, 189, 238, 153, 7, 222, 184, 94, - 24, 65, 114, 192, 244, 207, 199, 87, 232, 192, 224, 171, 207, - ]; - assert_eq(sha256_var(input, input.len() as u64), result); - } - - #[test] - fn msg_multiple_over_block() { - let input = [ - 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, - 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, - 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, - 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 115, 45, 97, 115, - 99, 105, 105, 13, 10, 109, 105, 109, 101, 45, 118, 101, 114, 115, 105, 111, 110, 58, 49, - 46, 48, 32, 40, 77, 97, 99, 32, 79, 83, 32, 88, 32, 77, 97, 105, 108, 32, 49, 54, 46, - 48, 32, 92, 40, 51, 55, 51, 49, 46, 53, 48, 48, 46, 50, 51, 49, 92, 41, 41, 13, 10, 115, - 117, 98, 106, 101, 99, 116, 58, 72, 101, 108, 108, 111, 13, 10, 109, 101, 115, 115, 97, - 103, 101, 45, 105, 100, 58, 60, 56, 70, 56, 49, 57, 68, 51, 50, 45, 66, 54, 65, 67, 45, - 52, 56, 57, 68, 45, 57, 55, 55, 70, 45, 52, 51, 56, 66, 66, 67, 52, 67, 65, 66, 50, 55, - 64, 109, 101, 46, 99, 111, 109, 62, 13, 10, 100, 97, 116, 101, 58, 83, 97, 116, 44, 32, - 50, 54, 32, 65, 117, 103, 32, 50, 48, 50, 51, 32, 49, 50, 58, 50, 53, 58, 50, 50, 32, - 43, 48, 52, 48, 48, 13, 10, 116, 111, 58, 122, 107, 101, 119, 116, 101, 115, 116, 64, - 103, 109, 97, 105, 108, 46, 99, 111, 109, 13, 10, 100, 107, 105, 109, 45, 115, 105, 103, - 110, 97, 116, 117, 114, 101, 58, 118, 61, 49, 59, 32, 97, 61, 114, 115, 97, 45, 115, - 104, 97, 50, 53, 54, 59, 32, 99, 61, 114, 101, 108, 97, 120, 101, 100, 47, 114, 101, - 108, 97, 120, 101, 100, 59, 32, 100, 61, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, - 59, 32, 115, 61, 49, 97, 49, 104, 97, 105, 59, 32, 116, 61, 49, 54, 57, 51, 48, 51, 56, - 51, 51, 55, 59, 32, 98, 104, 61, 55, 120, 81, 77, 68, 117, 111, 86, 86, 85, 52, 109, 48, - 87, 48, 87, 82, 86, 83, 114, 86, 88, 77, 101, 71, 83, 73, 65, 83, 115, 110, 117, 99, 75, - 57, 100, 74, 115, 114, 99, 43, 118, 85, 61, 59, 32, 104, 61, 102, 114, 111, 109, 58, 67, - 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 77, 105, 109, 101, 45, 86, 101, - 114, 115, 105, 111, 110, 58, 83, 117, 98, 106, 101, 99, - ]; - let result = [ - 116, 90, 151, 31, 78, 22, 138, 180, 211, 189, 69, 76, 227, 200, 155, 29, 59, 123, 154, - 60, 47, 153, 203, 129, 157, 251, 48, 2, 79, 11, 65, 47, - ]; - assert_eq(sha256_var(input, input.len() as u64), result); - } - - #[test] - fn msg_just_under_block() { - let input = [ - 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, - 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, - 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, - 108, 97, 105, 110, 59, - ]; - let result = [ - 143, 140, 76, 173, 222, 123, 102, 68, 70, 149, 207, 43, 39, 61, 34, 79, 216, 252, 213, - 165, 74, 16, 110, 74, 29, 64, 138, 167, 30, 1, 9, 119, - ]; - assert_eq(sha256_var(input, input.len() as u64), result); - } - - #[test] - fn msg_big_not_block_multiple() { - let input = [ - 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, - 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, - 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, - 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 115, 45, 97, 115, - 99, 105, 105, 13, 10, 109, 105, 109, 101, 45, 118, 101, 114, 115, 105, 111, 110, 58, 49, - 46, 48, 32, 40, 77, 97, 99, 32, 79, 83, 32, 88, 32, 77, 97, 105, 108, 32, 49, 54, 46, - 48, 32, 92, 40, 51, 55, 51, 49, 46, 53, 48, 48, 46, 50, 51, 49, 92, 41, 41, 13, 10, 115, - 117, 98, 106, 101, 99, 116, 58, 72, 101, 108, 108, 111, 13, 10, 109, 101, 115, 115, 97, - 103, 101, 45, 105, 100, 58, 60, 56, 70, 56, 49, 57, 68, 51, 50, 45, 66, 54, 65, 67, 45, - 52, 56, 57, 68, 45, 57, 55, 55, 70, 45, 52, 51, 56, 66, 66, 67, 52, 67, 65, 66, 50, 55, - 64, 109, 101, 46, 99, 111, 109, 62, 13, 10, 100, 97, 116, 101, 58, 83, 97, 116, 44, 32, - 50, 54, 32, 65, 117, 103, 32, 50, 48, 50, 51, 32, 49, 50, 58, 50, 53, 58, 50, 50, 32, - 43, 48, 52, 48, 48, 13, 10, 116, 111, 58, 122, 107, 101, 119, 116, 101, 115, 116, 64, - 103, 109, 97, 105, 108, 46, 99, 111, 109, 13, 10, 100, 107, 105, 109, 45, 115, 105, 103, - 110, 97, 116, 117, 114, 101, 58, 118, 61, 49, 59, 32, 97, 61, 114, 115, 97, 45, 115, - 104, 97, 50, 53, 54, 59, 32, 99, 61, 114, 101, 108, 97, 120, 101, 100, 47, 114, 101, - 108, 97, 120, 101, 100, 59, 32, 100, 61, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, - 59, 32, 115, 61, 49, 97, 49, 104, 97, 105, 59, 32, 116, 61, 49, 54, 57, 51, 48, 51, 56, - 51, 51, 55, 59, 32, 98, 104, 61, 55, 120, 81, 77, 68, 117, 111, 86, 86, 85, 52, 109, 48, - 87, 48, 87, 82, 86, 83, 114, 86, 88, 77, 101, 71, 83, 73, 65, 83, 115, 110, 117, 99, 75, - 57, 100, 74, 115, 114, 99, 43, 118, 85, 61, 59, 32, 104, 61, 102, 114, 111, 109, 58, 67, - 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 77, 105, 109, 101, 45, 86, 101, - 114, 115, 105, 111, 110, 58, 83, 117, 98, 106, 101, 99, 116, 58, 77, 101, 115, 115, 97, - 103, 101, 45, 73, 100, 58, 68, 97, 116, 101, 58, 116, 111, 59, 32, 98, 61, - ]; - let result = [ - 112, 144, 73, 182, 208, 98, 9, 238, 54, 229, 61, 145, 222, 17, 72, 62, 148, 222, 186, - 55, 192, 82, 220, 35, 66, 47, 193, 200, 22, 38, 26, 186, - ]; - assert_eq(sha256_var(input, input.len() as u64), result); - } - - #[test] - fn msg_big_with_padding() { - let input = [ - 48, 130, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 48, 130, 1, 17, - 48, 37, 2, 1, 1, 4, 32, 176, 223, 31, 133, 108, 84, 158, 102, 70, 11, 165, 175, 196, 12, - 201, 130, 25, 131, 46, 125, 156, 194, 28, 23, 55, 133, 157, 164, 135, 136, 220, 78, 48, - 37, 2, 1, 2, 4, 32, 190, 82, 180, 235, 222, 33, 79, 50, 152, 136, 142, 35, 116, 224, 6, - 242, 156, 141, 128, 248, 10, 61, 98, 86, 248, 45, 207, 210, 90, 232, 175, 38, 48, 37, 2, - 1, 3, 4, 32, 0, 194, 104, 108, 237, 246, 97, 230, 116, 198, 69, 110, 26, 87, 17, 89, - 110, 199, 108, 250, 36, 21, 39, 87, 110, 102, 250, 213, 174, 131, 171, 174, 48, 37, 2, - 1, 11, 4, 32, 136, 155, 87, 144, 111, 15, 152, 127, 85, 25, 154, 81, 20, 58, 51, 75, - 193, 116, 234, 0, 60, 30, 29, 30, 183, 141, 72, 247, 255, 203, 100, 124, 48, 37, 2, 1, - 12, 4, 32, 41, 234, 106, 78, 31, 11, 114, 137, 237, 17, 92, 71, 134, 47, 62, 78, 189, - 233, 201, 214, 53, 4, 47, 189, 201, 133, 6, 121, 34, 131, 64, 142, 48, 37, 2, 1, 13, 4, - 32, 91, 222, 210, 193, 62, 222, 104, 82, 36, 41, 138, 253, 70, 15, 148, 208, 156, 45, - 105, 171, 241, 195, 185, 43, 217, 162, 146, 201, 222, 89, 238, 38, 48, 37, 2, 1, 14, 4, - 32, 76, 123, 216, 13, 51, 227, 72, 245, 59, 193, 238, 166, 103, 49, 23, 164, 171, 188, - 194, 197, 156, 187, 249, 28, 198, 95, 69, 15, 182, 56, 54, 38, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]; - let result = [ - 32, 85, 108, 174, 127, 112, 178, 182, 8, 43, 134, 123, 192, 211, 131, 66, 184, 240, 212, - 181, 240, 180, 106, 195, 24, 117, 54, 129, 19, 10, 250, 53, - ]; - let message_size = 297; - assert_eq(sha256_var(input, message_size), result); - } - - #[test] - fn msg_big_no_padding() { - let input = [ - 48, 130, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 48, 130, 1, 17, - 48, 37, 2, 1, 1, 4, 32, 176, 223, 31, 133, 108, 84, 158, 102, 70, 11, 165, 175, 196, 12, - 201, 130, 25, 131, 46, 125, 156, 194, 28, 23, 55, 133, 157, 164, 135, 136, 220, 78, 48, - 37, 2, 1, 2, 4, 32, 190, 82, 180, 235, 222, 33, 79, 50, 152, 136, 142, 35, 116, 224, 6, - 242, 156, 141, 128, 248, 10, 61, 98, 86, 248, 45, 207, 210, 90, 232, 175, 38, 48, 37, 2, - 1, 3, 4, 32, 0, 194, 104, 108, 237, 246, 97, 230, 116, 198, 69, 110, 26, 87, 17, 89, - 110, 199, 108, 250, 36, 21, 39, 87, 110, 102, 250, 213, 174, 131, 171, 174, 48, 37, 2, - 1, 11, 4, 32, 136, 155, 87, 144, 111, 15, 152, 127, 85, 25, 154, 81, 20, 58, 51, 75, - 193, 116, 234, 0, 60, 30, 29, 30, 183, 141, 72, 247, 255, 203, 100, 124, 48, 37, 2, 1, - 12, 4, 32, 41, 234, 106, 78, 31, 11, 114, 137, 237, 17, 92, 71, 134, 47, 62, 78, 189, - 233, 201, 214, 53, 4, 47, 189, 201, 133, 6, 121, 34, 131, 64, 142, 48, 37, 2, 1, 13, 4, - 32, 91, 222, 210, 193, 62, 222, 104, 82, 36, 41, 138, 253, 70, 15, 148, 208, 156, 45, - 105, 171, 241, 195, 185, 43, 217, 162, 146, 201, 222, 89, 238, 38, 48, 37, 2, 1, 14, 4, - 32, 76, 123, 216, 13, 51, 227, 72, 245, 59, 193, 238, 166, 103, 49, 23, 164, 171, 188, - 194, 197, 156, 187, 249, 28, 198, 95, 69, 15, 182, 56, 54, 38, - ]; - let result = [ - 32, 85, 108, 174, 127, 112, 178, 182, 8, 43, 134, 123, 192, 211, 131, 66, 184, 240, 212, - 181, 240, 180, 106, 195, 24, 117, 54, 129, 19, 10, 250, 53, - ]; - assert_eq(sha256_var(input, input.len() as u64), result); - } - - #[test] - fn same_msg_len_variable_padding() { - let input = [ - 29, 81, 165, 84, 243, 114, 101, 37, 242, 146, 127, 99, 69, 145, 39, 72, 213, 39, 253, - 179, 218, 37, 217, 201, 172, 93, 198, 50, 249, 70, 15, 30, 162, 112, 187, 40, 140, 9, - 236, 53, 32, 44, 38, 163, 113, 254, 192, 197, 44, 89, 71, 130, 169, 242, 17, 211, 214, - 72, 19, 178, 186, 168, 147, 127, 99, 101, 252, 227, 8, 147, 150, 85, 97, 158, 17, 107, - 218, 244, 82, 113, 247, 91, 208, 214, 60, 244, 87, 137, 173, 201, 130, 18, 66, 56, 198, - 149, 207, 189, 175, 120, 123, 224, 177, 167, 251, 159, 143, 110, 68, 183, 189, 70, 126, - 32, 35, 164, 44, 30, 44, 12, 65, 18, 62, 239, 242, 2, 248, 104, 2, 178, 64, 28, 126, 36, - 137, 24, 14, 116, 91, 98, 90, 159, 218, 102, 45, 11, 110, 223, 245, 184, 52, 99, 59, - 245, 136, 175, 3, 72, 164, 146, 145, 116, 22, 66, 24, 49, 193, 121, 3, 60, 37, 41, 97, - 3, 190, 66, 195, 225, 63, 46, 3, 118, 4, 208, 15, 1, 40, 254, 235, 151, 123, 70, 180, - 170, 44, 172, 90, 4, 254, 53, 239, 116, 246, 67, 56, 129, 61, 22, 169, 213, 65, 27, 216, - 116, 162, 239, 214, 207, 126, 177, 20, 100, 25, 48, 143, 84, 215, 70, 197, 53, 65, 70, - 86, 172, 61, 62, 9, 212, 167, 169, 133, 41, 126, 213, 196, 33, 192, 238, 0, 63, 246, - 215, 58, 128, 110, 101, 92, 3, 170, 214, 130, 149, 52, 81, 125, 118, 233, 3, 118, 193, - 104, 207, 120, 115, 77, 253, 191, 122, 0, 107, 164, 207, 113, 81, 169, 36, 201, 228, 74, - 134, 131, 218, 178, 35, 30, 216, 101, 2, 103, 174, 87, 95, 50, 50, 215, 157, 5, 210, - 188, 54, 211, 78, 45, 199, 96, 121, 241, 241, 176, 226, 194, 134, 130, 89, 217, 210, - 186, 32, 140, 39, 91, 103, 212, 26, 87, 32, 72, 144, 228, 230, 117, 99, 188, 50, 15, 69, - 79, 179, 50, 12, 106, 86, 218, 101, 73, 142, 243, 29, 250, 122, 228, 233, 29, 255, 22, - 121, 114, 125, 103, 41, 250, 241, 179, 126, 158, 198, 116, 209, 65, 94, 98, 228, 175, - 169, 96, 3, 9, 233, 133, 214, 55, 161, 164, 103, 80, 85, 24, 186, 64, 167, 92, 131, 53, - 101, 202, 47, 25, 104, 118, 155, 14, 12, 12, 25, 116, 45, 221, 249, 28, 246, 212, 200, - 157, 167, 169, 56, 197, 181, 4, 245, 146, 1, 140, 234, 191, 212, 228, 125, 87, 81, 86, - 119, 30, 63, 129, 143, 32, 96, - ]; - - // Prepare inputs of different lengths - let mut input_511 = [0; 511]; - let mut input_512 = [0; 512]; // Next block - let mut input_575 = [0; 575]; - let mut input_576 = [0; 576]; // Next block - for i in 0..input.len() { - input_511[i] = input[i]; - input_512[i] = input[i]; - input_575[i] = input[i]; - input_576[i] = input[i]; - } - - // Compute hashes of all inputs (with same message length) - let fixed_length_hash = super::sha256(input); - let var_full_length_hash = sha256_var(input, input.len() as u64); - let var_length_hash_511 = sha256_var(input_511, input.len() as u64); - let var_length_hash_512 = sha256_var(input_512, input.len() as u64); - let var_length_hash_575 = sha256_var(input_575, input.len() as u64); - let var_length_hash_576 = sha256_var(input_576, input.len() as u64); - - // All of the above should have produced the same hash - assert_eq(var_full_length_hash, fixed_length_hash); - assert_eq(var_length_hash_511, fixed_length_hash); - assert_eq(var_length_hash_512, fixed_length_hash); - assert_eq(var_length_hash_575, fixed_length_hash); - assert_eq(var_length_hash_576, fixed_length_hash); - } - - #[test] - fn test_get_item_byte() { - let fld = make_item(10, 20, 30, 40); - assert_eq(fld, 0x0a141e28); - assert_eq(get_item_byte(fld, 0), 10); - assert_eq(get_item_byte(fld, 4), 10); - assert_eq(get_item_byte(fld, 6), 30); - } - - #[test] - fn test_byte_into_item() { - let fld = make_item(0, 20, 0, 0); - assert_eq(byte_into_item(20, 1), fld); - assert_eq(byte_into_item(20, 5), fld); - } - - #[test] - fn test_set_item_zeros() { - let fld0 = make_item(10, 20, 30, 40); - let fld1 = make_item(10, 0, 0, 0); - assert_eq(set_item_zeros(fld0, 3), fld1); - assert_eq(set_item_zeros(fld0, 4), 0); - assert_eq(set_item_zeros(0, 4), 0); - } - - #[test] - fn test_set_item_byte_then_zeros() { - let fld0 = make_item(10, 20, 30, 40); - let fld1 = make_item(10, 50, 0, 0); - assert_eq(set_item_byte_then_zeros(fld0, 1, 50), fld1); - } - - #[test] - fn test_build_msg_block_start_0() { - let input = [ - 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, - 101, 115, 46, 48, - ]; - assert_eq(input.len(), 22); - - // Safety: testing context - let (msg_block, msg_byte_ptr) = unsafe { build_msg_block(input, input.len(), 0) }; - assert_eq(msg_byte_ptr, input.len()); - assert_eq(msg_block[0], make_item(input[0], input[1], input[2], input[3])); - assert_eq(msg_block[1], make_item(input[4], input[5], input[6], input[7])); - assert_eq(msg_block[5], make_item(input[20], input[21], 0, 0)); - assert_eq(msg_block[6], 0); - } - - #[test] - fn test_build_msg_block_start_1() { - let input = [ - 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, - 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, - 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, - 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, - ]; - assert_eq(input.len(), 68); - // Safety: test context - let (msg_block, msg_byte_ptr) = unsafe { build_msg_block(input, input.len(), 64) }; - assert_eq(msg_byte_ptr, 4); - assert_eq(msg_block[0], make_item(input[64], input[65], input[66], input[67])); - assert_eq(msg_block[1], 0); - } - - #[test] - fn test_attach_len_to_msg_block() { - let input: INT_BLOCK = [ - 2152555847, 1397309779, 1936618851, 1262052426, 1936876331, 1985297723, 543702374, - 1919905082, 1131376244, 1701737517, 1417244773, 978151789, 1697470053, 1920166255, - 1849316213, 1651139939, - ]; - // Safety: testing context - let msg_block = unsafe { attach_len_to_msg_block(input, 1, 448) }; - assert_eq(msg_block[0], ((1 << 7) as u32) * 256 * 256 * 256); - assert_eq(msg_block[1], 0); - assert_eq(msg_block[15], 3584); - } -} diff --git a/noir/noir-repo/noir_stdlib/src/hash/sha512.nr b/noir/noir-repo/noir_stdlib/src/hash/sha512.nr deleted file mode 100644 index 5630139c1f12..000000000000 --- a/noir/noir-repo/noir_stdlib/src/hash/sha512.nr +++ /dev/null @@ -1,165 +0,0 @@ -// Implementation of SHA-512 mapping a byte array of variable length to -// 64 bytes. -// Internal functions act on 64-bit unsigned integers for simplicity. -// Auxiliary mappings; names as in FIPS PUB 180-4 -fn rotr64(a: u64, b: u8) -> u64 // 64-bit right rotation -{ - // None of the bits overlap between `(a >> b)` and `(a << (64 - b))` - // Addition is then equivalent to OR, with fewer constraints. - (a >> b) + (a << (64 - b)) -} - -fn sha_ch(x: u64, y: u64, z: u64) -> u64 { - (x & y) ^ (!x & z) -} - -fn sha_maj(x: u64, y: u64, z: u64) -> u64 { - (x & y) ^ (x & z) ^ (y & z) -} - -fn sha_bigma0(x: u64) -> u64 { - rotr64(x, 28) ^ rotr64(x, 34) ^ rotr64(x, 39) -} - -fn sha_bigma1(x: u64) -> u64 { - rotr64(x, 14) ^ rotr64(x, 18) ^ rotr64(x, 41) -} - -fn sha_sigma0(x: u64) -> u64 { - rotr64(x, 1) ^ rotr64(x, 8) ^ (x >> 7) -} - -fn sha_sigma1(x: u64) -> u64 { - rotr64(x, 19) ^ rotr64(x, 61) ^ (x >> 6) -} - -fn sha_w(msg: [u64; 16]) -> [u64; 80] // Expanded message blocks -{ - let mut w: [u64; 80] = [0; 80]; - - for j in 0..16 { - w[j] = msg[j]; - } - - for j in 16..80 { - w[j] = crate::wrapping_add( - crate::wrapping_add(sha_sigma1(w[j - 2]), w[j - 7]), - crate::wrapping_add(sha_sigma0(w[j - 15]), w[j - 16]), - ); - } - w -} - -// SHA-512 compression function -#[no_predicates] -fn sha_c(msg: [u64; 16], hash: [u64; 8]) -> [u64; 8] { - // noir-fmt:ignore - let K: [u64; 80] = [4794697086780616226, 8158064640168781261, 13096744586834688815, 16840607885511220156, 4131703408338449720, 6480981068601479193, 10538285296894168987, 12329834152419229976, 15566598209576043074, 1334009975649890238, 2608012711638119052, 6128411473006802146, 8268148722764581231, 9286055187155687089, 11230858885718282805, 13951009754708518548, 16472876342353939154, 17275323862435702243, 1135362057144423861, 2597628984639134821, 3308224258029322869, 5365058923640841347, 6679025012923562964, 8573033837759648693, 10970295158949994411, 12119686244451234320, 12683024718118986047, 13788192230050041572, 14330467153632333762, 15395433587784984357, 489312712824947311, 1452737877330783856, 2861767655752347644, 3322285676063803686, 5560940570517711597, 5996557281743188959, 7280758554555802590, 8532644243296465576, 9350256976987008742, 10552545826968843579, 11727347734174303076, 12113106623233404929, 14000437183269869457, 14369950271660146224, 15101387698204529176, 15463397548674623760, 17586052441742319658, 1182934255886127544, 1847814050463011016, 2177327727835720531, 2830643537854262169, 3796741975233480872, 4115178125766777443, 5681478168544905931, 6601373596472566643, 7507060721942968483, 8399075790359081724, 8693463985226723168, 9568029438360202098, 10144078919501101548, 10430055236837252648, 11840083180663258601, 13761210420658862357, 14299343276471374635, 14566680578165727644, 15097957966210449927, 16922976911328602910, 17689382322260857208, 500013540394364858, 748580250866718886, 1242879168328830382, 1977374033974150939, 2944078676154940804, 3659926193048069267, 4368137639120453308, 4836135668995329356, 5532061633213252278, 6448918945643986474, 6902733635092675308, 7801388544844847127]; // first 64 bits of fractional parts of cube roots of first 80 primes - let mut out_h: [u64; 8] = hash; - let w = sha_w(msg); - for j in 0..80 { - let out1 = crate::wrapping_add(out_h[7], sha_bigma1(out_h[4])); - let out2 = crate::wrapping_add(out1, sha_ch(out_h[4], out_h[5], out_h[6])); - let t1 = crate::wrapping_add(crate::wrapping_add(out2, K[j]), w[j]); - let t2 = crate::wrapping_add(sha_bigma0(out_h[0]), sha_maj(out_h[0], out_h[1], out_h[2])); - out_h[7] = out_h[6]; - out_h[6] = out_h[5]; - out_h[5] = out_h[4]; - out_h[4] = crate::wrapping_add(out_h[3], t1); - out_h[3] = out_h[2]; - out_h[2] = out_h[1]; - out_h[1] = out_h[0]; - out_h[0] = crate::wrapping_add(t1, t2); - } - - out_h -} -// Convert 128-byte array to array of 16 u64s -fn msg_u8_to_u64(msg: [u8; 128]) -> [u64; 16] { - let mut msg64: [u64; 16] = [0; 16]; - - for i in 0..16 { - let mut msg_field: Field = 0; - for j in 0..8 { - msg_field = msg_field * 256 + msg[128 - 8 * (i + 1) + j] as Field; - } - msg64[15 - i] = msg_field as u64; - } - - msg64 -} -// SHA-512 hash function -pub fn digest(msg: [u8; N]) -> [u8; 64] { - let mut msg_block: [u8; 128] = [0; 128]; - // noir-fmt:ignore - let mut h: [u64; 8] = [7640891576956012808, 13503953896175478587, 4354685564936845355, 11912009170470909681, 5840696475078001361, 11170449401992604703, 2270897969802886507, 6620516959819538809]; // Intermediate hash, starting with the canonical initial value - let mut c: [u64; 8] = [0; 8]; // Compression of current message block as sequence of u64 - let mut out_h: [u8; 64] = [0; 64]; // Digest as sequence of bytes - let mut i: u64 = 0; // Message byte pointer - for k in 0..msg.len() { - // Populate msg_block - msg_block[i] = msg[k]; - i = i + 1; - if i == 128 { - // Enough to hash block - c = sha_c(msg_u8_to_u64(msg_block), h); - for j in 0..8 { - h[j] = crate::wrapping_add(h[j], c[j]); - } - - i = 0; - } - } - // Pad the rest such that we have a [u64; 2] block at the end representing the length - // of the message, and a block of 1 0 ... 0 following the message (i.e. [1 << 7, 0, ..., 0]). - msg_block[i] = 1 << 7; - i += 1; - // If i >= 113, there aren't enough bits in the current message block to accomplish this, so - // the 1 and 0s fill up the current block, which we then compress accordingly. - if i >= 113 { - // Not enough bits (128) to store length. Fill up with zeros. - if i < 128 { - for _i in 113..128 { - if i <= 127 { - msg_block[i] = 0; - i += 1; - } - } - } - c = sha_c(msg_u8_to_u64(msg_block), h); - for j in 0..8 { - h[j] = crate::wrapping_add(h[j], c[j]); - } - - i = 0; - } - - let len = 8 * msg.len(); - let len_bytes: [u8; 16] = (len as Field).to_le_bytes(); - for _i in 0..128 { - // In any case, fill blocks up with zeros until the last 128 (i.e. until i = 112). - if i < 112 { - msg_block[i] = 0; - i += 1; - } else if i < 128 { - for j in 0..16 { - msg_block[127 - j] = len_bytes[j]; - } - i += 16; // Done. - } - } - // Hash final padded block - c = sha_c(msg_u8_to_u64(msg_block), h); - for j in 0..8 { - h[j] = crate::wrapping_add(h[j], c[j]); - } - // Return final hash as byte array - for j in 0..8 { - let h_bytes: [u8; 8] = (h[7 - j] as Field).to_le_bytes(); - for k in 0..8 { - out_h[63 - 8 * j - k] = h_bytes[k]; - } - } - - out_h -} diff --git a/noir/noir-repo/noir_stdlib/src/lib.nr b/noir/noir-repo/noir_stdlib/src/lib.nr index d5c360792d91..3df401675513 100644 --- a/noir/noir-repo/noir_stdlib/src/lib.nr +++ b/noir/noir-repo/noir_stdlib/src/lib.nr @@ -6,8 +6,6 @@ pub mod merkle; pub mod ecdsa_secp256k1; pub mod ecdsa_secp256r1; pub mod embedded_curve_ops; -pub mod sha256; -pub mod sha512; pub mod field; pub mod collections; pub mod compat; @@ -19,7 +17,6 @@ pub mod cmp; pub mod ops; pub mod default; pub mod prelude; -pub mod uint128; pub mod runtime; pub mod meta; pub mod append; @@ -121,8 +118,46 @@ where pub fn as_witness(x: Field) {} mod tests { + use super::wrapping_mul; + #[test(should_fail_with = "custom message")] fn test_static_assert_custom_message() { super::static_assert(1 == 2, "custom message"); } + + #[test(should_fail)] + fn test_wrapping_mul() { + // This currently fails. + // See: https://github.com/noir-lang/noir/issues/7528 + let zero: u128 = 0; + let one: u128 = 1; + let two_pow_64: u128 = 0x10000000000000000; + let u128_max: u128 = 0xffffffffffffffffffffffffffffffff; + + // 1*0==0 + assert_eq(zero, wrapping_mul(zero, one)); + + // 0*1==0 + assert_eq(zero, wrapping_mul(one, zero)); + + // 1*1==1 + assert_eq(one, wrapping_mul(one, one)); + + // 0 * ( 1 << 64 ) == 0 + assert_eq(zero, wrapping_mul(zero, two_pow_64)); + + // ( 1 << 64 ) * 0 == 0 + assert_eq(zero, wrapping_mul(two_pow_64, zero)); + + // 1 * ( 1 << 64 ) == 1 << 64 + assert_eq(two_pow_64, wrapping_mul(two_pow_64, one)); + + // ( 1 << 64 ) * 1 == 1 << 64 + assert_eq(two_pow_64, wrapping_mul(one, two_pow_64)); + + // ( 1 << 64 ) * ( 1 << 64 ) == 1 << 64 + assert_eq(zero, wrapping_mul(two_pow_64, two_pow_64)); + // -1 * -1 == 1 + assert_eq(one, wrapping_mul(u128_max, u128_max)); + } } diff --git a/noir/noir-repo/noir_stdlib/src/meta/op.nr b/noir/noir-repo/noir_stdlib/src/meta/op.nr index 67c9b4cdb643..7dc846e3318b 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/op.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/op.nr @@ -222,4 +222,3 @@ impl BinaryOp { } } } - diff --git a/noir/noir-repo/noir_stdlib/src/ops/arith.nr b/noir/noir-repo/noir_stdlib/src/ops/arith.nr index 195ae7775803..3c5f011662c8 100644 --- a/noir/noir-repo/noir_stdlib/src/ops/arith.nr +++ b/noir/noir-repo/noir_stdlib/src/ops/arith.nr @@ -10,6 +10,11 @@ impl Add for Field { } } +impl Add for u128 { + fn add(self, other: u128) -> u128 { + self + other + } +} impl Add for u64 { fn add(self, other: u64) -> u64 { self + other @@ -69,6 +74,11 @@ impl Sub for Field { } } +impl Sub for u128 { + fn sub(self, other: u128) -> u128 { + self - other + } +} impl Sub for u64 { fn sub(self, other: u64) -> u64 { self - other @@ -128,6 +138,11 @@ impl Mul for Field { } } +impl Mul for u128 { + fn mul(self, other: u128) -> u128 { + self * other + } +} impl Mul for u64 { fn mul(self, other: u64) -> u64 { self * other @@ -187,6 +202,11 @@ impl Div for Field { } } +impl Div for u128 { + fn div(self, other: u128) -> u128 { + self / other + } +} impl Div for u64 { fn div(self, other: u64) -> u64 { self / other @@ -240,6 +260,11 @@ pub trait Rem { } // docs:end:rem-trait +impl Rem for u128 { + fn rem(self, other: u128) -> u128 { + self % other + } +} impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other @@ -321,4 +346,3 @@ impl Neg for i64 { } } // docs:end:neg-trait-impls - diff --git a/noir/noir-repo/noir_stdlib/src/ops/bit.nr b/noir/noir-repo/noir_stdlib/src/ops/bit.nr index 70c52ac38b00..3b1f99430cd9 100644 --- a/noir/noir-repo/noir_stdlib/src/ops/bit.nr +++ b/noir/noir-repo/noir_stdlib/src/ops/bit.nr @@ -11,6 +11,11 @@ impl Not for bool { } } +impl Not for u128 { + fn not(self) -> u128 { + !self + } +} impl Not for u64 { fn not(self) -> u64 { !self @@ -71,6 +76,11 @@ impl BitOr for bool { } } +impl BitOr for u128 { + fn bitor(self, other: u128) -> u128 { + self | other + } +} impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other @@ -130,6 +140,11 @@ impl BitAnd for bool { } } +impl BitAnd for u128 { + fn bitand(self, other: u128) -> u128 { + self & other + } +} impl BitAnd for u64 { fn bitand(self, other: u64) -> u64 { self & other @@ -189,6 +204,11 @@ impl BitXor for bool { } } +impl BitXor for u128 { + fn bitxor(self, other: u128) -> u128 { + self ^ other + } +} impl BitXor for u64 { fn bitxor(self, other: u64) -> u64 { self ^ other @@ -242,8 +262,8 @@ pub trait Shl { } // docs:end:shl-trait -impl Shl for u32 { - fn shl(self, other: u8) -> u32 { +impl Shl for u128 { + fn shl(self, other: u8) -> u128 { self << other } } @@ -252,6 +272,11 @@ impl Shl for u64 { self << other } } +impl Shl for u32 { + fn shl(self, other: u8) -> u32 { + self << other + } +} impl Shl for u16 { fn shl(self, other: u8) -> u16 { self << other @@ -295,6 +320,11 @@ pub trait Shr { } // docs:end:shr-trait +impl Shr for u128 { + fn shr(self, other: u8) -> u128 { + self >> other + } +} impl Shr for u64 { fn shr(self, other: u8) -> u64 { self >> other @@ -341,4 +371,3 @@ impl Shr for i64 { self >> other } } - diff --git a/noir/noir-repo/noir_stdlib/src/prelude.nr b/noir/noir-repo/noir_stdlib/src/prelude.nr index a4a6c35b615e..7aa60456b6dd 100644 --- a/noir/noir-repo/noir_stdlib/src/prelude.nr +++ b/noir/noir-repo/noir_stdlib/src/prelude.nr @@ -7,4 +7,3 @@ pub use crate::default::Default; pub use crate::meta::{derive, derive_via}; pub use crate::option::Option; pub use crate::panic::panic; -pub use crate::uint128::U128; diff --git a/noir/noir-repo/noir_stdlib/src/sha256.nr b/noir/noir-repo/noir_stdlib/src/sha256.nr deleted file mode 100644 index 534c954d3dc1..000000000000 --- a/noir/noir-repo/noir_stdlib/src/sha256.nr +++ /dev/null @@ -1,10 +0,0 @@ -// This file is kept for backwards compatibility. -#[deprecated("sha256 is being deprecated from the stdlib, use https://github.com/noir-lang/sha256 instead")] -pub fn digest(msg: [u8; N]) -> [u8; 32] { - crate::hash::sha256::digest(msg) -} - -#[deprecated("sha256 is being deprecated from the stdlib, use https://github.com/noir-lang/sha256 instead")] -pub fn sha256_var(msg: [u8; N], message_size: u64) -> [u8; 32] { - crate::hash::sha256::sha256_var(msg, message_size) -} diff --git a/noir/noir-repo/noir_stdlib/src/sha512.nr b/noir/noir-repo/noir_stdlib/src/sha512.nr deleted file mode 100644 index 27b53f4395f6..000000000000 --- a/noir/noir-repo/noir_stdlib/src/sha512.nr +++ /dev/null @@ -1,5 +0,0 @@ -// This file is kept for backwards compatibility. -#[deprecated] -pub fn digest(msg: [u8; N]) -> [u8; 64] { - crate::hash::sha512::digest(msg) -} diff --git a/noir/noir-repo/noir_stdlib/src/uint128.nr b/noir/noir-repo/noir_stdlib/src/uint128.nr deleted file mode 100644 index f41958e0e306..000000000000 --- a/noir/noir-repo/noir_stdlib/src/uint128.nr +++ /dev/null @@ -1,572 +0,0 @@ -use crate::cmp::{Eq, Ord, Ordering}; -use crate::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Not, Rem, Shl, Shr, Sub}; -use crate::static_assert; -use super::{convert::AsPrimitive, default::Default}; - -global pow64: Field = 18446744073709551616; //2^64; -global pow63: Field = 9223372036854775808; // 2^63; -pub struct U128 { - pub(crate) lo: Field, - pub(crate) hi: Field, -} - -impl U128 { - - pub fn from_u64s_le(lo: u64, hi: u64) -> U128 { - // in order to handle multiplication, we need to represent the product of two u64 without overflow - assert(crate::field::modulus_num_bits() as u32 > 128); - U128 { lo: lo as Field, hi: hi as Field } - } - - pub fn from_u64s_be(hi: u64, lo: u64) -> U128 { - U128::from_u64s_le(lo, hi) - } - - pub fn zero() -> U128 { - U128 { lo: 0, hi: 0 } - } - - pub fn one() -> U128 { - U128 { lo: 1, hi: 0 } - } - pub fn from_le_bytes(bytes: [u8; 16]) -> U128 { - let mut lo = 0; - let mut base = 1; - for i in 0..8 { - lo += (bytes[i] as Field) * base; - base *= 256; - } - let mut hi = 0; - base = 1; - for i in 8..16 { - hi += (bytes[i] as Field) * base; - base *= 256; - } - U128 { lo, hi } - } - - pub fn to_be_bytes(self: Self) -> [u8; 16] { - let lo: [u8; 8] = self.lo.to_be_bytes(); - let hi: [u8; 8] = self.hi.to_be_bytes(); - let mut bytes = [0; 16]; - for i in 0..8 { - bytes[i] = hi[i]; - bytes[i + 8] = lo[i]; - } - bytes - } - - pub fn to_le_bytes(self: Self) -> [u8; 16] { - let lo: [u8; 8] = self.lo.to_le_bytes(); - let hi: [u8; 8] = self.hi.to_le_bytes(); - let mut bytes = [0; 16]; - for i in 0..8 { - bytes[i] = lo[i]; - bytes[i + 8] = hi[i]; - } - bytes - } - - pub fn from_hex(hex: str) -> U128 { - let bytes = hex.as_bytes(); - // string must starts with "0x" - assert((bytes[0] == 48) & (bytes[1] == 120), "Invalid hexadecimal string"); - static_assert(N < 35, "Input does not fit into a U128"); - - let mut lo = 0; - let mut hi = 0; - let mut base = 1; - if N <= 18 { - for i in 0..N - 2 { - lo += U128::decode_ascii(bytes[N - i - 1]) * base; - base = base * 16; - } - } else { - for i in 0..16 { - lo += U128::decode_ascii(bytes[N - i - 1]) * base; - base = base * 16; - } - base = 1; - for i in 17..N - 1 { - hi += U128::decode_ascii(bytes[N - i]) * base; - base = base * 16; - } - } - U128 { lo: lo as Field, hi: hi as Field } - } - - unconstrained fn unconstrained_check_is_upper_ascii(ascii: u8) -> bool { - ((ascii >= 65) & (ascii <= 90)) // Between 'A' and 'Z' - } - - pub(crate) fn decode_ascii(ascii: u8) -> Field { - ( - if ascii < 58 { - ascii - 48 - } else { - // Safety: optionally adds 32 and then check (below) the result is in 'a..f' range - let ascii = - ascii + 32 * (unsafe { U128::unconstrained_check_is_upper_ascii(ascii) as u8 }); - assert(ascii >= 97); // enforce >= 'a' - assert(ascii <= 102); // enforce <= 'f' - ascii - 87 - } - ) as Field - } - - // TODO: Replace with a faster version. - // A circuit that uses this function can be slow to compute - // (we're doing up to 127 calls to compute the quotient) - unconstrained fn unconstrained_div(self: Self, b: U128) -> (U128, U128) { - if b == U128::zero() { - // Return 0,0 to avoid eternal loop - (U128::zero(), U128::zero()) - } else if self < b { - (U128::zero(), self) - } else if self == b { - (U128::one(), U128::zero()) - } else { - let (q, r) = if b.hi as u64 >= pow63 as u64 { - // The result of multiplication by 2 would overflow - (U128::zero(), self) - } else { - self.unconstrained_div(b * U128::from_u64s_le(2, 0)) - }; - let q_mul_2 = q * U128::from_u64s_le(2, 0); - if r < b { - (q_mul_2, r) - } else { - (q_mul_2 + U128::one(), r - b) - } - } - } - - pub fn from_integer(i: T) -> U128 - where - T: AsPrimitive, - { - let f = i.as_(); - // Reject values which would overflow a u128 - f.assert_max_bit_size::<128>(); - let lo = f as u64 as Field; - let hi = (f - lo) / pow64; - U128 { lo, hi } - } - - pub fn to_integer(self) -> T - where - Field: AsPrimitive, - { - AsPrimitive::as_(self.lo + self.hi * pow64) - } - - fn wrapping_mul(self: Self, b: U128) -> U128 { - let low = self.lo * b.lo; - let lo = low as u64 as Field; - let carry = (low - lo) / pow64; - let high = self.lo * b.hi + self.hi * b.lo + carry; - let hi = high as u64 as Field; - U128 { lo, hi } - } -} - -impl Add for U128 { - fn add(self: Self, b: U128) -> U128 { - let low = self.lo + b.lo; - let lo = low as u64 as Field; - let carry = (low - lo) / pow64; - let high = self.hi + b.hi + carry; - let hi = high as u64 as Field; - assert(hi == high, "attempt to add with overflow"); - U128 { lo, hi } - } -} - -impl Sub for U128 { - fn sub(self: Self, b: U128) -> U128 { - let low = pow64 + self.lo - b.lo; - let lo = low as u64 as Field; - let borrow = (low == lo) as Field; - let high = self.hi - b.hi - borrow; - let hi = high as u64 as Field; - assert(hi == high, "attempt to subtract with underflow"); - U128 { lo, hi } - } -} - -impl Mul for U128 { - fn mul(self: Self, b: U128) -> U128 { - assert(self.hi * b.hi == 0, "attempt to multiply with overflow"); - let low = self.lo * b.lo; - let lo = low as u64 as Field; - let carry = (low - lo) / pow64; - let high = if crate::field::modulus_num_bits() as u32 > 196 { - (self.lo + self.hi) * (b.lo + b.hi) - low + carry - } else { - self.lo * b.hi + self.hi * b.lo + carry - }; - let hi = high as u64 as Field; - assert(hi == high, "attempt to multiply with overflow"); - U128 { lo, hi } - } -} - -impl Div for U128 { - fn div(self: Self, b: U128) -> U128 { - // Safety: euclidian division is asserted to be correct: assert(a == b * q + r); and assert(r < b); - // Furthermore, U128 addition and multiplication ensures that b * q + r does not overflow - unsafe { - let (q, r) = self.unconstrained_div(b); - let a = b * q + r; - assert_eq(self, a); - assert(r < b); - q - } - } -} - -impl Rem for U128 { - fn rem(self: Self, b: U128) -> U128 { - // Safety: cf div() above - unsafe { - let (q, r) = self.unconstrained_div(b); - let a = b * q + r; - assert_eq(self, a); - assert(r < b); - - r - } - } -} - -impl Eq for U128 { - fn eq(self: Self, b: U128) -> bool { - (self.lo == b.lo) & (self.hi == b.hi) - } -} - -impl Ord for U128 { - fn cmp(self, other: Self) -> Ordering { - let hi_ordering = (self.hi as u64).cmp((other.hi as u64)); - let lo_ordering = (self.lo as u64).cmp((other.lo as u64)); - - if hi_ordering == Ordering::equal() { - lo_ordering - } else { - hi_ordering - } - } -} - -impl Not for U128 { - fn not(self) -> U128 { - U128 { lo: (!(self.lo as u64)) as Field, hi: (!(self.hi as u64)) as Field } - } -} - -impl BitOr for U128 { - fn bitor(self, other: U128) -> U128 { - U128 { - lo: ((self.lo as u64) | (other.lo as u64)) as Field, - hi: ((self.hi as u64) | (other.hi as u64)) as Field, - } - } -} - -impl BitAnd for U128 { - fn bitand(self, other: U128) -> U128 { - U128 { - lo: ((self.lo as u64) & (other.lo as u64)) as Field, - hi: ((self.hi as u64) & (other.hi as u64)) as Field, - } - } -} - -impl BitXor for U128 { - fn bitxor(self, other: U128) -> U128 { - U128 { - lo: ((self.lo as u64) ^ (other.lo as u64)) as Field, - hi: ((self.hi as u64) ^ (other.hi as u64)) as Field, - } - } -} - -impl Shl for U128 { - fn shl(self, other: u8) -> U128 { - assert(other < 128, "attempt to shift left with overflow"); - let exp_bits: [u1; 7] = (other as Field).to_be_bits(); - - let mut r: Field = 2; - let mut y: Field = 1; - for i in 1..8 { - let bit = exp_bits[7 - i] as Field; - y = bit * (r * y) + (1 - bit) * y; - r *= r; - } - self.wrapping_mul(U128::from_integer(y)) - } -} - -impl Shr for U128 { - fn shr(self, other: u8) -> U128 { - assert(other < 128, "attempt to shift right with overflow"); - let exp_bits: [u1; 7] = (other as Field).to_be_bits(); - - let mut r: Field = 2; - let mut y: Field = 1; - for i in 1..8 { - let bit = exp_bits[7 - i] as Field; - y = bit * (r * y) + (1 - bit) * y; - r *= r; - } - self / U128::from_integer(y) - } -} - -impl Default for U128 { - fn default() -> Self { - U128::zero() - } -} - -mod tests { - use crate::default::Default; - use crate::ops::Not; - use crate::uint128::{pow63, pow64, U128}; - - #[test] - fn test_not(lo: u64, hi: u64) { - let num = U128::from_u64s_le(lo, hi); - let not_num = num.not(); - - assert_eq(not_num.hi, (hi.not() as Field)); - assert_eq(not_num.lo, (lo.not() as Field)); - - let not_not_num = not_num.not(); - assert_eq(num, not_not_num); - } - #[test] - fn test_construction() { - // Check little-endian u64 is inversed with big-endian u64 construction - let a = U128::from_u64s_le(2, 1); - let b = U128::from_u64s_be(1, 2); - assert_eq(a, b); - // Check byte construction is equivalent - let c = U128::from_le_bytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let d = U128::from_u64s_le(0x0706050403020100, 0x0f0e0d0c0b0a0908); - assert_eq(c, d); - } - #[test] - fn test_byte_decomposition() { - let a = U128::from_u64s_le(0x0706050403020100, 0x0f0e0d0c0b0a0908); - // Get big-endian and little-endian byte decompostions - let le_bytes_a = a.to_le_bytes(); - let be_bytes_a = a.to_be_bytes(); - - // Check equivalence - for i in 0..16 { - assert_eq(le_bytes_a[i], be_bytes_a[15 - i]); - } - // Reconstruct U128 from byte decomposition - let b = U128::from_le_bytes(le_bytes_a); - // Check that it's the same element - assert_eq(a, b); - } - #[test] - fn test_hex_constuction() { - let a = U128::from_u64s_le(0x1, 0x2); - let b = U128::from_hex("0x20000000000000001"); - assert_eq(a, b); - - let c = U128::from_hex("0xffffffffffffffffffffffffffffffff"); - let d = U128::from_u64s_le(0xffffffffffffffff, 0xffffffffffffffff); - assert_eq(c, d); - - let e = U128::from_hex("0x00000000000000000000000000000000"); - let f = U128::from_u64s_le(0, 0); - assert_eq(e, f); - } - - // Ascii decode tests - - #[test] - fn test_ascii_decode_correct_range() { - // '0'..'9' range - for i in 0..10 { - let decoded = U128::decode_ascii(48 + i); - assert_eq(decoded, i as Field); - } - // 'A'..'F' range - for i in 0..6 { - let decoded = U128::decode_ascii(65 + i); - assert_eq(decoded, (i + 10) as Field); - } - // 'a'..'f' range - for i in 0..6 { - let decoded = U128::decode_ascii(97 + i); - assert_eq(decoded, (i + 10) as Field); - } - } - - #[test(should_fail)] - fn test_ascii_decode_range_less_than_48_fails_0() { - crate::println(U128::decode_ascii(0)); - } - #[test(should_fail)] - fn test_ascii_decode_range_less_than_48_fails_1() { - crate::println(U128::decode_ascii(47)); - } - - #[test(should_fail)] - fn test_ascii_decode_range_58_64_fails_0() { - let _ = U128::decode_ascii(58); - } - #[test(should_fail)] - fn test_ascii_decode_range_58_64_fails_1() { - let _ = U128::decode_ascii(64); - } - #[test(should_fail)] - fn test_ascii_decode_range_71_96_fails_0() { - let _ = U128::decode_ascii(71); - } - #[test(should_fail)] - fn test_ascii_decode_range_71_96_fails_1() { - let _ = U128::decode_ascii(96); - } - #[test(should_fail)] - fn test_ascii_decode_range_greater_than_102_fails() { - let _ = U128::decode_ascii(103); - } - - #[test(should_fail)] - fn test_ascii_decode_regression() { - // This code will actually fail because of ascii_decode, - // but in the past it was possible to create a value > (1<<128) - let a = U128::from_hex("0x~fffffffffffffffffffffffffffffff"); - let b: Field = a.to_integer(); - let c: [u8; 17] = b.to_le_bytes(); - assert(c[16] != 0); - } - - #[test] - fn test_unconstrained_div() { - // Test the potential overflow case - let a = U128::from_u64s_le(0x0, 0xffffffffffffffff); - let b = U128::from_u64s_le(0x0, 0xfffffffffffffffe); - let c = U128::one(); - let d = U128::from_u64s_le(0x0, 0x1); - // Safety: testing context - unsafe { - let (q, r) = a.unconstrained_div(b); - assert_eq(q, c); - assert_eq(r, d); - } - - let a = U128::from_u64s_le(2, 0); - let b = U128::one(); - // Check the case where a is a multiple of b - // Safety: testing context - unsafe { - let (c, d) = a.unconstrained_div(b); - assert_eq((c, d), (a, U128::zero())); - } - - // Check where b is a multiple of a - // Safety: testing context - unsafe { - let (c, d) = b.unconstrained_div(a); - assert_eq((c, d), (U128::zero(), b)); - } - - // Dividing by zero returns 0,0 - let a = U128::from_u64s_le(0x1, 0x0); - let b = U128::zero(); - // Safety: testing context - unsafe { - let (c, d) = a.unconstrained_div(b); - assert_eq((c, d), (U128::zero(), U128::zero())); - } - // Dividing 1<<127 by 1<<127 (special case) - let a = U128::from_u64s_le(0x0, pow63 as u64); - let b = U128::from_u64s_le(0x0, pow63 as u64); - // Safety: testing context - unsafe { - let (c, d) = a.unconstrained_div(b); - assert_eq((c, d), (U128::one(), U128::zero())); - } - } - - #[test] - fn integer_conversions() { - // Maximum - let start: Field = 0xffffffffffffffffffffffffffffffff; - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - - // Minimum - let start: Field = 0x0; - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - - // Low limb - let start: Field = 0xffffffffffffffff; - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - - // High limb - let start: Field = 0xffffffffffffffff0000000000000000; - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - } - - #[test] - fn integer_conversions_fuzz(lo: u64, hi: u64) { - let start: Field = (lo as Field) + pow64 * (hi as Field); - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - } - - #[test] - fn test_wrapping_mul() { - // 1*0==0 - assert_eq(U128::zero(), U128::zero().wrapping_mul(U128::one())); - - // 0*1==0 - assert_eq(U128::zero(), U128::one().wrapping_mul(U128::zero())); - - // 1*1==1 - assert_eq(U128::one(), U128::one().wrapping_mul(U128::one())); - - // 0 * ( 1 << 64 ) == 0 - assert_eq(U128::zero(), U128::zero().wrapping_mul(U128::from_u64s_le(0, 1))); - - // ( 1 << 64 ) * 0 == 0 - assert_eq(U128::zero(), U128::from_u64s_le(0, 1).wrapping_mul(U128::zero())); - - // 1 * ( 1 << 64 ) == 1 << 64 - assert_eq(U128::from_u64s_le(0, 1), U128::from_u64s_le(0, 1).wrapping_mul(U128::one())); - - // ( 1 << 64 ) * 1 == 1 << 64 - assert_eq(U128::from_u64s_le(0, 1), U128::one().wrapping_mul(U128::from_u64s_le(0, 1))); - - // ( 1 << 64 ) * ( 1 << 64 ) == 1 << 64 - assert_eq(U128::zero(), U128::from_u64s_le(0, 1).wrapping_mul(U128::from_u64s_le(0, 1))); - // -1 * -1 == 1 - assert_eq( - U128::one(), - U128::from_u64s_le(0xffffffffffffffff, 0xffffffffffffffff).wrapping_mul( - U128::from_u64s_le(0xffffffffffffffff, 0xffffffffffffffff), - ), - ); - } - - #[test] - fn test_default() { - assert_eq(U128::default(), U128::zero()); - } -} diff --git a/noir/noir-repo/rust-toolchain.toml b/noir/noir-repo/rust-toolchain.toml index e647d5cbf460..ed1915d2613a 100644 --- a/noir/noir-repo/rust-toolchain.toml +++ b/noir/noir-repo/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.75.0" +channel = "1.85.0" components = [ "rust-src" ] -targets = [ "wasm32-unknown-unknown", "wasm32-wasi", "aarch64-apple-darwin" ] +targets = [ "wasm32-unknown-unknown", "wasm32-wasip1", "aarch64-apple-darwin" ] profile = "default" diff --git a/noir/noir-repo/scripts/bump-aztec-packges-commit.sh b/noir/noir-repo/scripts/bump-aztec-packages-commit.sh similarity index 100% rename from noir/noir-repo/scripts/bump-aztec-packges-commit.sh rename to noir/noir-repo/scripts/bump-aztec-packages-commit.sh diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256/Prover.toml b/noir/noir-repo/test_programs/benchmarks/bench_sha256/Prover.toml deleted file mode 100644 index 66779dea9d7b..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256/Prover.toml +++ /dev/null @@ -1 +0,0 @@ -input = [1,2] diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_sha256/src/main.nr deleted file mode 100644 index c94d359239dd..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256/src/main.nr +++ /dev/null @@ -1,4 +0,0 @@ - -fn main(input: [u8; 2]) -> pub [u8; 32] { - std::hash::sha256(input) -} \ No newline at end of file diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Prover.toml b/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Prover.toml deleted file mode 100644 index 542b4a08dd6f..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Prover.toml +++ /dev/null @@ -1,102 +0,0 @@ -input = [ - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], -] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/src/main.nr deleted file mode 100644 index 6e4bfc27c8f2..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/src/main.nr +++ /dev/null @@ -1,10 +0,0 @@ -global SIZE: u32 = 100; - -fn main(input: [[u8; 2]; SIZE]) -> pub [[u8; 32]; SIZE] { - let mut results: [[u8; 32]; SIZE] = [[0; 32]; SIZE]; - for i in 0..SIZE { - results[i] = std::hash::sha256(input[i]); - } - - results -} diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Nargo.toml b/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Nargo.toml deleted file mode 100644 index c1dc76df3942..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "bench_sha256_30" -version = "0.1.0" -type = "bin" -authors = [""] - -[dependencies] diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Prover.toml b/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Prover.toml deleted file mode 100644 index 7792a9ab8e36..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Prover.toml +++ /dev/null @@ -1,32 +0,0 @@ -input = [ - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], -] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/src/main.nr deleted file mode 100644 index 0a4288114e34..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/src/main.nr +++ /dev/null @@ -1,10 +0,0 @@ -global SIZE: u32 = 30; - -fn main(input: [[u8; 2]; SIZE]) -> pub [[u8; 32]; SIZE] { - let mut results: [[u8; 32]; SIZE] = [[0; 32]; SIZE]; - for i in 0..SIZE { - results[i] = std::hash::sha256(input[i]); - } - - results -} diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Prover.toml b/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Prover.toml deleted file mode 100644 index ba4bbc1540dc..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Prover.toml +++ /dev/null @@ -1,191 +0,0 @@ -# 2*64+60=188 bytes -input = [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 72, - 73, - 74, - 75, - 76, - 77, - 78, - 79, - 80, - 81, - 82, - 83, - 84, - 85, - 86, - 87, - 88, - 89, - 90, - 91, - 92, - 93, - 94, - 95, - 96, - 97, - 98, - 99, - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 72, - 73, - 74, - 75, - 76, - 77, - 78, - 79, - 80, - 81, - 82, - 83, - 84, - 85, - 86, - 87, -] diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/src/main.nr deleted file mode 100644 index c47bdc2a5610..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/src/main.nr +++ /dev/null @@ -1,7 +0,0 @@ -// Input size long enough that we have to compress a few times -// and then pad the last block out. -global INPUT_SIZE: u32 = 2 * 64 + 60; - -fn main(input: [u8; INPUT_SIZE]) -> pub [u8; 32] { - std::hash::sha256(input) -} diff --git a/noir/noir-repo/test_programs/compilation_report.sh b/noir/noir-repo/test_programs/compilation_report.sh index 6f7ef254477b..aa1bbef39dec 100755 --- a/noir/noir-repo/test_programs/compilation_report.sh +++ b/noir/noir-repo/test_programs/compilation_report.sh @@ -6,7 +6,7 @@ current_dir=$(pwd) base_path="$current_dir/execution_success" # Tests to be profiled for compilation report -tests_to_profile=("sha256_regression" "regression_4709" "ram_blowup_regression" "global_var_regression_entry_points") +tests_to_profile=("regression_4709" "ram_blowup_regression" "global_var_regression_entry_points") echo "[ " > $current_dir/compilation_report.json diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Nargo.toml b/noir/noir-repo/test_programs/compile_failure/broken_impl/Nargo.toml similarity index 51% rename from noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Nargo.toml rename to noir/noir-repo/test_programs/compile_failure/broken_impl/Nargo.toml index d0c90d75088e..a200adc27b07 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Nargo.toml +++ b/noir/noir-repo/test_programs/compile_failure/broken_impl/Nargo.toml @@ -1,7 +1,7 @@ [package] -name = "bench_sha256_100" -version = "0.1.0" +name = "broken_impl" type = "bin" authors = [""] +compiler_version = ">=0.26.0" [dependencies] diff --git a/noir/noir-repo/test_programs/compile_failure/broken_impl/src/main.nr b/noir/noir-repo/test_programs/compile_failure/broken_impl/src/main.nr new file mode 100644 index 000000000000..033b0e741ed6 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_failure/broken_impl/src/main.nr @@ -0,0 +1,2 @@ +// This used to crash the compiler +impl< Foo for diff --git a/noir/noir-repo/test_programs/compile_success_empty/brillig_integer_binary_operations/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/brillig_integer_binary_operations/src/main.nr index 032fbf457b63..2c02582c14d9 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/brillig_integer_binary_operations/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/brillig_integer_binary_operations/src/main.nr @@ -77,4 +77,3 @@ unconstrained fn shr(x: u32, y: u8) -> u32 { unconstrained fn shl(x: u32, y: u8) -> u32 { x << y } - diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_as_field/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_as_field/src/main.nr deleted file mode 100644 index e13db54b80bd..000000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_as_field/src/main.nr +++ /dev/null @@ -1,6 +0,0 @@ -fn main() { - comptime { - let _: U128 = U128::from_integer(1); - } -} - diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_as_field/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/comptime_as_primitive/Nargo.toml similarity index 100% rename from noir/noir-repo/test_programs/compile_success_empty/comptime_as_field/Nargo.toml rename to noir/noir-repo/test_programs/compile_success_empty/comptime_as_primitive/Nargo.toml diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_as_primitive/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_as_primitive/src/main.nr new file mode 100644 index 000000000000..392ccf2ebfc6 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_as_primitive/src/main.nr @@ -0,0 +1,7 @@ +fn main() { + comptime { + let x: u64 = 1; + let y = x as Field; + let _ = y as u128; + } +} 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 index 8cdd15aaa0ec..adee6e6fe935 100644 --- 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 @@ -26,4 +26,3 @@ fn hello_world() {} comptime fn call(x: Quoted) -> Quoted { quote { $x() } } - diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/Nargo.toml deleted file mode 100644 index 38a46ba0dbec..000000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/Nargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "comptime_from_field" -type = "bin" -authors = [""] - -[dependencies] diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/src/main.nr deleted file mode 100644 index 722c5c7eb329..000000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/src/main.nr +++ /dev/null @@ -1,6 +0,0 @@ -fn main() { - comptime { - let _: Field = U128::from_hex("0x0").to_integer(); - } -} - diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_function_definition/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_function_definition/src/main.nr index 4ce641cbe7df..57bcd2ba90ec 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_function_definition/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_function_definition/src/main.nr @@ -116,7 +116,7 @@ mod test_as_typed_expr_1 { comptime fn foo(module: Module) -> Quoted { let method = module.functions().filter(|f| f.name() == quote { method })[0]; let func = method.as_typed_expr(); - quote { + quote { pub fn bar() -> i32 { $func(1) } @@ -140,7 +140,7 @@ mod test_as_typed_expr_2 { comptime fn foo(module: Module) -> Quoted { let method = module.functions().filter(|f| f.name() == quote { method })[0]; let func = method.as_typed_expr(); - quote { + quote { pub fn bar() -> u32 { // Safety: test program unsafe { $func([1, 2, 3, 0]) } @@ -166,7 +166,7 @@ mod test_as_typed_expr_3 { comptime fn foo(module: Module) -> Quoted { let method = module.functions().filter(|f| f.name() == quote { method })[0]; let func = method.as_typed_expr(); - quote { + quote { pub fn bar() -> u32 { // Safety: test program comptime { $func(([1, 2, 3, 0], "a")) } diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/Nargo.toml deleted file mode 100644 index 47c8654804d6..000000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "comptime_keccak" -type = "bin" -authors = [""] -compiler_version = ">=0.33.0" - -[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/src/main.nr deleted file mode 100644 index dc4e88b7ab2d..000000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/src/main.nr +++ /dev/null @@ -1,32 +0,0 @@ -// Tests a very simple program. -// -// The features being tested is keccak256 in brillig -fn main() { - comptime { - let x = 0xbd; - let result = [ - 0x5a, 0x50, 0x2f, 0x9f, 0xca, 0x46, 0x7b, 0x26, 0x6d, 0x5b, 0x78, 0x33, 0x65, 0x19, - 0x37, 0xe8, 0x05, 0x27, 0x0c, 0xa3, 0xf3, 0xaf, 0x1c, 0x0d, 0xd2, 0x46, 0x2d, 0xca, - 0x4b, 0x3b, 0x1a, 0xbf, - ]; - // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field - // The padding is taken care of by the program - let digest = keccak256([x as u8], 1); - assert(digest == result); - //#1399: variable message size - let message_size = 4; - let hash_a = keccak256([1, 2, 3, 4], message_size); - let hash_b = keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); - - assert(hash_a == hash_b); - - let message_size_big = 8; - let hash_c = keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); - - assert(hash_a != hash_c); - } -} - -comptime fn keccak256(data: [u8; N], msg_len: u32) -> [u8; 32] { - std::hash::keccak256(data, msg_len) -} 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 11025d450246..5573e8ad798b 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 @@ -31,4 +31,3 @@ pub fn neg_at_comptime() { let _result = -value; } } - diff --git a/noir/noir-repo/test_programs/compile_success_empty/conditional_regression_to_bits/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/conditional_regression_to_bits/src/main.nr index dd80d2c576f2..ef0eb4cf0689 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/conditional_regression_to_bits/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/conditional_regression_to_bits/src/main.nr @@ -23,4 +23,3 @@ fn main() { } assert(c1 == 1); } - diff --git a/noir/noir-repo/test_programs/compile_success_empty/enums/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/enums/src/main.nr index 03fc10a1c35a..3b12af1c15d5 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/enums/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/enums/src/main.nr @@ -3,6 +3,7 @@ fn main() { foo_tests(); option_tests(); abc_tests(); + match_on_structs(); } fn primitive_tests() { @@ -19,8 +20,20 @@ fn primitive_tests() { false => fail(), true => (), } + + // Ensure we can match on MIN and use globals as constants + let i64_min = I64_MIN; + match i64_min { + 9_223_372_036_854_775_807 => fail(), + -9_223_372_036_854_775_807 => fail(), + 0 => fail(), + I64_MIN => (), + _ => fail(), + } } +global I64_MIN: i64 = -9_223_372_036_854_775_808; + enum Foo { A(Field, Field), B(u32), @@ -103,7 +116,7 @@ fn abc_tests() { // Mut is only to throw the optimizer off a bit so we can see // the `eq`s that get generated before they're removed because each of these are constant let mut tuple = (ABC::A, ABC::B); - match tuple { + let _ = match tuple { (ABC::A, _) => 1, (_, ABC::A) => 2, (_, ABC::B) => 3, @@ -114,3 +127,27 @@ fn abc_tests() { _ => 0, }; } + +fn match_on_structs() { + let foo = MyStruct { x: 10, y: 20 }; + match foo { + MyStruct { x, y } => { + assert_eq(x, 10); + assert_eq(y, 20); + }, + } + + match MyOption::Some(foo) { + MyOption::Some(MyStruct { x: x2, y: y2 }) => { + assert_eq(x2, 10); + assert_eq(y2, 20); + }, + MyOption::None => fail(), + MyOption::Maybe => fail(), + } +} + +struct MyStruct { + x: i32, + y: Field, +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/eq_derivation_with_numeric_generics/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/eq_derivation_with_numeric_generics/src/main.nr index 3307aeb15adc..e2f3cb46d11d 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/eq_derivation_with_numeric_generics/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/eq_derivation_with_numeric_generics/src/main.nr @@ -9,4 +9,3 @@ fn main() { let bar = Foo { a: [0; 10], b: 28 }; assert(foo != bar); } - diff --git a/noir/noir-repo/test_programs/compile_success_empty/trait_override_implementation/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/trait_override_implementation/src/main.nr index 12a625b08432..589232d00d8e 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/trait_override_implementation/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/trait_override_implementation/src/main.nr @@ -80,4 +80,3 @@ fn main(x: Field) { assert(bar_mut.f4() == 4, "14"); assert(bar_mut.f5() == 50, "15"); } - diff --git a/noir/noir-repo/test_programs/compile_success_empty/unquote_struct/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/unquote_struct/src/main.nr index 12c683a94a85..c7820c094b7a 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/unquote_struct/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/unquote_struct/src/main.nr @@ -1,3 +1,5 @@ +use std::meta::unquote; + fn main() { let foo = Foo { x: 4, y: 4 }; foo.assert_equal(); @@ -21,3 +23,16 @@ comptime fn output_struct(f: FunctionDefinition) -> Quoted { } } } + +// The following code proves that `Bar::` can be unquoted +// into a constructor position without losing its generic types. + +pub struct Bar {} + +pub fn bar() { + let _ = comptime { + let x = quote { Bar:: }; + let q = quote { $x {} }; + unquote!(q) + }; +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/unquote_trait_constraint_in_trait_impl_position/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/unquote_trait_constraint_in_trait_impl_position/src/main.nr index 92ddf99f1c0b..8bf3863bbe3f 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/unquote_trait_constraint_in_trait_impl_position/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/unquote_trait_constraint_in_trait_impl_position/src/main.nr @@ -21,4 +21,3 @@ comptime fn foo(_: FunctionDefinition) -> Quoted { } } } - diff --git a/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Nargo.toml b/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Nargo.toml new file mode 100644 index 000000000000..c04a74f24df4 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "u128_multiplication_overflow" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] diff --git a/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Prover.toml b/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Prover.toml new file mode 100644 index 000000000000..90212f582036 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Prover.toml @@ -0,0 +1,2 @@ +x = "257295058452732708167448229940904157557" +y = "85070591730234615865843651857942052864" diff --git a/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/src/main.nr b/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/src/main.nr new file mode 100644 index 000000000000..45884059a881 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/src/main.nr @@ -0,0 +1,7 @@ +fn main(x: u128, y: u128) -> pub u128 { + // Multiplying 257295058452732708167448229940904157557 * 85070591730234615865843651857942052864 + // using bn254 field elements wraps the maximum field element value, + // resulting in 31631953497925087476338759149270597631 which is less than u128::MAX. + // We want this to be caught as an overflow. + x * y +} diff --git a/noir/noir-repo/test_programs/execution_report.sh b/noir/noir-repo/test_programs/execution_report.sh index 5c916ef6bd75..c7b12d996811 100755 --- a/noir/noir-repo/test_programs/execution_report.sh +++ b/noir/noir-repo/test_programs/execution_report.sh @@ -6,7 +6,7 @@ current_dir=$(pwd) base_path="$current_dir/execution_success" # Tests to be profiled for execution report -tests_to_profile=("sha256_regression" "regression_4709" "ram_blowup_regression" "global_var_regression_entry_points") +tests_to_profile=("regression_4709" "ram_blowup_regression" "global_var_regression_entry_points") echo "[" > $current_dir/execution_report.json diff --git a/noir/noir-repo/test_programs/execution_success/6/Prover.toml b/noir/noir-repo/test_programs/execution_success/6/Prover.toml index 1c52aef063c9..d370032bd530 100644 --- a/noir/noir-repo/test_programs/execution_success/6/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/6/Prover.toml @@ -3,37 +3,4 @@ # used : https://emn178.github.io/online-tools/sha256.html x = [104, 101, 108, 108, 111] -result = [ - 0x2c, - 0xf2, - 0x4d, - 0xba, - 0x5f, - 0xb0, - 0xa3, - 0x0e, - 0x26, - 0xe8, - 0x3b, - 0x2a, - 0xc5, - 0xb9, - 0xe2, - 0x9e, - 0x1b, - 0x16, - 0x1e, - 0x5c, - 0x1f, - 0xa7, - 0x42, - 0x5e, - 0x73, - 0x04, - 0x33, - 0x62, - 0x93, - 0x8b, - 0x98, - 0x24, -] +result = [234, 143, 22, 61, 179, 134, 130, 146, 94, 68, 145, 197, 229, 141, 75, 179, 80, 110, 248, 193, 78, 183, 138, 134, 233, 8, 197, 98, 74, 103, 32, 15] diff --git a/noir/noir-repo/test_programs/execution_success/6/src/main.nr b/noir/noir-repo/test_programs/execution_success/6/src/main.nr index 5b71174614f5..e950e309df2d 100644 --- a/noir/noir-repo/test_programs/execution_success/6/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/6/src/main.nr @@ -1,11 +1,11 @@ -// Sha256 circuit where the input is 5 bytes -// not five field elements since sha256 operates over +// blake3 circuit where the input is 5 bytes +// not five field elements since blake3 operates over // bytes. // // If you do not cast, it will take all the bytes from the field element! fn main(x: [u8; 5], result: pub [u8; 32]) { - let mut digest = std::hash::sha256(x); + let mut digest = std::hash::blake3(x); digest[0] = 5 as u8; - digest = std::hash::sha256(x); + digest = std::hash::blake3(x); assert(digest == result); } diff --git a/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/Prover.toml b/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/Prover.toml index cc60eb8a8baa..f852a79a1033 100644 --- a/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/Prover.toml @@ -1,4 +1,4 @@ index = "1" leaf = ["51", "109", "224", "175", "60", "42", "79", "222", "117", "255", "174", "79", "126", "242", "74", "34", "100", "35", "20", "200", "109", "89", "191", "219", "41", "10", "118", "217", "165", "224", "215", "109"] path = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63"] -root = [79, 230, 126, 184, 98, 125, 226, 58, 117, 45, 140, 15, 72, 118, 89, 173, 117, 161, 166, 0, 214, 125, 13, 16, 113, 81, 173, 156, 97, 15, 57, 216] +root = [186, 47, 168, 70, 152, 149, 203, 90, 138, 188, 96, 15, 111, 179, 82, 106, 198, 166, 172, 38, 110, 187, 182, 64, 29, 101, 171, 221, 89, 105, 243, 22] diff --git a/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr b/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr index 260d609928b8..603ae704d704 100644 --- a/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr @@ -18,7 +18,7 @@ fn compute_root(leaf: [u8; 32], path: [u8; 64], _index: u32, root: [u8; 32]) { hash_input[j + b] = path[offset + j]; } - current = std::hash::sha256(hash_input); + current = std::hash::blake3(hash_input); index = index >> 1; } diff --git a/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/Prover.toml b/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/Prover.toml index 1f2915324144..85b480415b1b 100644 --- a/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/Prover.toml @@ -1,5 +1,5 @@ y = "3" -hash_result = [50, 53, 90, 252, 105, 236, 223, 30, 135, 229, 193, 172, 51, 139, 8, 32, 188, 104, 151, 115, 129, 168, 27, 71, 203, 47, 40, 228, 89, 177, 129, 100] +hash_result = [77, 43, 36, 42, 132, 232, 186, 191, 119, 43, 192, 121, 66, 137, 143, 205, 13, 23, 80, 25, 162, 45, 100, 31, 178, 150, 138, 4, 72, 73, 120, 70] [[x]] a = "1" @@ -20,4 +20,4 @@ a = "7" b = ["8", "9", "22"] [x.bar] -inner = ["106", "107", "108"] \ No newline at end of file +inner = ["106", "107", "108"] diff --git a/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/src/main.nr b/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/src/main.nr index 15a2747eaa9d..14f110a23a0b 100644 --- a/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/src/main.nr @@ -15,6 +15,6 @@ fn main(mut x: [Foo; 3], y: pub Field, hash_result: pub [u8; 32]) { // Make sure that we are passing a dynamic array to the black box function call // by setting the array using a dynamic index here hash_input[y - 1] = 0; - let hash = std::hash::sha256(hash_input); + let hash = std::hash::blake3(hash_input); assert_eq(hash, hash_result); } diff --git a/noir/noir-repo/test_programs/execution_success/bit_and/src/main.nr b/noir/noir-repo/test_programs/execution_success/bit_and/src/main.nr index 5a0aa17e3ede..2e2ce62def7c 100644 --- a/noir/noir-repo/test_programs/execution_success/bit_and/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/bit_and/src/main.nr @@ -13,4 +13,3 @@ fn main(x: Field, y: Field, a: Field, b: Field) { let b_as_u8 = b as u8; assert((a_as_u8 & b_as_u8) == a_as_u8); } - diff --git a/noir/noir-repo/test_programs/execution_success/bool_not/src/main.nr b/noir/noir-repo/test_programs/execution_success/bool_not/src/main.nr index 935d8cc074de..d848225b3f26 100644 --- a/noir/noir-repo/test_programs/execution_success/bool_not/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/bool_not/src/main.nr @@ -1,4 +1,3 @@ fn main(x: u1) { assert(!x == 0); } - diff --git a/noir/noir-repo/test_programs/execution_success/bool_or/src/main.nr b/noir/noir-repo/test_programs/execution_success/bool_or/src/main.nr index 6cb959e61e64..87bbb757fbcd 100644 --- a/noir/noir-repo/test_programs/execution_success/bool_or/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/bool_or/src/main.nr @@ -3,4 +3,3 @@ fn main(x: u1, y: u1) { assert(x | y | x == 1); } - diff --git a/noir/noir-repo/test_programs/execution_success/brillig_calls_conditionals/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_calls_conditionals/src/main.nr index e8bce638ebe8..8d0e78bd6235 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_calls_conditionals/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_calls_conditionals/src/main.nr @@ -36,4 +36,3 @@ unconstrained fn entry_point(x: u32) -> u32 { result } - diff --git a/noir/noir-repo/test_programs/execution_success/brillig_cow_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_cow_regression/src/main.nr index 69273bc3dcac..4b70f2961b66 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_cow_regression/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_cow_regression/src/main.nr @@ -172,6 +172,6 @@ unconstrained fn main(kernel_data: DataToHash) -> pub [Field; NUM_FIELDS_PER_SHA } } - let sha_digest = std::hash::sha256(hash_input_flattened); - U256::from_bytes32(sha_digest).to_u128_limbs() + let blake3_digest = std::hash::blake3(hash_input_flattened); + U256::from_bytes32(blake3_digest).to_u128_limbs() } diff --git a/noir/noir-repo/test_programs/execution_success/brillig_nested_arrays/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_nested_arrays/src/main.nr index 3f5e5a0f8eb5..7835bca55a75 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_nested_arrays/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_nested_arrays/src/main.nr @@ -41,4 +41,3 @@ fn main(x: Field, y: Field) { create_and_assert_inside_brillig(x, y); } } - diff --git a/noir/noir-repo/test_programs/execution_success/brillig_pedersen/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_pedersen/src/main.nr index 55f81882bd4d..e4d2d2d0e784 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_pedersen/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_pedersen/src/main.nr @@ -24,4 +24,3 @@ unconstrained fn main( let hash = std::hash::pedersen_commitment_with_separator([state], 0); assert(std::hash::pedersen_commitment_with_separator([43], 0).x == hash.x); } - diff --git a/noir/noir-repo/test_programs/execution_success/cast_bool/src/main.nr b/noir/noir-repo/test_programs/execution_success/cast_bool/src/main.nr index 422d3b98f83f..f6742fb62f3f 100644 --- a/noir/noir-repo/test_programs/execution_success/cast_bool/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/cast_bool/src/main.nr @@ -3,4 +3,3 @@ fn main(x: Field, y: Field) { let t = z as u8; assert(t == 1); } - diff --git a/noir/noir-repo/test_programs/execution_success/conditional_1/Prover.toml b/noir/noir-repo/test_programs/execution_success/conditional_1/Prover.toml index baad8be126a5..b06d750fda0b 100644 --- a/noir/noir-repo/test_programs/execution_success/conditional_1/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/conditional_1/Prover.toml @@ -3,7 +3,7 @@ a=0 x = [104, 101, 108, 108, 111] result = [ - 0x2c, + 234, 0xf2, 0x4d, 0xba, diff --git a/noir/noir-repo/test_programs/execution_success/conditional_1/src/main.nr b/noir/noir-repo/test_programs/execution_success/conditional_1/src/main.nr index eedb8a697d1d..e0292c733bd1 100644 --- a/noir/noir-repo/test_programs/execution_success/conditional_1/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/conditional_1/src/main.nr @@ -53,7 +53,7 @@ fn main(a: u32, mut c: [u32; 4], x: [u8; 5], result: pub [u8; 32]) { let mut y = 0; if a == 0 { - let digest = std::hash::sha256(x); + let digest = std::hash::blake3(x); y = digest[0]; } else { y = 5; diff --git a/noir/noir-repo/test_programs/execution_success/conditional_regression_661/src/main.nr b/noir/noir-repo/test_programs/execution_success/conditional_regression_661/src/main.nr index 1036acc6da4b..a8459515634b 100644 --- a/noir/noir-repo/test_programs/execution_success/conditional_regression_661/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/conditional_regression_661/src/main.nr @@ -25,4 +25,3 @@ fn issue_661_bar(a: [u32; 4]) -> [u32; 4] { b[0] = a[0] + 1; b } - diff --git a/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/Prover.toml b/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/Prover.toml index baad8be126a5..5f098ae39d81 100644 --- a/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/Prover.toml @@ -2,37 +2,4 @@ c=[2, 4, 3, 0, ] a=0 x = [104, 101, 108, 108, 111] -result = [ - 0x2c, - 0xf2, - 0x4d, - 0xba, - 0x5f, - 0xb0, - 0xa3, - 0x0e, - 0x26, - 0xe8, - 0x3b, - 0x2a, - 0xc5, - 0xb9, - 0xe2, - 0x9e, - 0x1b, - 0x16, - 0x1e, - 0x5c, - 0x1f, - 0xa7, - 0x42, - 0x5e, - 0x73, - 0x04, - 0x33, - 0x62, - 0x93, - 0x8b, - 0x98, - 0x24, -] +result = [234, 143, 22, 61, 179, 134, 130, 146, 94, 68, 145, 197, 229, 141, 75, 179, 80, 110, 248, 193, 78, 183, 138, 134, 233, 8, 197, 98, 74, 103, 32, 15] diff --git a/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/src/main.nr b/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/src/main.nr index de5ad20a6423..9312655c8f36 100644 --- a/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/src/main.nr @@ -24,9 +24,9 @@ fn bar(x: Field) { } fn call_intrinsic(x: [u8; 5], result: [u8; 32]) { - let mut digest = std::hash::sha256(x); + let mut digest = std::hash::blake3(x); digest[0] = 5 as u8; - digest = std::hash::sha256(x); + digest = std::hash::blake3(x); assert(digest == result); } 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 ecc7794cf9ea..02c2a7d06832 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 @@ -9,4 +9,3 @@ fn main(mut x: u32, y: call_data(0) u32, z: call_data(0) [u32; 4]) -> return_dat unconstrained fn foo(x: u32) -> u32 { x + 1 } - diff --git a/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/Prover.toml b/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/Prover.toml index 412c7b36e4c2..e78fc19cb71b 100644 --- a/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/Prover.toml @@ -33,46 +33,6 @@ hashed_message = [ 0xc1, 0xe2, ] -message = [ - 0x49, - 0x6e, - 0x73, - 0x74, - 0x72, - 0x75, - 0x63, - 0x74, - 0x69, - 0x6f, - 0x6e, - 0x73, - 0x20, - 0x75, - 0x6e, - 0x63, - 0x6c, - 0x65, - 0x61, - 0x72, - 0x2c, - 0x20, - 0x61, - 0x73, - 0x6b, - 0x20, - 0x61, - 0x67, - 0x61, - 0x69, - 0x6e, - 0x20, - 0x6c, - 0x61, - 0x74, - 0x65, - 0x72, - 0x2e, -] pub_key_x = [ 0xa0, 0x43, diff --git a/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/src/main.nr b/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/src/main.nr index 00d420089fc9..1c94bf8961e5 100644 --- a/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/src/main.nr @@ -1,14 +1,4 @@ -fn main( - message: [u8; 38], - hashed_message: [u8; 32], - pub_key_x: [u8; 32], - pub_key_y: [u8; 32], - signature: [u8; 64], -) { - // Hash the message, since secp256k1 expects a hashed_message - let expected = std::hash::sha256(message); - assert(hashed_message == expected); - +fn main(hashed_message: [u8; 32], pub_key_x: [u8; 32], pub_key_y: [u8; 32], signature: [u8; 64]) { let valid_signature = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); assert(valid_signature); diff --git a/noir/noir-repo/test_programs/execution_success/fold_complex_outputs/src/main.nr b/noir/noir-repo/test_programs/execution_success/fold_complex_outputs/src/main.nr index b433c8b416cc..b83a88c4a7d6 100644 --- a/noir/noir-repo/test_programs/execution_success/fold_complex_outputs/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/fold_complex_outputs/src/main.nr @@ -69,4 +69,3 @@ fn map_fields(mut input: ParentStruct) -> ParentStruct { input } - diff --git a/noir/noir-repo/test_programs/execution_success/global_var_entry_point_used_in_another_entry/src/main.nr b/noir/noir-repo/test_programs/execution_success/global_var_entry_point_used_in_another_entry/src/main.nr index 364c27cc4ab7..d57416772717 100644 --- a/noir/noir-repo/test_programs/execution_success/global_var_entry_point_used_in_another_entry/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/global_var_entry_point_used_in_another_entry/src/main.nr @@ -41,4 +41,3 @@ unconstrained fn entry_point_one_diff_global(x: Field, y: Field) { let z = THREE + x + y; assert(z == 4); } - diff --git a/noir/noir-repo/test_programs/execution_success/if_else_chain/src/main.nr b/noir/noir-repo/test_programs/execution_success/if_else_chain/src/main.nr index 2705d5b31110..2c80acd273cd 100644 --- a/noir/noir-repo/test_programs/execution_success/if_else_chain/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/if_else_chain/src/main.nr @@ -12,4 +12,3 @@ fn main(a: u32, mut c: [u32; 4]) { assert(c[0] == 10); } } - diff --git a/noir/noir-repo/test_programs/execution_success/keccak256/Prover.toml b/noir/noir-repo/test_programs/execution_success/keccak256/Prover.toml deleted file mode 100644 index d65c4011d3f8..000000000000 --- a/noir/noir-repo/test_programs/execution_success/keccak256/Prover.toml +++ /dev/null @@ -1,35 +0,0 @@ -x = 0xbd -result = [ - 0x5a, - 0x50, - 0x2f, - 0x9f, - 0xca, - 0x46, - 0x7b, - 0x26, - 0x6d, - 0x5b, - 0x78, - 0x33, - 0x65, - 0x19, - 0x37, - 0xe8, - 0x05, - 0x27, - 0x0c, - 0xa3, - 0xf3, - 0xaf, - 0x1c, - 0x0d, - 0xd2, - 0x46, - 0x2d, - 0xca, - 0x4b, - 0x3b, - 0x1a, - 0xbf, -] diff --git a/noir/noir-repo/test_programs/execution_success/keccak256/src/main.nr b/noir/noir-repo/test_programs/execution_success/keccak256/src/main.nr deleted file mode 100644 index 1e13fa028b79..000000000000 --- a/noir/noir-repo/test_programs/execution_success/keccak256/src/main.nr +++ /dev/null @@ -1,20 +0,0 @@ -// docs:start:keccak256 -fn main(x: Field, result: [u8; 32]) { - // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field - // The padding is taken care of by the program - let digest = std::hash::keccak256([x as u8], 1); - assert(digest == result); - - //#1399: variable message size - let message_size = 4; - let hash_a = std::hash::keccak256([1, 2, 3, 4], message_size); - let hash_b = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); - - assert(hash_a == hash_b); - - let message_size_big = 8; - let hash_c = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); - - assert(hash_a != hash_c); -} -// docs:end:keccak256 diff --git a/noir/noir-repo/test_programs/execution_success/nested_array_dynamic/src/main.nr b/noir/noir-repo/test_programs/execution_success/nested_array_dynamic/src/main.nr index 2c53822d6b94..4ac6b4124ae5 100644 --- a/noir/noir-repo/test_programs/execution_success/nested_array_dynamic/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/nested_array_dynamic/src/main.nr @@ -73,4 +73,3 @@ fn main(mut x: [Foo; 4], y: pub Field) { foo_parents[y - 2].foos[y - 3].b = foo_parents[y - 2].foos[y - 2].b; assert(foo_parents[1].foos[0].b == [20, 19, 5000]); } - diff --git a/noir/noir-repo/test_programs/execution_success/nested_arrays_from_brillig/src/main.nr b/noir/noir-repo/test_programs/execution_success/nested_arrays_from_brillig/src/main.nr index 387454b9e21e..66021005ce3a 100644 --- a/noir/noir-repo/test_programs/execution_success/nested_arrays_from_brillig/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/nested_arrays_from_brillig/src/main.nr @@ -24,4 +24,3 @@ fn main(values: [Field; 6]) { let notes = unsafe { create_inside_brillig(values) }; assert(access_nested(notes) == (2 + 4 + 3 + 1)); } - diff --git a/noir/noir-repo/test_programs/execution_success/pedersen_check/src/main.nr b/noir/noir-repo/test_programs/execution_success/pedersen_check/src/main.nr index c71b2b570da4..00afa4b8e276 100644 --- a/noir/noir-repo/test_programs/execution_success/pedersen_check/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/pedersen_check/src/main.nr @@ -17,4 +17,3 @@ fn main(x: Field, y: Field, salt: Field, out_x: Field, out_y: Field, out_hash: F let hash = std::hash::pedersen_commitment([state]); assert(std::hash::pedersen_commitment([43]).x == hash.x); } - diff --git a/noir/noir-repo/test_programs/execution_success/pedersen_commitment/src/main.nr b/noir/noir-repo/test_programs/execution_success/pedersen_commitment/src/main.nr index c2225edcdf11..e9a7ea4816f1 100644 --- a/noir/noir-repo/test_programs/execution_success/pedersen_commitment/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/pedersen_commitment/src/main.nr @@ -5,4 +5,3 @@ fn main(x: Field, y: Field, expected_commitment: std::embedded_curve_ops::Embedd assert_eq(commitment.y, expected_commitment.y); } // docs:end:pedersen-commitment - diff --git a/noir/noir-repo/test_programs/execution_success/pedersen_hash/src/main.nr b/noir/noir-repo/test_programs/execution_success/pedersen_hash/src/main.nr index de981d44bca5..c55e947930b4 100644 --- a/noir/noir-repo/test_programs/execution_success/pedersen_hash/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/pedersen_hash/src/main.nr @@ -4,4 +4,3 @@ fn main(x: Field, y: Field, expected_hash: Field) { assert_eq(hash, expected_hash); } // docs:end:pedersen-hash - diff --git a/noir/noir-repo/test_programs/execution_success/ram_blowup_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/ram_blowup_regression/src/main.nr index 6deb54dd21d7..5f63bf03e558 100644 --- a/noir/noir-repo/test_programs/execution_success/ram_blowup_regression/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/ram_blowup_regression/src/main.nr @@ -20,9 +20,9 @@ pub fn field_from_bytes_32_trunc(bytes32: [u8; 32]) -> Field { low + high * v } -pub fn sha256_to_field(bytes_to_hash: [u8; N]) -> Field { - let sha256_hashed = std::hash::sha256(bytes_to_hash); - let hash_in_a_field = field_from_bytes_32_trunc(sha256_hashed); +pub fn blake3_to_field(bytes_to_hash: [u8; N]) -> Field { + let blake3_hashed = std::hash::blake3(bytes_to_hash); + let hash_in_a_field = field_from_bytes_32_trunc(blake3_hashed); hash_in_a_field } @@ -36,6 +36,6 @@ fn main(tx_effects_hash_input: [Field; TX_EFFECTS_HASH_INPUT_FIELDS]) -> pub Fie } } - let sha_digest = sha256_to_field(hash_input_flattened); - sha_digest + let blake3_digest = blake3_to_field(hash_input_flattened); + blake3_digest } diff --git a/noir/noir-repo/test_programs/execution_success/reference_only_used_as_alias/src/main.nr b/noir/noir-repo/test_programs/execution_success/reference_only_used_as_alias/src/main.nr index c8dd38d9aca4..cf84d1030205 100644 --- a/noir/noir-repo/test_programs/execution_success/reference_only_used_as_alias/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/reference_only_used_as_alias/src/main.nr @@ -90,4 +90,3 @@ fn main(input: [Field; 4], randomness: Field, context_input: u32) { let event0 = ExampleEvent0 { value0: input[0], value1: input[1] }; event0.emit(encode_event_with_randomness(&mut context, randomness)); } - diff --git a/noir/noir-repo/test_programs/execution_success/regression_4449/Prover.toml b/noir/noir-repo/test_programs/execution_success/regression_4449/Prover.toml index 81af476bcc98..12b95c0dbfa1 100644 --- a/noir/noir-repo/test_programs/execution_success/regression_4449/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/regression_4449/Prover.toml @@ -1,3 +1,3 @@ x = 0xbd -result = [204, 59, 83, 197, 18, 1, 128, 43, 247, 28, 104, 225, 106, 13, 20, 187, 42, 26, 67, 150, 48, 75, 238, 168, 121, 247, 142, 160, 71, 222, 97, 188] \ No newline at end of file +result = [3, 128, 126, 121, 21, 242, 202, 74, 58, 183, 180, 171, 169, 186, 245, 81, 206, 26, 69, 29, 25, 207, 152, 152, 52, 33, 40, 106, 200, 237, 90, 156] diff --git a/noir/noir-repo/test_programs/execution_success/regression_4449/src/main.nr b/noir/noir-repo/test_programs/execution_success/regression_4449/src/main.nr index 3fda39bd8741..88b80dabc7f5 100644 --- a/noir/noir-repo/test_programs/execution_success/regression_4449/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/regression_4449/src/main.nr @@ -5,7 +5,7 @@ fn main(x: u8, result: [u8; 32]) { for i in 0..70 { let y = x + i; let a = [y, x, 32, 0, y + 1, y - 1, y - 2, 5]; - digest = std::sha256::digest(a); + digest = std::hash::blake3(a); } assert(digest == result); diff --git a/noir/noir-repo/test_programs/execution_success/sha256/Nargo.toml b/noir/noir-repo/test_programs/execution_success/regression_7195/Nargo.toml similarity index 50% rename from noir/noir-repo/test_programs/execution_success/sha256/Nargo.toml rename to noir/noir-repo/test_programs/execution_success/regression_7195/Nargo.toml index 255d2156ef67..d9ef0fa7388d 100644 --- a/noir/noir-repo/test_programs/execution_success/sha256/Nargo.toml +++ b/noir/noir-repo/test_programs/execution_success/regression_7195/Nargo.toml @@ -1,6 +1,6 @@ [package] -name = "sha256" +name = "regression_7195" type = "bin" authors = [""] -[dependencies] +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/regression_7195/Prover.toml b/noir/noir-repo/test_programs/execution_success/regression_7195/Prover.toml new file mode 100644 index 000000000000..8c12ebba6cf7 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/regression_7195/Prover.toml @@ -0,0 +1,2 @@ +x = "1" +y = "2" diff --git a/noir/noir-repo/test_programs/execution_success/regression_7195/src/main.nr b/noir/noir-repo/test_programs/execution_success/regression_7195/src/main.nr new file mode 100644 index 000000000000..004b388d40da --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/regression_7195/src/main.nr @@ -0,0 +1,21 @@ +fn bar(y: Field) { + assert(y != 0); +} + +fn foo(x: Field) { + // Safety: test + let y = unsafe { baz(x) }; + bar(y); +} + +unconstrained fn baz(x: Field) -> Field { + x +} + +fn main(x: Field, y: pub Field) { + // Safety: test + let x = unsafe { baz(x) }; + foo(x); + foo(y); + assert(x != y); +} diff --git a/noir/noir-repo/test_programs/execution_success/sha2_byte/Nargo.toml b/noir/noir-repo/test_programs/execution_success/regression_7451/Nargo.toml similarity index 68% rename from noir/noir-repo/test_programs/execution_success/sha2_byte/Nargo.toml rename to noir/noir-repo/test_programs/execution_success/regression_7451/Nargo.toml index efd691fce585..92740ca82f3e 100644 --- a/noir/noir-repo/test_programs/execution_success/sha2_byte/Nargo.toml +++ b/noir/noir-repo/test_programs/execution_success/regression_7451/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "sha2_byte" +name = "regression_7451" type = "bin" authors = [""] diff --git a/noir/noir-repo/test_programs/execution_success/regression_7451/Prover.toml b/noir/noir-repo/test_programs/execution_success/regression_7451/Prover.toml new file mode 100644 index 000000000000..7b70754b0690 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/regression_7451/Prover.toml @@ -0,0 +1 @@ +x="1" diff --git a/noir/noir-repo/test_programs/execution_success/regression_7451/src/main.nr b/noir/noir-repo/test_programs/execution_success/regression_7451/src/main.nr new file mode 100644 index 000000000000..c8d990a9c82e --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/regression_7451/src/main.nr @@ -0,0 +1,12 @@ +fn main(x: u8) { + // Safety: test code + let predicate = unsafe { return_true() }; + let tmp: u8 = if predicate { 0xff } else { 0 }; + + let y = tmp & x; + assert_eq(y, 1); +} + +unconstrained fn return_true() -> bool { + true +} diff --git a/noir/noir-repo/test_programs/execution_success/sha256/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256/Prover.toml deleted file mode 100644 index b4bf41623709..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256/Prover.toml +++ /dev/null @@ -1,38 +0,0 @@ - -x = 0xbd -result = [ - 0x68, - 0x32, - 0x57, - 0x20, - 0xaa, - 0xbd, - 0x7c, - 0x82, - 0xf3, - 0x0f, - 0x55, - 0x4b, - 0x31, - 0x3d, - 0x05, - 0x70, - 0xc9, - 0x5a, - 0xcc, - 0xbb, - 0x7d, - 0xc4, - 0xb5, - 0xaa, - 0xe1, - 0x12, - 0x04, - 0xc0, - 0x8f, - 0xfe, - 0x73, - 0x2b, -] -input = [0, 0] -toggle = false \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256/src/main.nr deleted file mode 100644 index 8e5e46b9837e..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256/src/main.nr +++ /dev/null @@ -1,27 +0,0 @@ -// Sha256 example -// -// Calls Sha256 from the standard library. -// -// The Compiler sees this special function and creates an ACIR gate -// -// The ACIR SHA256 gate is passed to PLONK who should -// know how to create the necessary constraints. -// -// Not yet here: For R1CS, it is more about manipulating arithmetic gates to get performance -// This can be done in ACIR! -fn main(x: Field, result: [u8; 32], input: [u8; 2], toggle: bool) { - // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field - // The padding is taken care of by the program - // docs:start:sha256_var - let digest = std::hash::sha256_var([x as u8], 1); - // docs:end:sha256_var - assert(digest == result); - - let digest = std::hash::sha256([x as u8]); - assert(digest == result); - - // variable size - let size: Field = 1 + toggle as Field; - let var_sha = std::hash::sha256_var(input, size as u64); - assert(var_sha == std::hash::sha256_var(input, 1)); -} diff --git a/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Nargo.toml deleted file mode 100644 index f7076311e1dc..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "sha256_brillig_performance_regression" -type = "bin" -authors = [""] -compiler_version = ">=0.33.0" - -[dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Prover.toml deleted file mode 100644 index 5bb7f3542573..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Prover.toml +++ /dev/null @@ -1,16 +0,0 @@ -input_amount = "1" -minimum_output_amount = "2" -secret_hash_for_L1_to_l2_message = "3" -uniswap_fee_tier = "4" - -[aztec_recipient] -inner = "5" - -[caller_on_L1] -inner = "6" - -[input_asset_bridge_portal_address] -inner = "7" - -[output_asset_bridge_portal_address] -inner = "8" diff --git a/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/src/main.nr deleted file mode 100644 index 42cc6d4ff3bc..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/src/main.nr +++ /dev/null @@ -1,104 +0,0 @@ -// Performance regression extracted from an aztec protocol contract. - -unconstrained fn main( - input_asset_bridge_portal_address: EthAddress, - input_amount: Field, - uniswap_fee_tier: Field, - output_asset_bridge_portal_address: EthAddress, - minimum_output_amount: Field, - aztec_recipient: AztecAddress, - secret_hash_for_L1_to_l2_message: Field, - caller_on_L1: EthAddress, -) -> pub Field { - let mut hash_bytes = [0; 260]; // 8 fields of 32 bytes each + 4 bytes fn selector - let input_token_portal_bytes: [u8; 32] = - input_asset_bridge_portal_address.to_field().to_be_bytes(); - let in_amount_bytes: [u8; 32] = input_amount.to_be_bytes(); - let uniswap_fee_tier_bytes: [u8; 32] = uniswap_fee_tier.to_be_bytes(); - let output_token_portal_bytes: [u8; 32] = - output_asset_bridge_portal_address.to_field().to_be_bytes(); - let amount_out_min_bytes: [u8; 32] = minimum_output_amount.to_be_bytes(); - let aztec_recipient_bytes: [u8; 32] = aztec_recipient.to_field().to_be_bytes(); - let secret_hash_for_L1_to_l2_message_bytes: [u8; 32] = - secret_hash_for_L1_to_l2_message.to_be_bytes(); - let caller_on_L1_bytes: [u8; 32] = caller_on_L1.to_field().to_be_bytes(); - - // The purpose of including the following selector is to make the message unique to that specific call. Note that - // it has nothing to do with calling the function. - let selector = comptime { - std::hash::keccak256( - "swap_public(address,uint256,uint24,address,uint256,bytes32,bytes32,address)".as_bytes(), - 75, - ) - }; - - hash_bytes[0] = selector[0]; - hash_bytes[1] = selector[1]; - hash_bytes[2] = selector[2]; - hash_bytes[3] = selector[3]; - - for i in 0..32 { - hash_bytes[i + 4] = input_token_portal_bytes[i]; - hash_bytes[i + 36] = in_amount_bytes[i]; - hash_bytes[i + 68] = uniswap_fee_tier_bytes[i]; - hash_bytes[i + 100] = output_token_portal_bytes[i]; - hash_bytes[i + 132] = amount_out_min_bytes[i]; - hash_bytes[i + 164] = aztec_recipient_bytes[i]; - hash_bytes[i + 196] = secret_hash_for_L1_to_l2_message_bytes[i]; - hash_bytes[i + 228] = caller_on_L1_bytes[i]; - } - - let content_hash = sha256_to_field(hash_bytes); - content_hash -} - -// Convert a 32 byte array to a field element by truncating the final byte -pub fn field_from_bytes_32_trunc(bytes32: [u8; 32]) -> Field { - // Convert it to a field element - let mut v = 1; - let mut high = 0 as Field; - let mut low = 0 as Field; - - for i in 0..15 { - // covers bytes 16..30 (31 is truncated and ignored) - low = low + (bytes32[15 + 15 - i] as Field) * v; - v = v * 256; - // covers bytes 0..14 - high = high + (bytes32[14 - i] as Field) * v; - } - // covers byte 15 - low = low + (bytes32[15] as Field) * v; - - low + high * v -} - -pub fn sha256_to_field(bytes_to_hash: [u8; N]) -> Field { - let sha256_hashed = std::hash::sha256(bytes_to_hash); - let hash_in_a_field = field_from_bytes_32_trunc(sha256_hashed); - - hash_in_a_field -} - -pub trait ToField { - fn to_field(self) -> Field; -} - -pub struct EthAddress { - inner: Field, -} - -impl ToField for EthAddress { - fn to_field(self) -> Field { - self.inner - } -} - -pub struct AztecAddress { - pub inner: Field, -} - -impl ToField for AztecAddress { - fn to_field(self) -> Field { - self.inner - } -} diff --git a/noir/noir-repo/test_programs/execution_success/sha256_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/sha256_regression/Nargo.toml deleted file mode 100644 index ce98d000bcb7..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_regression/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "sha256_regression" -type = "bin" -authors = [""] -compiler_version = ">=0.33.0" - -[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256_regression/Prover.toml deleted file mode 100644 index ea0a0f2e8a71..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_regression/Prover.toml +++ /dev/null @@ -1,14 +0,0 @@ -msg_just_over_block = [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116] -msg_multiple_of_block = [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 115, 45, 97, 115, 99, 105, 105, 13, 10, 109, 105, 109, 101, 45, 118, 101, 114, 115, 105, 111, 110, 58, 49, 46, 48, 32, 40, 77, 97, 99, 32, 79, 83, 32, 88, 32, 77, 97, 105, 108, 32, 49, 54, 46, 48, 32, 92, 40, 51, 55, 51, 49, 46, 53, 48, 48, 46, 50, 51, 49, 92, 41, 41, 13, 10, 115, 117, 98, 106, 101, 99, 116, 58, 72, 101, 108, 108, 111, 13, 10, 109, 101, 115, 115, 97, 103, 101, 45, 105, 100, 58, 60, 56, 70, 56, 49, 57, 68, 51, 50, 45, 66, 54, 65, 67, 45, 52, 56, 57, 68, 45, 57, 55, 55, 70, 45, 52, 51, 56, 66, 66, 67, 52, 67, 65, 66, 50, 55, 64, 109, 101, 46, 99, 111, 109, 62, 13, 10, 100, 97, 116, 101, 58, 83, 97, 116, 44, 32, 50, 54, 32, 65, 117, 103, 32, 50, 48, 50, 51, 32, 49, 50, 58, 50, 53, 58, 50, 50, 32, 43, 48, 52, 48, 48, 13, 10, 116, 111, 58, 122, 107, 101, 119, 116, 101, 115, 116, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 13, 10, 100, 107, 105, 109, 45, 115, 105, 103, 110, 97, 116, 117, 114, 101, 58, 118, 61, 49, 59, 32, 97, 61, 114, 115, 97, 45, 115, 104, 97, 50, 53, 54, 59, 32, 99, 61, 114, 101, 108, 97, 120, 101, 100, 47, 114, 101, 108, 97, 120, 101, 100, 59, 32, 100, 61, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 59, 32, 115, 61, 49, 97, 49, 104, 97, 105, 59, 32, 116, 61, 49, 54, 57, 51, 48, 51, 56, 51, 51, 55, 59, 32, 98, 104, 61, 55, 120, 81, 77, 68, 117, 111, 86, 86, 85, 52, 109, 48, 87, 48, 87, 82, 86, 83, 114, 86, 88, 77, 101, 71, 83, 73, 65, 83, 115, 110, 117, 99, 75, 57, 100, 74, 115, 114, 99, 43, 118, 85, 61, 59, 32, 104, 61, 102, 114, 111, 109, 58, 67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 77, 105, 109, 101, 45, 86, 101, 114, 115, 105, 111, 110, 58, 83, 117, 98, 106, 101, 99] -msg_just_under_block = [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59] -msg_big_not_block_multiple = [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 115, 45, 97, 115, 99, 105, 105, 13, 10, 109, 105, 109, 101, 45, 118, 101, 114, 115, 105, 111, 110, 58, 49, 46, 48, 32, 40, 77, 97, 99, 32, 79, 83, 32, 88, 32, 77, 97, 105, 108, 32, 49, 54, 46, 48, 32, 92, 40, 51, 55, 51, 49, 46, 53, 48, 48, 46, 50, 51, 49, 92, 41, 41, 13, 10, 115, 117, 98, 106, 101, 99, 116, 58, 72, 101, 108, 108, 111, 13, 10, 109, 101, 115, 115, 97, 103, 101, 45, 105, 100, 58, 60, 56, 70, 56, 49, 57, 68, 51, 50, 45, 66, 54, 65, 67, 45, 52, 56, 57, 68, 45, 57, 55, 55, 70, 45, 52, 51, 56, 66, 66, 67, 52, 67, 65, 66, 50, 55, 64, 109, 101, 46, 99, 111, 109, 62, 13, 10, 100, 97, 116, 101, 58, 83, 97, 116, 44, 32, 50, 54, 32, 65, 117, 103, 32, 50, 48, 50, 51, 32, 49, 50, 58, 50, 53, 58, 50, 50, 32, 43, 48, 52, 48, 48, 13, 10, 116, 111, 58, 122, 107, 101, 119, 116, 101, 115, 116, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 13, 10, 100, 107, 105, 109, 45, 115, 105, 103, 110, 97, 116, 117, 114, 101, 58, 118, 61, 49, 59, 32, 97, 61, 114, 115, 97, 45, 115, 104, 97, 50, 53, 54, 59, 32, 99, 61, 114, 101, 108, 97, 120, 101, 100, 47, 114, 101, 108, 97, 120, 101, 100, 59, 32, 100, 61, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 59, 32, 115, 61, 49, 97, 49, 104, 97, 105, 59, 32, 116, 61, 49, 54, 57, 51, 48, 51, 56, 51, 51, 55, 59, 32, 98, 104, 61, 55, 120, 81, 77, 68, 117, 111, 86, 86, 85, 52, 109, 48, 87, 48, 87, 82, 86, 83, 114, 86, 88, 77, 101, 71, 83, 73, 65, 83, 115, 110, 117, 99, 75, 57, 100, 74, 115, 114, 99, 43, 118, 85, 61, 59, 32, 104, 61, 102, 114, 111, 109, 58, 67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 77, 105, 109, 101, 45, 86, 101, 114, 115, 105, 111, 110, 58, 83, 117, 98, 106, 101, 99, 116, 58, 77, 101, 115, 115, 97, 103, 101, 45, 73, 100, 58, 68, 97, 116, 101, 58, 116, 111, 59, 32, 98, 61] -msg_big_with_padding = [48, 130, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 48, 130, 1, 17, 48, 37, 2, 1, 1, 4, 32, 176, 223, 31, 133, 108, 84, 158, 102, 70, 11, 165, 175, 196, 12, 201, 130, 25, 131, 46, 125, 156, 194, 28, 23, 55, 133, 157, 164, 135, 136, 220, 78, 48, 37, 2, 1, 2, 4, 32, 190, 82, 180, 235, 222, 33, 79, 50, 152, 136, 142, 35, 116, 224, 6, 242, 156, 141, 128, 248, 10, 61, 98, 86, 248, 45, 207, 210, 90, 232, 175, 38, 48, 37, 2, 1, 3, 4, 32, 0, 194, 104, 108, 237, 246, 97, 230, 116, 198, 69, 110, 26, 87, 17, 89, 110, 199, 108, 250, 36, 21, 39, 87, 110, 102, 250, 213, 174, 131, 171, 174, 48, 37, 2, 1, 11, 4, 32, 136, 155, 87, 144, 111, 15, 152, 127, 85, 25, 154, 81, 20, 58, 51, 75, 193, 116, 234, 0, 60, 30, 29, 30, 183, 141, 72, 247, 255, 203, 100, 124, 48, 37, 2, 1, 12, 4, 32, 41, 234, 106, 78, 31, 11, 114, 137, 237, 17, 92, 71, 134, 47, 62, 78, 189, 233, 201, 214, 53, 4, 47, 189, 201, 133, 6, 121, 34, 131, 64, 142, 48, 37, 2, 1, 13, 4, 32, 91, 222, 210, 193, 62, 222, 104, 82, 36, 41, 138, 253, 70, 15, 148, 208, 156, 45, 105, 171, 241, 195, 185, 43, 217, 162, 146, 201, 222, 89, 238, 38, 48, 37, 2, 1, 14, 4, 32, 76, 123, 216, 13, 51, 227, 72, 245, 59, 193, 238, 166, 103, 49, 23, 164, 171, 188, 194, 197, 156, 187, 249, 28, 198, 95, 69, 15, 182, 56, 54, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] -msg_big_no_padding = [48, 130, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 48, 130, 1, 17, 48, 37, 2, 1, 1, 4, 32, 176, 223, 31, 133, 108, 84, 158, 102, 70, 11, 165, 175, 196, 12, 201, 130, 25, 131, 46, 125, 156, 194, 28, 23, 55, 133, 157, 164, 135, 136, 220, 78, 48, 37, 2, 1, 2, 4, 32, 190, 82, 180, 235, 222, 33, 79, 50, 152, 136, 142, 35, 116, 224, 6, 242, 156, 141, 128, 248, 10, 61, 98, 86, 248, 45, 207, 210, 90, 232, 175, 38, 48, 37, 2, 1, 3, 4, 32, 0, 194, 104, 108, 237, 246, 97, 230, 116, 198, 69, 110, 26, 87, 17, 89, 110, 199, 108, 250, 36, 21, 39, 87, 110, 102, 250, 213, 174, 131, 171, 174, 48, 37, 2, 1, 11, 4, 32, 136, 155, 87, 144, 111, 15, 152, 127, 85, 25, 154, 81, 20, 58, 51, 75, 193, 116, 234, 0, 60, 30, 29, 30, 183, 141, 72, 247, 255, 203, 100, 124, 48, 37, 2, 1, 12, 4, 32, 41, 234, 106, 78, 31, 11, 114, 137, 237, 17, 92, 71, 134, 47, 62, 78, 189, 233, 201, 214, 53, 4, 47, 189, 201, 133, 6, 121, 34, 131, 64, 142, 48, 37, 2, 1, 13, 4, 32, 91, 222, 210, 193, 62, 222, 104, 82, 36, 41, 138, 253, 70, 15, 148, 208, 156, 45, 105, 171, 241, 195, 185, 43, 217, 162, 146, 201, 222, 89, 238, 38, 48, 37, 2, 1, 14, 4, 32, 76, 123, 216, 13, 51, 227, 72, 245, 59, 193, 238, 166, 103, 49, 23, 164, 171, 188, 194, 197, 156, 187, 249, 28, 198, 95, 69, 15, 182, 56, 54, 38] -message_size = 297 - -# Results matched against ethers library -result_just_over_block = [91, 122, 146, 93, 52, 109, 133, 148, 171, 61, 156, 70, 189, 238, 153, 7, 222, 184, 94, 24, 65, 114, 192, 244, 207, 199, 87, 232, 192, 224, 171, 207] -result_multiple_of_block = [116, 90, 151, 31, 78, 22, 138, 180, 211, 189, 69, 76, 227, 200, 155, 29, 59, 123, 154, 60, 47, 153, 203, 129, 157, 251, 48, 2, 79, 11, 65, 47] -result_just_under_block = [143, 140, 76, 173, 222, 123, 102, 68, 70, 149, 207, 43, 39, 61, 34, 79, 216, 252, 213, 165, 74, 16, 110, 74, 29, 64, 138, 167, 30, 1, 9, 119] -result_big = [112, 144, 73, 182, 208, 98, 9, 238, 54, 229, 61, 145, 222, 17, 72, 62, 148, 222, 186, 55, 192, 82, 220, 35, 66, 47, 193, 200, 22, 38, 26, 186] -result_big_with_padding = [32, 85, 108, 174, 127, 112, 178, 182, 8, 43, 134, 123, 192, 211, 131, 66, 184, 240, 212, 181, 240, 180, 106, 195, 24, 117, 54, 129, 19, 10, 250, 53] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256_regression/src/main.nr deleted file mode 100644 index dbbcc07e501e..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_regression/src/main.nr +++ /dev/null @@ -1,39 +0,0 @@ -// A bunch of different test cases for sha256_var in the stdlib -fn main( - msg_just_over_block: [u8; 68], - result_just_over_block: pub [u8; 32], - msg_multiple_of_block: [u8; 448], - result_multiple_of_block: pub [u8; 32], - // We want to make sure we are testing a message with a size >= 57 but < 64 - msg_just_under_block: [u8; 60], - result_just_under_block: pub [u8; 32], - msg_big_not_block_multiple: [u8; 472], - result_big: pub [u8; 32], - // This message is only 297 elements and we want to hash only a variable amount - msg_big_with_padding: [u8; 700], - // This is the same as `msg_big_with_padding` but with no padding - msg_big_no_padding: [u8; 297], - message_size: u64, - result_big_with_padding: pub [u8; 32], -) { - let hash = std::hash::sha256_var(msg_just_over_block, msg_just_over_block.len() as u64); - assert_eq(hash, result_just_over_block); - - let hash = std::hash::sha256_var(msg_multiple_of_block, msg_multiple_of_block.len() as u64); - assert_eq(hash, result_multiple_of_block); - - let hash = std::hash::sha256_var(msg_just_under_block, msg_just_under_block.len() as u64); - assert_eq(hash, result_just_under_block); - - let hash = std::hash::sha256_var( - msg_big_not_block_multiple, - msg_big_not_block_multiple.len() as u64, - ); - assert_eq(hash, result_big); - - let hash_padding = std::hash::sha256_var(msg_big_with_padding, message_size); - assert_eq(hash_padding, result_big_with_padding); - - let hash_no_padding = std::hash::sha256_var(msg_big_no_padding, message_size); - assert_eq(hash_no_padding, result_big_with_padding); -} diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Nargo.toml deleted file mode 100644 index a80677c585d7..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "sha256_var_padding_regression" -type = "bin" -authors = [""] -compiler_version = ">=0.34.0" - -[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Prover.toml deleted file mode 100644 index 7b20e8701281..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -preimage = [29, 81, 165, 84, 243, 114, 101, 37, 242, 146, 127, 99, 69, 145, 39, 72, 213, 39, 253, 179, 218, 37, 217, 201, 172, 93, 198, 50, 249, 70, 15, 30, 162, 112, 187, 40, 140, 9, 236, 53, 32, 44, 38, 163, 113, 254, 192, 197, 44, 89, 71, 130, 169, 242, 17, 211, 214, 72, 19, 178, 186, 168, 147, 127, 99, 101, 252, 227, 8, 147, 150, 85, 97, 158, 17, 107, 218, 244, 82, 113, 247, 91, 208, 214, 60, 244, 87, 137, 173, 201, 130, 18, 66, 56, 198, 149, 207, 189, 175, 120, 123, 224, 177, 167, 251, 159, 143, 110, 68, 183, 189, 70, 126, 32, 35, 164, 44, 30, 44, 12, 65, 18, 62, 239, 242, 2, 248, 104, 2, 178, 64, 28, 126, 36, 137, 24, 14, 116, 91, 98, 90, 159, 218, 102, 45, 11, 110, 223, 245, 184, 52, 99, 59, 245, 136, 175, 3, 72, 164, 146, 145, 116, 22, 66, 24, 49, 193, 121, 3, 60, 37, 41, 97, 3, 190, 66, 195, 225, 63, 46, 3, 118, 4, 208, 15, 1, 40, 254, 235, 151, 123, 70, 180, 170, 44, 172, 90, 4, 254, 53, 239, 116, 246, 67, 56, 129, 61, 22, 169, 213, 65, 27, 216, 116, 162, 239, 214, 207, 126, 177, 20, 100, 25, 48, 143, 84, 215, 70, 197, 53, 65, 70, 86, 172, 61, 62, 9, 212, 167, 169, 133, 41, 126, 213, 196, 33, 192, 238, 0, 63, 246, 215, 58, 128, 110, 101, 92, 3, 170, 214, 130, 149, 52, 81, 125, 118, 233, 3, 118, 193, 104, 207, 120, 115, 77, 253, 191, 122, 0, 107, 164, 207, 113, 81, 169, 36, 201, 228, 74, 134, 131, 218, 178, 35, 30, 216, 101, 2, 103, 174, 87, 95, 50, 50, 215, 157, 5, 210, 188, 54, 211, 78, 45, 199, 96, 121, 241, 241, 176, 226, 194, 134, 130, 89, 217, 210, 186, 32, 140, 39, 91, 103, 212, 26, 87, 32, 72, 144, 228, 230, 117, 99, 188, 50, 15, 69, 79, 179, 50, 12, 106, 86, 218, 101, 73, 142, 243, 29, 250, 122, 228, 233, 29, 255, 22, 121, 114, 125, 103, 41, 250, 241, 179, 126, 158, 198, 116, 209, 65, 94, 98, 228, 175, 169, 96, 3, 9, 233, 133, 214, 55, 161, 164, 103, 80, 85, 24, 186, 64, 167, 92, 131, 53, 101, 202, 47, 25, 104, 118, 155, 14, 12, 12, 25, 116, 45, 221, 249, 28, 246, 212, 200, 157, 167, 169, 56, 197, 181, 4, 245, 146, 1, 140, 234, 191, 212, 228, 125, 87, 81, 86, 119, 30, 63, 129, 143, 32, 96] -result = [205, 74, 73, 134, 202, 93, 199, 152, 171, 244, 133, 193, 132, 40, 42, 9, 248, 11, 99, 200, 135, 58, 220, 227, 45, 253, 183, 241, 69, 69, 80, 219] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/src/main.nr deleted file mode 100644 index 13f87a0efc51..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/src/main.nr +++ /dev/null @@ -1,29 +0,0 @@ -// Test to check sha256_var produces same results irrespective of number of padding bytes after message.length -// Ref: https://github.com/noir-lang/noir/issues/6163, https://gist.github.com/jp4g/d5953faae9eadb2909357474f7901e58 -fn main(preimage: [u8; 448], result: [u8; 32]) { - // Construct arrays of different lengths - let mut preimage_511 = [0; 511]; - let mut preimage_512 = [0; 512]; // Next block - let mut preimage_575 = [0; 575]; - let mut preimage_576 = [0; 576]; // Next block - for i in 0..preimage.len() { - preimage_511[i] = preimage[i]; - preimage_512[i] = preimage[i]; - preimage_575[i] = preimage[i]; - preimage_576[i] = preimage[i]; - } - let fixed_length_hash = std::hash::sha256::digest(preimage); - let var_full_length_hash = std::hash::sha256::sha256_var(preimage, preimage.len() as u64); - let var_length_hash_511 = std::hash::sha256::sha256_var(preimage_511, preimage.len() as u64); - let var_length_hash_512 = std::hash::sha256::sha256_var(preimage_512, preimage.len() as u64); - let var_length_hash_575 = std::hash::sha256::sha256_var(preimage_575, preimage.len() as u64); - let var_length_hash_576 = std::hash::sha256::sha256_var(preimage_576, preimage.len() as u64); - - // All of the above should have produced the same hash (result) - assert(fixed_length_hash == result); - assert(var_full_length_hash == result); - assert(var_length_hash_511 == result); - assert(var_length_hash_512 == result); - assert(var_length_hash_575 == result); - assert(var_length_hash_576 == result); -} diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Nargo.toml deleted file mode 100644 index 3e141ee5d5f6..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "sha256_var_size_regression" -type = "bin" -authors = [""] -compiler_version = ">=0.33.0" - -[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Prover.toml deleted file mode 100644 index df632a428584..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Prover.toml +++ /dev/null @@ -1,3 +0,0 @@ -enable = [true, false] -foo = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] -toggle = false diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/src/main.nr deleted file mode 100644 index 4278cdda8a30..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/src/main.nr +++ /dev/null @@ -1,17 +0,0 @@ -global NUM_HASHES: u32 = 2; - -fn main(foo: [u8; 95], toggle: bool, enable: [bool; NUM_HASHES]) { - let mut result = [[0; 32]; NUM_HASHES]; - let mut const_result = [[0; 32]; NUM_HASHES]; - let size: Field = 93 + toggle as Field * 2; - for i in 0..NUM_HASHES { - if enable[i] { - result[i] = std::sha256::sha256_var(foo, size as u64); - const_result[i] = std::sha256::sha256_var(foo, 93); - } - } - - for i in 0..NUM_HASHES { - assert_eq(result[i], const_result[i]); - } -} diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Nargo.toml deleted file mode 100644 index e8f3e6bbe643..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "sha256_var_witness_const_regression" -type = "bin" -authors = [""] -compiler_version = ">=0.33.0" - -[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Prover.toml deleted file mode 100644 index 7b91051c1a0c..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -input = [0, 0] -toggle = false \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/src/main.nr deleted file mode 100644 index 97c4435d41d2..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/src/main.nr +++ /dev/null @@ -1,9 +0,0 @@ -fn main(input: [u8; 2], toggle: bool) { - let size: Field = 1 + toggle as Field; - assert(!toggle); - - let variable_sha = std::sha256::sha256_var(input, size as u64); - let constant_sha = std::sha256::sha256_var(input, 1); - - assert_eq(variable_sha, constant_sha); -} diff --git a/noir/noir-repo/test_programs/execution_success/sha2_byte/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha2_byte/Prover.toml deleted file mode 100644 index 2f82f14a58d4..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha2_byte/Prover.toml +++ /dev/null @@ -1,5 +0,0 @@ -# Values obtainable from https://emn178.github.io/online-tools/sha256.html and https://emn178.github.io/online-tools/sha512.html -x = 0xbd -result256 = [0x68, 0x32, 0x57, 0x20, 0xaa, 0xbd, 0x7c, 0x82, 0xf3, 0x0f, 0x55, 0x4b, 0x31, 0x3d, 0x05, 0x70, 0xc9, 0x5a, 0xcc, 0xbb, 0x7d, 0xc4, 0xb5, 0xaa, 0xe1, 0x12, 0x04, 0xc0, 0x8f, 0xfe, 0x73, 0x2b] -result512 = [0x29, 0x6e, 0x22, 0x67, 0xd7, 0x4c, 0x27, 0x8d, 0xaa, 0xaa, 0x94, 0x0d, 0x17, 0xb0, 0xcf, 0xb7, 0x4a, 0x50, 0x83, 0xf8, 0xe0, 0x69, 0x72, 0x6d, 0x8c, 0x84, 0x1c, 0xbe, 0x59, 0x6e, 0x04, 0x31, 0xcb, 0x77, 0x41, 0xa5, 0xb5, 0x0f, 0x71, 0x66, 0x6c, 0xfd, 0x54, 0xba, 0xcb, 0x7b, 0x00, 0xae, 0xa8, 0x91, 0x49, 0x9c, 0xf4, 0xef, 0x6a, 0x03, 0xc8, 0xa8, 0x3f, 0xe3, 0x7c, 0x3f, 0x7b, 0xaf] - diff --git a/noir/noir-repo/test_programs/execution_success/sha2_byte/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha2_byte/src/main.nr deleted file mode 100644 index a1663642c69b..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha2_byte/src/main.nr +++ /dev/null @@ -1,8 +0,0 @@ -// Test Noir implementations of SHA256 and SHA512 on a one-byte message. -fn main(x: Field, result256: [u8; 32], result512: [u8; 64]) { - let digest256 = std::hash::sha256([x as u8]); - assert(digest256 == result256); - - let digest512 = std::hash::sha512::digest([x as u8]); - assert(digest512 == result512); -} diff --git a/noir/noir-repo/test_programs/execution_success/keccak256/Nargo.toml b/noir/noir-repo/test_programs/execution_success/shift_right_overflow/Nargo.toml similarity index 63% rename from noir/noir-repo/test_programs/execution_success/keccak256/Nargo.toml rename to noir/noir-repo/test_programs/execution_success/shift_right_overflow/Nargo.toml index 7e48c3b342cb..a883299fbc26 100644 --- a/noir/noir-repo/test_programs/execution_success/keccak256/Nargo.toml +++ b/noir/noir-repo/test_programs/execution_success/shift_right_overflow/Nargo.toml @@ -1,6 +1,5 @@ [package] -name = "keccak256" +name = "shift_right_overflow" type = "bin" authors = [""] - [dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/shift_right_overflow/Prover.toml b/noir/noir-repo/test_programs/execution_success/shift_right_overflow/Prover.toml new file mode 100644 index 000000000000..57cb8b2eac81 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/shift_right_overflow/Prover.toml @@ -0,0 +1 @@ +x = 9 diff --git a/noir/noir-repo/test_programs/execution_success/shift_right_overflow/src/main.nr b/noir/noir-repo/test_programs/execution_success/shift_right_overflow/src/main.nr new file mode 100644 index 000000000000..c5d23ab5cd97 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/shift_right_overflow/src/main.nr @@ -0,0 +1,5 @@ +fn main(x: u8) { + // This would previously overflow in ACIR. Now it returns zero. + let value = 1 >> x; + assert_eq(value, 0); +} diff --git a/noir/noir-repo/test_programs/execution_success/slice_dynamic_index/src/main.nr b/noir/noir-repo/test_programs/execution_success/slice_dynamic_index/src/main.nr index 2ab633eb98c0..c6fd64d58059 100644 --- a/noir/noir-repo/test_programs/execution_success/slice_dynamic_index/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/slice_dynamic_index/src/main.nr @@ -305,4 +305,3 @@ fn dynamic_slice_merge_push_then_pop(mut slice: [Field], x: Field, y: Field) { let (_, elem) = slice.pop_back(); assert(elem == y); } - 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 index 67901f10f299..ba20fdd61f1f 100644 --- 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 @@ -805,4 +805,3 @@ fn main() { // 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/struct_fields_ordering/src/main.nr b/noir/noir-repo/test_programs/execution_success/struct_fields_ordering/src/main.nr index 1a2e2d462e2d..fe26ac31424a 100644 --- a/noir/noir-repo/test_programs/execution_success/struct_fields_ordering/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/struct_fields_ordering/src/main.nr @@ -9,4 +9,3 @@ fn main(y: pub myStruct) { assert(y.foo == 5); assert(y.bar == 7); } - diff --git a/noir/noir-repo/test_programs/execution_success/submodules/src/main.nr b/noir/noir-repo/test_programs/execution_success/submodules/src/main.nr index f937af746274..b58e64b4990c 100644 --- a/noir/noir-repo/test_programs/execution_success/submodules/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/submodules/src/main.nr @@ -12,4 +12,3 @@ mod my_submodule { pub fn my_helper() {} } - diff --git a/noir/noir-repo/test_programs/execution_success/u128/Nargo.toml b/noir/noir-repo/test_programs/execution_success/u128/Nargo.toml deleted file mode 100644 index c1dcd84db046..000000000000 --- a/noir/noir-repo/test_programs/execution_success/u128/Nargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "u128" -type = "bin" -authors = [""] - -[dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/u128/Prover.toml b/noir/noir-repo/test_programs/execution_success/u128/Prover.toml deleted file mode 100644 index 961db9825a70..000000000000 --- a/noir/noir-repo/test_programs/execution_success/u128/Prover.toml +++ /dev/null @@ -1,7 +0,0 @@ -x = "3" -y = "4" -z = "7" -hexa ="0x1f03a" -[big_int] -lo = 1 -hi = 2 \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/u128/src/main.nr b/noir/noir-repo/test_programs/execution_success/u128/src/main.nr deleted file mode 100644 index 4ea934378143..000000000000 --- a/noir/noir-repo/test_programs/execution_success/u128/src/main.nr +++ /dev/null @@ -1,42 +0,0 @@ -fn main(mut x: u32, y: u32, z: u32, big_int: U128, hexa: str<7>) { - let a = U128::from_u64s_le(x as u64, x as u64); - let b = U128::from_u64s_le(y as u64, x as u64); - let c = a + b; - assert(c.lo == z as Field); - assert(c.hi == 2 * x as Field); - assert(U128::from_hex(hexa).lo == 0x1f03a); - let t1 = U128::from_hex("0x9d9c7a87771f03a23783f9d9c7a8777"); - let t2 = U128::from_hex("0x45a26c708BFCF39041"); - let t = t1 + t2; - assert(t.lo == 0xc5e4b029996e17b8); - assert(t.hi == 0x09d9c7a87771f07f); - let t3 = U128::from_le_bytes(t.to_le_bytes()); - assert(t == t3); - - let t4 = t - t2; - assert(t4 == t1); - - let t5 = U128::from_u64s_le(0, 1); - let t6 = U128::from_u64s_le(1, 0); - assert((t5 - t6).hi == 0); - - assert( - (U128::from_hex("0x71f03a23783f9d9c7a8777") * U128::from_hex("0x8BFCF39041")).hi - == U128::from_hex("0x3e4e0471b873470e247c824e61445537").hi, - ); - let q = U128::from_hex("0x3e4e0471b873470e247c824e61445537") / U128::from_hex("0x8BFCF39041"); - assert(q == U128::from_hex("0x71f03a23783f9d9c7a8777")); - - assert(big_int.hi == 2); - - let mut small_int = U128::from_integer(x); - assert(small_int.lo == x as Field); - assert(x == small_int.to_integer()); - let shift = small_int << (x as u8); - assert(shift == U128::from_integer(x << (x as u8))); - assert(shift >> (x as u8) == small_int); - assert(shift >> 127 == U128::from_integer(0)); - assert(shift << 127 == U128::from_integer(0)); - assert(U128::from_integer(3).to_integer() == 3); -} - diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256/Nargo.toml b/noir/noir-repo/test_programs/execution_success/u128_type/Nargo.toml similarity index 52% rename from noir/noir-repo/test_programs/benchmarks/bench_sha256/Nargo.toml rename to noir/noir-repo/test_programs/execution_success/u128_type/Nargo.toml index 488b94ca8586..f86639a9e745 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256/Nargo.toml +++ b/noir/noir-repo/test_programs/execution_success/u128_type/Nargo.toml @@ -1,7 +1,7 @@ [package] -name = "bench_sha256" -version = "0.1.0" +name = "u128_type" type = "bin" authors = [""] +compiler_version = ">=0.23.0" [dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/u128_type/Prover.toml b/noir/noir-repo/test_programs/execution_success/u128_type/Prover.toml new file mode 100644 index 000000000000..75da077f2e6d --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/u128_type/Prover.toml @@ -0,0 +1,3 @@ +x = 12345 +y = 2345678 +z = 2 diff --git a/noir/noir-repo/test_programs/execution_success/u128_type/src/main.nr b/noir/noir-repo/test_programs/execution_success/u128_type/src/main.nr new file mode 100644 index 000000000000..acfa3d28eec2 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/u128_type/src/main.nr @@ -0,0 +1,22 @@ +fn main(x: u128, y: u128, z: u8) { + let const_x = 12345; + let const_y = 2345678; + let const_z = 2; + + assert_eq(x + y, const_x + const_y); + assert_eq(y - x, const_y - const_x); + assert_eq(x * y, const_x * const_y); + assert_eq(y / x, const_y / const_x); + assert_eq(y % x, const_y % const_x); + assert_eq(!x, !const_x); + assert_eq(x ^ y, const_x ^ const_y); + assert_eq(x & y, const_x & const_y); + assert_eq(x | y, const_x | const_y); + assert_eq(x >> z, const_x >> const_z); + assert_eq(x << z, const_x << const_z); + assert_eq(x < y, const_x < const_y); + assert_eq(x <= y, const_x <= const_y); + assert_eq(x != y, const_x != const_y); + assert_eq(y > x, const_y > const_x); + assert_eq(y >= x, const_y >= const_x); +} diff --git a/noir/noir-repo/test_programs/execution_success/wrapping_operations/src/main.nr b/noir/noir-repo/test_programs/execution_success/wrapping_operations/src/main.nr index d8345884c828..0c4be84a7659 100644 --- a/noir/noir-repo/test_programs/execution_success/wrapping_operations/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/wrapping_operations/src/main.nr @@ -3,4 +3,3 @@ fn main(x: u8, y: u8) { assert(std::wrapping_add(y, 1) == x); assert(std::wrapping_mul(y, y) == 1); } - diff --git a/noir/noir-repo/test_programs/memory_report.sh b/noir/noir-repo/test_programs/memory_report.sh index eb83004affdb..ff64449fcf5e 100755 --- a/noir/noir-repo/test_programs/memory_report.sh +++ b/noir/noir-repo/test_programs/memory_report.sh @@ -8,7 +8,7 @@ PARSE_MEMORY=$(realpath "$(dirname "$0")/parse_memory.sh") # Tests to be profiled for memory report -tests_to_profile=("keccak256" "workspace" "regression_4709" "ram_blowup_regression" "global_var_regression_entry_points") +tests_to_profile=("workspace" "regression_4709" "ram_blowup_regression" "global_var_regression_entry_points") current_dir=$(pwd) base_path="$current_dir/execution_success" diff --git a/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr index fa2c55e69347..cfd985a55eaa 100644 --- a/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr @@ -269,4 +269,3 @@ fn test_vec_any_not_default() { vec.extend_from_array([2, 4]); assert(!vec.any(|v| v == default_value)); } - diff --git a/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr index 019f757591ee..de6fd15f068e 100644 --- a/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr @@ -20,4 +20,3 @@ unconstrained fn test_overflow_mul() { let b: u8 = 2; assert_eq(a * b, 0); } - diff --git a/noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/src/main.nr index 446692c485bb..3b1f2963b9f9 100644 --- a/noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/src/main.nr @@ -117,13 +117,16 @@ fn test_ecdsa_secp256r1() { /// Test that sha256_compression is implemented. #[test] -fn test_sha256() { +fn test_sha256_compression() { + let input: [u32; 16] = [0xbd; 16]; + let state: [u32; 8] = [0; 8]; + let hash = comptime { - let input: [u8; 1] = [0xbd]; - std::hash::sha256(input) + let input: [u32; 16] = [0xbd; 16]; + let state: [u32; 8] = [0; 8]; + std::hash::sha256_compression(input, state) }; - assert_eq(hash[0], 0x68); - assert_eq(hash[31], 0x2b); + assert_eq(hash, std::hash::sha256_compression(input, state)); } /// Test that `embedded_curve_add` and `multi_scalar_mul` are implemented. diff --git a/noir/noir-repo/test_programs/noir_test_success/global_eval/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/global_eval/src/main.nr index 87a2d50a9162..da962bf52032 100644 --- a/noir/noir-repo/test_programs/noir_test_success/global_eval/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/global_eval/src/main.nr @@ -1,20 +1,9 @@ -use std::uint128::U128; - // These definitions require `to_be_bits` and `to_le_bits` to be supported at comptime. global BITS_BE_13: [u1; 4] = (13 as Field).to_be_bits(); global BITS_LE_13: [u1; 4] = (13 as Field).to_le_bits(); -// Examples from #6691 which use the above behind the scenes. -global POW64_A: Field = 2.pow_32(64); -global POW64_B: Field = (U128::one() << 64).to_integer(); - #[test] fn test_be_and_le_bits() { assert_eq(BITS_BE_13, [1, 1, 0, 1]); assert_eq(BITS_LE_13, [1, 0, 1, 1]); } - -#[test] -fn test_pow64() { - assert_eq(POW64_A, POW64_B); -} diff --git a/noir/noir-repo/test_programs/noir_test_success/mock_oracle/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/mock_oracle/src/main.nr index bdffd62bb809..3bde9a0b3ef8 100644 --- a/noir/noir-repo/test_programs/noir_test_success/mock_oracle/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/mock_oracle/src/main.nr @@ -151,4 +151,3 @@ fn test_mock_struct_field() { assert_eq(20, struct_field(point, another_array)); } } - diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Nargo.toml b/noir/noir-repo/test_programs/noir_test_success/u128_type/Nargo.toml similarity index 52% rename from noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Nargo.toml rename to noir/noir-repo/test_programs/noir_test_success/u128_type/Nargo.toml index ae66d7ed5a60..f86639a9e745 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Nargo.toml +++ b/noir/noir-repo/test_programs/noir_test_success/u128_type/Nargo.toml @@ -1,7 +1,7 @@ [package] -name = "bench_sha256_long" -version = "0.1.0" +name = "u128_type" type = "bin" authors = [""] +compiler_version = ">=0.23.0" [dependencies] diff --git a/noir/noir-repo/test_programs/noir_test_success/u128_type/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/u128_type/src/main.nr new file mode 100644 index 000000000000..c0bca7b3d7aa --- /dev/null +++ b/noir/noir-repo/test_programs/noir_test_success/u128_type/src/main.nr @@ -0,0 +1,49 @@ +#[test] +fn test_u128_sub() { + let a: u128 = 671967750576550571863734675757137222; + let b = 671967750576550571863734675757137221; + let c = a - 1; + assert(c == b); +} + +#[test] +fn test_u128_add() { + let a: u128 = 671967750576550571863734675757137222; + let b: u128 = 671967750576550571863734675757137221; + let c: u128 = b + 1; + assert(c == a); +} + +#[test] +fn test_u128_mul() { + let a: u128 = 671967750576550571863734675757137222; + let b: u128 = 335983875288275285931867337878568611; + let c: u128 = b * 2; + assert(c == a); +} + +#[test] +fn test_u128_div() { + let a: u128 = 671967750576550571863734675757137222; + let b: u128 = 335983875288275285931867337878568611; + let c = a / 2; + assert(c == b); +} + +#[test] +fn test_u128_not() { + let a: u128 = 1; + let b = !a; + assert_eq(b, 340282366920938463463374607431768211454); + + let a: u128 = 340282366920938463463374607431768211454; + let b = !a; + assert_eq(b, 1); +} + +#[test] +fn test_max_u128() { + let a: u128 = 340282366920938463463374607431768211455; + let c = 170141183460469231731687303715884105727; + assert_eq(a / 2, c); +} diff --git a/noir/noir-repo/tooling/acvm_cli/src/cli/execute_cmd.rs b/noir/noir-repo/tooling/acvm_cli/src/cli/execute_cmd.rs index 6f6fba7cd1e7..c1d4533230e7 100644 --- a/noir/noir-repo/tooling/acvm_cli/src/cli/execute_cmd.rs +++ b/noir/noir-repo/tooling/acvm_cli/src/cli/execute_cmd.rs @@ -1,14 +1,14 @@ use std::io::{self, Write}; use std::path::PathBuf; +use acir::FieldElement; use acir::circuit::Program; use acir::native_types::{WitnessMap, WitnessStack}; -use acir::FieldElement; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; use nargo::PrintOutput; -use nargo::{foreign_calls::DefaultForeignCallBuilder, ops::execute_program}; +use nargo::foreign_calls::DefaultForeignCallBuilder; use noir_artifact_cli::errors::CliError; use noir_artifact_cli::fs::artifact::read_bytecode_from_file; use noir_artifact_cli::fs::witness::save_witness_to_dir; @@ -56,7 +56,7 @@ fn run_command(args: ExecuteCommand) -> Result { )?; if args.output_witness.is_some() { save_witness_to_dir( - output_witness, + &output_witness, &args.output_witness.unwrap(), &args.working_directory, )?; @@ -80,7 +80,8 @@ pub(crate) fn execute_program_from_witness( ) -> Result, CliError> { let program: Program = Program::deserialize_program(bytecode).map_err(CliError::CircuitDeserializationError)?; - execute_program( + + nargo::ops::execute_program( &program, inputs_map, &Bn254BlackBoxSolver(pedantic_solving), diff --git a/noir/noir-repo/tooling/acvm_cli/src/fs/witness.rs b/noir/noir-repo/tooling/acvm_cli/src/fs/witness.rs index 5c2d2a9ad058..d1b88758cc74 100644 --- a/noir/noir-repo/tooling/acvm_cli/src/fs/witness.rs +++ b/noir/noir-repo/tooling/acvm_cli/src/fs/witness.rs @@ -1,8 +1,8 @@ //! These witness functions are only used by the ACVM CLI and we'll most likely deprecate them. use std::{collections::BTreeMap, path::Path}; -use acir::{native_types::Witness, FieldElement}; -use acvm::acir::{native_types::WitnessMap, AcirField}; +use acir::{FieldElement, native_types::Witness}; +use acvm::acir::{AcirField, native_types::WitnessMap}; use noir_artifact_cli::errors::{CliError, FilesystemError}; diff --git a/noir/noir-repo/tooling/acvm_cli/src/main.rs b/noir/noir-repo/tooling/acvm_cli/src/main.rs index a30360a947ce..8f9edc48f32c 100644 --- a/noir/noir-repo/tooling/acvm_cli/src/main.rs +++ b/noir/noir-repo/tooling/acvm_cli/src/main.rs @@ -8,7 +8,7 @@ mod cli; use std::env; use tracing_appender::rolling; -use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter}; +use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan}; mod fs; diff --git a/noir/noir-repo/tooling/artifact_cli/src/bin/execute.rs b/noir/noir-repo/tooling/artifact_cli/src/bin/execute.rs index b66177a3d572..8bca901e4852 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/bin/execute.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/bin/execute.rs @@ -1,10 +1,10 @@ #![forbid(unsafe_code)] #![warn(unreachable_pub)] -use clap::{command, Parser, Subcommand}; +use clap::{Parser, Subcommand, command}; use color_eyre::eyre; use const_format::formatcp; -use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter}; +use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan}; use noir_artifact_cli::commands::execute_cmd; diff --git a/noir/noir-repo/tooling/artifact_cli/src/commands/execute_cmd.rs b/noir/noir-repo/tooling/artifact_cli/src/commands/execute_cmd.rs index 9a117329c151..df5d469e75b4 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/commands/execute_cmd.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/commands/execute_cmd.rs @@ -1,18 +1,15 @@ -use std::{collections::BTreeMap, path::PathBuf}; +use std::path::PathBuf; -use acir::{circuit::Program, native_types::WitnessStack, FieldElement}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; -use color_eyre::eyre::{self, bail}; use crate::{ - errors::CliError, - fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}, Artifact, + errors::CliError, + execution::{self, ExecutionResults}, }; -use nargo::{foreign_calls::DefaultForeignCallBuilder, NargoError, PrintOutput}; -use noirc_abi::{input_parser::InputValue, Abi}; -use noirc_artifacts::debug::DebugArtifact; +use nargo::{PrintOutput, foreign_calls::DefaultForeignCallBuilder}; +use noirc_driver::CompiledProgram; use super::parse_and_normalize_path; @@ -21,106 +18,84 @@ use super::parse_and_normalize_path; pub struct ExecuteCommand { /// Path to the JSON build artifact (either a program or a contract). #[clap(long, short, value_parser = parse_and_normalize_path)] - artifact_path: PathBuf, + pub artifact_path: PathBuf, /// Path to the Prover.toml file which contains the inputs and the /// optional return value in ABI format. #[clap(long, short, value_parser = parse_and_normalize_path)] - prover_file: PathBuf, + pub prover_file: PathBuf, /// Path to the directory where the output witness should be saved. /// If empty then the results are discarded. #[clap(long, short, value_parser = parse_and_normalize_path)] - output_dir: Option, + pub output_dir: Option, /// Write the execution witness to named file /// /// Defaults to the name of the circuit being executed. #[clap(long, short)] - witness_name: Option, + pub witness_name: Option, /// Name of the function to execute, if the artifact is a contract. #[clap(long)] - contract_fn: Option, + pub contract_fn: Option, /// JSON RPC url to solve oracle calls. #[clap(long)] - oracle_resolver: Option, + pub oracle_resolver: Option, /// Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. #[clap(long, default_value_t = false)] - pedantic_solving: bool, + pub pedantic_solving: bool, } -pub fn run(args: ExecuteCommand) -> eyre::Result<()> { +pub fn run(args: ExecuteCommand) -> Result<(), CliError> { let artifact = Artifact::read_from_file(&args.artifact_path)?; + let artifact_name = args.artifact_path.file_stem().and_then(|s| s.to_str()).unwrap_or_default(); - let circuit = match artifact { - Artifact::Program(program) => Circuit { - name: None, - abi: program.abi, - bytecode: program.bytecode, - debug_symbols: program.debug_symbols, - file_map: program.file_map, - }, + let (circuit, circuit_name): (CompiledProgram, String) = match artifact { + Artifact::Program(program) => (program.into(), artifact_name.to_string()), Artifact::Contract(contract) => { - let names = - contract.functions.iter().map(|f| f.name.clone()).collect::>().join(","); + let names = || contract.functions.iter().map(|f| f.name.clone()).collect::>(); let Some(ref name) = args.contract_fn else { - bail!("--contract-fn missing; options: [{names}]"); + return Err(CliError::MissingContractFn { names: names() }); }; - let Some(function) = contract.functions.into_iter().find(|f| f.name == *name) else { - bail!("unknown --contract-fn '{name}'; options: [{names}]"); + let Some(program) = contract.function_as_compiled_program(name) else { + return Err(CliError::UnknownContractFn { name: name.clone(), names: names() }); }; - Circuit { - name: Some(name.clone()), - abi: function.abi, - bytecode: function.bytecode, - debug_symbols: function.debug_symbols, - file_map: contract.file_map, - } + (program, format!("{artifact_name}::{name}")) } }; match execute(&circuit, &args) { - Ok(solved) => { - save_witness(circuit, args, solved)?; - } - Err(CliError::CircuitExecutionError(err)) => { - show_diagnostic(circuit, err); + Ok(results) => { + execution::save_and_check_witness( + &circuit, + results, + &circuit_name, + args.output_dir.as_deref(), + args.witness_name.as_deref(), + )?; } Err(e) => { - bail!("failed to execute the circuit: {e}"); + if let CliError::CircuitExecutionError(ref err) = e { + execution::show_diagnostic(&circuit, err); + } + // Still returning the error to facilitate command forwarding, to indicate that the command failed. + return Err(e); } } Ok(()) } -/// Parameters necessary to execute a circuit, display execution failures, etc. -struct Circuit { - name: Option, - abi: Abi, - bytecode: Program, - debug_symbols: noirc_errors::debug_info::ProgramDebugInfo, - file_map: BTreeMap, -} - -struct SolvedWitnesses { - expected_return: Option, - actual_return: Option, - witness_stack: WitnessStack, -} - /// Execute a circuit and return the output witnesses. -fn execute(circuit: &Circuit, args: &ExecuteCommand) -> Result { - let (input_map, expected_return) = read_inputs_from_file(&args.prover_file, &circuit.abi)?; - - let initial_witness = circuit.abi.encode(&input_map, None)?; - +fn execute(circuit: &CompiledProgram, args: &ExecuteCommand) -> Result { // TODO: Build a custom foreign call executor that reads from the Oracle transcript, - // and use it as a base for the default executor; see `DefaultForeignCallBuilder::build_with_base` + // and use it as a base for the default executor. Using it as the innermost rather + // than top layer so that any extra `print` added for debugging is handled by the + // default, rather than trying to match it to the transcript. let mut foreign_call_executor = DefaultForeignCallBuilder { output: PrintOutput::Stdout, enable_mocks: false, @@ -130,80 +105,7 @@ fn execute(circuit: &Circuit, args: &ExecuteCommand) -> Result) { - if let Some(diagnostic) = nargo::errors::try_to_diagnose_runtime_error( - &err, - &circuit.abi, - &circuit.debug_symbols.debug_infos, - ) { - let debug_artifact = DebugArtifact { - debug_symbols: circuit.debug_symbols.debug_infos, - file_map: circuit.file_map, - }; - diagnostic.report(&debug_artifact, false); - } -} - -/// Print information about the witness and compare to expectations, -/// returning errors if something isn't right. -fn save_witness( - circuit: Circuit, - args: ExecuteCommand, - solved: SolvedWitnesses, -) -> eyre::Result<()> { - let artifact = args.artifact_path.file_stem().and_then(|s| s.to_str()).unwrap_or_default(); - let name = circuit - .name - .as_ref() - .map(|name| format!("{artifact}.{name}")) - .unwrap_or_else(|| artifact.to_string()); - - println!("[{}] Circuit witness successfully solved", name); - - if let Some(ref witness_dir) = args.output_dir { - let witness_path = save_witness_to_dir( - solved.witness_stack, - &args.witness_name.unwrap_or_else(|| name.clone()), - witness_dir, - )?; - println!("[{}] Witness saved to {}", name, witness_path.display()); - } - - // Check that the circuit returned a non-empty result if the ABI expects a return value. - if let Some(ref expected) = circuit.abi.return_type { - if solved.actual_return.is_none() { - bail!("Missing return witness; expected a value of type {expected:?}"); - } - } - - // Check that if the prover file contained a `return` entry then that's what we got. - if let Some(expected) = solved.expected_return { - match solved.actual_return { - None => { - bail!("Missing return witness;\nexpected:\n{expected:?}"); - } - Some(actual) if actual != expected => { - bail!("Unexpected return witness;\nexpected:\n{expected:?}\ngot:\n{actual:?}"); - } - _ => {} - } - } - - Ok(()) + execution::execute(circuit, &blackbox_solver, &mut foreign_call_executor, &args.prover_file) } diff --git a/noir/noir-repo/tooling/artifact_cli/src/commands/mod.rs b/noir/noir-repo/tooling/artifact_cli/src/commands/mod.rs index 78f3b19292f2..9049b3695b7a 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/commands/mod.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/commands/mod.rs @@ -1,3 +1,4 @@ +//! This module is for commands that we might want to invoke from `nargo` as-is. use std::path::PathBuf; use color_eyre::eyre; diff --git a/noir/noir-repo/tooling/artifact_cli/src/errors.rs b/noir/noir-repo/tooling/artifact_cli/src/errors.rs index 5f302f78695a..6c87273cb370 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/errors.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/errors.rs @@ -1,6 +1,10 @@ use acir::FieldElement; use nargo::NargoError; -use noirc_abi::errors::{AbiError, InputParserError}; +use noirc_abi::{ + AbiReturnType, + errors::{AbiError, InputParserError}, + input_parser::InputValue, +}; use std::path::PathBuf; use thiserror::Error; @@ -61,4 +65,16 @@ pub enum CliError { #[error("Failed to serialize output witness: {0}")] OutputWitnessSerializationFailed(#[from] toml::ser::Error), + + #[error("Unexpected return value: expected {expected:?}; got {actual:?}")] + UnexpectedReturn { expected: InputValue, actual: Option }, + + #[error("Missing return witnesses; expected {expected:?}")] + MissingReturn { expected: AbiReturnType }, + + #[error("Missing contract function name; options: {names:?}")] + MissingContractFn { names: Vec }, + + #[error("Unknown contract function '{name}'; options: {names:?}")] + UnknownContractFn { name: String, names: Vec }, } diff --git a/noir/noir-repo/tooling/artifact_cli/src/execution.rs b/noir/noir-repo/tooling/artifact_cli/src/execution.rs new file mode 100644 index 000000000000..2e19ab551613 --- /dev/null +++ b/noir/noir-repo/tooling/artifact_cli/src/execution.rs @@ -0,0 +1,135 @@ +use std::path::Path; + +use acir::{FieldElement, native_types::WitnessStack}; +use acvm::BlackBoxFunctionSolver; +use nargo::{NargoError, foreign_calls::ForeignCallExecutor}; +use noirc_abi::input_parser::InputValue; +use noirc_artifacts::debug::DebugArtifact; +use noirc_driver::CompiledProgram; + +use crate::{ + errors::CliError, + fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}, +}; + +/// Results of a circuit execution. +#[derive(Clone, Debug)] +pub struct ExecutionResults { + pub witness_stack: WitnessStack, + pub return_values: ReturnValues, +} + +/// The decoded `return` witnesses. +#[derive(Clone, Debug)] +pub struct ReturnValues { + /// The `return` value from the `Prover.toml` file, if present. + pub expected_return: Option, + /// The `return` value from the circuit execution. + pub actual_return: Option, +} + +/// Execute a circuit and return the output witnesses. +pub fn execute( + circuit: &CompiledProgram, + blackbox_solver: &B, + foreign_call_executor: &mut E, + prover_file: &Path, +) -> Result +where + B: BlackBoxFunctionSolver, + E: ForeignCallExecutor, +{ + let (input_map, expected_return) = read_inputs_from_file(prover_file, &circuit.abi)?; + + let initial_witness = circuit.abi.encode(&input_map, None)?; + + let witness_stack = nargo::ops::execute_program( + &circuit.program, + initial_witness, + blackbox_solver, + foreign_call_executor, + )?; + + let main_witness = + &witness_stack.peek().expect("Should have at least one witness on the stack").witness; + + let (_, actual_return) = circuit.abi.decode(main_witness)?; + + Ok(ExecutionResults { + witness_stack, + return_values: ReturnValues { actual_return, expected_return }, + }) +} + +/// Print an error stack trace, if possible. +pub fn show_diagnostic(circuit: &CompiledProgram, err: &NargoError) { + if let Some(diagnostic) = + nargo::errors::try_to_diagnose_runtime_error(err, &circuit.abi, &circuit.debug) + { + let debug_artifact = DebugArtifact { + debug_symbols: circuit.debug.clone(), + file_map: circuit.file_map.clone(), + }; + + diagnostic.report(&debug_artifact, false); + } +} + +/// Print some information and save the witness if an output directory is specified, +/// then checks if the expected return values were the ones we expected. +pub fn save_and_check_witness( + circuit: &CompiledProgram, + results: ExecutionResults, + circuit_name: &str, + witness_dir: Option<&Path>, + witness_name: Option<&str>, +) -> Result<(), CliError> { + println!("[{}] Circuit witness successfully solved", circuit_name); + // Save first, so that we can potentially look at the output if the expectations fail. + if let Some(witness_dir) = witness_dir { + save_witness(&results.witness_stack, circuit_name, witness_dir, witness_name)?; + } + check_witness(circuit, results.return_values) +} + +/// Save the witness stack to a file. +pub fn save_witness( + witness_stack: &WitnessStack, + circuit_name: &str, + witness_dir: &Path, + witness_name: Option<&str>, +) -> Result<(), CliError> { + let witness_name = witness_name.unwrap_or(circuit_name); + let witness_path = save_witness_to_dir(witness_stack, witness_name, witness_dir)?; + println!("[{}] Witness saved to {}", circuit_name, witness_path.display()); + Ok(()) +} + +/// Compare return values to expectations, returning errors if something unexpected was returned. +pub fn check_witness( + circuit: &CompiledProgram, + return_values: ReturnValues, +) -> Result<(), CliError> { + // Check that the circuit returned a non-empty result if the ABI expects a return value. + if let Some(ref expected) = circuit.abi.return_type { + if return_values.actual_return.is_none() { + return Err(CliError::MissingReturn { expected: expected.clone() }); + } + } + + // Check that if the prover file contained a `return` entry then that's what we got. + if let Some(expected) = return_values.expected_return { + match return_values.actual_return { + None => { + return Err(CliError::UnexpectedReturn { expected, actual: None }); + } + Some(actual) => { + if actual != expected { + return Err(CliError::UnexpectedReturn { expected, actual: Some(actual) }); + } + } + } + } + + Ok(()) +} diff --git a/noir/noir-repo/tooling/artifact_cli/src/fs/artifact.rs b/noir/noir-repo/tooling/artifact_cli/src/fs/artifact.rs index 856fc472fc73..1a55764a9fed 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/fs/artifact.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/fs/artifact.rs @@ -1,16 +1,18 @@ -use std::path::Path; +use std::path::{Path, PathBuf}; use crate::{ - errors::{CliError, FilesystemError}, Artifact, + errors::{CliError, FilesystemError}, }; use noirc_artifacts::contract::ContractArtifact; use noirc_artifacts::program::ProgramArtifact; +use noirc_driver::CrateName; +use serde::de::Error; impl Artifact { /// Try to parse an artifact as a binary program or a contract pub fn read_from_file(path: &Path) -> Result { - let json = std::fs::read(path).map_err(FilesystemError::from)?; + let json = std::fs::read(path.with_extension("json")).map_err(FilesystemError::from)?; let as_program = || serde_json::from_slice::(&json).map(Artifact::Program); let as_contract = @@ -35,3 +37,55 @@ pub fn read_bytecode_from_file( .map_err(|e| FilesystemError::InvalidBytecodeFile(file_path, e.to_string()))?; Ok(bytecode) } + +/// Read a `ProgramArtifact`. Returns error if it turns out to be a `ContractArtifact`. +pub fn read_program_from_file(path: &Path) -> Result { + match Artifact::read_from_file(path)? { + Artifact::Program(program) => Ok(program), + Artifact::Contract(contract) => { + let msg = format!( + "expected a program artifact but found a contract in {}: {}", + path.display(), + contract.name + ); + Err(CliError::ArtifactDeserializationError(serde_json::Error::custom(msg))) + } + } +} + +pub fn save_program_to_file( + program_artifact: &ProgramArtifact, + crate_name: &CrateName, + output_dir: &Path, +) -> Result { + let circuit_name: String = crate_name.into(); + save_build_artifact_to_file(program_artifact, &circuit_name, output_dir) +} + +pub fn save_contract_to_file( + compiled_contract: &ContractArtifact, + circuit_name: &str, + output_dir: &Path, +) -> Result { + save_build_artifact_to_file(compiled_contract, circuit_name, output_dir) +} + +fn save_build_artifact_to_file( + build_artifact: &T, + artifact_name: &str, + output_dir: &Path, +) -> Result { + let artifact_path = output_dir.join(artifact_name).with_extension("json"); + let bytes = serde_json::to_vec(build_artifact)?; + write_to_file(&bytes, &artifact_path)?; + Ok(artifact_path) +} + +/// Create the parent directory if needed and write the bytes to a file. +pub fn write_to_file(bytes: &[u8], path: &Path) -> Result<(), FilesystemError> { + if let Some(dir) = path.parent() { + std::fs::create_dir_all(dir)?; + } + std::fs::write(path, bytes)?; + Ok(()) +} diff --git a/noir/noir-repo/tooling/artifact_cli/src/fs/inputs.rs b/noir/noir-repo/tooling/artifact_cli/src/fs/inputs.rs index f115af92041b..753343af8c65 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/fs/inputs.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/fs/inputs.rs @@ -1,6 +1,6 @@ use noirc_abi::{ - input_parser::{Format, InputValue}, Abi, InputMap, MAIN_RETURN_NAME, + input_parser::{Format, InputValue}, }; use std::{collections::BTreeMap, path::Path}; diff --git a/noir/noir-repo/tooling/artifact_cli/src/fs/witness.rs b/noir/noir-repo/tooling/artifact_cli/src/fs/witness.rs index f486a53f14d8..fee31ec1f224 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/fs/witness.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/fs/witness.rs @@ -1,16 +1,16 @@ use std::path::{Path, PathBuf}; -use acir::{native_types::WitnessStackError, FieldElement}; +use acir::{FieldElement, native_types::WitnessStackError}; use acvm::acir::native_types::WitnessStack; -use crate::errors::FilesystemError; +use crate::errors::{CliError, FilesystemError}; /// Write `witness.gz` to the output directory. pub fn save_witness_to_dir( - witnesses: WitnessStack, + witnesses: &WitnessStack, witness_name: &str, witness_dir: &Path, -) -> Result { +) -> Result { std::fs::create_dir_all(witness_dir)?; let witness_path = witness_dir.join(witness_name).with_extension("gz"); diff --git a/noir/noir-repo/tooling/artifact_cli/src/lib.rs b/noir/noir-repo/tooling/artifact_cli/src/lib.rs index 2cd2341b7b70..ac4316f5801a 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/lib.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/lib.rs @@ -2,6 +2,7 @@ use noirc_artifacts::{contract::ContractArtifact, program::ProgramArtifact}; pub mod commands; pub mod errors; +pub mod execution; pub mod fs; /// A parsed JSON build artifact. diff --git a/noir/noir-repo/tooling/debugger/src/context.rs b/noir/noir-repo/tooling/debugger/src/context.rs index 9741749df643..79e03672e8d7 100644 --- a/noir/noir-repo/tooling/debugger/src/context.rs +++ b/noir/noir-repo/tooling/debugger/src/context.rs @@ -5,22 +5,22 @@ use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; use acvm::acir::native_types::{Witness, WitnessMap, WitnessStack}; use acvm::brillig_vm::MemoryValue; use acvm::pwg::{ - ACVMStatus, AcirCallWaitInfo, BrilligSolver, BrilligSolverStatus, ForeignCallWaitInfo, - OpcodeNotSolvable, StepResult, ACVM, + ACVM, ACVMStatus, AcirCallWaitInfo, BrilligSolver, BrilligSolverStatus, ForeignCallWaitInfo, + OpcodeNotSolvable, StepResult, }; use acvm::{BlackBoxFunctionSolver, FieldElement}; use codespan_reporting::files::{Files, SimpleFile}; use fm::FileId; -use nargo::errors::{ExecutionError, Location}; use nargo::NargoError; +use nargo::errors::{ExecutionError, Location}; use noirc_artifacts::debug::{DebugArtifact, StackFrame}; use noirc_driver::DebugFile; use thiserror::Error; use std::collections::BTreeMap; -use std::collections::{hash_set::Iter, HashSet}; +use std::collections::{HashSet, hash_set::Iter}; /// A Noir program is composed by /// `n` ACIR circuits @@ -429,6 +429,12 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { .filter(|v: &Vec| !v.is_empty()) } + /// Returns the `FileId` of the file associated with the innermost function on the call stack. + pub(super) fn get_current_file(&mut self) -> Option { + self.get_current_source_location() + .and_then(|locations| locations.last().map(|location| location.file)) + } + /// Returns the (possible) stack of source locations corresponding to the /// given opcode location. Due to compiler inlining it's possible for this /// function to return multiple source locations. An empty vector means that @@ -788,11 +794,11 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } pub(super) fn get_variables(&self) -> Vec> { - return self.foreign_call_executor.get_variables(); + self.foreign_call_executor.get_variables() } pub(super) fn current_stack_frame(&self) -> Option> { - return self.foreign_call_executor.current_stack_frame(); + self.foreign_call_executor.current_stack_frame() } fn breakpoint_reached(&self) -> bool { @@ -954,13 +960,13 @@ mod tests { use crate::foreign_calls::DefaultDebugForeignCallExecutor; use acvm::{ acir::{ + AcirField, brillig::{HeapVector, IntegerBitSize}, circuit::{ brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}, opcodes::{AcirFunctionId, BlockId, BlockType}, }, native_types::Expression, - AcirField, }, blackbox_solver::StubbedBlackBoxSolver, brillig_vm::brillig::{ diff --git a/noir/noir-repo/tooling/debugger/src/dap.rs b/noir/noir-repo/tooling/debugger/src/dap.rs index 0d2095954e07..1df27d8ea6fd 100644 --- a/noir/noir-repo/tooling/debugger/src/dap.rs +++ b/noir/noir-repo/tooling/debugger/src/dap.rs @@ -1,8 +1,8 @@ use std::collections::BTreeMap; use std::io::{Read, Write}; -use acvm::acir::circuit::brillig::BrilligBytecode; use acvm::acir::circuit::Circuit; +use acvm::acir::circuit::brillig::BrilligBytecode; use acvm::acir::native_types::WitnessMap; use acvm::{BlackBoxFunctionSolver, FieldElement}; use nargo::PrintOutput; @@ -466,14 +466,14 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< } fn map_source_breakpoints(&mut self, args: &SetBreakpointsArguments) -> Vec { - let Some(ref source) = &args.source.path else { + let Some(source) = &args.source.path else { return vec![]; }; let Some(file_id) = self.find_file_id(source) else { eprintln!("WARN: file ID for source {source} not found"); return vec![]; }; - let Some(ref breakpoints) = &args.breakpoints else { + let Some(breakpoints) = &args.breakpoints else { return vec![]; }; let mut breakpoints_to_set: Vec<(DebugLocation, i64)> = vec![]; diff --git a/noir/noir-repo/tooling/debugger/src/foreign_calls.rs b/noir/noir-repo/tooling/debugger/src/foreign_calls.rs index b92e22844ea9..efae3df407a2 100644 --- a/noir/noir-repo/tooling/debugger/src/foreign_calls.rs +++ b/noir/noir-repo/tooling/debugger/src/foreign_calls.rs @@ -1,13 +1,13 @@ use acvm::{ + AcirField, FieldElement, acir::brillig::{ForeignCallParam, ForeignCallResult}, pwg::ForeignCallWaitInfo, - AcirField, FieldElement, }; use nargo::{ + PrintOutput, foreign_calls::{ - layers::Layer, DefaultForeignCallBuilder, ForeignCallError, ForeignCallExecutor, + DefaultForeignCallBuilder, ForeignCallError, ForeignCallExecutor, layers::Layer, }, - PrintOutput, }; use noirc_artifacts::debug::{DebugArtifact, DebugVars, StackFrame}; use noirc_errors::debug_info::{DebugFnId, DebugVarId}; @@ -66,7 +66,7 @@ impl DefaultDebugForeignCallExecutor { pub fn from_artifact<'a>( output: PrintOutput<'a>, artifact: &DebugArtifact, - ) -> impl DebugForeignCallExecutor + 'a { + ) -> impl DebugForeignCallExecutor + use<'a> { let mut ex = Self::default(); ex.load_artifact(artifact); Self::make(output, ex) diff --git a/noir/noir-repo/tooling/debugger/src/lib.rs b/noir/noir-repo/tooling/debugger/src/lib.rs index 37ac088ca355..f0dc859beb3e 100644 --- a/noir/noir-repo/tooling/debugger/src/lib.rs +++ b/noir/noir-repo/tooling/debugger/src/lib.rs @@ -19,8 +19,9 @@ pub fn run_repl_session>( solver: &B, program: CompiledProgram, initial_witness: WitnessMap, + raw_source_printing: bool, ) -> Result>, NargoError> { - repl::run(solver, program, initial_witness) + repl::run(solver, program, initial_witness, raw_source_printing) } pub fn run_dap_loop>( diff --git a/noir/noir-repo/tooling/debugger/src/repl.rs b/noir/noir-repo/tooling/debugger/src/repl.rs index eda3cbfd895b..081561469853 100644 --- a/noir/noir-repo/tooling/debugger/src/repl.rs +++ b/noir/noir-repo/tooling/debugger/src/repl.rs @@ -1,12 +1,12 @@ use crate::context::{DebugCommandResult, DebugContext, DebugLocation}; +use acvm::AcirField; use acvm::acir::brillig::BitSize; use acvm::acir::circuit::brillig::{BrilligBytecode, BrilligFunctionId}; use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; use acvm::acir::native_types::{Witness, WitnessMap, WitnessStack}; -use acvm::brillig_vm::brillig::Opcode as BrilligOpcode; use acvm::brillig_vm::MemoryValue; -use acvm::AcirField; +use acvm::brillig_vm::brillig::Opcode as BrilligOpcode; use acvm::{BlackBoxFunctionSolver, FieldElement}; use nargo::{NargoError, PrintOutput}; use noirc_driver::CompiledProgram; @@ -14,7 +14,7 @@ use noirc_driver::CompiledProgram; use crate::foreign_calls::DefaultDebugForeignCallExecutor; use noirc_artifacts::debug::DebugArtifact; -use easy_repl::{command, CommandStatus, Repl}; +use easy_repl::{CommandStatus, Repl, command}; use noirc_printable_type::PrintableValueDisplay; use std::cell::RefCell; @@ -32,6 +32,10 @@ pub struct ReplDebugger<'a, B: BlackBoxFunctionSolver> { // Brillig functions referenced from the ACIR circuits above unconstrained_functions: &'a [BrilligBytecode], + + // whether to print the source without highlighting, pretty-printing, + // or line numbers + raw_source_printing: bool, } impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { @@ -41,6 +45,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { debug_artifact: &'a DebugArtifact, initial_witness: WitnessMap, unconstrained_functions: &'a [BrilligBytecode], + raw_source_printing: bool, ) -> Self { let foreign_call_executor = Box::new(DefaultDebugForeignCallExecutor::from_artifact( PrintOutput::Stdout, @@ -68,6 +73,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { initial_witness, last_result, unconstrained_functions, + raw_source_printing, } } @@ -97,7 +103,11 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { } } let locations = self.context.get_source_location_for_debug_location(&location); - print_source_code_location(self.debug_artifact, &locations); + print_source_code_location( + self.debug_artifact, + &locations, + self.raw_source_printing, + ); } } } @@ -125,7 +135,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { } } let locations = self.context.get_source_location_for_debug_location(debug_location); - print_source_code_location(self.debug_artifact, &locations); + print_source_code_location(self.debug_artifact, &locations, self.raw_source_printing); } pub fn show_current_call_stack(&self) { @@ -233,6 +243,24 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { } } + fn add_breakpoint_at_line(&mut self, line_number: i64) { + let Some(current_file) = self.context.get_current_file() else { + println!("No current file."); + return; + }; + + let best_location = + self.context.find_opcode_for_source_location(¤t_file, line_number); + + match best_location { + Some(location) => { + println!("Added breakpoint at line {}", line_number); + self.add_breakpoint_at(location) + } + None => println!("No opcode at line {}", line_number), + } + } + fn delete_breakpoint_at(&mut self, location: DebugLocation) { if self.context.delete_breakpoint(&location) { println!("Breakpoint at {location} deleted"); @@ -427,6 +455,7 @@ pub fn run>( blackbox_solver: &B, program: CompiledProgram, initial_witness: WitnessMap, + raw_source_printing: bool, ) -> Result>, NargoError> { let circuits = &program.program.functions; let debug_artifact = @@ -438,6 +467,7 @@ pub fn run>( debug_artifact, initial_witness, unconstrained_functions, + raw_source_printing, )); let ref_context = &context; @@ -524,6 +554,16 @@ pub fn run>( } }, ) + .add( + "break", + command! { + "add a breakpoint at a line of the current file", + (line_number: i64) => |line_number| { + ref_context.borrow_mut().add_breakpoint_at_line(line_number); + Ok(CommandStatus::Done) + } + }, + ) .add( "break", command! { diff --git a/noir/noir-repo/tooling/debugger/src/source_code_printer.rs b/noir/noir-repo/tooling/debugger/src/source_code_printer.rs index b3682c9016fb..a756de8d98ba 100644 --- a/noir/noir-repo/tooling/debugger/src/source_code_printer.rs +++ b/noir/noir-repo/tooling/debugger/src/source_code_printer.rs @@ -30,7 +30,11 @@ struct LocationPrintContext { // Given a DebugArtifact and an OpcodeLocation, prints all the source code // locations the OpcodeLocation maps to, with some surrounding context and // visual aids to highlight the location itself. -pub(super) fn print_source_code_location(debug_artifact: &DebugArtifact, locations: &[Location]) { +pub(super) fn print_source_code_location( + debug_artifact: &DebugArtifact, + locations: &[Location], + raw_source_printing: bool, +) { let locations = locations.iter(); for loc in locations { @@ -41,9 +45,11 @@ pub(super) fn print_source_code_location(debug_artifact: &DebugArtifact, locatio for line in lines { match line { PrintedLine::Skip => {} - PrintedLine::Ellipsis { line_number } => print_ellipsis(line_number), + PrintedLine::Ellipsis { line_number } => { + print_ellipsis(line_number, raw_source_printing) + } PrintedLine::Content { line_number, cursor, content, highlight } => { - print_content(line_number, cursor, content, highlight) + print_content(line_number, cursor, content, highlight, raw_source_printing) } } } @@ -57,11 +63,29 @@ fn print_location_path(debug_artifact: &DebugArtifact, loc: Location) { println!("At {}:{line_number}:{column_number}", debug_artifact.name(loc.file).unwrap()); } -fn print_ellipsis(line_number: usize) { +fn print_ellipsis(line_number: usize, raw_source_printing: bool) { + if raw_source_printing { + println!("..."); + return; + } + println!("{:>3} {:2} {}", line_number.dimmed(), "", "...".dimmed()); } -fn print_content(line_number: usize, cursor: &str, content: &str, highlight: Option>) { +fn print_content( + line_number: usize, + cursor: &str, + content: &str, + highlight: Option>, + raw_source_printing: bool, +) { + if raw_source_printing { + if cursor == "->" && highlight.is_some() { + println!("{}", content); + } + return; + } + match highlight { Some(highlight) => { println!( @@ -220,17 +244,17 @@ fn render_location<'a>( #[cfg(test)] mod tests { - use crate::source_code_printer::render_location; use crate::source_code_printer::PrintedLine::Content; + use crate::source_code_printer::render_location; use acvm::acir::circuit::OpcodeLocation; use fm::FileManager; use noirc_artifacts::debug::DebugArtifact; - use noirc_errors::{debug_info::DebugInfo, Location, Span}; + use noirc_errors::{Location, Span, debug_info::DebugInfo}; use std::collections::BTreeMap; use std::ops::Range; use std::path::Path; use std::path::PathBuf; - use tempfile::{tempdir, TempDir}; + use tempfile::{TempDir, tempdir}; // Returns the absolute path to the file fn create_dummy_file(dir: &TempDir, file_name: &Path) -> PathBuf { diff --git a/noir/noir-repo/tooling/debugger/tests/debug.rs b/noir/noir-repo/tooling/debugger/tests/debug.rs index eb43cf9cc6dd..079857420853 100644 --- a/noir/noir-repo/tooling/debugger/tests/debug.rs +++ b/noir/noir-repo/tooling/debugger/tests/debug.rs @@ -49,4 +49,115 @@ mod tests { // Exit the bash session. dbg_session.send_line("exit").expect("Failed to quit bash session"); } + + #[test] + fn debugger_expected_call_stack() { + let nargo_bin = + cargo_bin("nargo").into_os_string().into_string().expect("Cannot parse nargo path"); + + let timeout_seconds = 30; + let mut dbg_session = + spawn_bash(Some(timeout_seconds * 1000)).expect("Could not start bash session"); + + let test_program_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../test_programs/execution_success/regression_7195"); + let test_program_dir = test_program_path.display(); + + // Start debugger and test that it loads for the given program. + dbg_session + .execute( + &format!( + "{nargo_bin} debug --raw-source-printing true --program-dir {test_program_dir} --force-brillig --expression-width 3" + ), + ".*\\Starting debugger.*", + ) + .expect("Could not start debugger"); + + let num_steps = 16; + for _ in 1..=num_steps { + // While running the debugger, issue a "next" cmd, + // which should run to the program to the next source line given + // we haven't set any breakpoints. + // ">" is the debugger's prompt, so finding one + // after running "next" indicates that the + // debugger has not panicked for this step. + dbg_session + .send_line("next") + .expect("Debugger panicked while attempting to step through program."); + dbg_session + .exp_string(">") + .expect("Failed while waiting for debugger to step through program."); + } + + let mut lines = vec![]; + while let Ok(line) = dbg_session.read_line() { + if !(line.starts_with(">next") || line.starts_with("At ") || line.starts_with("...")) { + lines.push(line); + } + } + + let lines_expected_to_contain: Vec<&str> = vec![ + "> next", + " let x = unsafe { baz(x) };", + "unconstrained fn baz(x: Field) -> Field {", + "> next", + " let x = unsafe { baz(x) };", + "unconstrained fn baz(x: Field) -> Field {", + "> next", + " let x = unsafe { baz(x) };", + "}", + "> next", + " let x = unsafe { baz(x) };", + "> next", + " foo(x);", + "fn foo(x: Field) {", + "> next", + " foo(x);", + "fn foo(x: Field) {", + "> next", + " foo(x);", + " let y = unsafe { baz(x) };", + "unconstrained fn baz(x: Field) -> Field {", + "> next", + " foo(x);", + " let y = unsafe { baz(x) };", + "unconstrained fn baz(x: Field) -> Field {", + "> next", + " foo(x);", + " let y = unsafe { baz(x) };", + "}", + "> next", + " foo(x);", + " let y = unsafe { baz(x) };", + "> next", + " foo(x);", + " bar(y);", + "fn bar(y: Field) {", + "> next", + " foo(x);", + " bar(y);", + "fn bar(y: Field) {", + "> next", + " foo(x);", + " bar(y);", + " assert(y != 0);", + ]; + + for (line, line_expected_to_contain) in lines.into_iter().zip(lines_expected_to_contain) { + let ascii_line: String = line.chars().filter(char::is_ascii).collect(); + let line_expected_to_contain = line_expected_to_contain.trim_start(); + assert!( + ascii_line.contains(line_expected_to_contain), + "{:?}\ndid not contain\n{:?}", + ascii_line, + line_expected_to_contain, + ); + } + + // Run the "quit" command + dbg_session.send_line("quit").expect("Failed to quit debugger"); + + // Exit the bash session. + dbg_session.send_line("exit").expect("Failed to quit bash session"); + } } diff --git a/noir/noir-repo/tooling/fuzzer/src/dictionary/mod.rs b/noir/noir-repo/tooling/fuzzer/src/dictionary/mod.rs index 7fe1c4cd602c..4f15a336a3e9 100644 --- a/noir/noir-repo/tooling/fuzzer/src/dictionary/mod.rs +++ b/noir/noir-repo/tooling/fuzzer/src/dictionary/mod.rs @@ -6,16 +6,16 @@ use std::collections::HashSet; use acvm::{ + AcirField, acir::{ circuit::{ + Circuit, Opcode, Program, brillig::{BrilligBytecode, BrilligInputs}, opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum}, - Circuit, Opcode, Program, }, native_types::Expression, }, brillig_vm::brillig::Opcode as BrilligOpcode, - AcirField, }; /// Constructs a [HashSet] of values pulled from a [Program] which are likely to be correspond diff --git a/noir/noir-repo/tooling/fuzzer/src/lib.rs b/noir/noir-repo/tooling/fuzzer/src/lib.rs index 324be323fc21..471f3da88f65 100644 --- a/noir/noir-repo/tooling/fuzzer/src/lib.rs +++ b/noir/noir-repo/tooling/fuzzer/src/lib.rs @@ -4,11 +4,11 @@ //! Code is used under the MIT license. use acvm::{ + FieldElement, acir::{ circuit::Program, native_types::{WitnessMap, WitnessStack}, }, - FieldElement, }; use dictionary::build_dictionary_from_program; use noirc_abi::InputMap; diff --git a/noir/noir-repo/tooling/fuzzer/src/strategies/int.rs b/noir/noir-repo/tooling/fuzzer/src/strategies/int.rs index 22dded7c7b76..5fb8d469a926 100644 --- a/noir/noir-repo/tooling/fuzzer/src/strategies/int.rs +++ b/noir/noir-repo/tooling/fuzzer/src/strategies/int.rs @@ -54,20 +54,12 @@ impl IntStrategy { /// Maximum allowed positive number. fn type_max(&self) -> i128 { - if self.bits < 128 { - (1i128 << (self.bits - 1)) - 1 - } else { - i128::MAX - } + if self.bits < 128 { (1i128 << (self.bits - 1)) - 1 } else { i128::MAX } } /// Minimum allowed negative number. fn type_min(&self) -> i128 { - if self.bits < 128 { - -(1i128 << (self.bits - 1)) - } else { - i128::MIN - } + if self.bits < 128 { -(1i128 << (self.bits - 1)) } else { i128::MIN } } } diff --git a/noir/noir-repo/tooling/fuzzer/src/strategies/mod.rs b/noir/noir-repo/tooling/fuzzer/src/strategies/mod.rs index 99c7ca56f2e7..4c8181ea804a 100644 --- a/noir/noir-repo/tooling/fuzzer/src/strategies/mod.rs +++ b/noir/noir-repo/tooling/fuzzer/src/strategies/mod.rs @@ -4,7 +4,7 @@ use proptest::prelude::*; use acvm::{AcirField, FieldElement}; -use noirc_abi::{input_parser::InputValue, Abi, AbiType, InputMap, Sign}; +use noirc_abi::{Abi, AbiType, InputMap, Sign, input_parser::InputValue}; use std::collections::{BTreeMap, HashSet}; use uint::UintStrategy; diff --git a/noir/noir-repo/tooling/fuzzer/src/strategies/uint.rs b/noir/noir-repo/tooling/fuzzer/src/strategies/uint.rs index 402e65597524..904a5cef2878 100644 --- a/noir/noir-repo/tooling/fuzzer/src/strategies/uint.rs +++ b/noir/noir-repo/tooling/fuzzer/src/strategies/uint.rs @@ -78,11 +78,7 @@ impl UintStrategy { /// Maximum integer that fits in the given bit width. fn type_max(&self) -> u128 { - if self.bits < 128 { - (1 << self.bits) - 1 - } else { - u128::MAX - } + if self.bits < 128 { (1 << self.bits) - 1 } else { u128::MAX } } } diff --git a/noir/noir-repo/tooling/inspector/Cargo.toml b/noir/noir-repo/tooling/inspector/Cargo.toml index 2124f7e9a282..d7ab641e4837 100644 --- a/noir/noir-repo/tooling/inspector/Cargo.toml +++ b/noir/noir-repo/tooling/inspector/Cargo.toml @@ -26,3 +26,4 @@ const_format.workspace = true acir.workspace = true noirc_artifacts.workspace = true noirc_artifacts_info.workspace = true +noir_artifact_cli.workspace = true diff --git a/noir/noir-repo/tooling/inspector/src/cli/info_cmd.rs b/noir/noir-repo/tooling/inspector/src/cli/info_cmd.rs index 6a9db2676f2b..34107ebea3a9 100644 --- a/noir/noir-repo/tooling/inspector/src/cli/info_cmd.rs +++ b/noir/noir-repo/tooling/inspector/src/cli/info_cmd.rs @@ -2,8 +2,8 @@ use std::path::PathBuf; use clap::Args; use color_eyre::eyre; -use noirc_artifacts::program::ProgramArtifact; -use noirc_artifacts_info::{count_opcodes_and_gates_in_program, show_info_report, InfoReport}; +use noir_artifact_cli::Artifact; +use noirc_artifacts_info::{InfoReport, count_opcodes_and_gates_in_program, show_info_report}; #[derive(Debug, Clone, Args)] pub(crate) struct InfoCommand { @@ -13,22 +13,42 @@ pub(crate) struct InfoCommand { /// Output a JSON formatted report. Changes to this format are not currently considered breaking. #[clap(long, hide = true)] json: bool, + + /// Name of the function to print, if the artifact is a contract. + #[clap(long)] + contract_fn: Option, } pub(crate) fn run(args: InfoCommand) -> eyre::Result<()> { - let file = std::fs::File::open(args.artifact.clone())?; - let artifact: ProgramArtifact = serde_json::from_reader(file)?; - - let package_name = args - .artifact - .with_extension("") - .file_name() - .map(|s| s.to_string_lossy().to_string()) - .unwrap_or_else(|| "artifact".to_string()); - - let program_info = count_opcodes_and_gates_in_program(artifact, package_name.to_string(), None); - - let info_report = InfoReport { programs: vec![program_info] }; + let artifact = Artifact::read_from_file(&args.artifact)?; + + let programs = match artifact { + Artifact::Program(program) => { + let package_name = args + .artifact + .with_extension("") + .file_name() + .map(|s| s.to_string_lossy().to_string()) + .unwrap_or_else(|| "artifact".to_string()); + + vec![count_opcodes_and_gates_in_program(program, package_name, None)] + } + Artifact::Contract(contract) => contract + .functions + .into_iter() + .filter(|f| args.contract_fn.as_ref().map(|n| *n == f.name).unwrap_or(true)) + .map(|f| { + let package_name = format!("{}::{}", contract.name, f.name); + let program = f.into_compiled_program( + contract.noir_version.clone(), + contract.file_map.clone(), + ); + count_opcodes_and_gates_in_program(program.into(), package_name, None) + }) + .collect::>(), + }; + + let info_report = InfoReport { programs }; show_info_report(info_report, args.json); Ok(()) diff --git a/noir/noir-repo/tooling/inspector/src/cli/mod.rs b/noir/noir-repo/tooling/inspector/src/cli/mod.rs index 8cce6ec3a6f2..411f2076973d 100644 --- a/noir/noir-repo/tooling/inspector/src/cli/mod.rs +++ b/noir/noir-repo/tooling/inspector/src/cli/mod.rs @@ -1,4 +1,4 @@ -use clap::{command, Parser, Subcommand}; +use clap::{Parser, Subcommand, command}; use color_eyre::eyre; use const_format::formatcp; diff --git a/noir/noir-repo/tooling/inspector/src/cli/print_acir_cmd.rs b/noir/noir-repo/tooling/inspector/src/cli/print_acir_cmd.rs index f3dfe528973f..c3580a715d88 100644 --- a/noir/noir-repo/tooling/inspector/src/cli/print_acir_cmd.rs +++ b/noir/noir-repo/tooling/inspector/src/cli/print_acir_cmd.rs @@ -2,20 +2,38 @@ use std::path::PathBuf; use clap::Args; use color_eyre::eyre; -use noirc_artifacts::program::ProgramArtifact; +use noir_artifact_cli::Artifact; #[derive(Debug, Clone, Args)] pub(crate) struct PrintAcirCommand { /// The artifact to print artifact: PathBuf, + + /// Name of the function to print, if the artifact is a contract. + #[clap(long)] + contract_fn: Option, } pub(crate) fn run(args: PrintAcirCommand) -> eyre::Result<()> { - let file = std::fs::File::open(args.artifact.clone())?; - let artifact: ProgramArtifact = serde_json::from_reader(file)?; + let artifact = Artifact::read_from_file(&args.artifact)?; - println!("Compiled ACIR for main:"); - println!("{}", artifact.bytecode); + match artifact { + Artifact::Program(program) => { + println!("Compiled ACIR for main:"); + println!("{}", program.bytecode); + } + Artifact::Contract(contract) => { + println!("Compiled circuits for contract '{}':", contract.name); + for function in contract + .functions + .into_iter() + .filter(|f| args.contract_fn.as_ref().map(|n| *n == f.name).unwrap_or(true)) + { + println!("Compiled ACIR for function '{}':", function.name); + println!("{}", function.bytecode); + } + } + } Ok(()) } diff --git a/noir/noir-repo/tooling/lsp/Cargo.toml b/noir/noir-repo/tooling/lsp/Cargo.toml index a055a9a6bcec..d0b67f53c241 100644 --- a/noir/noir-repo/tooling/lsp/Cargo.toml +++ b/noir/noir-repo/tooling/lsp/Cargo.toml @@ -31,6 +31,7 @@ thiserror.workspace = true fm.workspace = true rayon.workspace = true fxhash.workspace = true +iter-extended.workspace = true convert_case = "0.6.0" num-bigint.workspace = true diff --git a/noir/noir-repo/tooling/lsp/src/attribute_reference_finder.rs b/noir/noir-repo/tooling/lsp/src/attribute_reference_finder.rs index e3f31b65b463..384c575f0c65 100644 --- a/noir/noir-repo/tooling/lsp/src/attribute_reference_finder.rs +++ b/noir/noir-repo/tooling/lsp/src/attribute_reference_finder.rs @@ -9,6 +9,7 @@ use std::collections::BTreeMap; use fm::FileId; use noirc_errors::Span; use noirc_frontend::{ + ParsedModule, ast::{AttributeTarget, Visitor}, graph::CrateId, hir::{ @@ -19,7 +20,6 @@ use noirc_frontend::{ parser::ParsedSubModule, token::MetaAttribute, usage_tracker::UsageTracker, - ParsedModule, }; use crate::modules::module_def_id_to_reference_id; @@ -65,7 +65,7 @@ impl<'a> AttributeReferenceFinder<'a> { } } -impl<'a> Visitor for AttributeReferenceFinder<'a> { +impl Visitor for AttributeReferenceFinder<'_> { fn visit_parsed_submodule(&mut self, parsed_sub_module: &ParsedSubModule, _span: Span) -> bool { // Switch `self.module_id` to the submodule let previous_module_id = self.module_id; @@ -90,7 +90,7 @@ impl<'a> Visitor for AttributeReferenceFinder<'a> { attribute: &MetaAttribute, _target: AttributeTarget, ) -> bool { - if !self.includes_span(attribute.span) { + if !self.includes_span(attribute.location.span) { return false; } diff --git a/noir/noir-repo/tooling/lsp/src/lib.rs b/noir/noir-repo/tooling/lsp/src/lib.rs index c722bfdfd3e1..784ac8cf93d6 100644 --- a/noir/noir-repo/tooling/lsp/src/lib.rs +++ b/noir/noir-repo/tooling/lsp/src/lib.rs @@ -15,35 +15,35 @@ use std::{ use acvm::{BlackBoxFunctionSolver, FieldElement}; use async_lsp::{ - router::Router, AnyEvent, AnyNotification, AnyRequest, ClientSocket, Error, LspService, - ResponseError, + AnyEvent, AnyNotification, AnyRequest, ClientSocket, Error, LspService, ResponseError, + router::Router, }; -use fm::{codespan_files as files, FileManager}; +use fm::{FileManager, codespan_files as files}; use fxhash::FxHashSet; use lsp_types::{ + CodeLens, request::{ CodeActionRequest, Completion, DocumentSymbolRequest, HoverRequest, InlayHintRequest, PrepareRenameRequest, References, Rename, SignatureHelpRequest, }, - CodeLens, }; use nargo::{ package::{Package, PackageType}, parse_all, workspace::Workspace, }; -use nargo_toml::{find_file_manifest, resolve_workspace_from_toml, PackageSelection}; -use noirc_driver::{file_manager_with_stdlib, prepare_crate, NOIR_ARTIFACT_VERSION_STRING}; +use nargo_toml::{PackageSelection, find_file_manifest, resolve_workspace_from_toml}; +use noirc_driver::{NOIR_ARTIFACT_VERSION_STRING, file_manager_with_stdlib, prepare_crate}; use noirc_frontend::{ + ParsedModule, graph::{CrateGraph, CrateId, CrateName}, hir::{ - def_map::{parse_file, CrateDefMap}, Context, FunctionNameMatch, ParsedFiles, + def_map::{CrateDefMap, parse_file}, }, node_interner::NodeInterner, parser::ParserError, usage_tracker::UsageTracker, - ParsedModule, }; use rayon::prelude::*; @@ -52,12 +52,11 @@ use notifications::{ on_did_open_text_document, on_did_save_text_document, on_exit, on_initialized, }; use requests::{ - on_code_action_request, on_code_lens_request, on_completion_request, + LspInitializationOptions, on_code_action_request, on_code_lens_request, on_completion_request, on_document_symbol_request, on_formatting, on_goto_declaration_request, on_goto_definition_request, on_goto_type_definition_request, on_hover_request, on_initialize, on_inlay_hint_request, on_prepare_rename_request, on_references_request, on_rename_request, on_shutdown, on_signature_help_request, on_test_run_request, on_tests_request, - LspInitializationOptions, }; use serde_json::Value as JsonValue; use thiserror::Error; @@ -74,12 +73,14 @@ mod types; mod use_segment_positions; mod utils; mod visibility; +mod with_file; #[cfg(test)] mod test_utils; use solver::WrapperSolver; -use types::{notification, request, NargoTest, NargoTestId, Position, Range, Url}; +use types::{NargoTest, NargoTestId, Position, Range, Url, notification, request}; +use with_file::parsed_module_with_file; #[derive(Debug, Error)] pub enum LspError { @@ -237,11 +238,7 @@ fn get_package_tests_in_crate( }) .collect(); - if package_tests.is_empty() { - None - } else { - Some(package_tests) - } + if package_tests.is_empty() { None } else { Some(package_tests) } } fn byte_span_to_range<'a, F: files::Files<'a> + ?Sized>( @@ -388,18 +385,22 @@ fn parse_diff(file_manager: &FileManager, state: &mut LspState) -> ParsedFiles { }) .collect(); - let cache_hits: Vec<_> = noir_file_hashes + let cache_hits = noir_file_hashes .par_iter() .filter_map(|(file_id, file_path, current_hash)| { let cached_version = state.cached_parsed_files.get(file_path); - if let Some((hash, cached_parsing)) = cached_version { + if let Some((hash, (parsed_module, errors))) = cached_version { if hash == current_hash { - return Some((*file_id, cached_parsing.clone())); + // The cached ParsedModule might have FileIDs in it that are different than the file_id we get here, + // so we must replace all of those FileIDs with the one here. + let parsed_module = + parsed_module_with_file(parsed_module.clone(), *file_id); + return Some((*file_id, (parsed_module, errors.clone()))); } } None }) - .collect(); + .collect::>(); let cache_hits_ids: FxHashSet<_> = cache_hits.iter().map(|(file_id, _)| *file_id).collect(); diff --git a/noir/noir-repo/tooling/lsp/src/modules.rs b/noir/noir-repo/tooling/lsp/src/modules.rs index 56529949b3ea..4e7ef64b2c08 100644 --- a/noir/noir-repo/tooling/lsp/src/modules.rs +++ b/noir/noir-repo/tooling/lsp/src/modules.rs @@ -248,15 +248,12 @@ pub(crate) fn module_def_id_relative_path( interner, ) } else { - let Some(module_full_path) = relative_module_full_path( + relative_module_full_path( module_def_id, current_module_id, current_module_parent_id, interner, - ) else { - return None; - }; - module_full_path + )? }; let path = if defining_module.is_some() || !matches!(module_def_id, ModuleDefId::ModuleId(..)) { diff --git a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs index ac8bf7309799..b7ba8cd47615 100644 --- a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs @@ -3,25 +3,25 @@ use std::ops::ControlFlow; use std::path::PathBuf; use crate::{ - insert_all_files_for_workspace_into_file_manager, PackageCacheData, WorkspaceCacheData, + PackageCacheData, WorkspaceCacheData, insert_all_files_for_workspace_into_file_manager, }; use async_lsp::{ErrorCode, LanguageClient, ResponseError}; -use fm::{FileId, FileManager, FileMap}; +use fm::{FileManager, FileMap}; use fxhash::FxHashMap as HashMap; use lsp_types::{DiagnosticRelatedInformation, DiagnosticTag, Url}; use noirc_driver::check_crate; use noirc_errors::reporter::CustomLabel; -use noirc_errors::{DiagnosticKind, FileDiagnostic, Location}; +use noirc_errors::{CustomDiagnostic, DiagnosticKind, Location}; use crate::types::{ - notification, Diagnostic, DiagnosticSeverity, DidChangeConfigurationParams, - DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams, - DidSaveTextDocumentParams, InitializedParams, NargoPackageTests, PublishDiagnosticsParams, + Diagnostic, DiagnosticSeverity, DidChangeConfigurationParams, DidChangeTextDocumentParams, + DidCloseTextDocumentParams, DidOpenTextDocumentParams, DidSaveTextDocumentParams, + InitializedParams, NargoPackageTests, PublishDiagnosticsParams, notification, }; use crate::{ - byte_span_to_range, get_package_tests_in_crate, parse_diff, resolve_workspace_for_source_path, - LspState, + LspState, byte_span_to_range, get_package_tests_in_crate, parse_diff, + resolve_workspace_for_source_path, }; pub(super) fn on_initialized( @@ -191,16 +191,16 @@ fn publish_diagnostics( package_root_dir: &PathBuf, files: &FileMap, fm: &FileManager, - file_diagnostics: Vec, + custom_diagnostics: Vec, ) { let mut diagnostics_per_url: HashMap> = HashMap::default(); - for file_diagnostic in file_diagnostics.into_iter() { - let file_id = file_diagnostic.file_id; - let path = fm.path(file_id).expect("file must exist to have emitted diagnostic"); + for custom_diagnostic in custom_diagnostics.into_iter() { + let file = custom_diagnostic.file; + let path = fm.path(file).expect("file must exist to have emitted diagnostic"); if let Ok(uri) = Url::from_file_path(path) { if let Some(diagnostic) = - file_diagnostic_to_diagnostic(file_diagnostic, files, fm, uri.clone()) + custom_diagnostic_to_diagnostic(custom_diagnostic, files, fm, uri.clone()) { diagnostics_per_url.entry(uri).or_default().push(diagnostic); } @@ -232,21 +232,18 @@ fn publish_diagnostics( state.files_with_errors.insert(package_root_dir.clone(), new_files_with_errors); } -fn file_diagnostic_to_diagnostic( - file_diagnostic: FileDiagnostic, +fn custom_diagnostic_to_diagnostic( + diagnostic: CustomDiagnostic, files: &FileMap, fm: &FileManager, uri: Url, ) -> Option { - let file_id = file_diagnostic.file_id; - let diagnostic = file_diagnostic.diagnostic; - if diagnostic.secondaries.is_empty() { return None; } - let span = diagnostic.secondaries.first().unwrap().span; - let range = byte_span_to_range(files, file_id, span.into())?; + let span = diagnostic.secondaries.first().unwrap().location.span; + let range = byte_span_to_range(files, diagnostic.file, span.into())?; let severity = match diagnostic.kind { DiagnosticKind::Error => DiagnosticSeverity::ERROR, @@ -266,7 +263,7 @@ fn file_diagnostic_to_diagnostic( let secondaries = diagnostic .secondaries .into_iter() - .filter_map(|secondary| secondary_to_related_information(secondary, file_id, files, fm)); + .filter_map(|secondary| secondary_to_related_information(secondary, files, fm)); let notes = diagnostic.notes.into_iter().map(|message| DiagnosticRelatedInformation { location: lsp_types::Location { uri: uri.clone(), range }, message, @@ -293,14 +290,13 @@ fn file_diagnostic_to_diagnostic( fn secondary_to_related_information( secondary: CustomLabel, - file_id: FileId, files: &FileMap, fm: &FileManager, ) -> Option { - let secondary_file = secondary.file.unwrap_or(file_id); + let secondary_file = secondary.location.file; let path = fm.path(secondary_file)?; let uri = Url::from_file_path(path).ok()?; - let range = byte_span_to_range(files, secondary_file, secondary.span.into())?; + let range = byte_span_to_range(files, secondary_file, secondary.location.span.into())?; let message = secondary.message; Some(DiagnosticRelatedInformation { location: lsp_types::Location { uri, range }, message }) } diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action.rs index d5512855b3be..6d74ce2d1f96 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action.rs @@ -11,6 +11,10 @@ use lsp_types::{ TextDocumentPositionParams, TextEdit, Url, WorkspaceEdit, }; use noirc_errors::Span; +use noirc_frontend::{ + ParsedModule, + parser::{Item, ItemKind, ParsedSubModule}, +}; use noirc_frontend::{ ast::{ CallExpression, ConstructorExpression, ItemVisibility, MethodCallExpression, NoirTraitImpl, @@ -21,14 +25,10 @@ use noirc_frontend::{ node_interner::{NodeInterner, Reexport}, usage_tracker::UsageTracker, }; -use noirc_frontend::{ - parser::{Item, ItemKind, ParsedSubModule}, - ParsedModule, -}; use crate::{ - modules::get_ancestor_module_reexport, use_segment_positions::UseSegmentPositions, utils, - visibility::module_def_id_is_visible, LspState, + LspState, modules::get_ancestor_module_reexport, use_segment_positions::UseSegmentPositions, + utils, visibility::module_def_id_is_visible, }; use super::{process_request, to_lsp_location}; @@ -44,7 +44,7 @@ mod tests; pub(crate) fn on_code_action_request( state: &mut LspState, params: CodeActionParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let uri = params.text_document.clone().uri; let position = params.range.start; let text_document_position_params = @@ -56,7 +56,7 @@ pub(crate) fn on_code_action_request( utils::range_to_byte_span(args.files, file_id, ¶ms.range).and_then(|byte_range| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let mut finder = CodeActionFinder::new( uri, @@ -233,16 +233,16 @@ impl<'a> CodeActionFinder<'a> { } } -impl<'a> Visitor for CodeActionFinder<'a> { +impl Visitor for CodeActionFinder<'_> { fn visit_item(&mut self, item: &Item) -> bool { if let ItemKind::Import(use_tree, _) = &item.kind { - if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { + if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.location.span) { self.auto_import_line = (lsp_location.range.end.line + 1) as usize; } self.use_segment_positions.add(use_tree); } - self.includes_span(item.span) + self.includes_span(item.location.span) } fn visit_parsed_submodule(&mut self, parsed_sub_module: &ParsedSubModule, span: Span) -> bool { @@ -306,7 +306,7 @@ impl<'a> Visitor for CodeActionFinder<'a> { } if call.is_macro_call { - self.remove_bang_from_call(call.func.span); + self.remove_bang_from_call(call.func.location.span); } true diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/fill_struct_fields.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/fill_struct_fields.rs index fc8be7c51630..0f9188388ee2 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/fill_struct_fields.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/fill_struct_fields.rs @@ -1,5 +1,5 @@ use lsp_types::TextEdit; -use noirc_errors::{Location, Span}; +use noirc_errors::Span; use noirc_frontend::{ ast::{ConstructorExpression, UnresolvedTypeData}, node_interner::ReferenceId, @@ -9,7 +9,7 @@ use crate::byte_span_to_range; use super::CodeActionFinder; -impl<'a> CodeActionFinder<'a> { +impl CodeActionFinder<'_> { pub(super) fn fill_struct_fields(&mut self, constructor: &ConstructorExpression, span: Span) { if !self.includes_span(span) { return; @@ -19,7 +19,7 @@ impl<'a> CodeActionFinder<'a> { return; }; - let location = Location::new(path.span, self.file); + let location = path.location; let Some(ReferenceId::Type(type_id)) = self.interner.find_referenced(location) else { return; }; diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/implement_missing_members.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/implement_missing_members.rs index c29caf79848a..a39df735b34f 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/implement_missing_members.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/implement_missing_members.rs @@ -11,7 +11,7 @@ use crate::{byte_span_to_range, trait_impl_method_stub_generator::TraitImplMetho use super::CodeActionFinder; -impl<'a> CodeActionFinder<'a> { +impl CodeActionFinder<'_> { pub(super) fn implement_missing_members( &mut self, noir_trait_impl: &NoirTraitImpl, diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/import_or_qualify.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/import_or_qualify.rs index 1141aca23d2d..070dcfcde901 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/import_or_qualify.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/import_or_qualify.rs @@ -6,13 +6,13 @@ use crate::{ byte_span_to_range, modules::module_def_id_relative_path, use_segment_positions::{ - use_completion_item_additional_text_edits, UseCompletionItemAdditionTextEditsRequest, + UseCompletionItemAdditionTextEditsRequest, use_completion_item_additional_text_edits, }, }; use super::CodeActionFinder; -impl<'a> CodeActionFinder<'a> { +impl CodeActionFinder<'_> { pub(super) fn import_or_qualify(&mut self, path: &Path) { if path.segments.len() != 1 { return; diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/import_trait.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/import_trait.rs index 52b3e66033a2..379b627f8785 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/import_trait.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/import_trait.rs @@ -11,13 +11,13 @@ use crate::{ modules::module_def_id_relative_path, requests::TraitReexport, use_segment_positions::{ - use_completion_item_additional_text_edits, UseCompletionItemAdditionTextEditsRequest, + UseCompletionItemAdditionTextEditsRequest, use_completion_item_additional_text_edits, }, }; use super::CodeActionFinder; -impl<'a> CodeActionFinder<'a> { +impl CodeActionFinder<'_> { pub(super) fn import_trait_in_method_call(&mut self, method_call: &MethodCallExpression) { // First see if the method name already points to a function. let name_location = Location::new(method_call.method_name.span(), self.file); @@ -36,7 +36,7 @@ impl<'a> CodeActionFinder<'a> { } // Find out the type of the object - let object_location = Location::new(method_call.object.span, self.file); + let object_location = method_call.object.location; let Some(typ) = self.interner.type_at_location(object_location) else { return; }; diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_bang_from_call.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_bang_from_call.rs index 90f4fef0efd2..e2ef2ef84eed 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_bang_from_call.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_bang_from_call.rs @@ -1,12 +1,12 @@ use lsp_types::TextEdit; use noirc_errors::{Location, Span}; -use noirc_frontend::{node_interner::ReferenceId, QuotedType, Type}; +use noirc_frontend::{QuotedType, Type, node_interner::ReferenceId}; use crate::byte_span_to_range; use super::CodeActionFinder; -impl<'a> CodeActionFinder<'a> { +impl CodeActionFinder<'_> { pub(super) fn remove_bang_from_call(&mut self, span: Span) { // If we can't find the referenced function, there's nothing we can do let Some(ReferenceId::Function(func_id)) = diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_unused_import.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_unused_import.rs index 4822a9d61ec8..41d43baa656f 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_unused_import.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_unused_import.rs @@ -1,19 +1,20 @@ use std::collections::HashMap; +use fm::FileId; use lsp_types::TextEdit; -use noirc_errors::Span; +use noirc_errors::{Location, Span}; use noirc_frontend::{ + ParsedModule, ast::{Ident, ItemVisibility, UseTree, UseTreeKind}, parser::{Item, ItemKind}, usage_tracker::UnusedItem, - ParsedModule, }; use crate::byte_span_to_range; use super::CodeActionFinder; -impl<'a> CodeActionFinder<'a> { +impl CodeActionFinder<'_> { pub(super) fn remove_unused_import( &mut self, use_tree: &UseTree, @@ -80,11 +81,7 @@ fn use_tree_without_unused_import( match &use_tree.kind { UseTreeKind::Path(name, alias) => { let ident = alias.as_ref().unwrap_or(name); - if unused_items.contains_key(ident) { - (None, 1) - } else { - (Some(use_tree.clone()), 0) - } + if unused_items.contains_key(ident) { (None, 1) } else { (Some(use_tree.clone()), 0) } } UseTreeKind::List(use_trees) => { let mut new_use_trees: Vec = Vec::new(); @@ -106,12 +103,12 @@ fn use_tree_without_unused_import( let mut prefix = use_tree.prefix.clone(); prefix.segments.extend(new_use_tree.prefix.segments); - Some(UseTree { prefix, kind: new_use_tree.kind, span: use_tree.span }) + Some(UseTree { prefix, kind: new_use_tree.kind, location: use_tree.location }) } else { Some(UseTree { prefix: use_tree.prefix.clone(), kind: UseTreeKind::List(new_use_trees), - span: use_tree.span, + location: use_tree.location, }) }; @@ -130,7 +127,7 @@ fn use_tree_to_string(use_tree: UseTree, visibility: ItemVisibility, nesting: us let parsed_module = ParsedModule { items: vec![Item { kind: ItemKind::Import(use_tree, visibility), - span: Span::from(0..source.len() as u32), + location: Location::new(Span::from(0..source.len() as u32), FileId::dummy()), doc_comments: Vec::new(), }], inner_doc_comments: Vec::new(), diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/tests.rs index f5748e29d97b..23026d16d943 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/tests.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/tests.rs @@ -56,11 +56,7 @@ pub(crate) async fn assert_code_action(title: &str, src: &str, expected: &str) { .iter() .filter_map(|action| { if let CodeActionOrCommand::CodeAction(action) = action { - if action.title == title { - Some(action) - } else { - None - } + if action.title == title { Some(action) } else { None } } else { None } 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 ab98ab8bf100..1870e8e06024 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 @@ -7,9 +7,8 @@ use noirc_driver::check_crate; use noirc_frontend::hir::FunctionNameMatch; use crate::{ - byte_span_to_range, prepare_source, resolve_workspace_for_source_path, + LspState, byte_span_to_range, prepare_source, resolve_workspace_for_source_path, types::{CodeLens, CodeLensParams, CodeLensResult, Command}, - LspState, }; const ARROW: &str = "▶\u{fe0e}"; @@ -40,7 +39,7 @@ fn package_selection_args(workspace: &Workspace, package: &Package) -> Vec impl Future> { +) -> impl Future> + use<> { future::ready(on_code_lens_request_inner(state, params)) } diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion.rs b/noir/noir-repo/tooling/lsp/src/requests/completion.rs index 2f06f6607712..eab8522693f8 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion.rs @@ -15,6 +15,7 @@ use kinds::{FunctionCompletionKind, FunctionKind, RequestedItems}; use lsp_types::{CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse}; use noirc_errors::{Location, Span}; use noirc_frontend::{ + DataType, Kind, ParsedModule, Type, TypeBinding, ast::{ AsTraitPath, AttributeTarget, BlockExpression, CallExpression, ConstructorExpression, Expression, ExpressionKind, ForLoopStatement, GenericTypeArgs, Ident, IfExpression, @@ -35,17 +36,16 @@ use noirc_frontend::{ node_interner::{FuncId, NodeInterner, ReferenceId, TypeId}, parser::{Item, ItemKind, ParsedSubModule}, token::{MetaAttribute, Token, Tokens}, - DataType, Kind, ParsedModule, Type, TypeBinding, }; use sort_text::underscore_sort_text; use crate::{ - requests::to_lsp_location, trait_impl_method_stub_generator::TraitImplMethodStubGenerator, + LspState, requests::to_lsp_location, + trait_impl_method_stub_generator::TraitImplMethodStubGenerator, use_segment_positions::UseSegmentPositions, utils, visibility::module_def_id_is_visible, - LspState, }; -use super::{process_request, TraitReexport}; +use super::{TraitReexport, process_request}; mod auto_import; mod builtins; @@ -57,7 +57,7 @@ mod tests; pub(crate) fn on_completion_request( state: &mut LspState, params: CompletionParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let uri = params.text_document_position.clone().text_document.uri; let result = process_request(state, params.text_document_position.clone(), |args| { @@ -72,7 +72,7 @@ pub(crate) fn on_completion_request( let file = args.files.get_file(file_id).unwrap(); let source = file.source(); let byte = source.as_bytes().get(byte_index - 1).copied(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let mut finder = NodeFinder::new( args.files, @@ -196,7 +196,7 @@ impl<'a> NodeFinder<'a> { let span = if let UnresolvedTypeData::Named(path, _, _) = &constructor_expression.typ.typ { path.last_ident().span() } else { - constructor_expression.typ.span + constructor_expression.typ.location.span }; let location = Location::new(span, self.file); @@ -241,7 +241,7 @@ impl<'a> NodeFinder<'a> { requested_items: RequestedItems, mut in_the_middle: bool, ) { - if !self.includes_span(path.span) { + if !self.includes_span(path.location.span) { return; } @@ -268,7 +268,10 @@ impl<'a> NodeFinder<'a> { let substring = ident.0.contents[0..offset].to_string(); let ident = Ident::new( substring, - Span::from(ident.span().start()..ident.span().start() + offset as u32), + Location::new( + Span::from(ident.span().start()..ident.span().start() + offset as u32), + ident.location().file, + ), ); idents.push(ident); in_the_middle = true; @@ -605,7 +608,7 @@ impl<'a> NodeFinder<'a> { self.complete_tuple_fields(types, self_prefix); } Type::TypeVariable(var) | Type::NamedGeneric(var, _) => { - if let TypeBinding::Bound(ref typ) = &*var.borrow() { + if let TypeBinding::Bound(typ) = &*var.borrow() { return self.complete_type_fields_and_methods( typ, prefix, @@ -1020,7 +1023,7 @@ impl<'a> NodeFinder<'a> { noir_function: &NoirFunction, ) { // First find the trait - let location = Location::new(noir_trait_impl.r#trait.span, self.file); + let location = noir_trait_impl.r#trait.location; let Some(ReferenceId::Trait(trait_id)) = self.interner.find_referenced(location) else { return; }; @@ -1199,16 +1202,16 @@ impl<'a> NodeFinder<'a> { } } -impl<'a> Visitor for NodeFinder<'a> { +impl Visitor for NodeFinder<'_> { fn visit_item(&mut self, item: &Item) -> bool { if let ItemKind::Import(use_tree, _) = &item.kind { - if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { + if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.location.span) { self.auto_import_line = (lsp_location.range.end.line + 1) as usize; } self.use_segment_positions.add(use_tree); } - self.includes_span(item.span) + self.includes_span(item.location.span) } fn visit_import( @@ -1342,11 +1345,11 @@ impl<'a> Visitor for NodeFinder<'a> { self.type_parameters.clear(); self.collect_type_parameters_in_generics(&type_impl.generics); - for (method, span) in &type_impl.methods { - method.item.accept(*span, self); + for (method, location) in &type_impl.methods { + method.item.accept(location.span, self); // Optimization: stop looking in functions past the completion cursor - if span.end() as usize > self.byte_index { + if location.span.end() as usize > self.byte_index { break; } } @@ -1425,7 +1428,7 @@ impl<'a> Visitor for NodeFinder<'a> { // we don't want to insert arguments, because they are already there (even if // they could be wrong) just because inserting them would lead to broken code. if let ExpressionKind::Variable(path) = &call_expression.func.kind { - if self.includes_span(path.span) { + if self.includes_span(path.location.span) { self.find_in_path_impl(path, RequestedItems::AnyItems, true); return false; } @@ -1439,8 +1442,8 @@ impl<'a> Visitor for NodeFinder<'a> { // as "foo(...)" but if we are at a dot right after "foo" it means it's // the above case and we want to suggest methods of foo's type. let after_dot = self.byte == Some(b'.'); - if after_dot && call_expression.func.span.end() as usize == self.byte_index - 1 { - let location = Location::new(call_expression.func.span, self.file); + if after_dot && call_expression.func.location.span.end() as usize == self.byte_index - 1 { + let location = call_expression.func.location; if let Some(typ) = self.interner.type_at_location(location) { let prefix = ""; let self_prefix = false; @@ -1470,7 +1473,7 @@ impl<'a> Visitor for NodeFinder<'a> { // we don't want to insert arguments, because they are already there (even if // they could be wrong) just because inserting them would lead to broken code. if self.includes_span(method_call_expression.method_name.span()) { - let location = Location::new(method_call_expression.object.span, self.file); + let location = method_call_expression.object.location; if let Some(typ) = self.interner.type_at_location(location) { let prefix = method_call_expression.method_name.to_string(); let offset = @@ -1500,7 +1503,7 @@ impl<'a> Visitor for NodeFinder<'a> { statement.accept(self); // Optimization: stop looking in statements past the completion cursor - if statement.span.end() as usize > self.byte_index { + if statement.location.span.end() as usize > self.byte_index { break; } } @@ -1648,9 +1651,9 @@ impl<'a> Visitor for NodeFinder<'a> { // to complete for `bar`, not for `foo & bar`. if self.completion_items.is_empty() && self.byte == Some(b'.') - && expression.span.end() as usize == self.byte_index - 1 + && expression.location.span.end() as usize == self.byte_index - 1 { - let location = Location::new(expression.span, self.file); + let location = expression.location; if let Some(typ) = self.interner.type_at_location(location) { let prefix = ""; let self_prefix = false; @@ -1723,7 +1726,7 @@ impl<'a> Visitor for NodeFinder<'a> { if self.byte_index == ident.span().end() as usize { // Assuming member_access_expression is of the form `foo.bar`, we are right after `bar` - let location = Location::new(member_access_expression.lhs.span, self.file); + let location = member_access_expression.lhs.location; if let Some(typ) = self.interner.type_at_location(location) { let prefix = ident.to_string().to_case(Case::Snake); let self_prefix = false; @@ -1780,7 +1783,7 @@ impl<'a> Visitor for NodeFinder<'a> { } fn visit_unresolved_type(&mut self, unresolved_type: &UnresolvedType) -> bool { - self.includes_span(unresolved_type.span) + self.includes_span(unresolved_type.location.span) } fn visit_named_type( @@ -1833,7 +1836,7 @@ impl<'a> Visitor for NodeFinder<'a> { } fn visit_meta_attribute(&mut self, attribute: &MetaAttribute, target: AttributeTarget) -> bool { - if self.byte_index == attribute.name.span.end() as usize { + if self.byte_index == attribute.name.location.span.end() as usize { self.suggest_builtin_attributes(&attribute.name.to_string(), target); } @@ -1846,7 +1849,7 @@ impl<'a> Visitor for NodeFinder<'a> { let mut last_was_dollar = false; for token in &tokens.0 { - let span = token.to_span(); + let span = token.span(); if span.end() as usize > self.byte_index { break; } @@ -1903,13 +1906,10 @@ fn get_field_type(typ: &Type, name: &str) -> Option { } } Type::Alias(alias_type, generics) => Some(alias_type.borrow().get_type(generics)), - Type::TypeVariable(var) | Type::NamedGeneric(var, _) => { - if let TypeBinding::Bound(ref typ) = &*var.borrow() { - get_field_type(typ, name) - } else { - None - } - } + Type::TypeVariable(var) | Type::NamedGeneric(var, _) => match &*var.borrow() { + TypeBinding::Bound(typ) => get_field_type(typ, name), + _ => None, + }, _ => None, } } @@ -1921,13 +1921,10 @@ fn get_array_element_type(typ: Type) -> Option { let typ = alias_type.borrow().get_type(&generics); get_array_element_type(typ) } - Type::TypeVariable(var) | Type::NamedGeneric(var, _) => { - if let TypeBinding::Bound(typ) = &*var.borrow() { - get_array_element_type(typ.clone()) - } else { - None - } - } + Type::TypeVariable(var) | Type::NamedGeneric(var, _) => match &*var.borrow() { + TypeBinding::Bound(typ) => get_array_element_type(typ.clone()), + _ => None, + }, _ => None, } } diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs index 08d155f333c8..cc80c8d8e010 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs @@ -3,18 +3,18 @@ use noirc_frontend::{ast::ItemVisibility, hir::def_map::ModuleDefId, node_intern use crate::{ modules::{get_ancestor_module_reexport, module_def_id_relative_path}, use_segment_positions::{ - use_completion_item_additional_text_edits, UseCompletionItemAdditionTextEditsRequest, + UseCompletionItemAdditionTextEditsRequest, use_completion_item_additional_text_edits, }, }; use super::{ + NodeFinder, kinds::{FunctionCompletionKind, FunctionKind, RequestedItems}, name_matches, sort_text::auto_import_sort_text, - NodeFinder, }; -impl<'a> NodeFinder<'a> { +impl NodeFinder<'_> { pub(super) fn complete_auto_imports( &mut self, prefix: &str, diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs index 10267d4719bc..ce5b5f35f461 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs @@ -3,15 +3,16 @@ use noirc_frontend::{ast::AttributeTarget, token::Keyword}; use strum::IntoEnumIterator; use super::{ + NodeFinder, completion_items::{ completion_item_with_trigger_parameter_hints_command, simple_completion_item, snippet_completion_item, }, kinds::FunctionCompletionKind, - name_matches, NodeFinder, + name_matches, }; -impl<'a> NodeFinder<'a> { +impl NodeFinder<'_> { pub(super) fn builtin_functions_completion( &mut self, prefix: &str, @@ -150,8 +151,8 @@ impl<'a> NodeFinder<'a> { } } -pub(super) fn builtin_integer_types() -> [&'static str; 8] { - ["i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64"] +pub(super) fn builtin_integer_types() -> [&'static str; 9] { + ["i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", "u128"] } /// If a keyword corresponds to a built-in type, returns that type's name. diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs index cfd11bfe1adf..bc266c03f768 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs @@ -3,29 +3,29 @@ use lsp_types::{ InsertTextFormat, MarkupContent, MarkupKind, }; use noirc_frontend::{ + QuotedType, Type, ast::AttributeTarget, hir::def_map::{ModuleDefId, ModuleId}, hir_def::{function::FuncMeta, stmt::HirPattern}, node_interner::{FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId, TypeId}, - QuotedType, Type, }; use crate::{ modules::{relative_module_full_path, relative_module_id_path}, use_segment_positions::{ - use_completion_item_additional_text_edits, UseCompletionItemAdditionTextEditsRequest, + UseCompletionItemAdditionTextEditsRequest, use_completion_item_additional_text_edits, }, }; use super::{ + FunctionCompletionKind, FunctionKind, NodeFinder, RequestedItems, TraitReexport, sort_text::{ crate_or_module_sort_text, default_sort_text, new_sort_text, operator_sort_text, self_mismatch_sort_text, }, - FunctionCompletionKind, FunctionKind, NodeFinder, RequestedItems, TraitReexport, }; -impl<'a> NodeFinder<'a> { +impl NodeFinder<'_> { pub(super) fn module_def_id_completion_items( &self, module_def_id: ModuleDefId, @@ -44,7 +44,7 @@ impl<'a> NodeFinder<'a> { }, RequestedItems::OnlyTraits => match module_def_id { ModuleDefId::FunctionId(_) | ModuleDefId::GlobalId(_) | ModuleDefId::TypeId(_) => { - return Vec::new() + return Vec::new(); } ModuleDefId::ModuleId(_) | ModuleDefId::TypeAliasId(_) diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs index ba6faada6f4f..042b10b9cbab 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs @@ -1,4 +1,4 @@ -use noirc_frontend::{ast::AttributeTarget, Type}; +use noirc_frontend::{Type, ast::AttributeTarget}; /// When suggest a function as a result of completion, whether to autocomplete its name or its name and parameters. #[derive(Clone, Copy, PartialEq, Eq, Debug)] diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs index cbe1a93391a7..3958b92deb08 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs @@ -67,11 +67,7 @@ mod completion_tests { .await .expect("Could not execute on_completion_request"); - if let Some(CompletionResponse::Array(items)) = response { - items - } else { - vec![] - } + if let Some(CompletionResponse::Array(items)) = response { items } else { vec![] } } fn assert_items_match(mut items: Vec, mut expected: Vec) { @@ -799,9 +795,11 @@ mod completion_tests { "#; let items = get_completions(src).await; - assert!(items - .iter() - .any(|item| item.label == "true" && item.kind == Some(CompletionItemKind::KEYWORD))); + assert!( + items + .iter() + .any(|item| item.label == "true" && item.kind == Some(CompletionItemKind::KEYWORD)) + ); } #[test] @@ -2659,8 +2657,8 @@ fn main() { } #[test] - async fn test_suggests_only_macro_call_if_comptime_function_returns_quoted_and_outside_comptime( - ) { + async fn test_suggests_only_macro_call_if_comptime_function_returns_quoted_and_outside_comptime() + { let src = r#" comptime fn foobar() -> Quoted {} 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 b32b2fc7ad7f..4827d8827afb 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/document_symbol.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/document_symbol.rs @@ -8,12 +8,12 @@ use lsp_types::{ }; use noirc_errors::Span; use noirc_frontend::{ + ParsedModule, ast::{ Expression, FunctionReturnType, Ident, LetStatement, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, TypeImpl, UnresolvedType, UnresolvedTypeData, Visitor, }, parser::ParsedSubModule, - ParsedModule, }; use crate::LspState; @@ -23,7 +23,7 @@ use super::process_request; pub(crate) fn on_document_symbol_request( state: &mut LspState, params: DocumentSymbolParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let Ok(file_path) = params.text_document.uri.to_file_path() else { return future::ready(Ok(None)); }; @@ -37,7 +37,7 @@ pub(crate) fn on_document_symbol_request( args.files.get_file_id(&PathString::from_path(file_path)).map(|file_id| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let mut collector = DocumentSymbolCollector::new(file_id, args.files); let symbols = collector.collect(&parsed_module); @@ -75,7 +75,7 @@ impl<'a> DocumentSymbolCollector<'a> { }; let span = if let Some(typ) = typ { - Span::from(name.span().start()..typ.span.end()) + Span::from(name.span().start()..typ.location.span.end()) } else { name.span() }; @@ -114,11 +114,11 @@ impl<'a> DocumentSymbolCollector<'a> { let mut span = name.span(); // If there's a type span, extend the span to include it - span = Span::from(span.start()..typ.span.end()); + span = Span::from(span.start()..typ.location.span.end()); // If there's a default value, extend the span to include it if let Some(default_value) = default_value { - span = Span::from(span.start()..default_value.span.end()); + span = Span::from(span.start()..default_value.location.span.end()); } let Some(location) = self.to_lsp_location(span) else { @@ -143,7 +143,7 @@ impl<'a> DocumentSymbolCollector<'a> { } } -impl<'a> Visitor for DocumentSymbolCollector<'a> { +impl Visitor for DocumentSymbolCollector<'_> { fn visit_noir_function(&mut self, noir_function: &NoirFunction, span: Span) -> bool { if noir_function.def.name.0.contents.is_empty() { return false; @@ -190,7 +190,7 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { for field in &noir_struct.fields { let field_name = &field.item.name; let typ = &field.item.typ; - let span = Span::from(field_name.span().start()..typ.span.end()); + let span = Span::from(field_name.span().start()..typ.location.span.end()); let Some(field_location) = self.to_lsp_location(span) else { continue; @@ -292,18 +292,18 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { // If there's a return type, extend the span to include it match return_type { - FunctionReturnType::Default(return_type_span) => { - span = Span::from(span.start()..return_type_span.end()); + FunctionReturnType::Default(return_type_location) => { + span = Span::from(span.start()..return_type_location.span.end()); } FunctionReturnType::Ty(typ) => { - span = Span::from(span.start()..typ.span.end()); + span = Span::from(span.start()..typ.location.span.end()); } } // If there's a body, extend the span to include it if let Some(body) = body { if let Some(statement) = body.statements.last() { - span = Span::from(span.start()..statement.span.end()); + span = Span::from(span.start()..statement.location.span.end()); } } @@ -349,14 +349,14 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { return false; }; - let name_span = + let name_location = if let UnresolvedTypeData::Named(trait_name, _, _) = &noir_trait_impl.r#trait.typ { - trait_name.span + trait_name.location } else { - noir_trait_impl.r#trait.span + noir_trait_impl.r#trait.location }; - let Some(name_location) = self.to_lsp_location(name_span) else { + let Some(name_location) = self.to_lsp_location(name_location.span) else { return false; }; @@ -418,15 +418,15 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { return false; } - let Some(name_location) = self.to_lsp_location(type_impl.object_type.span) else { + let Some(name_location) = self.to_lsp_location(type_impl.object_type.location.span) else { return false; }; let old_symbols = std::mem::take(&mut self.symbols); self.symbols = Vec::new(); - for (noir_function, noir_function_span) in &type_impl.methods { - noir_function.item.accept(*noir_function_span, self); + for (noir_function, noir_function_location) in &type_impl.methods { + noir_function.item.accept(noir_function_location.span, self); } let children = std::mem::take(&mut self.symbols); @@ -660,7 +660,7 @@ mod document_symbol_tests { deprecated: None, range: Range { start: Position { line: 15, character: 7 }, - end: Position { line: 15, character: 24 }, + end: Position { line: 15, character: 25 }, }, selection_range: Range { start: Position { line: 15, character: 7 }, diff --git a/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs b/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs index bd0f0afb827c..9df6a25f8cf1 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs @@ -1,7 +1,7 @@ use std::future::{self, Future}; -use crate::types::GotoDeclarationResult; use crate::LspState; +use crate::types::GotoDeclarationResult; use async_lsp::ResponseError; use lsp_types::request::{GotoDeclarationParams, GotoDeclarationResponse}; @@ -11,7 +11,7 @@ use super::{process_request, to_lsp_location}; pub(crate) fn on_goto_declaration_request( state: &mut LspState, params: GotoDeclarationParams, -) -> impl Future> { +) -> impl Future> + use<> { let result = on_goto_definition_inner(state, params); future::ready(result) } diff --git a/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs b/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs index a2443ea165dc..4e015e948610 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs @@ -2,7 +2,7 @@ use std::future::{self, Future}; use crate::attribute_reference_finder::AttributeReferenceFinder; use crate::utils; -use crate::{types::GotoDefinitionResult, LspState}; +use crate::{LspState, types::GotoDefinitionResult}; use async_lsp::ResponseError; use fm::PathString; @@ -14,7 +14,7 @@ use super::{process_request, to_lsp_location}; pub(crate) fn on_goto_definition_request( state: &mut LspState, params: GotoDefinitionParams, -) -> impl Future> { +) -> impl Future> + use<> { let result = on_goto_definition_inner(state, params, false); future::ready(result) } @@ -22,7 +22,7 @@ pub(crate) fn on_goto_definition_request( pub(crate) fn on_goto_type_definition_request( state: &mut LspState, params: GotoTypeDefinitionParams, -) -> impl Future> { +) -> impl Future> + use<> { let result = on_goto_definition_inner(state, params, true); future::ready(result) } @@ -40,7 +40,7 @@ fn on_goto_definition_inner( utils::position_to_byte_index(args.files, file_id, &position).and_then(|byte_index| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let mut finder = AttributeReferenceFinder::new( file_id, diff --git a/noir/noir-repo/tooling/lsp/src/requests/hover.rs b/noir/noir-repo/tooling/lsp/src/requests/hover.rs index cbb7dafd3c51..0b53b56eaf91 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/hover.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/hover.rs @@ -16,7 +16,7 @@ mod from_visitor; pub(crate) fn on_hover_request( state: &mut LspState, params: HoverParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let uri = params.text_document_position_params.text_document.uri.clone(); let position = params.text_document_position_params.position; let result = process_request(state, params.text_document_position_params, |args| { diff --git a/noir/noir-repo/tooling/lsp/src/requests/hover/from_reference.rs b/noir/noir-repo/tooling/lsp/src/requests/hover/from_reference.rs index 7f589b9df70a..3bc3b3bded70 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/hover/from_reference.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/hover/from_reference.rs @@ -1,6 +1,8 @@ use fm::{FileId, FileMap}; use lsp_types::{Hover, HoverContents, MarkupContent, MarkupKind, Position}; use noirc_frontend::{ + DataType, EnumVariant, Generics, Shared, StructField, Type, TypeAlias, TypeBinding, + TypeVariable, ast::{ItemVisibility, Visibility}, hir::def_map::ModuleId, hir_def::{ @@ -13,14 +15,12 @@ use noirc_frontend::{ DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, NodeInterner, ReferenceId, TraitId, TraitImplKind, TypeAliasId, TypeId, }, - DataType, EnumVariant, Generics, Shared, StructField, Type, TypeAlias, TypeBinding, - TypeVariable, }; use crate::{ attribute_reference_finder::AttributeReferenceFinder, modules::module_full_path, - requests::{to_lsp_location, ProcessRequestCallbackArgs}, + requests::{ProcessRequestCallbackArgs, to_lsp_location}, utils, }; @@ -34,7 +34,7 @@ pub(super) fn hover_from_reference( utils::position_to_byte_index(args.files, file_id, &position).and_then(|byte_index| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let mut finder = AttributeReferenceFinder::new( file_id, @@ -318,7 +318,7 @@ fn get_global_value(interner: &NodeInterner, expr: ExprId) -> Option { get_global_array_value(interner, hir_array_literal, true) } HirLiteral::Bool(value) => Some(value.to_string()), - HirLiteral::Integer(field_element, _) => Some(field_element.to_string()), + HirLiteral::Integer(value) => Some(value.to_string()), HirLiteral::Str(string) => Some(format!("{:?}", string)), HirLiteral::FmtStr(..) => None, HirLiteral::Unit => Some("()".to_string()), @@ -338,11 +338,7 @@ fn get_global_array_value( match literal { HirArrayLiteral::Standard(values) => { get_exprs_global_value(interner, &values).map(|value| { - if is_slice { - format!("&[{}]", value) - } else { - format!("[{}]", value) - } + if is_slice { format!("&[{}]", value) } else { format!("[{}]", value) } }) } HirArrayLiteral::Repeated { repeated_element, length } => { @@ -360,11 +356,7 @@ fn get_global_array_value( fn get_exprs_global_value(interner: &NodeInterner, exprs: &[ExprId]) -> Option { let strings: Vec = exprs.iter().filter_map(|value| get_global_value(interner, *value)).collect(); - if strings.len() == exprs.len() { - Some(strings.join(", ")) - } else { - None - } + if strings.len() == exprs.len() { Some(strings.join(", ")) } else { None } } fn format_function(id: FuncId, args: &ProcessRequestCallbackArgs) -> String { @@ -403,11 +395,7 @@ fn format_function(id: FuncId, args: &ProcessRequestCallbackArgs) -> String { .trait_generics .iter() .filter_map(|generic| { - if let Type::NamedGeneric(_, name) = generic { - Some(name) - } else { - None - } + if let Type::NamedGeneric(_, name) = generic { Some(name) } else { None } }) .collect(); @@ -781,7 +769,7 @@ struct TypeLinksGatherer<'a> { links: Vec, } -impl<'a> TypeLinksGatherer<'a> { +impl TypeLinksGatherer<'_> { fn gather_type_links(&mut self, typ: &Type) { match typ { Type::Array(typ, _) => self.gather_type_links(typ), diff --git a/noir/noir-repo/tooling/lsp/src/requests/hover/from_visitor.rs b/noir/noir-repo/tooling/lsp/src/requests/hover/from_visitor.rs index 2099d98a93f6..97ead183cd21 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/hover/from_visitor.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/hover/from_visitor.rs @@ -1,14 +1,15 @@ use std::str::FromStr; -use acvm::FieldElement; use fm::{FileId, FileMap}; use lsp_types::{Hover, HoverContents, MarkupContent, MarkupKind, Position}; use noirc_errors::{Location, Span}; -use noirc_frontend::{ast::Visitor, node_interner::NodeInterner, parse_program, Type}; +use noirc_frontend::{ + Type, ast::Visitor, node_interner::NodeInterner, parse_program, signed_field::SignedField, +}; use num_bigint::BigInt; use crate::{ - requests::{to_lsp_location, ProcessRequestCallbackArgs}, + requests::{ProcessRequestCallbackArgs, to_lsp_location}, utils, }; @@ -20,7 +21,7 @@ pub(super) fn hover_from_visitor( let file_id = file_id?; let file = args.files.get_file(file_id)?; let source = file.source(); - let (parsed_module, _errors) = parse_program(source); + let (parsed_module, _errors) = parse_program(source, file_id); let byte_index = utils::position_to_byte_index(args.files, file_id, &position)?; let mut finder = HoverFinder::new(args.files, file_id, args.interner, byte_index); @@ -50,8 +51,8 @@ impl<'a> HoverFinder<'a> { } } -impl<'a> Visitor for HoverFinder<'a> { - fn visit_literal_integer(&mut self, value: FieldElement, negative: bool, span: Span) { +impl Visitor for HoverFinder<'_> { + fn visit_literal_integer(&mut self, value: SignedField, span: Span) { if !self.intersects_span(span) { return; } @@ -63,28 +64,31 @@ impl<'a> Visitor for HoverFinder<'a> { return; }; - let value = format_integer(typ, value, negative); + let value = format_integer(typ, value); let contents = HoverContents::Markup(MarkupContent { kind: MarkupKind::Markdown, value }); self.hover = Some(Hover { contents, range }); } } -fn format_integer(typ: Type, value: FieldElement, negative: bool) -> String { - let value_base_10 = value.to_string(); +fn format_integer(typ: Type, value: SignedField) -> String { + let value_base_10 = value.field.to_string(); // For simplicity we parse the value as a BigInt to convert it to hex // because `FieldElement::to_hex` will include many leading zeros. let value_big_int = BigInt::from_str(&value_base_10).unwrap(); - let negative = if negative { "-" } else { "" }; + let negative = if value.is_negative { "-" } else { "" }; - format!(" {typ}\n---\nvalue of literal: `{negative}{value_base_10} ({negative}0x{value_big_int:02x})`") + format!( + " {typ}\n---\nvalue of literal: `{negative}{value_base_10} ({negative}0x{value_big_int:02x})`" + ) } #[cfg(test)] mod tests { use noirc_frontend::{ - ast::{IntegerBitSize, Signedness}, Type, + ast::{IntegerBitSize, Signedness}, + signed_field::SignedField, }; use super::format_integer; @@ -92,27 +96,24 @@ mod tests { #[test] fn format_integer_zero() { let typ = Type::FieldElement; - let value = 0_u128.into(); - let negative = false; + let value = SignedField::positive(0_u128); let expected = " Field\n---\nvalue of literal: `0 (0x00)`"; - assert_eq!(format_integer(typ, value, negative), expected); + assert_eq!(format_integer(typ, value), expected); } #[test] fn format_positive_integer() { let typ = Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo); - let value = 123456_u128.into(); - let negative = false; + let value = SignedField::positive(123456_u128); let expected = " u32\n---\nvalue of literal: `123456 (0x1e240)`"; - assert_eq!(format_integer(typ, value, negative), expected); + assert_eq!(format_integer(typ, value), expected); } #[test] fn format_negative_integer() { let typ = Type::Integer(Signedness::Signed, IntegerBitSize::SixtyFour); - let value = 987654_u128.into(); - let negative = true; + let value = SignedField::new(987654_u128.into(), true); let expected = " i64\n---\nvalue of literal: `-987654 (-0xf1206)`"; - assert_eq!(format_integer(typ, value, negative), expected); + assert_eq!(format_integer(typ, value), expected); } } 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 e2f793f06da8..be7722e744d2 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs @@ -8,7 +8,7 @@ use lsp_types::{ }; use noirc_errors::{Location, Span}; use noirc_frontend::{ - self, + self, Kind, Type, TypeBinding, TypeVariable, ast::{ CallExpression, Expression, ExpressionKind, ForLoopStatement, Ident, Lambda, LetStatement, MethodCallExpression, NoirFunction, NoirTraitImpl, Pattern, Statement, TypeImpl, @@ -17,17 +17,16 @@ use noirc_frontend::{ hir_def::stmt::HirPattern, node_interner::{NodeInterner, ReferenceId}, parser::{Item, ParsedSubModule}, - Kind, Type, TypeBinding, TypeVariable, }; -use crate::{utils, LspState}; +use crate::{LspState, utils}; -use super::{process_request, to_lsp_location, InlayHintsOptions}; +use super::{InlayHintsOptions, process_request, to_lsp_location}; pub(crate) fn on_inlay_hint_request( state: &mut LspState, params: InlayHintParams, -) -> impl Future>, ResponseError>> { +) -> impl Future>, ResponseError>> + use<> { let text_document_position_params = TextDocumentPositionParams { text_document: params.text_document.clone(), position: Position { line: 0, character: 0 }, @@ -40,7 +39,7 @@ pub(crate) fn on_inlay_hint_request( args.files.get_file_id(&path).map(|file_id| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let span = utils::range_to_byte_span(args.files, file_id, ¶ms.range) .map(|range| Span::from(range.start as u32..range.end as u32)); @@ -191,7 +190,7 @@ impl<'a> InlayHintCollector<'a> { for (call_argument, (pattern, _, _)) in arguments.iter().zip(parameters) { let Some(lsp_location) = - to_lsp_location(self.files, self.file_id, call_argument.span) + to_lsp_location(self.files, self.file_id, call_argument.location.span) else { continue; }; @@ -234,7 +233,7 @@ impl<'a> InlayHintCollector<'a> { fn collect_method_call_chain_hints(&mut self, method: &MethodCallExpression) { let Some(object_lsp_location) = - to_lsp_location(self.files, self.file_id, method.object.span) + to_lsp_location(self.files, self.file_id, method.object.location.span) else { return; }; @@ -249,7 +248,7 @@ impl<'a> InlayHintCollector<'a> { return; } - let object_location = Location::new(method.object.span, self.file_id); + let object_location = method.object.location; let Some(typ) = self.interner.type_at_location(object_location) else { return; }; @@ -302,7 +301,7 @@ impl<'a> InlayHintCollector<'a> { } fn intersects_span(&self, other_span: Span) -> bool { - self.span.map_or(true, |span| span.intersects(&other_span)) + self.span.is_none_or(|span| span.intersects(&other_span)) } fn show_closing_brace_hint(&mut self, span: Span, f: F) @@ -320,9 +319,9 @@ impl<'a> InlayHintCollector<'a> { } } -impl<'a> Visitor for InlayHintCollector<'a> { +impl Visitor for InlayHintCollector<'_> { fn visit_item(&mut self, item: &Item) -> bool { - self.intersects_span(item.span) + self.intersects_span(item.location.span) } fn visit_noir_trait_impl(&mut self, noir_trait_impl: &NoirTraitImpl, span: Span) -> bool { @@ -358,7 +357,7 @@ impl<'a> Visitor for InlayHintCollector<'a> { } fn visit_statement(&mut self, statement: &Statement) -> bool { - self.intersects_span(statement.span) + self.intersects_span(statement.location.span) } fn visit_let_statement(&mut self, let_statement: &LetStatement) -> bool { @@ -378,13 +377,13 @@ impl<'a> Visitor for InlayHintCollector<'a> { } fn visit_expression(&mut self, expression: &Expression) -> bool { - self.intersects_span(expression.span) + self.intersects_span(expression.location.span) } fn visit_call_expression(&mut self, call_expression: &CallExpression, _: Span) -> bool { self.collect_call_parameter_names( get_expression_name(&call_expression.func), - call_expression.func.span, + call_expression.func.location.span, &call_expression.arguments, ); @@ -516,18 +515,17 @@ fn push_type_parts(typ: &Type, parts: &mut Vec, files: &File parts.push(string_part("&mut ")); push_type_parts(typ, parts, files); } - Type::TypeVariable(binding) => { - if let TypeBinding::Unbound(_, kind) = &*binding.borrow() { - match kind { - Kind::Any | Kind::Normal => push_type_variable_parts(binding, parts, files), - Kind::Integer => push_type_parts(&Type::default_int_type(), parts, files), - Kind::IntegerOrField => parts.push(string_part("Field")), - Kind::Numeric(ref typ) => push_type_parts(typ, parts, files), - } - } else { + Type::TypeVariable(binding) => match &*binding.borrow() { + TypeBinding::Unbound(_, kind) => match kind { + Kind::Any | Kind::Normal => push_type_variable_parts(binding, parts, files), + Kind::Integer => push_type_parts(&Type::default_int_type(), parts, files), + Kind::IntegerOrField => parts.push(string_part("Field")), + Kind::Numeric(typ) => push_type_parts(typ, parts, files), + }, + _ => { push_type_variable_parts(binding, parts, files); } - } + }, Type::CheckedCast { to, .. } => push_type_parts(to, parts, files), Type::FieldElement @@ -919,8 +917,8 @@ mod inlay_hints_tests { } #[test] - async fn test_do_not_show_parameter_inlay_hints_if_single_param_name_is_suffix_of_function_name( - ) { + async fn test_do_not_show_parameter_inlay_hints_if_single_param_name_is_suffix_of_function_name() + { let inlay_hints = get_inlay_hints(64, 67, parameter_hints()).await; assert!(inlay_hints.is_empty()); } diff --git a/noir/noir-repo/tooling/lsp/src/requests/mod.rs b/noir/noir-repo/tooling/lsp/src/requests/mod.rs index 9bfe47bdaa55..9126ab38e103 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/mod.rs @@ -2,13 +2,13 @@ use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use std::{collections::HashMap, future::Future}; -use crate::{insert_all_files_for_workspace_into_file_manager, parse_diff, PackageCacheData}; +use crate::{PackageCacheData, insert_all_files_for_workspace_into_file_manager, parse_diff}; use crate::{ resolve_workspace_for_source_path, types::{CodeLensOptions, InitializeParams}, }; use async_lsp::{ErrorCode, ResponseError}; -use fm::{codespan_files::Error, FileMap, PathString}; +use fm::{FileMap, PathString, codespan_files::Error}; use lsp_types::{ CodeActionKind, DeclarationCapability, Location, Position, TextDocumentPositionParams, TextDocumentSyncCapability, TextDocumentSyncKind, TypeDefinitionProviderCapability, Url, @@ -25,8 +25,8 @@ use noirc_frontend::{graph::Dependency, node_interner::NodeInterner}; use serde::{Deserialize, Serialize}; use crate::{ - types::{InitializeResult, NargoCapability, NargoTestsOptions, ServerCapabilities}, LspState, + types::{InitializeResult, NargoCapability, NargoTestsOptions, ServerCapabilities}, }; // Handlers @@ -191,7 +191,7 @@ impl Default for LspInitializationOptions { pub(crate) fn on_initialize( state: &mut LspState, params: InitializeParams, -) -> impl Future> { +) -> impl Future> + use<> { state.root_path = params.root_uri.and_then(|root_uri| root_uri.to_file_path().ok()); let initialization_options: LspInitializationOptions = params .initialization_options @@ -293,7 +293,7 @@ pub(crate) fn on_initialize( pub(crate) fn on_formatting( state: &mut LspState, params: lsp_types::DocumentFormattingParams, -) -> impl Future>, ResponseError>> { +) -> impl Future>, ResponseError>> + use<> { std::future::ready(on_formatting_inner(state, params)) } @@ -308,7 +308,7 @@ fn on_formatting_inner( let path = params.text_document.uri.to_string(); if let Some(source) = state.input_files.get(&path) { - let (module, errors) = noirc_frontend::parse_program(source); + let (module, errors) = noirc_frontend::parse_program_with_dummy_file(source); let is_all_warnings = errors.iter().all(ParserError::is_warning); if !is_all_warnings { return Ok(None); @@ -441,7 +441,7 @@ where pub(crate) fn on_shutdown( _state: &mut LspState, _params: (), -) -> impl Future> { +) -> impl Future> + use<> { async { Ok(()) } } @@ -628,11 +628,7 @@ pub(crate) fn find_all_references_in_workspace( }); locations.dedup(); - if locations.is_empty() { - None - } else { - Some(locations) - } + if locations.is_empty() { None } else { Some(locations) } } else { None } @@ -671,7 +667,7 @@ mod initialization { }; use tokio::test; - use crate::{requests::on_initialize, types::ServerCapabilities, LspState}; + use crate::{LspState, requests::on_initialize, types::ServerCapabilities}; #[test] async fn test_on_initialize() { diff --git a/noir/noir-repo/tooling/lsp/src/requests/references.rs b/noir/noir-repo/tooling/lsp/src/requests/references.rs index 3b4ef12b5b74..fbe69c99871d 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/references.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/references.rs @@ -10,7 +10,7 @@ use super::{find_all_references_in_workspace, process_request}; pub(crate) fn on_references_request( state: &mut LspState, params: ReferenceParams, -) -> impl Future>, ResponseError>> { +) -> impl Future>, ResponseError>> + use<> { let include_declaration = params.context.include_declaration; let result = process_request(state, params.text_document_position, |args| { find_all_references_in_workspace( diff --git a/noir/noir-repo/tooling/lsp/src/requests/rename.rs b/noir/noir-repo/tooling/lsp/src/requests/rename.rs index 95dd6b506be2..00e39451caea 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/rename.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/rename.rs @@ -16,7 +16,7 @@ use super::{find_all_references_in_workspace, process_request}; pub(crate) fn on_prepare_rename_request( state: &mut LspState, params: TextDocumentPositionParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let result = process_request(state, params, |args| { let reference_id = args.interner.reference_at_location(args.location); let rename_possible = match reference_id { @@ -33,7 +33,7 @@ pub(crate) fn on_prepare_rename_request( pub(crate) fn on_rename_request( state: &mut LspState, params: RenameParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let result = process_request(state, params.text_document_position, |args| { let rename_changes = find_all_references_in_workspace( args.location, @@ -111,7 +111,10 @@ mod rename_tests { changes.iter().filter(|range| !ranges.contains(range)).collect(); let extra_in_ranges: Vec<_> = ranges.iter().filter(|range| !changes.contains(range)).collect(); - panic!("Rename locations did not match.\nThese renames were not found: {:?}\nThese renames should not have been found: {:?}", extra_in_ranges, extra_in_changes); + panic!( + "Rename locations did not match.\nThese renames were not found: {:?}\nThese renames should not have been found: {:?}", + extra_in_ranges, extra_in_changes + ); } assert_eq!(changes, ranges); } diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs index 4a2609d7ae38..1709106e1218 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs @@ -7,6 +7,7 @@ use lsp_types::{ }; use noirc_errors::{Location, Span}; use noirc_frontend::{ + ParsedModule, Type, ast::{ CallExpression, ConstrainExpression, ConstrainKind, Expression, FunctionReturnType, MethodCallExpression, Statement, Visitor, @@ -14,10 +15,9 @@ use noirc_frontend::{ hir_def::{function::FuncMeta, stmt::HirPattern}, node_interner::{NodeInterner, ReferenceId}, parser::Item, - ParsedModule, Type, }; -use crate::{utils, LspState}; +use crate::{LspState, utils}; use super::process_request; @@ -26,7 +26,7 @@ mod tests; pub(crate) fn on_signature_help_request( state: &mut LspState, params: SignatureHelpParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let uri = params.text_document_position_params.clone().text_document.uri; let result = process_request(state, params.text_document_position_params.clone(), |args| { @@ -40,7 +40,7 @@ pub(crate) fn on_signature_help_request( .and_then(|byte_index| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let mut finder = SignatureFinder::new(file_id, byte_index, args.interner); finder.find(&parsed_module) @@ -312,7 +312,9 @@ impl<'a> SignatureFinder<'a> { fn compute_active_parameter(&self, arguments: &[Expression]) -> Option { let mut active_parameter = None; for (index, arg) in arguments.iter().enumerate() { - if self.includes_span(arg.span) || arg.span.start() as usize >= self.byte_index { + if self.includes_span(arg.location.span) + || arg.location.span.start() as usize >= self.byte_index + { active_parameter = Some(index as u32); break; } @@ -330,24 +332,25 @@ impl<'a> SignatureFinder<'a> { } } -impl<'a> Visitor for SignatureFinder<'a> { +impl Visitor for SignatureFinder<'_> { fn visit_item(&mut self, item: &Item) -> bool { - self.includes_span(item.span) + self.includes_span(item.location.span) } fn visit_statement(&mut self, statement: &Statement) -> bool { - self.includes_span(statement.span) + self.includes_span(statement.location.span) } fn visit_expression(&mut self, expression: &Expression) -> bool { - self.includes_span(expression.span) + self.includes_span(expression.location.span) } fn visit_call_expression(&mut self, call_expression: &CallExpression, span: Span) -> bool { call_expression.accept_children(self); - let arguments_span = Span::from(call_expression.func.span.end() + 1..span.end() - 1); - let span = call_expression.func.span; + let arguments_span = + Span::from(call_expression.func.location.span.end() + 1..span.end() - 1); + let span = call_expression.func.location.span; let name_span = Span::from(span.end() - 1..span.end()); let has_self = false; @@ -391,7 +394,7 @@ impl<'a> Visitor for SignatureFinder<'a> { } let kind_len = constrain_statement.kind.to_string().len() as u32; - let span = constrain_statement.span; + let span = constrain_statement.location.span; let arguments_span = Span::from(span.start() + kind_len + 1..span.end() - 1); if !self.includes_span(arguments_span) { diff --git a/noir/noir-repo/tooling/lsp/src/requests/test_run.rs b/noir/noir-repo/tooling/lsp/src/requests/test_run.rs index e2d8edd46c84..6b7a53e4596e 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/test_run.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/test_run.rs @@ -3,24 +3,23 @@ use std::future::{self, Future}; use crate::insert_all_files_for_workspace_into_file_manager; use async_lsp::{ErrorCode, ResponseError}; use nargo::{ - foreign_calls::DefaultForeignCallBuilder, - ops::{run_test, TestStatus}, PrintOutput, + foreign_calls::DefaultForeignCallBuilder, + ops::{TestStatus, run_test}, }; -use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection}; -use noirc_driver::{check_crate, CompileOptions, NOIR_ARTIFACT_VERSION_STRING}; +use nargo_toml::{PackageSelection, find_package_manifest, resolve_workspace_from_toml}; +use noirc_driver::{CompileOptions, NOIR_ARTIFACT_VERSION_STRING, check_crate}; use noirc_frontend::hir::FunctionNameMatch; use crate::{ - parse_diff, + LspState, parse_diff, types::{NargoTestRunParams, NargoTestRunResult}, - LspState, }; pub(crate) fn on_test_run_request( state: &mut LspState, params: NargoTestRunParams, -) -> impl Future> { +) -> impl Future> + use<> { future::ready(on_test_run_request_inner(state, params)) } @@ -120,7 +119,7 @@ fn on_test_run_request_inner( TestStatus::CompileError(diag) => NargoTestRunResult { id: params.id.clone(), result: "error".to_string(), - message: Some(diag.diagnostic.message), + message: Some(diag.message), }, }; Ok(result) diff --git a/noir/noir-repo/tooling/lsp/src/requests/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/tests.rs index 81910bebedba..234bd971eb5d 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/tests.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/tests.rs @@ -3,19 +3,18 @@ use std::future::{self, Future}; use crate::insert_all_files_for_workspace_into_file_manager; use async_lsp::{ErrorCode, LanguageClient, ResponseError}; use lsp_types::{LogMessageParams, MessageType}; -use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection}; -use noirc_driver::{check_crate, NOIR_ARTIFACT_VERSION_STRING}; +use nargo_toml::{PackageSelection, find_package_manifest, resolve_workspace_from_toml}; +use noirc_driver::{NOIR_ARTIFACT_VERSION_STRING, check_crate}; use crate::{ - get_package_tests_in_crate, parse_diff, + LspState, get_package_tests_in_crate, parse_diff, types::{NargoPackageTests, NargoTestsParams, NargoTestsResult}, - LspState, }; pub(crate) fn on_tests_request( state: &mut LspState, params: NargoTestsParams, -) -> impl Future> { +) -> impl Future> + use<> { future::ready(on_tests_request_inner(state, params)) } @@ -73,9 +72,5 @@ fn on_tests_request_inner( }) .collect(); - if package_tests.is_empty() { - Ok(None) - } else { - Ok(Some(package_tests)) - } + if package_tests.is_empty() { Ok(None) } else { Ok(Some(package_tests)) } } diff --git a/noir/noir-repo/tooling/lsp/src/trait_impl_method_stub_generator.rs b/noir/noir-repo/tooling/lsp/src/trait_impl_method_stub_generator.rs index a7444f819935..3a60a882aeab 100644 --- a/noir/noir-repo/tooling/lsp/src/trait_impl_method_stub_generator.rs +++ b/noir/noir-repo/tooling/lsp/src/trait_impl_method_stub_generator.rs @@ -1,6 +1,7 @@ use std::collections::BTreeMap; use noirc_frontend::{ + Kind, ResolvedGeneric, Type, ast::{NoirTraitImpl, UnresolvedTypeData}, graph::CrateId, hir::{ @@ -9,7 +10,6 @@ use noirc_frontend::{ }, hir_def::{function::FuncMeta, stmt::HirPattern, traits::Trait}, node_interner::{FunctionModifiers, NodeInterner, ReferenceId}, - Kind, ResolvedGeneric, Type, }; use crate::modules::relative_module_id_path; @@ -447,7 +447,7 @@ impl<'a> TraitImplMethodStubGenerator<'a> { Kind::Any | Kind::Normal | Kind::Integer | Kind::IntegerOrField => { self.string.push_str(&generic.name); } - Kind::Numeric(ref typ) => { + Kind::Numeric(typ) => { self.string.push_str("let "); self.string.push_str(&generic.name); self.string.push_str(": "); diff --git a/noir/noir-repo/tooling/lsp/src/types.rs b/noir/noir-repo/tooling/lsp/src/types.rs index b49377787e85..21d274a37764 100644 --- a/noir/noir-repo/tooling/lsp/src/types.rs +++ b/noir/noir-repo/tooling/lsp/src/types.rs @@ -15,7 +15,7 @@ pub(crate) use lsp_types::{ }; pub(crate) mod request { - use lsp_types::{request::Request, InitializeParams}; + use lsp_types::{InitializeParams, request::Request}; use super::{ InitializeResult, NargoTestRunParams, NargoTestRunResult, NargoTestsParams, diff --git a/noir/noir-repo/tooling/lsp/src/use_segment_positions.rs b/noir/noir-repo/tooling/lsp/src/use_segment_positions.rs index 2cd406ee7734..7ff83aecdc2a 100644 --- a/noir/noir-repo/tooling/lsp/src/use_segment_positions.rs +++ b/noir/noir-repo/tooling/lsp/src/use_segment_positions.rs @@ -96,7 +96,7 @@ impl UseSegmentPositions { kind_string, UseSegmentPosition::BeforeSegment { segment_span_until_end: Span::from( - segment.ident.span().start()..use_tree.span.end() - 1, + segment.ident.span().start()..use_tree.location.span.end() - 1, ), }, ); @@ -119,7 +119,7 @@ impl UseSegmentPositions { UseSegmentPosition::BeforeSegment { segment_span_until_end: Span::from( use_tree.prefix.segments[index + 1].ident.span().start() - ..use_tree.span.end() - 1, + ..use_tree.location.span.end() - 1, ), }, ); @@ -163,7 +163,7 @@ impl UseSegmentPositions { prefix, UseSegmentPosition::BeforeSegment { segment_span_until_end: Span::from( - ident.span().start()..use_tree.span.end() - 1, + ident.span().start()..use_tree.location.span.end() - 1, ), }, ); @@ -182,7 +182,7 @@ impl UseSegmentPositions { prefix, UseSegmentPosition::BeforeList { first_entry_span: Span::from( - use_tree.span.end() - 1..use_tree.span.end() - 1, + use_tree.location.span.end() - 1..use_tree.location.span.end() - 1, ), list_is_empty: true, }, diff --git a/noir/noir-repo/tooling/lsp/src/utils.rs b/noir/noir-repo/tooling/lsp/src/utils.rs index 96db1f7bfa23..ca607128bf2e 100644 --- a/noir/noir-repo/tooling/lsp/src/utils.rs +++ b/noir/noir-repo/tooling/lsp/src/utils.rs @@ -51,9 +51,5 @@ pub(crate) fn character_to_line_offset(line: &str, character: u32) -> Option ParsedModule { + ParsedModule { + items: parsed_module.items.into_iter().map(|item| item_with_file(item, file)).collect(), + inner_doc_comments: parsed_module.inner_doc_comments, + } +} + +fn item_with_file(item: Item, file: FileId) -> Item { + Item { + kind: item_kind_with_file(item.kind, file), + location: item.location, + doc_comments: item.doc_comments, + } +} + +fn item_kind_with_file(item_kind: ItemKind, file: FileId) -> ItemKind { + match item_kind { + ItemKind::Import(use_tree, item_visibility) => { + ItemKind::Import(use_tree_with_file(use_tree, file), item_visibility) + } + ItemKind::Function(noir_function) => { + ItemKind::Function(noir_function_with_file(noir_function, file)) + } + ItemKind::Struct(noir_struct) => ItemKind::Struct(noir_struct_with_file(noir_struct, file)), + ItemKind::Enum(noir_enumeration) => { + ItemKind::Enum(noir_enumeration_with_file(noir_enumeration, file)) + } + ItemKind::Trait(noir_trait) => ItemKind::Trait(noir_trait_with_file(noir_trait, file)), + ItemKind::TraitImpl(noir_trait_impl) => { + ItemKind::TraitImpl(noir_trait_impl_with_file(noir_trait_impl, file)) + } + ItemKind::Impl(type_impl) => ItemKind::Impl(type_impl_with_file(type_impl, file)), + ItemKind::TypeAlias(noir_type_alias) => { + ItemKind::TypeAlias(noir_type_alias_with_file(noir_type_alias, file)) + } + ItemKind::Global(let_statement, item_visibility) => { + ItemKind::Global(let_statement_with_file(let_statement, file), item_visibility) + } + ItemKind::ModuleDecl(module_declaration) => { + ItemKind::ModuleDecl(module_declaration_with_file(module_declaration, file)) + } + ItemKind::Submodules(parsed_sub_module) => { + ItemKind::Submodules(parsed_sub_module_with_file(parsed_sub_module, file)) + } + ItemKind::InnerAttribute(secondary_attribute) => { + ItemKind::InnerAttribute(secondary_attribute_with_file(secondary_attribute, file)) + } + } +} + +fn parsed_sub_module_with_file(module: ParsedSubModule, file: FileId) -> ParsedSubModule { + ParsedSubModule { + visibility: module.visibility, + name: ident_with_file(module.name, file), + contents: parsed_module_with_file(module.contents, file), + outer_attributes: secondary_attributes_with_file(module.outer_attributes, file), + is_contract: module.is_contract, + } +} + +fn module_declaration_with_file(module: ModuleDeclaration, file: FileId) -> ModuleDeclaration { + ModuleDeclaration { + visibility: module.visibility, + ident: ident_with_file(module.ident, file), + outer_attributes: secondary_attributes_with_file(module.outer_attributes, file), + has_semicolon: module.has_semicolon, + } +} + +fn let_statement_with_file(let_statement: LetStatement, file: FileId) -> LetStatement { + LetStatement { + pattern: pattern_with_file(let_statement.pattern, file), + r#type: unresolved_type_with_file(let_statement.r#type, file), + expression: expression_with_file(let_statement.expression, file), + attributes: secondary_attributes_with_file(let_statement.attributes, file), + comptime: let_statement.comptime, + is_global_let: let_statement.is_global_let, + } +} + +fn patterns_with_file(patterns: Vec, file: FileId) -> Vec { + vecmap(patterns, |pattern| pattern_with_file(pattern, file)) +} + +fn pattern_with_file(pattern: Pattern, file: FileId) -> Pattern { + match pattern { + Pattern::Identifier(ident) => Pattern::Identifier(ident_with_file(ident, file)), + Pattern::Mutable(pattern, location, synthesized) => Pattern::Mutable( + Box::new(pattern_with_file(*pattern, file)), + location_with_file(location, file), + synthesized, + ), + Pattern::Tuple(patterns, location) => { + Pattern::Tuple(patterns_with_file(patterns, file), location_with_file(location, file)) + } + Pattern::Struct(path, items, location) => Pattern::Struct( + path_with_file(path, file), + vecmap(items, |(ident, pattern)| { + (ident_with_file(ident, file), pattern_with_file(pattern, file)) + }), + location_with_file(location, file), + ), + Pattern::Interned(interned_pattern, location) => { + Pattern::Interned(interned_pattern, location_with_file(location, file)) + } + } +} + +fn noir_type_alias_with_file(noir_type_alias: NoirTypeAlias, file: FileId) -> NoirTypeAlias { + NoirTypeAlias { + name: ident_with_file(noir_type_alias.name, file), + generics: unresolved_generics_with_file(noir_type_alias.generics, file), + typ: unresolved_type_with_file(noir_type_alias.typ, file), + visibility: noir_type_alias.visibility, + location: location_with_file(noir_type_alias.location, file), + } +} + +fn type_impl_with_file(type_impl: TypeImpl, file: FileId) -> TypeImpl { + TypeImpl { + object_type: unresolved_type_with_file(type_impl.object_type, file), + type_location: location_with_file(type_impl.type_location, file), + generics: unresolved_generics_with_file(type_impl.generics, file), + where_clause: unresolved_trait_constraints_with_file(type_impl.where_clause, file), + methods: documented_noir_functions_with_file(type_impl.methods, file), + } +} + +fn documented_noir_functions_with_file( + methods: Vec<(Documented, Location)>, + file: FileId, +) -> Vec<(Documented, Location)> { + vecmap(methods, |(method, location)| { + (documented_noir_function_with_file(method, file), location_with_file(location, file)) + }) +} + +fn documented_noir_function_with_file( + function: Documented, + file: FileId, +) -> Documented { + Documented { + item: noir_function_with_file(function.item, file), + doc_comments: function.doc_comments, + } +} + +fn noir_trait_impl_with_file(noir_trait_impl: NoirTraitImpl, file: FileId) -> NoirTraitImpl { + NoirTraitImpl { + impl_generics: unresolved_generics_with_file(noir_trait_impl.impl_generics, file), + r#trait: unresolved_type_with_file(noir_trait_impl.r#trait, file), + object_type: unresolved_type_with_file(noir_trait_impl.object_type, file), + where_clause: unresolved_trait_constraints_with_file(noir_trait_impl.where_clause, file), + items: documented_trait_impl_items_with_file(noir_trait_impl.items, file), + is_synthetic: noir_trait_impl.is_synthetic, + } +} + +fn documented_trait_impl_items_with_file( + items: Vec>, + file: FileId, +) -> Vec> { + vecmap(items, |item| documented_trait_impl_item_with_file(item, file)) +} + +fn documented_trait_impl_item_with_file( + item: Documented, + file: FileId, +) -> Documented { + Documented { item: trait_impl_item_with_file(item.item, file), doc_comments: item.doc_comments } +} + +fn trait_impl_item_with_file(item: TraitImplItem, file: FileId) -> TraitImplItem { + TraitImplItem { + kind: trait_impl_item_kind_with_file(item.kind, file), + location: location_with_file(item.location, file), + } +} + +fn trait_impl_item_kind_with_file(kind: TraitImplItemKind, file: FileId) -> TraitImplItemKind { + match kind { + TraitImplItemKind::Function(noir_function) => { + TraitImplItemKind::Function(noir_function_with_file(noir_function, file)) + } + TraitImplItemKind::Constant(ident, typ, expression) => TraitImplItemKind::Constant( + ident_with_file(ident, file), + unresolved_type_with_file(typ, file), + expression_with_file(expression, file), + ), + TraitImplItemKind::Type { name, alias } => TraitImplItemKind::Type { + name: ident_with_file(name, file), + alias: unresolved_type_with_file(alias, file), + }, + } +} + +fn noir_trait_with_file(noir_trait: NoirTrait, file: FileId) -> NoirTrait { + NoirTrait { + name: ident_with_file(noir_trait.name, file), + generics: unresolved_generics_with_file(noir_trait.generics, file), + bounds: trait_bounds_with_file(noir_trait.bounds, file), + where_clause: unresolved_trait_constraints_with_file(noir_trait.where_clause, file), + location: location_with_file(noir_trait.location, file), + items: documented_trait_items_with_file(noir_trait.items, file), + attributes: secondary_attributes_with_file(noir_trait.attributes, file), + visibility: noir_trait.visibility, + is_alias: noir_trait.is_alias, + } +} + +fn documented_trait_items_with_file( + items: Vec>, + file: FileId, +) -> Vec> { + vecmap(items, |item| documented_trait_item_with_file(item, file)) +} + +fn documented_trait_item_with_file( + item: Documented, + file: FileId, +) -> Documented { + Documented { item: trait_item_with_file(item.item, file), doc_comments: item.doc_comments } +} + +fn trait_item_with_file(item: TraitItem, file: FileId) -> TraitItem { + match item { + TraitItem::Function { + is_unconstrained, + visibility, + is_comptime, + name, + generics, + parameters, + return_type, + where_clause, + body, + } => TraitItem::Function { + is_unconstrained, + visibility, + is_comptime, + name: ident_with_file(name, file), + generics: unresolved_generics_with_file(generics, file), + parameters: vecmap(parameters, |(ident, typ)| { + (ident_with_file(ident, file), unresolved_type_with_file(typ, file)) + }), + return_type: function_return_type_with_file(return_type, file), + where_clause: unresolved_trait_constraints_with_file(where_clause, file), + body: body.map(|body| block_expression_with_file(body, file)), + }, + TraitItem::Constant { name, typ, default_value } => TraitItem::Constant { + name: ident_with_file(name, file), + typ: unresolved_type_with_file(typ, file), + default_value: default_value.map(|value| expression_with_file(value, file)), + }, + TraitItem::Type { name } => TraitItem::Type { name: ident_with_file(name, file) }, + } +} + +fn function_return_type_with_file(typ: FunctionReturnType, file: FileId) -> FunctionReturnType { + match typ { + FunctionReturnType::Default(location) => { + FunctionReturnType::Default(location_with_file(location, file)) + } + FunctionReturnType::Ty(typ) => FunctionReturnType::Ty(unresolved_type_with_file(typ, file)), + } +} + +fn noir_function_with_file(noir_function: NoirFunction, file: FileId) -> NoirFunction { + NoirFunction { + kind: noir_function.kind, + def: function_definition_with_file(noir_function.def, file), + } +} + +fn noir_struct_with_file(noir_struct: NoirStruct, file: FileId) -> NoirStruct { + NoirStruct { + name: ident_with_file(noir_struct.name, file), + attributes: secondary_attributes_with_file(noir_struct.attributes, file), + visibility: noir_struct.visibility, + generics: unresolved_generics_with_file(noir_struct.generics, file), + fields: documented_struct_fields_with_file(noir_struct.fields, file), + location: location_with_file(noir_struct.location, file), + } +} + +fn documented_struct_fields_with_file( + fields: Vec>, + file: FileId, +) -> Vec> { + vecmap(fields, |field| documented_struct_field_with_file(field, file)) +} + +fn documented_struct_field_with_file( + field: Documented, + file: FileId, +) -> Documented { + Documented { item: struct_field_with_file(field.item, file), doc_comments: field.doc_comments } +} + +fn struct_field_with_file(field: StructField, file: FileId) -> StructField { + StructField { + visibility: field.visibility, + name: ident_with_file(field.name, file), + typ: unresolved_type_with_file(field.typ, file), + } +} + +fn noir_enumeration_with_file(noir_enumeration: NoirEnumeration, file: FileId) -> NoirEnumeration { + NoirEnumeration { + name: ident_with_file(noir_enumeration.name, file), + attributes: secondary_attributes_with_file(noir_enumeration.attributes, file), + visibility: noir_enumeration.visibility, + generics: unresolved_generics_with_file(noir_enumeration.generics, file), + variants: documented_enum_variants_with_file(noir_enumeration.variants, file), + location: location_with_file(noir_enumeration.location, file), + } +} + +fn documented_enum_variants_with_file( + variants: Vec>, + file: FileId, +) -> Vec> { + vecmap(variants, |variant| documented_enum_variant_with_file(variant, file)) +} + +fn documented_enum_variant_with_file( + variant: Documented, + file: FileId, +) -> Documented { + Documented { + item: enum_variant_with_file(variant.item, file), + doc_comments: variant.doc_comments, + } +} + +fn enum_variant_with_file(variant: EnumVariant, file: FileId) -> EnumVariant { + EnumVariant { + name: ident_with_file(variant.name, file), + parameters: variant.parameters.map(|params| unresolved_types_with_file(params, file)), + } +} + +fn function_definition_with_file(func: FunctionDefinition, file: FileId) -> FunctionDefinition { + FunctionDefinition { + name: ident_with_file(func.name, file), + attributes: attributes_with_file(func.attributes, file), + is_unconstrained: func.is_unconstrained, + is_comptime: func.is_comptime, + visibility: func.visibility, + generics: unresolved_generics_with_file(func.generics, file), + parameters: params_with_file(func.parameters, file), + body: block_expression_with_file(func.body, file), + location: location_with_file(func.location, file), + where_clause: unresolved_trait_constraints_with_file(func.where_clause, file), + return_type: function_return_type_with_file(func.return_type, file), + return_visibility: func.return_visibility, + } +} + +fn params_with_file(params: Vec, file: FileId) -> Vec { + vecmap(params, |param| param_with_file(param, file)) +} + +fn param_with_file(param: Param, file: FileId) -> Param { + Param { + visibility: param.visibility, + pattern: pattern_with_file(param.pattern, file), + typ: unresolved_type_with_file(param.typ, file), + location: location_with_file(param.location, file), + } +} + +fn attributes_with_file(attributes: Attributes, file: FileId) -> Attributes { + Attributes { + function: attributes.function, + secondary: secondary_attributes_with_file(attributes.secondary, file), + } +} + +fn use_tree_with_file(use_tree: UseTree, file: FileId) -> UseTree { + UseTree { + prefix: path_with_file(use_tree.prefix, file), + kind: use_tree_kind_with_file(use_tree.kind, file), + location: location_with_file(use_tree.location, file), + } +} + +fn use_tree_kind_with_file(kind: UseTreeKind, file: FileId) -> UseTreeKind { + match kind { + UseTreeKind::Path(ident, alias) => UseTreeKind::Path( + ident_with_file(ident, file), + alias.map(|alias| ident_with_file(alias, file)), + ), + UseTreeKind::List(use_trees) => { + UseTreeKind::List(vecmap(use_trees, |use_tree| use_tree_with_file(use_tree, file))) + } + } +} + +fn path_with_file(path: Path, file: FileId) -> Path { + Path { + segments: vecmap(path.segments, |segment| path_segment_with_file(segment, file)), + kind: path.kind, + kind_location: location_with_file(path.kind_location, file), + location: location_with_file(path.location, file), + } +} + +fn path_segment_with_file(segment: PathSegment, file: FileId) -> PathSegment { + PathSegment { + ident: ident_with_file(segment.ident, file), + generics: segment.generics.map(|generics| unresolved_types_with_file(generics, file)), + location: location_with_file(segment.location, file), + } +} + +fn unresolved_types_with_file(types: Vec, file: FileId) -> Vec { + vecmap(types, |typ| unresolved_type_with_file(typ, file)) +} + +fn unresolved_type_with_file(typ: UnresolvedType, file: FileId) -> UnresolvedType { + UnresolvedType { + typ: unresolved_type_data_with_file(typ.typ, file), + location: location_with_file(typ.location, file), + } +} + +fn unresolved_type_data_with_file(typ: UnresolvedTypeData, file: FileId) -> UnresolvedTypeData { + match typ { + UnresolvedTypeData::Array(length, typ) => UnresolvedTypeData::Array( + unresolved_type_expression_with_file(length, file), + Box::new(unresolved_type_with_file(*typ, file)), + ), + UnresolvedTypeData::Slice(typ) => { + UnresolvedTypeData::Slice(Box::new(unresolved_type_with_file(*typ, file))) + } + UnresolvedTypeData::Expression(expr) => { + UnresolvedTypeData::Expression(unresolved_type_expression_with_file(expr, file)) + } + UnresolvedTypeData::String(expr) => { + UnresolvedTypeData::String(unresolved_type_expression_with_file(expr, file)) + } + UnresolvedTypeData::FormatString(expr, typ) => UnresolvedTypeData::FormatString( + unresolved_type_expression_with_file(expr, file), + Box::new(unresolved_type_with_file(*typ, file)), + ), + UnresolvedTypeData::Parenthesized(typ) => { + UnresolvedTypeData::Parenthesized(Box::new(unresolved_type_with_file(*typ, file))) + } + UnresolvedTypeData::Named(path, generic_type_args, synthesized) => { + UnresolvedTypeData::Named( + path_with_file(path, file), + generic_type_args_with_file(generic_type_args, file), + synthesized, + ) + } + UnresolvedTypeData::TraitAsType(path, generic_type_args) => { + UnresolvedTypeData::TraitAsType( + path_with_file(path, file), + generic_type_args_with_file(generic_type_args, file), + ) + } + UnresolvedTypeData::MutableReference(typ) => { + UnresolvedTypeData::MutableReference(Box::new(unresolved_type_with_file(*typ, file))) + } + UnresolvedTypeData::Tuple(types) => { + UnresolvedTypeData::Tuple(unresolved_types_with_file(types, file)) + } + UnresolvedTypeData::Function(args, ret, env, unconstrained) => { + UnresolvedTypeData::Function( + unresolved_types_with_file(args, file), + Box::new(unresolved_type_with_file(*ret, file)), + Box::new(unresolved_type_with_file(*env, file)), + unconstrained, + ) + } + UnresolvedTypeData::AsTraitPath(as_trait_path) => { + UnresolvedTypeData::AsTraitPath(Box::new(as_trait_path_with_file(*as_trait_path, file))) + } + UnresolvedTypeData::Quoted(..) + | UnresolvedTypeData::Resolved(..) + | UnresolvedTypeData::Interned(..) + | UnresolvedTypeData::Unit + | UnresolvedTypeData::Bool + | UnresolvedTypeData::Integer(..) + | UnresolvedTypeData::FieldElement + | UnresolvedTypeData::Unspecified + | UnresolvedTypeData::Error => typ, + } +} + +fn unresolved_type_expression_with_file( + type_expr: UnresolvedTypeExpression, + file: FileId, +) -> UnresolvedTypeExpression { + match type_expr { + UnresolvedTypeExpression::Variable(path) => { + UnresolvedTypeExpression::Variable(path_with_file(path, file)) + } + UnresolvedTypeExpression::Constant(field_element, location) => { + UnresolvedTypeExpression::Constant(field_element, location_with_file(location, file)) + } + UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, location) => { + UnresolvedTypeExpression::BinaryOperation( + Box::new(unresolved_type_expression_with_file(*lhs, file)), + op, + Box::new(unresolved_type_expression_with_file(*rhs, file)), + location_with_file(location, file), + ) + } + UnresolvedTypeExpression::AsTraitPath(as_trait_path) => { + UnresolvedTypeExpression::AsTraitPath(Box::new(as_trait_path_with_file( + *as_trait_path, + file, + ))) + } + } +} + +fn as_trait_path_with_file(as_trait_path: AsTraitPath, file: FileId) -> AsTraitPath { + AsTraitPath { + typ: unresolved_type_with_file(as_trait_path.typ, file), + trait_path: path_with_file(as_trait_path.trait_path, file), + trait_generics: generic_type_args_with_file(as_trait_path.trait_generics, file), + impl_item: ident_with_file(as_trait_path.impl_item, file), + } +} + +fn generic_type_args_with_file(generics: GenericTypeArgs, file: FileId) -> GenericTypeArgs { + GenericTypeArgs { + ordered_args: unresolved_types_with_file(generics.ordered_args, file), + named_args: vecmap(generics.named_args, |(ident, typ)| { + (ident_with_file(ident, file), unresolved_type_with_file(typ, file)) + }), + kinds: generics.kinds, + } +} + +fn ident_with_file(ident: Ident, file: FileId) -> Ident { + let location = location_with_file(ident.location(), file); + Ident::new(ident.0.contents, location) +} + +fn secondary_attributes_with_file( + attributes: Vec, + file: FileId, +) -> Vec { + vecmap(attributes, |attribute| secondary_attribute_with_file(attribute, file)) +} + +fn secondary_attribute_with_file( + secondary_attribute: SecondaryAttribute, + file: FileId, +) -> SecondaryAttribute { + match secondary_attribute { + SecondaryAttribute::Meta(meta_attribute) => { + SecondaryAttribute::Meta(meta_attribute_with_file(meta_attribute, file)) + } + SecondaryAttribute::Deprecated(_) + | SecondaryAttribute::ContractLibraryMethod + | SecondaryAttribute::Export + | SecondaryAttribute::Field(_) + | SecondaryAttribute::Tag(..) + | SecondaryAttribute::Abi(_) + | SecondaryAttribute::Varargs + | SecondaryAttribute::UseCallersScope + | SecondaryAttribute::Allow(_) => secondary_attribute, + } +} + +fn meta_attribute_with_file(meta_attribute: MetaAttribute, file: FileId) -> MetaAttribute { + MetaAttribute { + name: path_with_file(meta_attribute.name, file), + arguments: expressions_with_file(meta_attribute.arguments, file), + location: location_with_file(meta_attribute.location, file), + } +} + +fn expressions_with_file(expressions: Vec, file: FileId) -> Vec { + vecmap(expressions, |expr| expression_with_file(expr, file)) +} + +fn expression_with_file(expr: Expression, file: FileId) -> Expression { + Expression { + kind: expression_kind_with_file(expr.kind, file), + location: location_with_file(expr.location, file), + } +} + +fn expression_kind_with_file(kind: ExpressionKind, file: FileId) -> ExpressionKind { + match kind { + ExpressionKind::Literal(literal) => { + ExpressionKind::Literal(literal_with_file(literal, file)) + } + ExpressionKind::Block(block_expression) => { + ExpressionKind::Block(block_expression_with_file(block_expression, file)) + } + ExpressionKind::Prefix(prefix_expression) => { + ExpressionKind::Prefix(Box::new(prefix_expression_with_file(*prefix_expression, file))) + } + ExpressionKind::Index(index_expression) => { + ExpressionKind::Index(Box::new(index_expression_with_file(*index_expression, file))) + } + ExpressionKind::Call(call_expression) => { + ExpressionKind::Call(Box::new(call_expression_with_file(*call_expression, file))) + } + ExpressionKind::MethodCall(method_call_expression) => ExpressionKind::MethodCall(Box::new( + method_call_expression_with_file(*method_call_expression, file), + )), + ExpressionKind::Constrain(constrain_expression) => { + ExpressionKind::Constrain(constrain_expression_with_file(constrain_expression, file)) + } + ExpressionKind::Constructor(constructor_expression) => ExpressionKind::Constructor( + Box::new(constructor_expression_with_file(*constructor_expression, file)), + ), + ExpressionKind::MemberAccess(member_access_expression) => ExpressionKind::MemberAccess( + Box::new(member_access_expression_with_file(*member_access_expression, file)), + ), + ExpressionKind::Cast(cast_expression) => { + ExpressionKind::Cast(Box::new(cast_expression_with_file(*cast_expression, file))) + } + ExpressionKind::Infix(infix_expression) => { + ExpressionKind::Infix(Box::new(infix_expression_with_file(*infix_expression, file))) + } + ExpressionKind::If(if_expression) => { + ExpressionKind::If(Box::new(if_expression_with_file(*if_expression, file))) + } + ExpressionKind::Match(match_expression) => { + ExpressionKind::Match(Box::new(match_expression_with_file(*match_expression, file))) + } + ExpressionKind::Variable(path) => ExpressionKind::Variable(path_with_file(path, file)), + ExpressionKind::Tuple(expressions) => { + ExpressionKind::Tuple(expressions_with_file(expressions, file)) + } + ExpressionKind::Lambda(lambda) => { + ExpressionKind::Lambda(Box::new(lambda_with_file(*lambda, file))) + } + ExpressionKind::Parenthesized(expression) => { + ExpressionKind::Parenthesized(Box::new(expression_with_file(*expression, file))) + } + ExpressionKind::Quote(tokens) => ExpressionKind::Quote(tokens_with_file(tokens, file)), + ExpressionKind::Unquote(expression) => { + ExpressionKind::Unquote(Box::new(expression_with_file(*expression, file))) + } + ExpressionKind::Comptime(block_expression, location) => ExpressionKind::Comptime( + block_expression_with_file(block_expression, file), + location_with_file(location, file), + ), + ExpressionKind::Unsafe(UnsafeExpression { block, unsafe_keyword_location }) => { + ExpressionKind::Unsafe(UnsafeExpression { + block: block_expression_with_file(block, file), + unsafe_keyword_location: location_with_file(unsafe_keyword_location, file), + }) + } + ExpressionKind::AsTraitPath(as_trait_path) => { + ExpressionKind::AsTraitPath(as_trait_path_with_file(as_trait_path, file)) + } + ExpressionKind::TypePath(type_path) => { + ExpressionKind::TypePath(type_path_with_file(type_path, file)) + } + ExpressionKind::Resolved(..) + | ExpressionKind::Interned(..) + | ExpressionKind::InternedStatement(..) + | ExpressionKind::Error => kind, + } +} + +fn type_path_with_file(type_path: TypePath, file: FileId) -> TypePath { + TypePath { + typ: unresolved_type_with_file(type_path.typ, file), + item: ident_with_file(type_path.item, file), + turbofish: type_path.turbofish.map(|args| generic_type_args_with_file(args, file)), + } +} + +fn tokens_with_file(tokens: Tokens, file: FileId) -> Tokens { + Tokens(vecmap(tokens.0, |token| { + let location = location_with_file(token.location(), file); + LocatedToken::new(token_with_location(token.into_token(), file), location) + })) +} + +fn token_with_location(token: Token, file: FileId) -> Token { + if let Token::FmtStr(fragments, length) = token { + Token::FmtStr( + vecmap(fragments, |fragment| fmt_str_fragment_with_file(fragment, file)), + length, + ) + } else { + token + } +} + +fn fmt_str_fragment_with_file(fragment: FmtStrFragment, file: FileId) -> FmtStrFragment { + match fragment { + FmtStrFragment::Interpolation(string, location) => { + FmtStrFragment::Interpolation(string, location_with_file(location, file)) + } + FmtStrFragment::String(_) => fragment, + } +} + +fn lambda_with_file(lambda: Lambda, file: FileId) -> Lambda { + Lambda { + parameters: vecmap(lambda.parameters, |(pattern, typ)| { + (pattern_with_file(pattern, file), unresolved_type_with_file(typ, file)) + }), + return_type: unresolved_type_with_file(lambda.return_type, file), + body: expression_with_file(lambda.body, file), + } +} + +fn match_expression_with_file(expr: MatchExpression, file: FileId) -> MatchExpression { + MatchExpression { + expression: expression_with_file(expr.expression, file), + rules: vecmap(expr.rules, |(condition, body)| { + (expression_with_file(condition, file), expression_with_file(body, file)) + }), + } +} + +fn if_expression_with_file(expr: IfExpression, file: FileId) -> IfExpression { + IfExpression { + condition: expression_with_file(expr.condition, file), + consequence: expression_with_file(expr.consequence, file), + alternative: expr.alternative.map(|alternative| expression_with_file(alternative, file)), + } +} + +fn infix_expression_with_file(expr: InfixExpression, file: FileId) -> InfixExpression { + InfixExpression { + lhs: expression_with_file(expr.lhs, file), + rhs: expression_with_file(expr.rhs, file), + operator: expr.operator, + } +} + +fn cast_expression_with_file(expr: CastExpression, file: FileId) -> CastExpression { + CastExpression { + lhs: expression_with_file(expr.lhs, file), + r#type: unresolved_type_with_file(expr.r#type, file), + } +} + +fn member_access_expression_with_file( + expr: MemberAccessExpression, + file: FileId, +) -> MemberAccessExpression { + MemberAccessExpression { + lhs: expression_with_file(expr.lhs, file), + rhs: ident_with_file(expr.rhs, file), + } +} + +fn constructor_expression_with_file( + expr: ConstructorExpression, + file: FileId, +) -> ConstructorExpression { + ConstructorExpression { + typ: unresolved_type_with_file(expr.typ, file), + fields: vecmap(expr.fields, |(ident, expression)| { + (ident_with_file(ident, file), expression_with_file(expression, file)) + }), + } +} + +fn constrain_expression_with_file(expr: ConstrainExpression, file: FileId) -> ConstrainExpression { + ConstrainExpression { + kind: expr.kind, + arguments: expressions_with_file(expr.arguments, file), + location: location_with_file(expr.location, file), + } +} + +fn method_call_expression_with_file( + expr: MethodCallExpression, + file: FileId, +) -> MethodCallExpression { + MethodCallExpression { + object: expression_with_file(expr.object, file), + method_name: ident_with_file(expr.method_name, file), + generics: expr.generics.map(|generics| unresolved_types_with_file(generics, file)), + arguments: expressions_with_file(expr.arguments, file), + is_macro_call: expr.is_macro_call, + } +} + +fn call_expression_with_file(expr: CallExpression, file: FileId) -> CallExpression { + CallExpression { + func: Box::new(expression_with_file(*expr.func, file)), + arguments: expressions_with_file(expr.arguments, file), + is_macro_call: expr.is_macro_call, + } +} + +fn index_expression_with_file(expr: IndexExpression, file: FileId) -> IndexExpression { + IndexExpression { + collection: expression_with_file(expr.collection, file), + index: expression_with_file(expr.index, file), + } +} + +fn prefix_expression_with_file(expr: PrefixExpression, file: FileId) -> PrefixExpression { + PrefixExpression { operator: expr.operator, rhs: expression_with_file(expr.rhs, file) } +} + +fn literal_with_file(literal: Literal, file: FileId) -> Literal { + match literal { + Literal::Array(array_literal) => { + Literal::Array(array_literal_with_file(array_literal, file)) + } + Literal::Slice(array_literal) => { + Literal::Slice(array_literal_with_file(array_literal, file)) + } + Literal::FmtStr(fragments, length) => Literal::FmtStr( + vecmap(fragments, |fragment| fmt_str_fragment_with_file(fragment, file)), + length, + ), + Literal::Bool(..) + | Literal::Integer(..) + | Literal::Str(..) + | Literal::RawStr(..) + | Literal::Unit => literal, + } +} + +fn array_literal_with_file(array_literal: ArrayLiteral, file: FileId) -> ArrayLiteral { + match array_literal { + ArrayLiteral::Standard(expressions) => { + ArrayLiteral::Standard(expressions_with_file(expressions, file)) + } + ArrayLiteral::Repeated { repeated_element, length } => ArrayLiteral::Repeated { + repeated_element: Box::new(expression_with_file(*repeated_element, file)), + length: Box::new(expression_with_file(*length, file)), + }, + } +} + +fn block_expression_with_file(block: BlockExpression, file: FileId) -> BlockExpression { + BlockExpression { statements: statements_with_file(block.statements, file) } +} + +fn statements_with_file(statements: Vec, file: FileId) -> Vec { + vecmap(statements, |statement| statement_with_file(statement, file)) +} + +fn statement_with_file(statement: Statement, file: FileId) -> Statement { + Statement { + kind: statement_kind_with_file(statement.kind, file), + location: location_with_file(statement.location, file), + } +} + +fn statement_kind_with_file(kind: StatementKind, file: FileId) -> StatementKind { + match kind { + StatementKind::Let(let_statement) => { + StatementKind::Let(let_statement_with_file(let_statement, file)) + } + StatementKind::Expression(expression) => { + StatementKind::Expression(expression_with_file(expression, file)) + } + StatementKind::Assign(assign_statement) => { + StatementKind::Assign(assign_statement_with_file(assign_statement, file)) + } + StatementKind::For(for_loop_statement) => { + StatementKind::For(for_loop_statement_with_file(for_loop_statement, file)) + } + StatementKind::While(while_) => StatementKind::While(WhileStatement { + condition: expression_with_file(while_.condition, file), + body: expression_with_file(while_.body, file), + while_keyword_location: while_.while_keyword_location, + }), + StatementKind::Loop(expression, location) => StatementKind::Loop( + expression_with_file(expression, file), + location_with_file(location, file), + ), + StatementKind::Comptime(statement) => { + StatementKind::Comptime(Box::new(statement_with_file(*statement, file))) + } + StatementKind::Semi(expression) => { + StatementKind::Semi(expression_with_file(expression, file)) + } + StatementKind::Interned(..) + | StatementKind::Break + | StatementKind::Continue + | StatementKind::Error => kind, + } +} + +fn for_loop_statement_with_file(for_loop: ForLoopStatement, file: FileId) -> ForLoopStatement { + ForLoopStatement { + identifier: ident_with_file(for_loop.identifier, file), + range: for_range_with_file(for_loop.range, file), + block: expression_with_file(for_loop.block, file), + location: location_with_file(for_loop.location, file), + } +} + +fn for_range_with_file(range: ForRange, file: FileId) -> ForRange { + match range { + ForRange::Range(for_bounds) => ForRange::Range(for_bounds_with_file(for_bounds, file)), + ForRange::Array(expression) => ForRange::Array(expression_with_file(expression, file)), + } +} + +fn for_bounds_with_file(for_bounds: ForBounds, file: FileId) -> ForBounds { + ForBounds { + start: expression_with_file(for_bounds.start, file), + end: expression_with_file(for_bounds.end, file), + inclusive: for_bounds.inclusive, + } +} + +fn assign_statement_with_file(assign: AssignStatement, file: FileId) -> AssignStatement { + AssignStatement { + lvalue: lvalue_with_file(assign.lvalue, file), + expression: expression_with_file(assign.expression, file), + } +} + +fn lvalue_with_file(lvalue: LValue, file: FileId) -> LValue { + match lvalue { + LValue::Ident(ident) => LValue::Ident(ident_with_file(ident, file)), + LValue::MemberAccess { object, field_name, location } => LValue::MemberAccess { + object: Box::new(lvalue_with_file(*object, file)), + field_name: ident_with_file(field_name, file), + location: location_with_file(location, file), + }, + LValue::Index { array, index, location } => LValue::Index { + array: Box::new(lvalue_with_file(*array, file)), + index: expression_with_file(index, file), + location: location_with_file(location, file), + }, + LValue::Dereference(lvalue, location) => LValue::Dereference( + Box::new(lvalue_with_file(*lvalue, file)), + location_with_file(location, file), + ), + LValue::Interned(interned_expression_kind, location) => { + LValue::Interned(interned_expression_kind, location_with_file(location, file)) + } + } +} + +fn unresolved_generics_with_file( + generics: Vec, + file: FileId, +) -> Vec { + vecmap(generics, |generic| unresolved_generic_with_file(generic, file)) +} + +fn unresolved_generic_with_file(generic: UnresolvedGeneric, file: FileId) -> UnresolvedGeneric { + match generic { + UnresolvedGeneric::Variable(ident) => { + UnresolvedGeneric::Variable(ident_with_file(ident, file)) + } + UnresolvedGeneric::Numeric { ident, typ } => UnresolvedGeneric::Numeric { + ident: ident_with_file(ident, file), + typ: unresolved_type_with_file(typ, file), + }, + UnresolvedGeneric::Resolved(quoted_type_id, location) => { + UnresolvedGeneric::Resolved(quoted_type_id, location_with_file(location, file)) + } + } +} + +fn unresolved_trait_constraints_with_file( + constraints: Vec, + file: FileId, +) -> Vec { + vecmap(constraints, |constraint| unresolved_trait_constraint_with_file(constraint, file)) +} + +fn unresolved_trait_constraint_with_file( + constraint: UnresolvedTraitConstraint, + file: FileId, +) -> UnresolvedTraitConstraint { + UnresolvedTraitConstraint { + typ: unresolved_type_with_file(constraint.typ, file), + trait_bound: trait_bound_with_file(constraint.trait_bound, file), + } +} + +fn trait_bounds_with_file(trait_bounds: Vec, file: FileId) -> Vec { + vecmap(trait_bounds, |bound| trait_bound_with_file(bound, file)) +} + +fn trait_bound_with_file(trait_bound: TraitBound, file: FileId) -> TraitBound { + TraitBound { + trait_path: path_with_file(trait_bound.trait_path, file), + trait_id: trait_bound.trait_id, + trait_generics: generic_type_args_with_file(trait_bound.trait_generics, file), + } +} + +fn location_with_file(location: Location, file: FileId) -> Location { + Location { file, ..location } +} diff --git a/noir/noir-repo/tooling/nargo/src/errors.rs b/noir/noir-repo/tooling/nargo/src/errors.rs index 00c411bf7e43..f1743af79ca6 100644 --- a/noir/noir-repo/tooling/nargo/src/errors.rs +++ b/noir/noir-repo/tooling/nargo/src/errors.rs @@ -1,17 +1,15 @@ use std::collections::BTreeMap; use acvm::{ + AcirField, FieldElement, acir::circuit::{ - brillig::BrilligFunctionId, ErrorSelector, OpcodeLocation, RawAssertionPayload, - ResolvedAssertionPayload, ResolvedOpcodeLocation, + ErrorSelector, OpcodeLocation, RawAssertionPayload, ResolvedAssertionPayload, + ResolvedOpcodeLocation, brillig::BrilligFunctionId, }, pwg::{ErrorLocation, OpcodeResolutionError}, - AcirField, FieldElement, -}; -use noirc_abi::{display_abi_error, Abi, AbiErrorType}; -use noirc_errors::{ - debug_info::DebugInfo, reporter::ReportedErrors, CustomDiagnostic, FileDiagnostic, }; +use noirc_abi::{Abi, AbiErrorType, display_abi_error}; +use noirc_errors::{CustomDiagnostic, debug_info::DebugInfo, reporter::ReportedErrors}; pub use noirc_errors::Location; @@ -230,7 +228,7 @@ pub fn try_to_diagnose_runtime_error( nargo_err: &NargoError, abi: &Abi, debug: &[DebugInfo], -) -> Option { +) -> Option { let source_locations = match nargo_err { NargoError::ExecutionError(execution_error) => { extract_locations_from_error(execution_error, debug)? @@ -241,6 +239,6 @@ pub fn try_to_diagnose_runtime_error( // of the call stack (the last item in the Vec). let location = *source_locations.last()?; let message = extract_message_from_error(&abi.error_types, nargo_err); - let error = CustomDiagnostic::simple_error(message, String::new(), location.span); - Some(error.with_call_stack(source_locations).in_file(location.file)) + let error = CustomDiagnostic::simple_error(message, String::new(), location); + Some(error.with_call_stack(source_locations)) } diff --git a/noir/noir-repo/tooling/nargo/src/foreign_calls/default.rs b/noir/noir-repo/tooling/nargo/src/foreign_calls/default.rs index 19928e895630..c6053a1175e1 100644 --- a/noir/noir-repo/tooling/nargo/src/foreign_calls/default.rs +++ b/noir/noir-repo/tooling/nargo/src/foreign_calls/default.rs @@ -4,10 +4,10 @@ use serde::{Deserialize, Serialize}; use crate::PrintOutput; use super::{ + ForeignCallExecutor, layers::{self, Either, Layer, Layering}, mocker::{DisabledMockForeignCallExecutor, MockForeignCallExecutor}, print::PrintForeignCallExecutor, - ForeignCallExecutor, }; #[cfg(feature = "rpc")] @@ -29,7 +29,7 @@ pub struct DefaultForeignCallBuilder<'a> { pub package_name: Option, } -impl<'a> Default for DefaultForeignCallBuilder<'a> { +impl Default for DefaultForeignCallBuilder<'_> { fn default() -> Self { Self { output: PrintOutput::default(), @@ -80,7 +80,7 @@ impl<'a> DefaultForeignCallBuilder<'a> { use rand::Rng; base.add_layer(self.resolver_url.map(|resolver_url| { - let id = rand::thread_rng().gen(); + let id = rand::thread_rng().r#gen(); RPCForeignCallExecutor::new( &resolver_url, id, @@ -136,7 +136,7 @@ impl DefaultForeignCallExecutor { resolver_url: Option<&str>, root_path: Option, package_name: Option, - ) -> impl ForeignCallExecutor + 'a + ) -> impl ForeignCallExecutor + 'a + use<'a, F> where F: AcirField + Serialize + for<'de> Deserialize<'de> + 'a, { diff --git a/noir/noir-repo/tooling/nargo/src/foreign_calls/layers.rs b/noir/noir-repo/tooling/nargo/src/foreign_calls/layers.rs index 83145cacb441..4b1d17bdf65c 100644 --- a/noir/noir-repo/tooling/nargo/src/foreign_calls/layers.rs +++ b/noir/noir-repo/tooling/nargo/src/foreign_calls/layers.rs @@ -1,4 +1,4 @@ -use acvm::{acir::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo, AcirField}; +use acvm::{AcirField, acir::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo}; use super::{ForeignCallError, ForeignCallExecutor}; diff --git a/noir/noir-repo/tooling/nargo/src/foreign_calls/mocker.rs b/noir/noir-repo/tooling/nargo/src/foreign_calls/mocker.rs index 41fac610052b..3d432b18ff9e 100644 --- a/noir/noir-repo/tooling/nargo/src/foreign_calls/mocker.rs +++ b/noir/noir-repo/tooling/nargo/src/foreign_calls/mocker.rs @@ -1,7 +1,7 @@ use acvm::{ + AcirField, acir::brillig::{ForeignCallParam, ForeignCallResult}, pwg::ForeignCallWaitInfo, - AcirField, }; use noirc_abi::decode_string_value; diff --git a/noir/noir-repo/tooling/nargo/src/foreign_calls/print.rs b/noir/noir-repo/tooling/nargo/src/foreign_calls/print.rs index fb5621da9424..a225fe31dcbe 100644 --- a/noir/noir-repo/tooling/nargo/src/foreign_calls/print.rs +++ b/noir/noir-repo/tooling/nargo/src/foreign_calls/print.rs @@ -1,7 +1,7 @@ use acvm::{ + AcirField, acir::brillig::{ForeignCallParam, ForeignCallResult}, pwg::ForeignCallWaitInfo, - AcirField, }; use noirc_abi::{decode_printable_value, decode_string_value}; use noirc_printable_type::{PrintableType, PrintableValueDisplay}; diff --git a/noir/noir-repo/tooling/nargo/src/foreign_calls/rpc.rs b/noir/noir-repo/tooling/nargo/src/foreign_calls/rpc.rs index 89a748b6c455..6e4858128856 100644 --- a/noir/noir-repo/tooling/nargo/src/foreign_calls/rpc.rs +++ b/noir/noir-repo/tooling/nargo/src/foreign_calls/rpc.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use acvm::{acir::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo, AcirField}; +use acvm::{AcirField, acir::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo}; use jsonrpsee::{ core::client::ClientT, http_client::{HttpClient, HttpClientBuilder}, @@ -115,8 +115,8 @@ where #[cfg(test)] mod tests { use acvm::{ - acir::brillig::ForeignCallParam, brillig_vm::brillig::ForeignCallResult, - pwg::ForeignCallWaitInfo, FieldElement, + FieldElement, acir::brillig::ForeignCallParam, brillig_vm::brillig::ForeignCallResult, + pwg::ForeignCallWaitInfo, }; use jsonrpsee::proc_macros::rpc; use jsonrpsee::server::Server; diff --git a/noir/noir-repo/tooling/nargo/src/lib.rs b/noir/noir-repo/tooling/nargo/src/lib.rs index 30f25356e413..11615b3de729 100644 --- a/noir/noir-repo/tooling/nargo/src/lib.rs +++ b/noir/noir-repo/tooling/nargo/src/lib.rs @@ -22,11 +22,11 @@ use std::{ path::PathBuf, }; -use fm::{FileManager, FILE_EXTENSION}; +use fm::{FILE_EXTENSION, FileManager}; use noirc_driver::{add_dep, prepare_crate, prepare_dependency}; use noirc_frontend::{ graph::{CrateId, CrateName}, - hir::{def_map::parse_file, Context, ParsedFiles}, + hir::{Context, ParsedFiles, def_map::parse_file}, }; use package::{Dependency, Package}; use rayon::prelude::*; @@ -106,7 +106,7 @@ fn insert_all_files_for_package_into_file_manager( continue; } - if !entry.path().extension().map_or(false, |ext| ext == FILE_EXTENSION) { + if entry.path().extension().is_none_or(|ext| ext != FILE_EXTENSION) { continue; }; diff --git a/noir/noir-repo/tooling/nargo/src/ops/check.rs b/noir/noir-repo/tooling/nargo/src/ops/check.rs index f22def8bd91b..129aa1bd7885 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/check.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/check.rs @@ -1,6 +1,6 @@ use acvm::compiler::CircuitSimulator; use noirc_driver::{CompiledProgram, ErrorsAndWarnings}; -use noirc_errors::{CustomDiagnostic, FileDiagnostic}; +use noirc_errors::CustomDiagnostic; /// Run each function through a circuit simulator to check that they are solvable. #[tracing::instrument(level = "trace", skip_all)] @@ -8,13 +8,10 @@ pub fn check_program(compiled_program: &CompiledProgram) -> Result<(), ErrorsAnd for (i, circuit) in compiled_program.program.functions.iter().enumerate() { let mut simulator = CircuitSimulator::default(); if !simulator.check_circuit(circuit) { - let diag = FileDiagnostic { - file_id: fm::FileId::dummy(), - diagnostic: CustomDiagnostic::from_message(&format!( - "Circuit \"{}\" is not solvable", - compiled_program.names[i] - )), - }; + let diag = CustomDiagnostic::from_message( + &format!("Circuit \"{}\" is not solvable", compiled_program.names[i]), + fm::FileId::dummy(), + ); return Err(vec![diag]); } } diff --git a/noir/noir-repo/tooling/nargo/src/ops/compile.rs b/noir/noir-repo/tooling/nargo/src/ops/compile.rs index 8c44bf352435..7a3ddbfe3bc6 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/compile.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/compile.rs @@ -1,6 +1,6 @@ use fm::FileManager; use noirc_driver::{ - link_to_debug_crate, CompilationResult, CompileOptions, CompiledContract, CompiledProgram, + CompilationResult, CompileOptions, CompiledContract, CompiledProgram, link_to_debug_crate, }; use noirc_frontend::debug::DebugInstrumenter; use noirc_frontend::hir::ParsedFiles; @@ -120,11 +120,7 @@ pub fn collect_errors(results: Vec>) -> CompilationResul } } - if errors.is_empty() { - Ok((artifacts, warnings)) - } else { - Err(errors) - } + if errors.is_empty() { Ok((artifacts, warnings)) } else { Err(errors) } } pub fn report_errors( diff --git a/noir/noir-repo/tooling/nargo/src/ops/execute.rs b/noir/noir-repo/tooling/nargo/src/ops/execute.rs index 57116ec2efd9..699c54e3f528 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/execute.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/execute.rs @@ -4,14 +4,14 @@ use acvm::acir::circuit::{ }; use acvm::acir::native_types::WitnessStack; use acvm::pwg::{ - ACVMStatus, ErrorLocation, OpcodeNotSolvable, OpcodeResolutionError, ProfilingSamples, ACVM, + ACVM, ACVMStatus, ErrorLocation, OpcodeNotSolvable, OpcodeResolutionError, ProfilingSamples, }; -use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; use acvm::{AcirField, BlackBoxFunctionSolver}; +use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; +use crate::NargoError; use crate::errors::ExecutionError; use crate::foreign_calls::ForeignCallExecutor; -use crate::NargoError; struct ProgramExecutor<'a, F, B: BlackBoxFunctionSolver, E: ForeignCallExecutor> { functions: &'a [Circuit], diff --git a/noir/noir-repo/tooling/nargo/src/ops/mod.rs b/noir/noir-repo/tooling/nargo/src/ops/mod.rs index 7a52a829be36..7ce34b1acd25 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/mod.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/mod.rs @@ -7,7 +7,7 @@ pub use self::optimize::{optimize_contract, optimize_program}; pub use self::transform::{transform_contract, transform_program}; pub use self::execute::{execute_program, execute_program_with_profiling}; -pub use self::test::{run_test, TestStatus}; +pub use self::test::{TestStatus, run_test}; mod check; mod compile; diff --git a/noir/noir-repo/tooling/nargo/src/ops/optimize.rs b/noir/noir-repo/tooling/nargo/src/ops/optimize.rs index 07adfb57df4f..906309e36f8a 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/optimize.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/optimize.rs @@ -1,4 +1,4 @@ -use acvm::{acir::circuit::Program, FieldElement}; +use acvm::{FieldElement, acir::circuit::Program}; use iter_extended::vecmap; use noirc_driver::{CompiledContract, CompiledProgram}; use noirc_errors::debug_info::DebugInfo; diff --git a/noir/noir-repo/tooling/nargo/src/ops/test.rs b/noir/noir-repo/tooling/nargo/src/ops/test.rs index a2f94cd61eba..4f0e63564e2d 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/test.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/test.rs @@ -1,20 +1,20 @@ use acvm::{ + AcirField, BlackBoxFunctionSolver, FieldElement, acir::{ brillig::ForeignCallResult, native_types::{WitnessMap, WitnessStack}, }, pwg::ForeignCallWaitInfo, - AcirField, BlackBoxFunctionSolver, FieldElement, }; use noirc_abi::Abi; -use noirc_driver::{compile_no_check, CompileError, CompileOptions, DEFAULT_EXPRESSION_WIDTH}; -use noirc_errors::{debug_info::DebugInfo, FileDiagnostic}; -use noirc_frontend::hir::{def_map::TestFunction, Context}; +use noirc_driver::{CompileError, CompileOptions, DEFAULT_EXPRESSION_WIDTH, compile_no_check}; +use noirc_errors::{CustomDiagnostic, debug_info::DebugInfo}; +use noirc_frontend::hir::{Context, def_map::TestFunction}; use crate::{ - errors::try_to_diagnose_runtime_error, - foreign_calls::{layers, print::PrintOutput, ForeignCallError, ForeignCallExecutor}, NargoError, + errors::try_to_diagnose_runtime_error, + foreign_calls::{ForeignCallError, ForeignCallExecutor, layers, print::PrintOutput}, }; use super::execute_program; @@ -22,9 +22,9 @@ use super::execute_program; #[derive(Debug)] pub enum TestStatus { Pass, - Fail { message: String, error_diagnostic: Option }, + Fail { message: String, error_diagnostic: Option }, Skipped, - CompileError(FileDiagnostic), + CompileError(CustomDiagnostic), } impl TestStatus { @@ -218,7 +218,7 @@ fn test_status_program_compile_pass( fn check_expected_failure_message( test_function: &TestFunction, failed_assertion: Option, - error_diagnostic: Option, + error_diagnostic: Option, ) -> TestStatus { // Extract the expected failure message, if there was one // @@ -235,9 +235,7 @@ fn check_expected_failure_message( // expected_failure_message let expected_failure_message_matches = failed_assertion .as_ref() - .or_else(|| { - error_diagnostic.as_ref().map(|file_diagnostic| &file_diagnostic.diagnostic.message) - }) + .or_else(|| error_diagnostic.as_ref().map(|file_diagnostic| &file_diagnostic.message)) .map(|message| message.contains(expected_failure_message)) .unwrap_or(false); if expected_failure_message_matches { diff --git a/noir/noir-repo/tooling/nargo/src/ops/transform.rs b/noir/noir-repo/tooling/nargo/src/ops/transform.rs index fdda368d150c..14aa74591174 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/transform.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/transform.rs @@ -1,6 +1,6 @@ use acvm::{ - acir::circuit::{ExpressionWidth, Program}, FieldElement, + acir::circuit::{ExpressionWidth, Program}, }; use iter_extended::vecmap; use noirc_driver::{CompiledContract, CompiledProgram}; diff --git a/noir/noir-repo/tooling/nargo/src/workspace.rs b/noir/noir-repo/tooling/nargo/src/workspace.rs index 2a6669aabdca..f7edc5057f6d 100644 --- a/noir/noir-repo/tooling/nargo/src/workspace.rs +++ b/noir/noir-repo/tooling/nargo/src/workspace.rs @@ -4,7 +4,7 @@ // - library will be default use std::{ - iter::{once, Once}, + iter::{Once, once}, path::PathBuf, slice, }; diff --git a/noir/noir-repo/tooling/nargo_cli/Cargo.toml b/noir/noir-repo/tooling/nargo_cli/Cargo.toml index 92eeed1b3911..5aa37cffcda0 100644 --- a/noir/noir-repo/tooling/nargo_cli/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_cli/Cargo.toml @@ -37,6 +37,7 @@ nargo = { workspace = true, features = ["rpc"] } nargo_fmt.workspace = true nargo_toml.workspace = true noir_lsp.workspace = true +noir_artifact_cli.workspace = true noir_debugger.workspace = true noirc_driver = { workspace = true, features = ["bn254"] } noirc_frontend = { workspace = true, features = ["bn254"] } diff --git a/noir/noir-repo/tooling/nargo_cli/benches/criterion.rs b/noir/noir-repo/tooling/nargo_cli/benches/criterion.rs index 0722e957833e..22c1107c71ca 100644 --- a/noir/noir-repo/tooling/nargo_cli/benches/criterion.rs +++ b/noir/noir-repo/tooling/nargo_cli/benches/criterion.rs @@ -1,18 +1,14 @@ //! Select representative tests to bench with criterion -use acvm::{acir::native_types::WitnessMap, FieldElement}; +use acvm::{FieldElement, acir::native_types::WitnessMap}; use assert_cmd::prelude::{CommandCargoExt, OutputAssertExt}; -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{Criterion, criterion_group, criterion_main}; -use noirc_abi::{ - input_parser::{Format, InputValue}, - Abi, InputMap, -}; -use noirc_artifacts::program::ProgramArtifact; +use noir_artifact_cli::fs::{artifact::read_program_from_file, inputs::read_inputs_from_file}; use noirc_driver::CompiledProgram; use pprof::criterion::{Output, PProfProfiler}; +use std::cell::RefCell; use std::hint::black_box; use std::path::Path; -use std::{cell::RefCell, collections::BTreeMap}; use std::{process::Command, time::Duration}; include!("./utils.rs"); @@ -50,14 +46,14 @@ fn read_compiled_programs_and_inputs( for package in binary_packages { let program_artifact_path = workspace.package_build_path(package); - let program: CompiledProgram = read_program_from_file(&program_artifact_path).into(); + let program: CompiledProgram = + read_program_from_file(&program_artifact_path).unwrap().into(); let (inputs, _) = read_inputs_from_file( - &package.root_dir, - nargo::constants::PROVER_INPUT_FILE, - Format::Toml, + &package.root_dir.join(nargo::constants::PROVER_INPUT_FILE).with_extension("toml"), &program.abi, - ); + ) + .expect("failed to read input"); let initial_witness = program.abi.encode(&inputs, None).expect("failed to encode input witness"); @@ -67,40 +63,6 @@ fn read_compiled_programs_and_inputs( programs } -/// Read the bytecode and ABI from the compilation output -fn read_program_from_file(circuit_path: &Path) -> ProgramArtifact { - let file_path = circuit_path.with_extension("json"); - let input_string = std::fs::read(file_path).expect("failed to read artifact file"); - serde_json::from_slice(&input_string).expect("failed to deserialize artifact") -} - -/// Read the inputs from Prover.toml -fn read_inputs_from_file( - path: &Path, - file_name: &str, - format: Format, - abi: &Abi, -) -> (InputMap, Option) { - if abi.is_empty() { - return (BTreeMap::new(), None); - } - - let file_path = path.join(file_name).with_extension(format.ext()); - if !file_path.exists() { - if abi.parameters.is_empty() { - return (BTreeMap::new(), None); - } else { - panic!("input file doesn't exist: {}", file_path.display()); - } - } - - let input_string = std::fs::read_to_string(file_path).expect("failed to read input file"); - let mut input_map = format.parse(&input_string, abi).expect("failed to parse input"); - let return_value = input_map.remove(noirc_abi::MAIN_RETURN_NAME); - - (input_map, return_value) -} - /// Use the nargo CLI to compile a test program, then benchmark its execution /// by executing the command directly from the benchmark, so that we can have /// meaningful flamegraphs about the ACVM. diff --git a/noir/noir-repo/tooling/nargo_cli/benches/iai.rs b/noir/noir-repo/tooling/nargo_cli/benches/iai.rs index bcd60111ccf6..c0526e9a7e2f 100644 --- a/noir/noir-repo/tooling/nargo_cli/benches/iai.rs +++ b/noir/noir-repo/tooling/nargo_cli/benches/iai.rs @@ -6,7 +6,7 @@ use std::process::Command; include!("./utils.rs"); macro_rules! iai_command { - ($command_name:tt, $command_string:expr) => { + ($command_name:tt, $command_string:expr_2021) => { paste! { fn []() { let test_program_dirs = get_selected_tests(); diff --git a/noir/noir-repo/tooling/nargo_cli/build.rs b/noir/noir-repo/tooling/nargo_cli/build.rs index afc6994823bd..b9faf018dfda 100644 --- a/noir/noir-repo/tooling/nargo_cli/build.rs +++ b/noir/noir-repo/tooling/nargo_cli/build.rs @@ -198,6 +198,9 @@ fn test_{test_name}(force_brillig: ForceBrillig, inliner_aggressiveness: Inliner // Allow more bytecode in exchange to catch illegal states. nargo.arg("--enable-brillig-debug-assertions"); + // Enable enums as an unstable feature + nargo.arg("-Zenums"); + if force_brillig.0 {{ nargo.arg("--force-brillig"); @@ -435,7 +438,6 @@ fn generate_compile_success_no_bug_tests(test_file: &mut File, test_data_dir: &P &test_dir, "compile", r#" - nargo.arg("--enable-brillig-constraints-check"); nargo.assert().success().stderr(predicate::str::contains("bug:").not()); "#, &MatrixConfig::default(), @@ -465,7 +467,6 @@ fn generate_compile_success_with_bug_tests(test_file: &mut File, test_data_dir: &test_dir, "compile", r#" - nargo.arg("--enable-brillig-constraints-check"); nargo.assert().success().stderr(predicate::str::contains("bug:")); "#, &MatrixConfig::default(), diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs index bb7f06692a66..2247f40f1817 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs @@ -8,15 +8,15 @@ use nargo::{ package::Package, parse_all, prepare_package, workspace::Workspace, }; use nargo_toml::PackageSelection; +use noir_artifact_cli::fs::artifact::write_to_file; use noirc_abi::{AbiParameter, AbiType, MAIN_RETURN_NAME}; -use noirc_driver::{check_crate, compute_function_abi, CompileOptions, CrateId}; +use noirc_driver::{CompileOptions, CrateId, check_crate, compute_function_abi}; use noirc_frontend::{ hir::{Context, ParsedFiles}, monomorphization::monomorphize, }; -use super::{fs::write_to_file, PackageOptions}; -use super::{LockType, WorkspaceCommand}; +use super::{LockType, PackageOptions, WorkspaceCommand}; /// Check a local package and all of its dependencies for errors #[derive(Debug, Clone, Args)] @@ -103,7 +103,8 @@ fn check_package( if should_write_prover { let prover_toml = create_input_toml_template(parameters.clone(), None); - write_to_file(prover_toml.as_bytes(), &path_to_prover_input); + write_to_file(prover_toml.as_bytes(), &path_to_prover_input) + .expect("failed to write template"); } else { eprintln!("Note: Prover.toml already exists. Use --overwrite to force overwrite."); } 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 bb08d2675cb8..2ee19f5edc01 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,6 +9,9 @@ use nargo::package::Package; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::PackageSelection; +use noir_artifact_cli::fs::artifact::{ + read_program_from_file, save_contract_to_file, save_program_to_file, +}; use noirc_driver::DEFAULT_EXPRESSION_WIDTH; use noirc_driver::NOIR_ARTIFACT_VERSION_STRING; use noirc_driver::{CompilationResult, CompileOptions, CompiledContract}; @@ -20,7 +23,6 @@ use notify_debouncer_full::new_debouncer; use crate::errors::CliError; -use super::fs::program::{read_program_from_file, save_contract_to_file, save_program_to_file}; use super::{LockType, PackageOptions, WorkspaceCommand}; use rayon::prelude::*; @@ -80,7 +82,7 @@ fn watch_workspace(workspace: &Workspace, compile_options: &CompileOptions) -> n let noir_files_modified = debounced_events.iter().any(|event| { let mut event_paths = event.event.paths.iter(); let event_affects_noir_file = - event_paths.any(|path| path.extension().map_or(false, |ext| ext == "nr")); + event_paths.any(|path| path.extension().is_some_and(|ext| ext == "nr")); let is_relevant_event_kind = matches!( event.kind, @@ -181,7 +183,7 @@ fn compile_programs( // The loaded circuit includes backend specific transformations, which might be different from the current target. let load_cached_program = |package| { let program_artifact_path = workspace.package_build_path(package); - read_program_from_file(program_artifact_path) + read_program_from_file(&program_artifact_path) .ok() .filter(|p| p.noir_version == NOIR_ARTIFACT_VERSION_STRING) .map(|p| p.into()) @@ -241,7 +243,8 @@ fn compile_programs( // Check solvability. nargo::ops::check_program(&program)?; // Overwrite the build artifacts with the final circuit, which includes the backend specific transformations. - save_program_to_file(&program.into(), &package.name, workspace.target_directory_path()); + save_program_to_file(&program.into(), &package.name, &workspace.target_directory_path()) + .expect("failed to save program"); Ok(((), warnings)) }; @@ -292,7 +295,8 @@ fn save_contract( &contract.into(), &format!("{}-{}", package.name, contract_name), target_dir, - ); + ) + .expect("failed to save contract"); if show_artifact_paths { println!("Saved contract artifact to: {}", artifact_path.display()); } @@ -321,6 +325,7 @@ mod tests { use nargo::ops::compile_program; use nargo_toml::PackageSelection; use noirc_driver::{CompileOptions, CrateName}; + use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use crate::cli::test_cmd::formatters::diagnostic_to_string; use crate::cli::{ @@ -343,7 +348,7 @@ mod tests { fn read_test_program_dirs( test_programs_dir: &Path, test_sub_dir: &str, - ) -> impl Iterator { + ) -> impl Iterator + use<> { let test_case_dir = test_programs_dir.join(test_sub_dir); std::fs::read_dir(test_case_dir) .unwrap() @@ -393,7 +398,7 @@ mod tests { assert!(!test_workspaces.is_empty(), "should find some test workspaces"); - test_workspaces.iter().for_each(|workspace| { + test_workspaces.par_iter().for_each(|workspace| { let (file_manager, parsed_files) = parse_workspace(workspace); let binary_packages = workspace.into_iter().filter(|package| package.is_binary()); diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/dap_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/dap_cmd.rs index 91db844ead34..8987ed80d3e6 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/dap_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/dap_cmd.rs @@ -1,12 +1,12 @@ +use acvm::FieldElement; use acvm::acir::circuit::ExpressionWidth; use acvm::acir::native_types::WitnessMap; -use acvm::FieldElement; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; use nargo::constants::PROVER_INPUT_FILE; use nargo::workspace::Workspace; -use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; -use noirc_abi::input_parser::Format; +use nargo_toml::{PackageSelection, get_package_manifest, resolve_workspace_from_toml}; +use noir_artifact_cli::fs::inputs::read_inputs_from_file; use noirc_driver::{CompileOptions, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING}; use noirc_frontend::graph::CrateName; @@ -20,7 +20,6 @@ use dap::types::Capabilities; use serde_json::Value; use super::debug_cmd::compile_bin_package_for_debugging; -use super::fs::inputs::read_inputs_from_file; use crate::errors::CliError; use noir_debugger::errors::{DapError, LoadError}; @@ -125,11 +124,13 @@ fn load_and_compile_project( let compiled_program = nargo::ops::transform_program(compiled_program, expression_width); - let (inputs_map, _) = - read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &compiled_program.abi) - .map_err(|_| { - LoadError::Generic(format!("Failed to read program inputs from {}", prover_name)) - })?; + let (inputs_map, _) = read_inputs_from_file( + &package.root_dir.join(prover_name).with_extension("toml"), + &compiled_program.abi, + ) + .map_err(|e| { + LoadError::Generic(format!("Failed to read program inputs from {prover_name}: {e}")) + })?; let initial_witness = compiled_program .abi .encode(&inputs_map, None) @@ -143,12 +144,7 @@ fn loop_uninitialized_dap( expression_width: ExpressionWidth, pedantic_solving: bool, ) -> Result<(), DapError> { - loop { - let req = match server.poll_request()? { - Some(req) => req, - None => break, - }; - + while let Some(req) = server.poll_request()? { match req.command { Command::Initialize(_) => { let rsp = req.success(ResponseBody::Initialize(Capabilities { @@ -165,7 +161,7 @@ fn loop_uninitialized_dap( server.respond(req.error("Missing launch arguments"))?; continue; }; - let Some(Value::String(ref project_folder)) = additional_data.get("projectFolder") + let Some(Value::String(project_folder)) = additional_data.get("projectFolder") else { server.respond(req.error("Missing project folder argument"))?; continue; diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs index 259abbbbfd03..f9303180fc0d 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -1,7 +1,7 @@ -use std::path::PathBuf; +use std::path::Path; -use acvm::acir::native_types::WitnessStack; use acvm::FieldElement; +use acvm::acir::native_types::WitnessStack; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; @@ -13,14 +13,15 @@ use nargo::package::{CrateName, Package}; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::PackageSelection; -use noirc_abi::input_parser::{Format, InputValue}; +use noir_artifact_cli::fs::inputs::read_inputs_from_file; +use noir_artifact_cli::fs::witness::save_witness_to_dir; use noirc_abi::InputMap; -use noirc_driver::{file_manager_with_stdlib, CompileOptions, CompiledProgram}; +use noirc_abi::input_parser::InputValue; +use noirc_driver::{CompileOptions, CompiledProgram, file_manager_with_stdlib}; use noirc_frontend::debug::DebugInstrumenter; use noirc_frontend::hir::ParsedFiles; use super::compile_cmd::get_target_width; -use super::fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}; use super::{LockType, WorkspaceCommand}; use crate::errors::CliError; @@ -48,6 +49,10 @@ pub(crate) struct DebugCommand { /// Disable vars debug instrumentation (enabled by default) #[clap(long)] skip_instrumentation: Option, + + /// Raw string printing of source for testing + #[clap(long, hide = true)] + raw_source_printing: Option, } impl WorkspaceCommand for DebugCommand { @@ -97,6 +102,7 @@ pub(crate) fn run(args: DebugCommand, workspace: Workspace) -> Result<(), CliErr &args.witness_name, target_dir, args.compile_options.pedantic_solving, + args.raw_source_printing.unwrap_or(false), ) } @@ -107,7 +113,7 @@ pub(crate) fn compile_bin_package_for_debugging( skip_instrumentation: bool, compile_options: CompileOptions, ) -> Result { - let mut workspace_file_manager = file_manager_with_stdlib(std::path::Path::new("")); + let mut workspace_file_manager = file_manager_with_stdlib(Path::new("")); insert_all_files_for_workspace_into_file_manager(workspace, &mut workspace_file_manager); let mut parsed_files = parse_all(&workspace_file_manager); @@ -170,7 +176,7 @@ fn instrument_package_files( for ancestor in file_path.ancestors() { if ancestor == entry_path_parent { // file is in package - debug_instrumenter.instrument_module(&mut parsed_file.0); + debug_instrumenter.instrument_module(&mut parsed_file.0, *file_id); } } } @@ -183,16 +189,22 @@ fn run_async( program: CompiledProgram, prover_name: &str, witness_name: &Option, - target_dir: &PathBuf, + target_dir: &Path, pedantic_solving: bool, + raw_source_printing: bool, ) -> Result<(), CliError> { use tokio::runtime::Builder; let runtime = Builder::new_current_thread().enable_all().build().unwrap(); runtime.block_on(async { println!("[{}] Starting debugger", package.name); - let (return_value, witness_stack) = - debug_program_and_decode(program, package, prover_name, pedantic_solving)?; + let (return_value, witness_stack) = debug_program_and_decode( + program, + package, + prover_name, + pedantic_solving, + raw_source_printing, + )?; if let Some(solved_witness_stack) = witness_stack { println!("[{}] Circuit witness successfully solved", package.name); @@ -203,7 +215,7 @@ fn run_async( if let Some(witness_name) = witness_name { let witness_path = - save_witness_to_dir(solved_witness_stack, witness_name, target_dir)?; + save_witness_to_dir(&solved_witness_stack, witness_name, target_dir)?; println!("[{}] Witness saved to {}", package.name, witness_path.display()); } @@ -220,12 +232,15 @@ fn debug_program_and_decode( package: &Package, prover_name: &str, pedantic_solving: bool, + raw_source_printing: bool, ) -> Result<(Option, Option>), CliError> { // Parse the initial witness values from Prover.toml - let (inputs_map, _) = - read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &program.abi)?; + let (inputs_map, _) = read_inputs_from_file( + &package.root_dir.join(prover_name).with_extension("toml"), + &program.abi, + )?; let program_abi = program.abi.clone(); - let witness_stack = debug_program(program, &inputs_map, pedantic_solving)?; + let witness_stack = debug_program(program, &inputs_map, pedantic_solving, raw_source_printing)?; match witness_stack { Some(witness_stack) => { @@ -244,6 +259,7 @@ pub(crate) fn debug_program( compiled_program: CompiledProgram, inputs_map: &InputMap, pedantic_solving: bool, + raw_source_printing: bool, ) -> Result>, CliError> { let initial_witness = compiled_program.abi.encode(inputs_map, None)?; @@ -251,6 +267,7 @@ pub(crate) fn debug_program( &Bn254BlackBoxSolver(pedantic_solving), compiled_program, initial_witness, + raw_source_printing, ) .map_err(CliError::from) } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs index 884ba06cdcbf..07a7c1195af7 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -1,26 +1,12 @@ -use std::path::PathBuf; - -use acvm::acir::native_types::WitnessStack; -use acvm::FieldElement; -use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; use nargo::constants::PROVER_INPUT_FILE; -use nargo::errors::try_to_diagnose_runtime_error; -use nargo::foreign_calls::DefaultForeignCallBuilder; -use nargo::package::Package; use nargo::workspace::Workspace; -use nargo::PrintOutput; use nargo_toml::PackageSelection; -use noirc_abi::input_parser::{Format, InputValue}; -use noirc_abi::InputMap; -use noirc_artifacts::debug::DebugArtifact; -use noirc_driver::{CompileOptions, CompiledProgram}; +use noirc_driver::CompileOptions; use super::compile_cmd::compile_workspace_full; -use super::fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}; use super::{LockType, PackageOptions, WorkspaceCommand}; -use crate::cli::fs::program::read_program_from_file; use crate::errors::CliError; /// Executes a circuit to calculate its return value @@ -59,126 +45,27 @@ impl WorkspaceCommand for ExecuteCommand { } pub(crate) fn run(args: ExecuteCommand, workspace: Workspace) -> Result<(), CliError> { - let target_dir = &workspace.target_directory_path(); - // Compile the full workspace in order to generate any build artifacts. compile_workspace_full(&workspace, &args.compile_options)?; let binary_packages = workspace.into_iter().filter(|package| package.is_binary()); for package in binary_packages { let program_artifact_path = workspace.package_build_path(package); - let program: CompiledProgram = - read_program_from_file(program_artifact_path.clone())?.into(); - let abi = program.abi.clone(); - - let results = execute_program_and_decode( - program, - package, - &args.prover_name, - args.oracle_resolver.as_deref(), - Some(workspace.root_dir.clone()), - Some(package.name.to_string()), - args.compile_options.pedantic_solving, - )?; - - println!("[{}] Circuit witness successfully solved", package.name); - if let Some(ref return_value) = results.actual_return { - println!("[{}] Circuit output: {return_value:?}", package.name); - } - - let package_name = package.name.clone().into(); - let witness_name = args.witness_name.as_ref().unwrap_or(&package_name); - let witness_path = save_witness_to_dir(results.witness_stack, witness_name, target_dir)?; - println!("[{}] Witness saved to {}", package.name, witness_path.display()); - - // Sanity checks on the return value after the witness has been saved, so it can be inspected if necessary. - if let Some(expected) = results.expected_return { - if results.actual_return.as_ref() != Some(&expected) { - return Err(CliError::UnexpectedReturn { expected, actual: results.actual_return }); - } - } - // We can expect that if the circuit returns something, it should be non-empty after execution. - if let Some(ref expected) = abi.return_type { - if results.actual_return.is_none() { - return Err(CliError::MissingReturn { expected: expected.clone() }); - } - } + let prover_file = package.root_dir.join(&args.prover_name).with_extension("toml"); + + let cmd = noir_artifact_cli::commands::execute_cmd::ExecuteCommand { + artifact_path: program_artifact_path, + prover_file, + output_dir: Some(workspace.target_directory_path()), + witness_name: Some( + args.witness_name.clone().unwrap_or_else(|| package.name.to_string()), + ), + contract_fn: None, + oracle_resolver: args.oracle_resolver.clone(), + pedantic_solving: args.compile_options.pedantic_solving, + }; + + noir_artifact_cli::commands::execute_cmd::run(cmd)?; } Ok(()) } - -fn execute_program_and_decode( - program: CompiledProgram, - package: &Package, - prover_name: &str, - foreign_call_resolver_url: Option<&str>, - root_path: Option, - package_name: Option, - pedantic_solving: bool, -) -> Result { - // Parse the initial witness values from Prover.toml - let (inputs_map, expected_return) = - read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &program.abi)?; - let witness_stack = execute_program( - &program, - &inputs_map, - foreign_call_resolver_url, - root_path, - package_name, - pedantic_solving, - )?; - // Get the entry point witness for the ABI - let main_witness = - &witness_stack.peek().expect("Should have at least one witness on the stack").witness; - let (_, actual_return) = program.abi.decode(main_witness)?; - - Ok(ExecutionResults { expected_return, actual_return, witness_stack }) -} - -struct ExecutionResults { - expected_return: Option, - actual_return: Option, - witness_stack: WitnessStack, -} - -pub(crate) fn execute_program( - compiled_program: &CompiledProgram, - inputs_map: &InputMap, - foreign_call_resolver_url: Option<&str>, - root_path: Option, - package_name: Option, - pedantic_solving: bool, -) -> Result, CliError> { - let initial_witness = compiled_program.abi.encode(inputs_map, None)?; - - let solved_witness_stack_err = nargo::ops::execute_program( - &compiled_program.program, - initial_witness, - &Bn254BlackBoxSolver(pedantic_solving), - &mut DefaultForeignCallBuilder { - output: PrintOutput::Stdout, - enable_mocks: false, - resolver_url: foreign_call_resolver_url.map(|s| s.to_string()), - root_path, - package_name, - } - .build(), - ); - match solved_witness_stack_err { - Ok(solved_witness_stack) => Ok(solved_witness_stack), - Err(err) => { - let debug_artifact = DebugArtifact { - debug_symbols: compiled_program.debug.clone(), - file_map: compiled_program.file_map.clone(), - }; - - if let Some(diagnostic) = - try_to_diagnose_runtime_error(&err, &compiled_program.abi, &compiled_program.debug) - { - diagnostic.report(&debug_artifact, false); - } - - Err(CliError::NargoError(err)) - } - } -} diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs index 09fe11fdb749..373dfce86a91 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs @@ -1,6 +1,7 @@ use nargo::errors::CompileError; use nargo::ops::report_errors; -use noirc_errors::FileDiagnostic; +use noir_artifact_cli::fs::artifact::save_program_to_file; +use noirc_errors::CustomDiagnostic; use noirc_frontend::hir::ParsedFiles; use rayon::prelude::*; @@ -11,7 +12,7 @@ use nargo::prepare_package; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::PackageSelection; -use noirc_driver::{compile_no_check, CompileOptions, CompiledProgram}; +use noirc_driver::{CompileOptions, CompiledProgram, compile_no_check}; use clap::Args; @@ -19,7 +20,6 @@ use crate::errors::CliError; use super::check_cmd::check_crate_and_report_errors; -use super::fs::program::save_program_to_file; use super::{LockType, PackageOptions, WorkspaceCommand}; /// Exports functions marked with #[export] attribute @@ -82,7 +82,7 @@ fn compile_exported_functions( |(function_name, function_id)| -> Result<(String, CompiledProgram), CompileError> { // TODO: We should to refactor how to deal with compilation errors to avoid this. let program = compile_no_check(&mut context, compile_options, function_id, None, false) - .map_err(|error| vec![FileDiagnostic::from(error)]); + .map_err(|error| vec![CustomDiagnostic::from(error)]); let program = report_errors( program.map(|program| (program, Vec::new())), @@ -97,7 +97,7 @@ fn compile_exported_functions( let export_dir = workspace.export_directory_path(); for (function_name, program) in exported_programs { - save_program_to_file(&program.into(), &function_name.parse().unwrap(), &export_dir); + save_program_to_file(&program.into(), &function_name.parse().unwrap(), &export_dir)?; } Ok(()) } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs index 1cdfb1e0c4fb..53551c139508 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs @@ -56,11 +56,8 @@ pub(crate) fn run(args: FormatCommand, workspace: Workspace) -> Result<(), CliEr let is_all_warnings = errors.iter().all(ParserError::is_warning); if !is_all_warnings { let errors = errors - .into_iter() - .map(|error| { - let error = CustomDiagnostic::from(&error); - error.in_file(file_id) - }) + .iter() + .map(CustomDiagnostic::from) .collect(); let _ = report_errors::<()>( @@ -119,7 +116,7 @@ fn visit_noir_files( let path = entry.path(); if path.is_dir() { visit_noir_files(&path, cb)?; - } else if entry.path().extension().map_or(false, |extension| extension == "nr") { + } else if entry.path().extension().is_some_and(|extension| extension == "nr") { cb(&entry)?; } } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/inputs.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/fs/inputs.rs deleted file mode 100644 index 4a7a81431bbc..000000000000 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/inputs.rs +++ /dev/null @@ -1,42 +0,0 @@ -use noirc_abi::{ - input_parser::{Format, InputValue}, - Abi, InputMap, MAIN_RETURN_NAME, -}; -use std::{collections::BTreeMap, path::Path}; - -use crate::errors::FilesystemError; - -/// Returns the circuit's parameters and its return value, if one exists. -/// # Examples -/// -/// ```ignore -/// let (input_map, return_value): (InputMap, Option) = -/// read_inputs_from_file(path, "Verifier", Format::Toml, &abi)?; -/// ``` -pub(crate) fn read_inputs_from_file>( - path: P, - file_name: &str, - format: Format, - abi: &Abi, -) -> Result<(InputMap, Option), FilesystemError> { - if abi.is_empty() { - return Ok((BTreeMap::new(), None)); - } - - let file_path = path.as_ref().join(file_name).with_extension(format.ext()); - if !file_path.exists() { - if abi.parameters.is_empty() { - // Reading a return value from the `Prover.toml` is optional, - // so if the ABI has no parameters we can skip reading the file if it doesn't exist. - return Ok((BTreeMap::new(), None)); - } else { - return Err(FilesystemError::MissingTomlFile(file_name.to_owned(), file_path)); - } - } - - let input_string = std::fs::read_to_string(file_path).unwrap(); - let mut input_map = format.parse(&input_string, abi)?; - let return_value = input_map.remove(MAIN_RETURN_NAME); - - Ok((input_map, return_value)) -} diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/mod.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/fs/mod.rs deleted file mode 100644 index 8658bd5b2482..000000000000 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/mod.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::{ - fs::File, - io::Write, - path::{Path, PathBuf}, -}; - -pub(super) mod inputs; -pub(super) mod program; -pub(super) mod witness; - -pub(super) fn create_named_dir(named_dir: &Path, name: &str) -> PathBuf { - std::fs::create_dir_all(named_dir) - .unwrap_or_else(|_| panic!("could not create the `{name}` directory")); - - PathBuf::from(named_dir) -} - -pub(super) fn write_to_file(bytes: &[u8], path: &Path) -> String { - let display = path.display(); - - let mut file = match File::create(path) { - Err(why) => panic!("couldn't create {display}: {why}"), - Ok(file) => file, - }; - - match file.write_all(bytes) { - Err(why) => panic!("couldn't write to {display}: {why}"), - Ok(_) => display.to_string(), - } -} diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/program.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/fs/program.rs deleted file mode 100644 index 87783e4573a9..000000000000 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/program.rs +++ /dev/null @@ -1,51 +0,0 @@ -use std::path::{Path, PathBuf}; - -use nargo::package::CrateName; -use noirc_artifacts::{contract::ContractArtifact, program::ProgramArtifact}; - -use crate::errors::FilesystemError; - -use super::{create_named_dir, write_to_file}; - -pub(crate) fn save_program_to_file>( - program_artifact: &ProgramArtifact, - crate_name: &CrateName, - circuit_dir: P, -) -> PathBuf { - let circuit_name: String = crate_name.into(); - save_build_artifact_to_file(program_artifact, &circuit_name, circuit_dir) -} - -pub(crate) fn save_contract_to_file>( - compiled_contract: &ContractArtifact, - circuit_name: &str, - circuit_dir: P, -) -> PathBuf { - save_build_artifact_to_file(compiled_contract, circuit_name, circuit_dir) -} - -fn save_build_artifact_to_file, T: ?Sized + serde::Serialize>( - build_artifact: &T, - artifact_name: &str, - circuit_dir: P, -) -> PathBuf { - create_named_dir(circuit_dir.as_ref(), "target"); - let circuit_path = circuit_dir.as_ref().join(artifact_name).with_extension("json"); - write_to_file(&serde_json::to_vec(build_artifact).unwrap(), &circuit_path); - - circuit_path -} - -#[tracing::instrument(level = "trace", skip_all)] -pub(crate) fn read_program_from_file>( - circuit_path: P, -) -> Result { - let file_path = circuit_path.as_ref().with_extension("json"); - - let input_string = - std::fs::read(&file_path).map_err(|_| FilesystemError::PathNotValid(file_path))?; - let program = serde_json::from_slice(&input_string) - .map_err(|err| FilesystemError::ProgramSerializationError(err.to_string()))?; - - Ok(program) -} diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/witness.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/fs/witness.rs deleted file mode 100644 index f95eb3d7a4c0..000000000000 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/witness.rs +++ /dev/null @@ -1,22 +0,0 @@ -use std::path::{Path, PathBuf}; - -use acvm::{acir::native_types::WitnessStack, FieldElement}; -use nargo::constants::WITNESS_EXT; - -use super::{create_named_dir, write_to_file}; -use crate::errors::FilesystemError; - -pub(crate) fn save_witness_to_dir>( - witness_stack: WitnessStack, - witness_name: &str, - witness_dir: P, -) -> Result { - create_named_dir(witness_dir.as_ref(), "witness"); - let witness_path = witness_dir.as_ref().join(witness_name).with_extension(WITNESS_EXT); - - let buf: Vec = witness_stack.try_into()?; - - write_to_file(buf.as_slice(), &witness_path); - - Ok(witness_path) -} diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/generate_completion_script_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/generate_completion_script_cmd.rs index 0b8fa1ee3e70..5f7329e14ba2 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/generate_completion_script_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/generate_completion_script_cmd.rs @@ -23,7 +23,7 @@ pub(crate) fn run(command: GenerateCompletionScriptCommand) -> Result<(), CliErr return Err(CliError::Generic( "Invalid shell. Supported shells are: bash, elvish, fish, powershell, zsh" .to_string(), - )) + )); } }; diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs index 4561a1221f66..c8a9c289fc0e 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs @@ -7,22 +7,21 @@ use nargo::{ workspace::Workspace, }; use nargo_toml::PackageSelection; -use noirc_abi::input_parser::Format; +use noir_artifact_cli::fs::{artifact::read_program_from_file, inputs::read_inputs_from_file}; use noirc_artifacts::program::ProgramArtifact; use noirc_artifacts_info::{ - count_opcodes_and_gates_in_program, show_info_report, FunctionInfo, InfoReport, ProgramInfo, + FunctionInfo, InfoReport, ProgramInfo, count_opcodes_and_gates_in_program, show_info_report, }; use noirc_driver::CompileOptions; -use prettytable::{row, Row}; +use prettytable::{Row, row}; use rayon::prelude::*; use serde::Serialize; use crate::errors::CliError; use super::{ - compile_cmd::{compile_workspace_full, get_target_width}, - fs::{inputs::read_inputs_from_file, program::read_program_from_file}, LockType, PackageOptions, WorkspaceCommand, + compile_cmd::{compile_workspace_full, get_target_width}, }; /// Provides detailed information on each of a program's function (represented by a single circuit) @@ -75,7 +74,7 @@ pub(crate) fn run(mut args: InfoCommand, workspace: Workspace) -> Result<(), Cli .filter(|package| package.is_binary()) .map(|package| -> Result<(Package, ProgramArtifact), CliError> { let program_artifact_path = workspace.package_build_path(package); - let program = read_program_from_file(program_artifact_path)?; + let program = read_program_from_file(&program_artifact_path)?; Ok((package.clone(), program)) }) .collect::>()?; @@ -144,9 +143,7 @@ fn profile_brillig_execution( for (package, program_artifact) in binary_packages.iter() { // Parse the initial witness values from Prover.toml or Prover.json let (inputs_map, _) = read_inputs_from_file( - &package.root_dir, - prover_name, - Format::Toml, + &package.root_dir.join(prover_name).with_extension("toml"), &program_artifact.abi, )?; let initial_witness = program_artifact.abi.encode(&inputs_map, None)?; diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/init_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/init_cmd.rs index ffeb5d9ba74b..bc67a9ba3a87 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/init_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/init_cmd.rs @@ -1,10 +1,10 @@ use crate::errors::CliError; -use super::fs::{create_named_dir, write_to_file}; use super::NargoConfig; use clap::Args; use nargo::constants::{PKG_FILE, SRC_DIR}; use nargo::package::{CrateName, PackageType}; +use noir_artifact_cli::fs::artifact::write_to_file; use std::path::PathBuf; /// Create a Noir project in the current directory. @@ -58,7 +58,6 @@ pub(crate) fn initialize_project( package_type: PackageType, ) { let src_dir = package_dir.join(SRC_DIR); - create_named_dir(&src_dir, "src"); let toml_contents = format!( r#"[package] @@ -69,14 +68,18 @@ authors = [""] [dependencies]"# ); - write_to_file(toml_contents.as_bytes(), &package_dir.join(PKG_FILE)); + write_to_file(toml_contents.as_bytes(), &package_dir.join(PKG_FILE)).unwrap(); // This uses the `match` syntax instead of `if` so we get a compile error when we add new package types (which likely need new template files) match package_type { - PackageType::Binary => write_to_file(BIN_EXAMPLE.as_bytes(), &src_dir.join("main.nr")), + PackageType::Binary => { + write_to_file(BIN_EXAMPLE.as_bytes(), &src_dir.join("main.nr")).unwrap(); + } PackageType::Contract => { - write_to_file(CONTRACT_EXAMPLE.as_bytes(), &src_dir.join("main.nr")) + write_to_file(CONTRACT_EXAMPLE.as_bytes(), &src_dir.join("main.nr")).unwrap(); + } + PackageType::Library => { + write_to_file(LIB_EXAMPLE.as_bytes(), &src_dir.join("lib.nr")).unwrap(); } - PackageType::Library => write_to_file(LIB_EXAMPLE.as_bytes(), &src_dir.join("lib.nr")), }; println!("Project successfully created! It is located at {}", package_dir.display()); } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/mod.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/mod.rs index 1d59fdb806fd..0c644ae554c8 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/mod.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/mod.rs @@ -2,7 +2,7 @@ use clap::{Args, Parser, Subcommand}; use const_format::formatcp; use nargo::workspace::Workspace; use nargo_toml::{ - get_package_manifest, resolve_workspace_from_toml, ManifestError, PackageSelection, + ManifestError, PackageSelection, get_package_manifest, resolve_workspace_from_toml, }; use noirc_driver::{CrateName, NOIR_ARTIFACT_VERSION_STRING}; use std::{ @@ -14,8 +14,6 @@ use color_eyre::eyre; use crate::errors::CliError; -mod fs; - mod check_cmd; mod compile_cmd; mod dap_cmd; @@ -213,14 +211,15 @@ where /// Lock the (selected) packages in the workspace. /// The lock taken can be shared for commands that only read the artifacts, /// or exclusive for the ones that (might) write artifacts as well. -fn lock_workspace(workspace: &Workspace, exclusive: bool) -> Result, CliError> { - use fs2::FileExt as _; - +fn lock_workspace( + workspace: &Workspace, + exclusive: bool, +) -> Result>, CliError> { struct LockedFile(File); impl Drop for LockedFile { fn drop(&mut self) { - let _ = self.0.unlock(); + let _ = fs2::FileExt::unlock(&self.0); } } @@ -233,15 +232,17 @@ fn lock_workspace(workspace: &Workspace, exclusive: bool) -> Result TestRunner<'a> { }; } - if all_passed { - Ok(()) - } else { - Err(CliError::Generic(String::new())) - } + if all_passed { Ok(()) } else { Err(CliError::Generic(String::new())) } } /// Runs all tests. Returns `true` if all tests passed, `false` otherwise. @@ -261,21 +257,22 @@ impl<'a> TestRunner<'a> { // Specify a larger-than-default stack size to prevent overflowing stack in large programs. // (the default is 2MB) .stack_size(STACK_SIZE) - .spawn_scoped(scope, move || loop { - // Get next test to process from the iterator. - let Some(test) = iter.lock().unwrap().next() else { - break; - }; - - self.formatter - .test_start_async(&test.name, &test.package_name) - .expect("Could not display test start"); - - let time_before_test = std::time::Instant::now(); - let (status, output) = match catch_unwind(test.runner) { - Ok((status, output)) => (status, output), - Err(err) => ( - TestStatus::Fail { + .spawn_scoped(scope, move || { + loop { + // Get next test to process from the iterator. + let Some(test) = iter.lock().unwrap().next() else { + break; + }; + + self.formatter + .test_start_async(&test.name, &test.package_name) + .expect("Could not display test start"); + + let time_before_test = std::time::Instant::now(); + let (status, output) = match catch_unwind(test.runner) { + Ok((status, output)) => (status, output), + Err(err) => ( + TestStatus::Fail { message: // It seems `panic!("...")` makes the error be `&str`, so we handle this common case if let Some(message) = err.downcast_ref::<&str>() { @@ -285,31 +282,32 @@ impl<'a> TestRunner<'a> { }, error_diagnostic: None, }, - String::new(), - ), - }; - let time_to_run = time_before_test.elapsed(); - - let test_result = TestResult { - name: test.name, - package_name: test.package_name, - status, - output, - time_to_run, - }; - - self.formatter - .test_end_async( - &test_result, - self.file_manager, - self.args.show_output, - self.args.compile_options.deny_warnings, - self.args.compile_options.silence_warnings, - ) - .expect("Could not display test start"); - - if thread_sender.send(test_result).is_err() { - break; + String::new(), + ), + }; + let time_to_run = time_before_test.elapsed(); + + let test_result = TestResult { + name: test.name, + package_name: test.package_name, + status, + output, + time_to_run, + }; + + self.formatter + .test_end_async( + &test_result, + self.file_manager, + self.args.show_output, + self.args.compile_options.deny_warnings, + self.args.compile_options.silence_warnings, + ) + .expect("Could not display test start"); + + if thread_sender.send(test_result).is_err() { + break; + } } }) .unwrap(); @@ -407,19 +405,21 @@ impl<'a> TestRunner<'a> { // Specify a larger-than-default stack size to prevent overflowing stack in large programs. // (the default is 2MB) .stack_size(STACK_SIZE) - .spawn_scoped(scope, move || loop { - // Get next package to process from the iterator. - let Some(package) = iter.lock().unwrap().next() else { - break; - }; - let tests = self.collect_package_tests::( - package, - self.args.oracle_resolver.as_deref(), - Some(self.workspace.root_dir.clone()), - package.name.to_string(), - ); - if thread_sender.send((package, tests)).is_err() { - break; + .spawn_scoped(scope, move || { + loop { + // Get next package to process from the iterator. + let Some(package) = iter.lock().unwrap().next() else { + break; + }; + let tests = self.collect_package_tests::( + package, + self.args.oracle_resolver.as_deref(), + Some(self.workspace.root_dir.clone()), + package.name.to_string(), + ); + if thread_sender.send((package, tests)).is_err() { + break; + } } }) .unwrap(); @@ -440,11 +440,7 @@ impl<'a> TestRunner<'a> { } }); - if let Some(error) = error { - Err(error) - } else { - Ok(package_tests) - } + if let Some(error) = error { Err(error) } else { Ok(package_tests) } } /// Compiles a single package and returns all of its tests diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd/formatters.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd/formatters.rs index 91d6f0066b1c..686281292458 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd/formatters.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd/formatters.rs @@ -2,8 +2,8 @@ use std::{io::Write, panic::RefUnwindSafe, time::Duration}; use fm::FileManager; use nargo::ops::TestStatus; -use noirc_errors::{reporter::stack_trace, FileDiagnostic}; -use serde_json::{json, Map}; +use noirc_errors::{CustomDiagnostic, reporter::stack_trace}; +use serde_json::{Map, json}; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, StandardStreamLock, WriteColor}; use super::TestResult; @@ -438,7 +438,7 @@ impl Formatter for JsonFormatter { stdout.push_str(message.trim()); if let Some(diagnostic) = error_diagnostic { - if !(diagnostic.diagnostic.is_warning() && silence_warnings) { + if !(diagnostic.is_warning() && silence_warnings) { stdout.push('\n'); stdout.push_str(&diagnostic_to_string(diagnostic, file_manager)); } @@ -450,7 +450,7 @@ impl Formatter for JsonFormatter { TestStatus::CompileError(diagnostic) => { json.insert("event".to_string(), json!("failed")); - if !(diagnostic.diagnostic.is_warning() && silence_warnings) { + if !(diagnostic.is_warning() && silence_warnings) { if !stdout.is_empty() { stdout.push('\n'); } @@ -515,12 +515,11 @@ fn package_start(package_name: &str, test_count: usize) -> std::io::Result<()> { } pub(crate) fn diagnostic_to_string( - file_diagnostic: &FileDiagnostic, + custom_diagnostic: &CustomDiagnostic, file_manager: &FileManager, ) -> String { let file_map = file_manager.as_file_map(); - let custom_diagnostic = &file_diagnostic.diagnostic; let mut message = String::new(); message.push_str(custom_diagnostic.message.trim()); @@ -529,7 +528,7 @@ pub(crate) fn diagnostic_to_string( message.push_str(note.trim()); } - if let Ok(name) = file_map.get_name(file_diagnostic.file_id) { + if let Ok(name) = file_map.get_name(custom_diagnostic.file) { message.push('\n'); message.push_str(&format!("at {name}")); } diff --git a/noir/noir-repo/tooling/nargo_cli/src/errors.rs b/noir/noir-repo/tooling/nargo_cli/src/errors.rs index 9255d6fc6a69..fb241bcbd293 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/errors.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/errors.rs @@ -1,37 +1,11 @@ -use acvm::{acir::native_types::WitnessStackError, FieldElement}; -use nargo::{errors::CompileError, NargoError}; +use acvm::FieldElement; +use nargo::{NargoError, errors::CompileError}; use nargo_toml::ManifestError; use noir_debugger::errors::DapError; -use noirc_abi::{ - errors::{AbiError, InputParserError}, - input_parser::InputValue, - AbiReturnType, -}; +use noirc_abi::errors::AbiError; use std::path::PathBuf; use thiserror::Error; -#[derive(Debug, Error)] -pub(crate) enum FilesystemError { - #[error("Error: {} is not a valid path\nRun either `nargo compile` to generate missing build artifacts or `nargo prove` to construct a proof", .0.display())] - PathNotValid(PathBuf), - - #[error( - " Error: cannot find {0}.toml file.\n Expected location: {1:?} \n Please generate this file at the expected location." - )] - MissingTomlFile(String, PathBuf), - - /// Input parsing error - #[error(transparent)] - InputParserError(#[from] InputParserError), - - /// WitnessStack serialization error - #[error(transparent)] - WitnessStackSerialization(#[from] WitnessStackError), - - #[error("Error: could not deserialize build program: {0}")] - ProgramSerializationError(String), -} - #[derive(Debug, Error)] pub(crate) enum CliError { #[error("{0}")] @@ -43,13 +17,13 @@ pub(crate) enum CliError { #[error("Invalid package name {0}. Did you mean to use `--name`?")] InvalidPackageName(String), - /// ABI encoding/decoding error + /// Artifact CLI error #[error(transparent)] - AbiError(#[from] AbiError), + ArtifactError(#[from] noir_artifact_cli::errors::CliError), - /// Filesystem errors + /// ABI encoding/decoding error #[error(transparent)] - FilesystemError(#[from] FilesystemError), + AbiError(#[from] AbiError), #[error(transparent)] LspError(#[from] async_lsp::Error), @@ -68,10 +42,4 @@ pub(crate) enum CliError { /// Error from the compilation pipeline #[error(transparent)] CompileError(#[from] CompileError), - - #[error("Unexpected return value: expected {expected:?}; got {actual:?}")] - UnexpectedReturn { expected: InputValue, actual: Option }, - - #[error("Missing return witnesses; expected {expected:?}")] - MissingReturn { expected: AbiReturnType }, } diff --git a/noir/noir-repo/tooling/nargo_cli/src/main.rs b/noir/noir-repo/tooling/nargo_cli/src/main.rs index 3ea167b7ffad..f4cc74447bc6 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/main.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/main.rs @@ -15,7 +15,7 @@ use std::env; use color_eyre::config::HookBuilder; use tracing_appender::rolling; -use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter}; +use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan}; const PANIC_MESSAGE: &str = "This is a bug. We may have already fixed this in newer versions of Nargo so try searching for similar issues at https://github.com/noir-lang/noir/issues/.\nIf there isn't an open issue for this bug, consider opening one at https://github.com/noir-lang/noir/issues/new?labels=bug&template=bug_report.yml"; diff --git a/noir/noir-repo/tooling/nargo_cli/tests/stdlib-props.rs b/noir/noir-repo/tooling/nargo_cli/tests/stdlib-props.rs index 780b34bf0c34..bafcc13d855b 100644 --- a/noir/noir-repo/tooling/nargo_cli/tests/stdlib-props.rs +++ b/noir/noir-repo/tooling/nargo_cli/tests/stdlib-props.rs @@ -1,16 +1,15 @@ use std::{cell::RefCell, collections::BTreeMap, path::Path}; -use acvm::{acir::native_types::WitnessStack, AcirField, FieldElement}; +use acvm::{AcirField, FieldElement, acir::native_types::WitnessStack}; use iter_extended::vecmap; use nargo::{foreign_calls::DefaultForeignCallBuilder, ops::execute_program, parse_all}; use noirc_abi::input_parser::InputValue; use noirc_driver::{ - compile_main, file_manager_with_stdlib, prepare_crate, CompilationResult, CompileOptions, - CompiledProgram, CrateId, + CompilationResult, CompileOptions, CompiledProgram, CrateId, compile_main, + file_manager_with_stdlib, prepare_crate, }; use noirc_frontend::hir::Context; use proptest::prelude::*; -use sha3::Digest; /// Inputs and expected output of a snippet encoded in ABI format. #[derive(Debug)] @@ -105,64 +104,6 @@ fn run_snippet_proptest( }); } -/// Run property tests on a code snippet which is assumed to execute a hashing function with the following signature: -/// -/// ```ignore -/// fn main(input: [u8; {max_len}], message_size: u32) -> pub [u8; 32] -/// ``` -/// -/// The calls are executed with and without forcing brillig, because it seems common for hash functions to run different -/// code paths based on `runtime::is_unconstrained()`. -fn run_hash_proptest( - // Different generic maximum input sizes to try. - max_lengths: &[usize], - // Some hash functions allow inputs which are less than the generic parameters, others don't. - variable_length: bool, - // Make the source code specialized for a given expected input size. - source: impl Fn(usize) -> String, - // Rust implementation of the hash function. - hash: fn(&[u8]) -> [u8; N], -) { - for max_len in max_lengths { - let max_len = *max_len; - // The maximum length is used to pick the generic version of the method. - let source = source(max_len); - // Hash functions runs differently depending on whether the code is unconstrained or not. - for force_brillig in [false, true] { - let length_strategy = - if variable_length { (0..=max_len).boxed() } else { Just(max_len).boxed() }; - // The actual input length can be up to the maximum. - let strategy = length_strategy - .prop_flat_map(|len| prop::collection::vec(any::(), len)) - .prop_map(move |mut msg| { - // The output is the hash of the data as it is. - let output = hash(&msg); - - // The input has to be padded to the maximum length. - let msg_size = msg.len(); - msg.resize(max_len, 0u8); - - let mut inputs = vec![("input", bytes_input(&msg))]; - - // Omit the `message_size` if the hash function doesn't support it. - if variable_length { - inputs.push(( - "message_size", - InputValue::Field(FieldElement::from(msg_size)), - )); - } - - SnippetInputOutput::new(inputs, bytes_input(&output)).with_description(format!( - "force_brillig = {force_brillig}, max_len = {max_len}" - )) - }) - .boxed(); - - run_snippet_proptest(source.clone(), force_brillig, strategy); - } - } -} - /// This is just a simple test to check that property testing works. #[test] fn fuzz_basic() { @@ -187,72 +128,6 @@ fn fuzz_basic() { run_snippet_proptest(program.to_string(), false, strategy); } -#[test] -fn fuzz_keccak256_equivalence() { - run_hash_proptest( - // XXX: Currently it fails with inputs >= 135 bytes - &[0, 1, 100, 134], - true, - |max_len| { - format!( - "fn main(input: [u8; {max_len}], message_size: u32) -> pub [u8; 32] {{ - std::hash::keccak256(input, message_size) - }}" - ) - }, - |data| sha3::Keccak256::digest(data).into(), - ); -} - -#[test] -#[should_panic] // Remove once fixed -fn fuzz_keccak256_equivalence_over_135() { - run_hash_proptest( - &[135, 150], - true, - |max_len| { - format!( - "fn main(input: [u8; {max_len}], message_size: u32) -> pub [u8; 32] {{ - std::hash::keccak256(input, message_size) - }}" - ) - }, - |data| sha3::Keccak256::digest(data).into(), - ); -} - -#[test] -fn fuzz_sha256_equivalence() { - run_hash_proptest( - &[0, 1, 200, 511, 512], - true, - |max_len| { - format!( - "fn main(input: [u8; {max_len}], message_size: u64) -> pub [u8; 32] {{ - std::hash::sha256_var(input, message_size) - }}" - ) - }, - |data| sha2::Sha256::digest(data).into(), - ); -} - -#[test] -fn fuzz_sha512_equivalence() { - run_hash_proptest( - &[0, 1, 200], - false, - |max_len| { - format!( - "fn main(input: [u8; {max_len}]) -> pub [u8; 64] {{ - std::hash::sha512::digest(input) - }}" - ) - }, - |data| sha2::Sha512::digest(data).into(), - ); -} - #[test] fn fuzz_poseidon2_equivalence() { use bn254_blackbox_solver::poseidon_hash; @@ -332,12 +207,6 @@ fn fuzz_poseidon_equivalence() { } } -fn bytes_input(bytes: &[u8]) -> InputValue { - InputValue::Vec( - bytes.iter().map(|b| InputValue::Field(FieldElement::from(*b as u32))).collect(), - ) -} - fn field_vec_strategy(len: usize) -> impl Strategy> { // Generate Field elements from random 32 byte vectors. let field = prop::collection::vec(any::(), 32) diff --git a/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs b/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs index 2e3e3e2ae5af..6096b619e576 100644 --- a/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs +++ b/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs @@ -2,15 +2,15 @@ #![allow(clippy::items_after_test_module)] use clap::Parser; use fm::FileManager; -use nargo::foreign_calls::DefaultForeignCallBuilder; use nargo::PrintOutput; -use noirc_driver::{check_crate, file_manager_with_stdlib, CompileOptions}; +use nargo::foreign_calls::DefaultForeignCallBuilder; +use noirc_driver::{CompileOptions, check_crate, file_manager_with_stdlib}; use noirc_frontend::hir::FunctionNameMatch; use std::io::Write; use std::{collections::BTreeMap, path::PathBuf}; use nargo::{ - ops::{report_errors, run_test, TestStatus}, + ops::{TestStatus, report_errors, run_test}, package::{Package, PackageType}, parse_all, prepare_package, }; diff --git a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml index 710519712a2c..46dc61d30157 100644 --- a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml @@ -11,6 +11,7 @@ workspace = true [dependencies] noirc_frontend.workspace = true +noirc_errors.workspace = true serde.workspace = true toml.workspace = true thiserror.workspace = true diff --git a/noir/noir-repo/tooling/nargo_fmt/build.rs b/noir/noir-repo/tooling/nargo_fmt/build.rs index 988a7dcc94d5..f0dde56b0ca0 100644 --- a/noir/noir-repo/tooling/nargo_fmt/build.rs +++ b/noir/noir-repo/tooling/nargo_fmt/build.rs @@ -60,7 +60,7 @@ fn generate_formatter_tests(test_file: &mut File, test_data_dir: &Path) { let expected_output = r#"{output_source}"#; - let (parsed_module, _errors) = noirc_frontend::parse_program(input); + let (parsed_module, _errors) = noirc_frontend::parse_program_with_dummy_file(input); let config = nargo_fmt::Config::of("{config}", &std::path::PathBuf::new()).unwrap(); let fmt_text = nargo_fmt::format(input, parsed_module, &config); @@ -82,7 +82,7 @@ fn generate_formatter_tests(test_file: &mut File, test_data_dir: &Path) { fn format_idempotent_{test_name}() {{ let expected_output = r#"{output_source}"#; - let (parsed_module, _errors) = noirc_frontend::parse_program(expected_output); + let (parsed_module, _errors) = noirc_frontend::parse_program_with_dummy_file(expected_output); let config = nargo_fmt::Config::of("{config}", &std::path::PathBuf::new()).unwrap(); let fmt_text = nargo_fmt::format(expected_output, parsed_module, &config); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/chunks.rs b/noir/noir-repo/tooling/nargo_fmt/src/chunks.rs index fcef261284df..28e6d22f2c8e 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/chunks.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/chunks.rs @@ -113,11 +113,7 @@ impl Chunk { /// Returns the current chunk as a Group, if it is one. Otherwise returns None. pub(crate) fn group(self) -> Option { - if let Chunk::Group(group) = self { - Some(group) - } else { - None - } + if let Chunk::Group(group) = self { Some(group) } else { None } } } @@ -546,7 +542,7 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { /// Treating a `ChunkFormatter` as a `Formatter` in read-only mode is always fine, /// and reduces some boilerplate. -impl<'a, 'b> Deref for ChunkFormatter<'a, 'b> { +impl<'b> Deref for ChunkFormatter<'_, 'b> { type Target = Formatter<'b>; fn deref(&self) -> &Self::Target { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/config.rs b/noir/noir-repo/tooling/nargo_fmt/src/config.rs index f01afc87af2e..3950d267c5ea 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/config.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/config.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use crate::errors::ConfigError; macro_rules! config { - ($($field_name:ident: $field_ty:ty, $default_value:expr, $description:expr );+ $(;)*) => ( + ($($field_name:ident: $field_ty:ty, $default_value:expr_2021, $description:expr_2021 );+ $(;)*) => ( pub struct Config { $( #[doc = $description] diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter.rs index 2a8adb3fb28a..cff0b2c0619b 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter.rs @@ -1,10 +1,10 @@ use buffer::Buffer; use noirc_frontend::{ + ParsedModule, ast::Ident, hir::resolution::errors::Span, lexer::Lexer, token::{Keyword, SpannedToken, Token}, - ParsedModule, }; use crate::Config; @@ -80,7 +80,7 @@ pub(crate) struct Formatter<'a> { impl<'a> Formatter<'a> { pub(crate) fn new(source: &'a str, config: &'a Config) -> Self { - let lexer = Lexer::new(source).skip_comments(false).skip_whitespaces(false); + let lexer = Lexer::new_with_dummy_file(source).skip_comments(false).skip_whitespaces(false); let mut formatter = Self { config, source, @@ -107,6 +107,7 @@ impl<'a> Formatter<'a> { ); self.format_parsed_module(parsed_module, self.ignore_next); + self.buffer.trim_multiple_newlines(); } pub(crate) fn format_parsed_module(&mut self, parsed_module: ParsedModule, ignore_next: bool) { @@ -295,7 +296,7 @@ impl<'a> Formatter<'a> { self.ignore_next = false; let next_token = self.read_token_internal(); - self.token_span = next_token.to_span(); + self.token_span = next_token.span(); std::mem::replace(&mut self.token, next_token.into_token()) } @@ -303,7 +304,7 @@ impl<'a> Formatter<'a> { let token = self.lexer.next(); if let Some(token) = token { match token { - Ok(token) => token, + Ok(token) => token.into_spanned_token(), Err(err) => panic!("Expected lexer not to error, but got: {:?}", err), } } else { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/alias.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/alias.rs index d4c63ebdd9e1..96eedfd3d885 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/alias.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/alias.rs @@ -5,7 +5,7 @@ use noirc_frontend::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_type_alias(&mut self, type_alias: NoirTypeAlias) { self.write_indentation(); self.format_item_visibility(type_alias.visibility); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/attribute.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/attribute.rs index 19d5730a546c..d23a788eae4c 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/attribute.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/attribute.rs @@ -6,7 +6,7 @@ use crate::chunks::ChunkGroup; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_attributes(&mut self, attributes: Attributes) { let mut all_attributes = Vec::new(); for attribute in attributes.secondary { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/buffer.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/buffer.rs index 3e4bebef6081..0440f30eb586 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/buffer.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/buffer.rs @@ -56,6 +56,13 @@ impl Buffer { } } + /// Trim multiple newlines from the end of the buffer, keeping at most one. + pub(crate) fn trim_multiple_newlines(&mut self) { + while self.buffer.ends_with("\n\n") { + self.buffer.truncate(self.buffer.len() - 1); + } + } + pub(crate) fn contents(self) -> String { self.buffer } diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/comments_and_whitespace.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/comments_and_whitespace.rs index e20eb4291d18..da988e7039ec 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/comments_and_whitespace.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/comments_and_whitespace.rs @@ -7,7 +7,7 @@ const NEWLINE: &str = "\r\n"; #[cfg(not(windows))] const NEWLINE: &str = "\n"; -impl<'a> Formatter<'a> { +impl Formatter<'_> { /// Writes a single space, skipping any whitespace and comments. /// That is, suppose the next token is a big whitespace, possibly with multiple lines. /// Those are skipped but only one space is written. In this way if we have @@ -858,4 +858,11 @@ global x = 1; "; assert_format(src, expected); } + + #[test] + fn trims_newlines_from_the_end_of_the_file() { + let src = "global x: Field = 1;\n\n\n"; + let expected = "global x: Field = 1;\n"; + assert_format(src, expected); + } } diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/doc_comments.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/doc_comments.rs index f591f09e7299..6d25d7688d0b 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/doc_comments.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/doc_comments.rs @@ -2,7 +2,7 @@ use noirc_frontend::token::{DocStyle, Token}; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_inner_doc_comments(&mut self) { loop { self.skip_comments_and_whitespace(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/enums.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/enums.rs index 2d1182a941ce..beabf11fa46b 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/enums.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/enums.rs @@ -6,7 +6,7 @@ use noirc_frontend::{ use super::Formatter; use crate::chunks::ChunkGroup; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_enum(&mut self, noir_enum: NoirEnumeration) { self.format_secondary_attributes(noir_enum.attributes); self.write_indentation(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/expression.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/expression.rs index 1a6610364f28..6b46a4557a2b 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/expression.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/expression.rs @@ -6,7 +6,7 @@ use noirc_frontend::{ MemberAccessExpression, MethodCallExpression, PrefixExpression, TypePath, UnaryOp, UnresolvedTypeData, }, - token::{Keyword, Token}, + token::{Keyword, Token, TokenKind}, }; use crate::chunks::{Chunk, ChunkFormatter, ChunkGroup, GroupKind, GroupTag, TextChunk}; @@ -19,9 +19,21 @@ struct FormattedLambda { first_line_width: usize, } -impl<'a, 'b> ChunkFormatter<'a, 'b> { +impl ChunkFormatter<'_, '_> { pub(super) fn format_expression(&mut self, expression: Expression, group: &mut ChunkGroup) { - group.leading_comment(self.skip_comments_and_whitespace_chunk()); + group.leading_comment(self.chunk(|formatter| { + // Doc comments for an expression could come before a potential non-doc comment + if formatter.token.kind() == TokenKind::OuterDocComment { + formatter.format_outer_doc_comments_checking_safety(); + } + + formatter.skip_comments_and_whitespace(); + + // Or doc comments could come after a potential non-doc comment + if formatter.token.kind() == TokenKind::OuterDocComment { + formatter.format_outer_doc_comments_checking_safety(); + } + })); match expression.kind { ExpressionKind::Literal(literal) => self.format_literal(literal, group), @@ -86,9 +98,9 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { false, // force multiple lines )); } - ExpressionKind::Unsafe(block_expression, _span) => { + ExpressionKind::Unsafe(unsafe_xpression) => { group.group(self.format_unsafe_expression( - block_expression, + unsafe_xpression.block, false, // force multiple lines )); } @@ -349,7 +361,7 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { formatter.write_space(); formatter.write(&delimiter_start.to_string()); for token in tokens.0 { - formatter.write_source_span(token.to_span()); + formatter.write_source_span(token.span()); } formatter.write(&delimiter_end.to_string()); })); @@ -377,7 +389,6 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { ) -> ChunkGroup { let mut group = ChunkGroup::new(); group.text(self.chunk(|formatter| { - formatter.format_outer_doc_comments_checking_safety(); formatter.write_keyword(Keyword::Unsafe); formatter.write_space(); })); @@ -1156,7 +1167,9 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { ConstrainKind::Assert => Keyword::Assert, ConstrainKind::AssertEq => Keyword::AssertEq, ConstrainKind::Constrain => { - unreachable!("constrain always produces an error, and the formatter doesn't run when there are errors") + unreachable!( + "constrain always produces an error, and the formatter doesn't run when there are errors" + ) } }; @@ -1309,7 +1322,7 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { } } -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_empty_block_contents(&mut self) { if let Some(chunks) = self.chunk_formatter().empty_block_contents_chunk() { self.format_chunk_group(chunks); @@ -1331,7 +1344,7 @@ fn force_if_chunks_to_multiple_lines(group: &mut ChunkGroup, group_tag: GroupTag #[cfg(test)] mod tests { - use crate::{assert_format, assert_format_with_config, assert_format_with_max_width, Config}; + use crate::{Config, assert_format, assert_format_with_config, assert_format_with_max_width}; #[test] fn format_unit() { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/function.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/function.rs index ca905f3dcf88..f37683286d36 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/function.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/function.rs @@ -22,7 +22,7 @@ pub(super) struct FunctionToFormat { pub(super) skip_visibility: bool, } -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_function(&mut self, func: NoirFunction, skip_visibility: bool) { self.format_function_impl(FunctionToFormat { attributes: func.def.attributes, @@ -542,7 +542,6 @@ fn bar() { // noir-fmt:ignore fn baz() { let z = 3 ; } - "; assert_format(src, expected); } diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/generics.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/generics.rs index 4ee5a7439421..c457f4976d5e 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/generics.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/generics.rs @@ -5,7 +5,7 @@ use noirc_frontend::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_generics(&mut self, generics: Vec) { self.skip_comments_and_whitespace(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/global.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/global.rs index c351e15e3b66..4d6a43d0674f 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/global.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/global.rs @@ -6,7 +6,7 @@ use noirc_frontend::{ use super::Formatter; use crate::chunks::{ChunkFormatter, ChunkGroup}; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_global( &mut self, let_statement: LetStatement, @@ -20,7 +20,7 @@ impl<'a> Formatter<'a> { } } -impl<'a, 'b> ChunkFormatter<'a, 'b> { +impl ChunkFormatter<'_, '_> { pub(super) fn format_global( &mut self, let_statement: LetStatement, diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/impls.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/impls.rs index 71548dd5efac..b58b9381d172 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/impls.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/impls.rs @@ -2,7 +2,7 @@ use noirc_frontend::{ast::TypeImpl, token::Keyword}; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_impl(&mut self, type_impl: TypeImpl) { let has_where_clause = !type_impl.where_clause.is_empty(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/item.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/item.rs index 499acb8415c8..fa07478cee12 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/item.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/item.rs @@ -8,7 +8,7 @@ use crate::config::ImportsGranularity; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_items(&mut self, mut items: Vec, mut ignore_next: bool) { // Reverse the items because we'll be processing them one by one, and it's a bit // more efficient to pop than to shift. @@ -50,7 +50,7 @@ impl<'a> Formatter<'a> { ignore_next |= self.ignore_next; if ignore_next { - self.write_and_skip_span_without_formatting(item.span); + self.write_and_skip_span_without_formatting(item.location.span); return; } @@ -98,7 +98,7 @@ impl<'a> Formatter<'a> { let mut imports = Vec::new(); let item = items.last()?; - if self.span_has_comments(item.span) { + if self.span_has_comments(item.location.span) { return None; } @@ -112,16 +112,17 @@ impl<'a> Formatter<'a> { }; imports.push(use_tree); - let mut span_end = item.span.end(); + let mut span_end = item.location.span.end(); while let Some(item) = items.last() { - if self.span_is_import_group_separator(Span::from(span_end..item.span.start())) { + if self.span_is_import_group_separator(Span::from(span_end..item.location.span.start())) + { break; } let next_item_start = if items.len() > 1 { if let Some(next_item) = items.get(items.len() - 2) { - next_item.span.start() + next_item.location.span.start() } else { self.source.len() as u32 } @@ -129,8 +130,9 @@ impl<'a> Formatter<'a> { self.source.len() as u32 }; - if self.span_starts_with_trailing_comment(Span::from(item.span.end()..next_item_start)) - { + if self.span_starts_with_trailing_comment(Span::from( + item.location.span.end()..next_item_start, + )) { break; } @@ -147,7 +149,7 @@ impl<'a> Formatter<'a> { panic!("Expected import, got {:?}", item.kind); }; imports.push(use_tree); - span_end = item.span.end(); + span_end = item.location.span.end(); } Some(ImportGroup { imports, visibility, span_end }) diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/lvalue.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/lvalue.rs index 15fb7f5fa265..8dd1c76ab938 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/lvalue.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/lvalue.rs @@ -3,7 +3,7 @@ use noirc_frontend::{ast::LValue, token::Token}; use super::Formatter; use crate::chunks::ChunkGroup; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_lvalue(&mut self, lvalue: LValue) { // Parenthesized l-values exist but are not represented in the AST while let Token::LeftParen = self.token { @@ -12,12 +12,12 @@ impl<'a> Formatter<'a> { match lvalue { LValue::Ident(ident) => self.write_identifier(ident), - LValue::MemberAccess { object, field_name, span: _ } => { + LValue::MemberAccess { object, field_name, location: _ } => { self.format_lvalue(*object); self.write_token(Token::Dot); self.write_identifier_or_integer(field_name); } - LValue::Index { array, index, span: _ } => { + LValue::Index { array, index, location: _ } => { self.format_lvalue(*array); self.write_left_bracket(); let mut group = ChunkGroup::new(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/module.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/module.rs index e07d22c75863..b2cb250a90a6 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/module.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/module.rs @@ -1,10 +1,10 @@ use noirc_frontend::{ - ast::ModuleDeclaration, parser::ParsedSubModule, token::Keyword, ParsedModule, + ParsedModule, ast::ModuleDeclaration, parser::ParsedSubModule, token::Keyword, }; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_module_declaration(&mut self, module_declaration: ModuleDeclaration) { self.format_secondary_attributes(module_declaration.outer_attributes); self.write_indentation(); @@ -47,7 +47,7 @@ fn parsed_module_is_empty(parsed_module: &ParsedModule) -> bool { #[cfg(test)] mod tests { - use crate::{assert_format, assert_format_with_config, Config}; + use crate::{Config, assert_format, assert_format_with_config}; #[test] fn format_module_declaration() { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/path.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/path.rs index 2a46467bf72f..f79bb72eff16 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/path.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/path.rs @@ -5,7 +5,7 @@ use noirc_frontend::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_path(&mut self, path: Path) { self.skip_comments_and_whitespace(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/pattern.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/pattern.rs index 9a76612109bd..f0f94dfa7678 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/pattern.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/pattern.rs @@ -6,7 +6,7 @@ use noirc_frontend::{ use super::Formatter; use crate::chunks::ChunkGroup; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_pattern(&mut self, pattern: Pattern) { self.skip_comments_and_whitespace(); @@ -93,11 +93,7 @@ impl<'a> Formatter<'a> { } fn is_identifier_pattern(pattern: &Pattern, ident: &Ident) -> bool { - if let Pattern::Identifier(pattern_ident) = pattern { - pattern_ident == ident - } else { - false - } + if let Pattern::Identifier(pattern_ident) = pattern { pattern_ident == ident } else { false } } #[cfg(test)] diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/statement.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/statement.rs index f31cb80b9fdc..d70778ae5d11 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/statement.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/statement.rs @@ -8,7 +8,7 @@ use noirc_frontend::{ use crate::chunks::{ChunkFormatter, ChunkGroup, GroupKind}; -impl<'a, 'b> ChunkFormatter<'a, 'b> { +impl ChunkFormatter<'_, '_> { pub(super) fn format_statement( &mut self, statement: Statement, @@ -39,7 +39,7 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { if ignore_next { group.text(self.chunk(|formatter| { - formatter.write_and_skip_span_without_formatting(statement.span); + formatter.write_and_skip_span_without_formatting(statement.location.span); })); return; } @@ -52,9 +52,10 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { ExpressionKind::Block(block) => group.group(self.format_block_expression( block, true, // force multiple lines )), - ExpressionKind::Unsafe(block, _) => { + ExpressionKind::Unsafe(unsafe_expression) => { group.group(self.format_unsafe_expression( - block, true, // force multiple lines + unsafe_expression.block, + true, // force multiple lines )); } ExpressionKind::If(if_expression) => { @@ -408,7 +409,7 @@ mod tests { #[test] fn format_let_statement_with_unsafe_comment() { - let src = " fn foo() { + let src = " fn foo() { // Safety: some comment let x = unsafe { 1 } ; } "; let expected = "fn foo() { @@ -421,7 +422,7 @@ mod tests { #[test] fn format_let_statement_with_unsafe_doc_comment() { - let src = " fn foo() { + let src = " fn foo() { /// Safety: some comment let x = unsafe { 1 } ; } "; let expected = "fn foo() { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/structs.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/structs.rs index c26ab552f30c..9af10ff505c3 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/structs.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/structs.rs @@ -6,7 +6,7 @@ use noirc_frontend::{ use super::Formatter; use crate::chunks::ChunkGroup; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_struct(&mut self, noir_struct: NoirStruct) { self.format_secondary_attributes(noir_struct.attributes); self.write_indentation(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/trait_impl.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/trait_impl.rs index 896620c3bf80..9252082b26dd 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/trait_impl.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/trait_impl.rs @@ -5,7 +5,7 @@ use noirc_frontend::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_trait_impl(&mut self, trait_impl: NoirTraitImpl) { // skip synthetic trait impl's, e.g. generated from trait aliases if trait_impl.is_synthetic { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/traits.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/traits.rs index 175dcad61701..8195a30c2962 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/traits.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/traits.rs @@ -1,11 +1,12 @@ +use noirc_errors::Location; use noirc_frontend::{ ast::{NoirTrait, Param, Pattern, TraitItem, Visibility}, token::{Attributes, Keyword, Token}, }; -use super::{function::FunctionToFormat, Formatter}; +use super::{Formatter, function::FunctionToFormat}; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_trait(&mut self, noir_trait: NoirTrait) { self.format_secondary_attributes(noir_trait.attributes); self.write_indentation(); @@ -99,7 +100,7 @@ impl<'a> Formatter<'a> { visibility: Visibility::Private, pattern: Pattern::Identifier(name), typ, - span: Default::default(), // Doesn't matter + location: Location::dummy(), // Doesn't matter }) .collect(); @@ -299,6 +300,23 @@ mod tests { assert_format(src, expected); } + #[test] + fn format_trait_with_function_with_multiple_where_clauses() { + let src = " mod moo { trait Foo { + fn foo () where A: B, C: D; + } }"; + let expected = "mod moo { + trait Foo { + fn foo() + where + A: B, + C: D; + } +} +"; + assert_format(src, expected); + } + #[test] fn format_trait_with_function_with_visibility() { let src = " mod moo { trait Foo { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/type_expression.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/type_expression.rs index 95b0c0451566..8bebfd42f0cf 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/type_expression.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/type_expression.rs @@ -2,7 +2,7 @@ use noirc_frontend::{ast::UnresolvedTypeExpression, token::Token}; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_type_expression(&mut self, type_expr: UnresolvedTypeExpression) { self.skip_comments_and_whitespace(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/types.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/types.rs index e52704ddaa74..6a0e66bc1f98 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/types.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/types.rs @@ -5,7 +5,7 @@ use noirc_frontend::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_type(&mut self, typ: UnresolvedType) { self.skip_comments_and_whitespace(); @@ -178,7 +178,7 @@ mod tests { fn assert_format_type(src: &str, expected: &str) { let module_src = format!("type X = {};", src); - let (parsed_module, errors) = parser::parse_program(&module_src); + let (parsed_module, errors) = parser::parse_program_with_dummy_file(&module_src); if !errors.is_empty() { panic!("Expected no errors, got: {:?}", errors); } @@ -187,7 +187,7 @@ mod tests { let type_result = &type_result[..type_result.len() - 2]; similar_asserts::assert_eq!(type_result, expected); - let (parsed_module, errors) = parser::parse_program(&result); + let (parsed_module, errors) = parser::parse_program_with_dummy_file(&result); if !errors.is_empty() { panic!("Expected no errors in idempotent check, got: {:?}", errors); } diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree.rs index 98d63ef6611b..471dd00ca301 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree.rs @@ -7,7 +7,7 @@ use crate::chunks::{Chunk, ChunkFormatter, ChunkGroup}; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_import(&mut self, use_tree: UseTree, visibility: ItemVisibility) { let group = self.chunk_formatter().format_import(use_tree, visibility); @@ -16,7 +16,7 @@ impl<'a> Formatter<'a> { } } -impl<'a, 'b> ChunkFormatter<'a, 'b> { +impl ChunkFormatter<'_, '_> { pub(super) fn format_import( &mut self, use_tree: UseTree, @@ -121,7 +121,7 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { #[cfg(test)] mod tests { - use crate::{assert_format_with_config, config::ImportsGranularity, Config}; + use crate::{Config, assert_format_with_config, config::ImportsGranularity}; fn assert_format(src: &str, expected: &str) { let config = Config { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree_merge.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree_merge.rs index a679e026435b..f64b0cb128ee 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree_merge.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree_merge.rs @@ -9,7 +9,7 @@ use crate::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn merge_and_format_imports( &mut self, imports: Vec, @@ -180,11 +180,7 @@ impl Ord for Segment { if let (Segment::Plain(self_string), Segment::Plain(other_string)) = (self, other) { // Case-insensitive comparison for plain segments let ordering = self_string.to_lowercase().cmp(&other_string.to_lowercase()); - if ordering == Ordering::Equal { - self_string.cmp(other_string) - } else { - ordering - } + if ordering == Ordering::Equal { self_string.cmp(other_string) } else { ordering } } else { order_number_ordering } @@ -299,7 +295,7 @@ fn merge_imports_in_tree(imports: Vec, mut tree: &mut ImportTree) { #[cfg(test)] mod tests { - use crate::{assert_format_with_config, config::ImportsGranularity, Config}; + use crate::{Config, assert_format_with_config, config::ImportsGranularity}; fn assert_format(src: &str, expected: &str) { let config = Config { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/visibility.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/visibility.rs index 27441b977bb2..2c2279ecb48f 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/visibility.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/visibility.rs @@ -4,7 +4,7 @@ use noirc_frontend::{ token::Keyword, }; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_item_visibility(&mut self, visibility: ItemVisibility) { self.skip_comments_and_whitespace(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/where_clause.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/where_clause.rs index 538d2ba8c011..c5ecb178bbb1 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/where_clause.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/where_clause.rs @@ -5,7 +5,7 @@ use noirc_frontend::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_where_clause( &mut self, constraints: Vec, @@ -23,7 +23,8 @@ impl<'a> Formatter<'a> { // To format it we'll have to skip the second type `F` if we find a `+` token. let mut write_type = true; - for constraint in constraints { + let constrains_len = constraints.len(); + for (index, constraint) in constraints.into_iter().enumerate() { if write_type { self.write_line(); self.write_indentation(); @@ -45,7 +46,9 @@ impl<'a> Formatter<'a> { write_type = true; - if self.is_at(Token::Comma) { + if index < constrains_len - 1 { + self.write_token(Token::Comma); + } else if self.is_at(Token::Comma) { if write_trailing_comma_and_new_line { self.write_token(Token::Comma); } else { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/lib.rs b/noir/noir-repo/tooling/nargo_fmt/src/lib.rs index 05bfb0e7d570..bf3cf48184df 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/lib.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/lib.rs @@ -65,7 +65,7 @@ pub(crate) fn assert_format_with_max_width(src: &str, expected: &str, max_width: pub(crate) fn assert_format_with_config(src: &str, expected: &str, config: Config) { use noirc_frontend::parser; - let (parsed_module, errors) = parser::parse_program(src); + let (parsed_module, errors) = parser::parse_program_with_dummy_file(src); let errors: Vec<_> = errors.into_iter().filter(|error| !error.is_warning()).collect(); if !errors.is_empty() { panic!("Expected no errors, got: {:?}", errors); @@ -78,7 +78,7 @@ pub(crate) fn assert_format_with_config(src: &str, expected: &str, config: Confi similar_asserts::assert_eq!(result, expected); let src = &result; - let (parsed_module, errors) = parser::parse_program(src); + let (parsed_module, errors) = parser::parse_program_with_dummy_file(src); let errors: Vec<_> = errors.into_iter().filter(|error| !error.is_warning()).collect(); if !errors.is_empty() { panic!("Expected no errors in idempotent check, got: {:?}", errors); diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/databus.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/databus.nr index 0e9761ed52dc..15ba3fc12729 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/databus.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/databus.nr @@ -1,2 +1 @@ fn main(x: pub u8, y: call_data(0) u8) -> return_data u32 {} - diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr index afdb8883e158..571ec33f5d36 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr @@ -95,4 +95,3 @@ fn four() {} #[test(should_fail_with = "oops")] fn five() {} - diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/ignore.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/ignore.nr index 6e957539405b..1c84e178f34b 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/ignore.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/ignore.nr @@ -24,4 +24,3 @@ fn mk_array() { ]; let array = [1]; } - diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait.nr index 0467585fac39..8b64273a1a21 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait.nr @@ -1,2 +1 @@ trait Foo: Bar + Baz {} - diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait_alias.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait_alias.nr index 926f31602790..5b2eb0ce5523 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait_alias.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait_alias.nr @@ -83,4 +83,3 @@ fn main() { assert(0.foo_3().bar_3() == baz_3(0)); } - diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr index 265cba9604f6..ca32b3954e0f 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr @@ -12,4 +12,3 @@ fn main(x: pub u8, y: u8) { assert_eq(x, y); } } - diff --git a/noir/noir-repo/tooling/nargo_toml/src/errors.rs b/noir/noir-repo/tooling/nargo_toml/src/errors.rs index 7e1003d04f76..5aeb6a135f1b 100644 --- a/noir/noir-repo/tooling/nargo_toml/src/errors.rs +++ b/noir/noir-repo/tooling/nargo_toml/src/errors.rs @@ -28,7 +28,9 @@ pub enum ManifestError { #[error("Nargo.toml is badly formed, could not parse.\n\n {0}")] MalformedFile(#[from] toml::de::Error), - #[error("Unexpected workspace definition found in {0}. If you're attempting to load this as a dependency, you may need to add a `directory` field to your `Nargo.toml` to show which package within the workspace to use")] + #[error( + "Unexpected workspace definition found in {0}. If you're attempting to load this as a dependency, you may need to add a `directory` field to your `Nargo.toml` to show which package within the workspace to use" + )] UnexpectedWorkspace(PathBuf), #[error("Cannot find file {entry} which was specified as the `entry` field in {toml}")] @@ -80,16 +82,24 @@ pub enum ManifestError { #[allow(clippy::enum_variant_names)] #[derive(Error, Debug, PartialEq, Eq, Clone)] pub enum SemverError { - #[error("Invalid value for `compiler_version` in package {package_name}. Requirements may only refer to full releases")] + #[error( + "Invalid value for `compiler_version` in package {package_name}. Requirements may only refer to full releases" + )] InvalidCompilerVersionRequirement { package_name: CrateName, required_compiler_version: String }, - #[error("Incompatible compiler version in package {package_name}. Required compiler version is {required_compiler_version} but the compiler version is {compiler_version_found}.\n Update the compiler_version field in Nargo.toml to >={required_compiler_version} or compile this project with version {required_compiler_version}")] + #[error( + "Incompatible compiler version in package {package_name}. Required compiler version is {required_compiler_version} but the compiler version is {compiler_version_found}.\n Update the compiler_version field in Nargo.toml to >={required_compiler_version} or compile this project with version {required_compiler_version}" + )] IncompatibleVersion { package_name: CrateName, required_compiler_version: String, compiler_version_found: String, }, - #[error("Could not parse the required compiler version for package {package_name} in Nargo.toml. Error: {error}")] + #[error( + "Could not parse the required compiler version for package {package_name} in Nargo.toml. Error: {error}" + )] CouldNotParseRequiredVersion { package_name: String, error: String }, - #[error("Could not parse the package version for package {package_name} in Nargo.toml. Error: {error}")] + #[error( + "Could not parse the package version for package {package_name} in Nargo.toml. Error: {error}" + )] CouldNotParsePackageVersion { package_name: String, error: String }, } diff --git a/noir/noir-repo/tooling/nargo_toml/src/flock.rs b/noir/noir-repo/tooling/nargo_toml/src/flock.rs index 031dbcff647e..1433827c3fa9 100644 --- a/noir/noir-repo/tooling/nargo_toml/src/flock.rs +++ b/noir/noir-repo/tooling/nargo_toml/src/flock.rs @@ -1,4 +1,3 @@ -use fs2::FileExt; use std::{ fs::{File, OpenOptions}, path::Path, @@ -14,11 +13,11 @@ impl FileLock { pub(crate) fn new(file_path: &Path, lock_name: &str) -> std::io::Result { std::fs::create_dir_all(file_path.parent().expect("can't create lock on filesystem root"))?; let file = OpenOptions::new().create(true).truncate(false).write(true).open(file_path)?; - if file.try_lock_exclusive().is_err() { + if fs2::FileExt::try_lock_exclusive(&file).is_err() { eprintln!("Waiting for lock on {lock_name}..."); } - file.lock_exclusive()?; + fs2::FileExt::lock_exclusive(&file)?; Ok(Self { file }) } @@ -26,7 +25,7 @@ impl FileLock { impl Drop for FileLock { fn drop(&mut self) { - if let Err(e) = self.file.unlock() { + if let Err(e) = fs2::FileExt::unlock(&self.file) { tracing::warn!("failed to release lock: {e:?}"); } } diff --git a/noir/noir-repo/tooling/nargo_toml/src/lib.rs b/noir/noir-repo/tooling/nargo_toml/src/lib.rs index edf26411cf5a..b62884f28c4c 100644 --- a/noir/noir-repo/tooling/nargo_toml/src/lib.rs +++ b/noir/noir-repo/tooling/nargo_toml/src/lib.rs @@ -9,7 +9,7 @@ use std::{ }; use errors::SemverError; -use fm::{NormalizePath, FILE_EXTENSION}; +use fm::{FILE_EXTENSION, NormalizePath}; use nargo::{ package::{Dependency, Package, PackageType}, workspace::Workspace, @@ -52,11 +52,7 @@ pub fn find_file_manifest(current_path: &Path) -> Option { /// /// Returns a [ManifestError] if no parent directories of `current_path` contain a manifest file. pub fn find_root(current_path: &Path, workspace: bool) -> Result { - if workspace { - find_package_root(current_path) - } else { - find_file_root(current_path) - } + if workspace { find_package_root(current_path) } else { find_file_root(current_path) } } /// Returns the [PathBuf] of the directory containing the `Nargo.toml` by searching from `current_path` to the root of its [Path], @@ -190,7 +186,7 @@ impl PackageConfig { return Err(ManifestError::InvalidPackageType( root_dir.join("Nargo.toml"), invalid.to_string(), - )) + )); } None => return Err(ManifestError::MissingPackageType(root_dir.join("Nargo.toml"))), }; @@ -389,7 +385,7 @@ fn toml_to_workspace( let member = package_config.resolve_to_package(&nargo_toml.root_dir, &mut resolved)?; match &package_selection { PackageSelection::Selected(selected_name) if selected_name != &member.name => { - return Err(ManifestError::MissingSelectedPackage(member.name)) + return Err(ManifestError::MissingSelectedPackage(member.name)); } _ => Workspace { root_dir: nargo_toml.root_dir, @@ -540,7 +536,7 @@ mod tests { use test_case::test_matrix; - use crate::{find_root, Config, ManifestError}; + use crate::{Config, ManifestError, find_root}; #[test] fn parse_standard_toml() { @@ -649,7 +645,8 @@ mod tests { assert!( indent <= current_indent + 1, - "cannot increase indent by more than {INDENT_SIZE}; item = {item}, current_dir={}", current_dir.display() + "cannot increase indent by more than {INDENT_SIZE}; item = {item}, current_dir={}", + current_dir.display() ); // Go into the last created directory diff --git a/noir/noir-repo/tooling/nargo_toml/src/semver.rs b/noir/noir-repo/tooling/nargo_toml/src/semver.rs index ececa1b30dd0..83f31c71a5e2 100644 --- a/noir/noir-repo/tooling/nargo_toml/src/semver.rs +++ b/noir/noir-repo/tooling/nargo_toml/src/semver.rs @@ -1,4 +1,4 @@ -use crate::{errors::SemverError, ManifestError}; +use crate::{ManifestError, errors::SemverError}; use nargo::{ package::{Dependency, Package}, workspace::Workspace, @@ -37,7 +37,7 @@ fn semver_check_package(package: &Package, compiler_version: &Version) -> Result return Err(SemverError::CouldNotParseRequiredVersion { package_name: package.name.clone().into(), error: err.to_string(), - }) + }); } }; @@ -109,12 +109,16 @@ mod tests { expression_width: None, }; if let Err(err) = semver_check_package(&package, &compiler_version) { - panic!("semver check should have passed. compiler version is 0.1.0 and required version from the package is 0.1.0\n error: {err:?}") + panic!( + "semver check should have passed. compiler version is 0.1.0 and required version from the package is 0.1.0\n error: {err:?}" + ) }; package.compiler_required_version = Some("0.2.0".to_string()); let got_err = match semver_check_package(&package, &compiler_version) { - Ok(_) => panic!("semver check should have failed. compiler version is 0.1.0 and required version from the package is 0.2.0"), + Ok(_) => panic!( + "semver check should have failed. compiler version is 0.1.0 and required version from the package is 0.2.0" + ), Err(err) => err, }; @@ -168,15 +172,19 @@ mod tests { ); if let Err(err) = semver_check_package(&package, &compiler_version) { - panic!("semver check should have passed. compiler version is 0.1.0 and required version from the package is 0.1.0\n error: {err:?}") + panic!( + "semver check should have passed. compiler version is 0.1.0 and required version from the package is 0.1.0\n error: {err:?}" + ) }; package.dependencies.insert( CrateName::from_str("test_dep_invalid").unwrap(), Dependency::Local { package: invalid_dependency.clone() }, ); - let got_err = match semver_check_package(&package,&compiler_version) { - Ok(_) => panic!("semver check should have failed. compiler version is 0.1.0 and required version from the package is 0.2.0"), + let got_err = match semver_check_package(&package, &compiler_version) { + Ok(_) => panic!( + "semver check should have failed. compiler version is 0.1.0 and required version from the package is 0.2.0" + ), Err(err) => err, }; @@ -204,7 +212,9 @@ mod tests { }; if let Err(err) = semver_check_package(&package, &compiler_version) { - panic!("semver check should have passed. compiler version is 0.2.0 and required version from the package is >=0.1.0\n error: {err:?}") + panic!( + "semver check should have passed. compiler version is 0.2.0 and required version from the package is >=0.1.0\n error: {err:?}" + ) }; } @@ -244,7 +254,9 @@ mod tests { }; if let Err(err) = semver_check_package(&package, &compiler_version) { - panic!("semver check should have passed. compiler version is 0.1.0+build_data and required version from the package is 0.1.0\n The build data should be ignored\n error: {err:?}") + panic!( + "semver check should have passed. compiler version is 0.1.0+build_data and required version from the package is 0.1.0\n The build data should be ignored\n error: {err:?}" + ) }; } } diff --git a/noir/noir-repo/tooling/noir_codegen/package.json b/noir/noir-repo/tooling/noir_codegen/package.json index 1297658b14eb..5f818f694d18 100644 --- a/noir/noir-repo/tooling/noir_codegen/package.json +++ b/noir/noir-repo/tooling/noir_codegen/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "1.0.0-beta.2", + "version": "1.0.0-beta.3", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/noir/noir-repo/tooling/noir_js/package.json b/noir/noir-repo/tooling/noir_js/package.json index fee79c680de2..5269867708f0 100644 --- a/noir/noir-repo/tooling/noir_js/package.json +++ b/noir/noir-repo/tooling/noir_js/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "1.0.0-beta.2", + "version": "1.0.0-beta.3", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/noir/noir-repo/tooling/noir_js_types/package.json b/noir/noir-repo/tooling/noir_js_types/package.json index 4cf637db0db8..31a1e6fdb79a 100644 --- a/noir/noir-repo/tooling/noir_js_types/package.json +++ b/noir/noir-repo/tooling/noir_js_types/package.json @@ -4,7 +4,7 @@ "The Noir Team " ], "packageManager": "yarn@3.5.1", - "version": "1.0.0-beta.2", + "version": "1.0.0-beta.3", "license": "(MIT OR Apache-2.0)", "homepage": "https://noir-lang.org/", "repository": { diff --git a/noir/noir-repo/tooling/noirc_abi/proptest-regressions/input_parser/json.txt b/noir/noir-repo/tooling/noirc_abi/proptest-regressions/input_parser/json.txt index 19de8eeaf489..66aeed8f1202 100644 --- a/noir/noir-repo/tooling/noirc_abi/proptest-regressions/input_parser/json.txt +++ b/noir/noir-repo/tooling/noirc_abi/proptest-regressions/input_parser/json.txt @@ -5,3 +5,4 @@ # It is recommended to check this file in to source control so that # everyone who runs the test benefits from these saved cases. cc b3f9ae88d54944ca274764f4d99a2023d4b0ac09beb89bc599cbba1e45dd3620 # shrinks to (typ, value) = (Integer { sign: Signed, width: 1 }, -1) +cc afc4c7b26dcb04c8bb68af61dd8422745802d5a722b2200a9cb69a8425154b0a # shrinks to (abi, input_map) = (Abi { parameters: [AbiParameter { name: "A", typ: Struct { path: "\u{8}\u{6d174}\0:.Ѩ**¥\u{feff}`\u{4}o\u{55a3b}2=\u{6}:<", fields: [("\u{10bba4}Y&\u{83c23}?${\u{dc4b5}Z𬰫'º\u{1b}\u{37592}\u{feff}Ѩ.\u{4b753}c\u{736c6}", Integer { sign: Signed, width: 128 })] }, visibility: Public }], return_type: Some(AbiReturnType { abi_type: Tuple { fields: [Array { length: 1, typ: Field }, Tuple { fields: [Boolean] }, Array { length: 5, typ: Struct { path: "|\u{99332}?\u{7}\u{5131a}{q", fields: [("*🕴\u{d41d7}?\r*&O\\\u{c01a6}5{\t\u{9a8f1}\"\u{464e9}hM\\", Integer { sign: Signed, width: 83 }), ("ä:$\u{d4b96}\u{911ce}\u{1cc1c}EL{.¥🕴\u{36eb8}\u{feff}\u{a0}\u{40267}.🕴🕴{\u{97de2}\u{df645}\u{b}B}\u{ba0d1}(iter: impl Iterator) { proptest::prop_compose! { pub(super) fn arb_field_from_integer(bit_size: u32)(value: u128)-> FieldElement { - let width = (bit_size % 128).clamp(1, 127); - let max_value = 2u128.pow(width) - 1; + let width = (bit_size % 129).clamp(1, 128); + let max_value = if bit_size == 128 { u128::MAX } else { (1u128 << width) - 1 }; FieldElement::from(value.clamp(0, max_value)) } } @@ -86,7 +86,7 @@ fn arb_primitive_abi_type() -> SBoxedStrategy { Just(AbiType::Field), Just(AbiType::Boolean), any::<(Sign, u32)>().prop_map(|(sign, width)| { - let width = (width % 128).clamp(1, 127); + let width = (width % 129).clamp(1, 128); AbiType::Integer { sign, width } }), // restrict length of strings to avoid running out of memory diff --git a/noir/noir-repo/tooling/noirc_abi/src/errors.rs b/noir/noir-repo/tooling/noirc_abi/src/errors.rs index c46945d8ff22..f08f7b217219 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/errors.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/errors.rs @@ -1,8 +1,8 @@ use crate::{ - input_parser::{InputTypecheckingError, InputValue}, AbiType, + input_parser::{InputTypecheckingError, InputValue}, }; -use acvm::{acir::native_types::Witness, AcirField, FieldElement}; +use acvm::{AcirField, FieldElement, acir::native_types::Witness}; use thiserror::Error; #[derive(Debug, Error)] @@ -13,9 +13,13 @@ pub enum InputParserError { "The value passed for parameter `{arg_name}` is invalid:\nExpected witness values to be integers, but `{value}` failed with `{error}`" )] ParseStr { arg_name: String, value: String, error: String }, - #[error("The value passed for parameter `{arg_name}` is invalid:\nValue {value} is less than minimum allowed value of {min}")] + #[error( + "The value passed for parameter `{arg_name}` is invalid:\nValue {value} is less than minimum allowed value of {min}" + )] InputUnderflowsMinimum { arg_name: String, value: String, min: String }, - #[error("The value passed for parameter `{arg_name}` is invalid:\nValue {value} exceeds maximum allowed value of {max}")] + #[error( + "The value passed for parameter `{arg_name}` is invalid:\nValue {value} exceeds maximum allowed value of {max}" + )] InputOverflowsMaximum { arg_name: String, value: String, max: String }, #[error( "The value passed for parameter `{arg_name}` is invalid:\nValue {value} exceeds field modulus. Values must fall within [0, {})", @@ -58,9 +62,13 @@ pub enum AbiError { "Could not read witness value at index {witness_index:?} (required for parameter \"{name}\")" )] MissingParamWitnessValue { name: String, witness_index: Witness }, - #[error("Attempted to write to witness index {0:?} but it is already initialized to a different value")] + #[error( + "Attempted to write to witness index {0:?} but it is already initialized to a different value" + )] InconsistentWitnessAssignment(Witness), - #[error("The return value is expected to be a {return_type:?} but found incompatible value {value:?}")] + #[error( + "The return value is expected to be a {return_type:?} but found incompatible value {value:?}" + )] ReturnTypeMismatch { return_type: AbiType, value: InputValue }, #[error("No return value is expected but received {0:?}")] UnexpectedReturnValue(InputValue), diff --git a/noir/noir-repo/tooling/noirc_abi/src/input_parser/json.rs b/noir/noir-repo/tooling/noirc_abi/src/input_parser/json.rs index 7b2e8be454bf..f8b73ad05a5c 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/input_parser/json.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/input_parser/json.rs @@ -1,8 +1,8 @@ use super::{ - field_to_signed_hex, parse_integer_to_signed, parse_str_to_field, parse_str_to_signed, - InputValue, + InputValue, field_to_signed_hex, parse_integer_to_signed, parse_str_to_field, + parse_str_to_signed, }; -use crate::{errors::InputParserError, Abi, AbiType, MAIN_RETURN_NAME}; +use crate::{Abi, AbiType, MAIN_RETURN_NAME, errors::InputParserError}; use acvm::{AcirField, FieldElement}; use iter_extended::{try_btree_map, try_vecmap}; use serde::{Deserialize, Serialize}; @@ -225,9 +225,9 @@ mod test { use proptest::prelude::*; use crate::{ - arbitrary::arb_abi_and_input_map, - input_parser::{arbitrary::arb_signed_integer_type_and_value, json::JsonTypes, InputValue}, AbiType, + arbitrary::arb_abi_and_input_map, + input_parser::{InputValue, arbitrary::arb_signed_integer_type_and_value, json::JsonTypes}, }; use super::{parse_json, serialize_to_json}; diff --git a/noir/noir-repo/tooling/noirc_abi/src/input_parser/mod.rs b/noir/noir-repo/tooling/noirc_abi/src/input_parser/mod.rs index fce627c8d313..72aef1f48bb3 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/input_parser/mod.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/input_parser/mod.rs @@ -27,7 +27,9 @@ pub enum InputValue { pub enum InputTypecheckingError { #[error("Value {value:?} does not fall within range of allowable values for a {typ:?}")] OutsideOfValidRange { path: String, typ: AbiType, value: InputValue }, - #[error("Type {typ:?} is expected to have length {expected_length} but value {value:?} has length {actual_length}")] + #[error( + "Type {typ:?} is expected to have length {expected_length} but value {value:?} has length {actual_length}" + )] LengthMismatch { path: String, typ: AbiType, @@ -35,9 +37,13 @@ pub enum InputTypecheckingError { expected_length: usize, actual_length: usize, }, - #[error("Could not find value for required field `{expected_field}`. Found values for fields {found_fields:?}")] + #[error( + "Could not find value for required field `{expected_field}`. Found values for fields {found_fields:?}" + )] MissingField { path: String, expected_field: String, found_fields: Vec }, - #[error("Additional unexpected field was provided for type {typ:?}. Found field named `{extra_field}`")] + #[error( + "Additional unexpected field was provided for type {typ:?}. Found field named `{extra_field}`" + )] UnexpectedField { path: String, typ: AbiType, extra_field: String }, #[error("Type {typ:?} and value {value:?} do not match")] IncompatibleTypes { path: String, typ: AbiType, value: InputValue }, @@ -242,8 +248,8 @@ mod serialization_tests { use strum::IntoEnumIterator; use crate::{ - input_parser::InputValue, Abi, AbiParameter, AbiReturnType, AbiType, AbiVisibility, Sign, - MAIN_RETURN_NAME, + Abi, AbiParameter, AbiReturnType, AbiType, AbiVisibility, MAIN_RETURN_NAME, Sign, + input_parser::InputValue, }; use super::Format; @@ -357,8 +363,11 @@ fn parse_str_to_signed( error: err_msg.to_string(), }) .and_then(|bigint| { - let max = BigInt::from(2_u128.pow(width - 1) - 1); - let min = BigInt::from(-(2_i128.pow(width - 1))); + let min = if width == 128 { i128::MIN } else { -(1 << (width - 1)) }; + let max = if width == 128 { i128::MAX } else { (1 << (width - 1)) - 1 }; + + let max = BigInt::from(max); + let min = BigInt::from(min); if bigint < min { return Err(InputParserError::InputUnderflowsMinimum { @@ -398,8 +407,8 @@ fn parse_integer_to_signed( width: u32, arg_name: &str, ) -> Result { - let min = -(1 << (width - 1)); - let max = (1 << (width - 1)) - 1; + let min = if width == 128 { i128::MIN } else { -(1 << (width - 1)) }; + let max = if width == 128 { i128::MAX } else { (1 << (width - 1)) - 1 }; if integer < min { return Err(InputParserError::InputUnderflowsMinimum { @@ -417,8 +426,12 @@ fn parse_integer_to_signed( }); } - let integer = if integer < 0 { (1 << width) + integer } else { integer }; - Ok(FieldElement::from(integer as u128)) + let integer = if integer < 0 { + FieldElement::from(2u32).pow(&width.into()) + FieldElement::from(integer) + } else { + FieldElement::from(integer) + }; + Ok(integer) } fn field_from_big_uint(bigint: BigUint) -> FieldElement { @@ -439,9 +452,9 @@ fn field_from_big_int(bigint: BigInt) -> FieldElement { fn field_to_signed_hex(f: FieldElement, bit_size: u32) -> String { let f_u128 = f.to_u128(); - let max = 2_u128.pow(bit_size - 1) - 1; + let max = if bit_size == 128 { i128::MAX as u128 } else { (1 << (bit_size - 1)) - 1 }; if f_u128 > max { - let f = FieldElement::from(2_u128.pow(bit_size) - f_u128); + let f = FieldElement::from(2u32).pow(&bit_size.into()) - f; format!("-0x{}", f.to_hex()) } else { format!("0x{}", f.to_hex()) @@ -454,7 +467,7 @@ mod test { use num_bigint::BigUint; use strum::IntoEnumIterator; - use super::{parse_str_to_field, parse_str_to_signed, Format}; + use super::{Format, parse_str_to_field, parse_str_to_signed}; fn big_uint_from_field(field: FieldElement) -> BigUint { BigUint::from_bytes_be(&field.to_be_bytes()) diff --git a/noir/noir-repo/tooling/noirc_abi/src/input_parser/toml.rs b/noir/noir-repo/tooling/noirc_abi/src/input_parser/toml.rs index aaa3358f4fca..2e2d12f853c5 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/input_parser/toml.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/input_parser/toml.rs @@ -1,8 +1,8 @@ use super::{ - field_to_signed_hex, parse_integer_to_signed, parse_str_to_field, parse_str_to_signed, - InputValue, + InputValue, field_to_signed_hex, parse_integer_to_signed, parse_str_to_field, + parse_str_to_signed, }; -use crate::{errors::InputParserError, Abi, AbiType, MAIN_RETURN_NAME}; +use crate::{Abi, AbiType, MAIN_RETURN_NAME, errors::InputParserError}; use acvm::{AcirField, FieldElement}; use iter_extended::{try_btree_map, try_vecmap}; use serde::{Deserialize, Serialize}; @@ -210,9 +210,9 @@ mod test { use proptest::prelude::*; use crate::{ - arbitrary::arb_abi_and_input_map, - input_parser::{arbitrary::arb_signed_integer_type_and_value, toml::TomlTypes, InputValue}, AbiType, + arbitrary::arb_abi_and_input_map, + input_parser::{InputValue, arbitrary::arb_signed_integer_type_and_value, toml::TomlTypes}, }; use super::{parse_toml, serialize_to_toml}; diff --git a/noir/noir-repo/tooling/noirc_abi/src/lib.rs b/noir/noir-repo/tooling/noirc_abi/src/lib.rs index 5f5f3748bc46..17f9a07c2217 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/lib.rs @@ -4,11 +4,11 @@ #![warn(clippy::semicolon_if_nothing_returned)] use acvm::{ + AcirField, FieldElement, acir::{ circuit::ErrorSelector, native_types::{Witness, WitnessMap}, }, - AcirField, FieldElement, }; use errors::AbiError; use input_parser::InputValue; @@ -202,7 +202,7 @@ impl Abi { let has_public_return = self .return_type .as_ref() - .map_or(false, |typ| matches!(typ.visibility, AbiVisibility::Public)); + .is_some_and(|typ| matches!(typ.visibility, AbiVisibility::Public)); has_public_args || has_public_return } @@ -263,7 +263,7 @@ impl Abi { encoded_inputs.push(encoded_return_fields); } (None, Some(return_value)) => { - return Err(AbiError::UnexpectedReturnValue(return_value)) + return Err(AbiError::UnexpectedReturnValue(return_value)); } // We allow not passing a return value despite the circuit defining one // in order to generate the initial partial witness. diff --git a/noir/noir-repo/tooling/noirc_abi_wasm/Cargo.toml b/noir/noir-repo/tooling/noirc_abi_wasm/Cargo.toml index b00d580515ef..1d9dc02939fc 100644 --- a/noir/noir-repo/tooling/noirc_abi_wasm/Cargo.toml +++ b/noir/noir-repo/tooling/noirc_abi_wasm/Cargo.toml @@ -31,5 +31,5 @@ getrandom = { workspace = true, features = ["js"] } [build-dependencies] build-data.workspace = true -[dev-dependencies] +[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dev-dependencies] wasm-bindgen-test.workspace = true 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/tooling/noirc_abi_wasm/package.json b/noir/noir-repo/tooling/noirc_abi_wasm/package.json index 769efe1d92a1..0a0941be34a8 100644 --- a/noir/noir-repo/tooling/noirc_abi_wasm/package.json +++ b/noir/noir-repo/tooling/noirc_abi_wasm/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "1.0.0-beta.2", + "version": "1.0.0-beta.3", "license": "(MIT OR Apache-2.0)", "homepage": "https://noir-lang.org/", "repository": { diff --git a/noir/noir-repo/tooling/noirc_abi_wasm/src/js_witness_map.rs b/noir/noir-repo/tooling/noirc_abi_wasm/src/js_witness_map.rs index a82621822e46..8b751426ac20 100644 --- a/noir/noir-repo/tooling/noirc_abi_wasm/src/js_witness_map.rs +++ b/noir/noir-repo/tooling/noirc_abi_wasm/src/js_witness_map.rs @@ -1,11 +1,11 @@ //! This can most likely be imported from acvm_js to avoid redefining it here. use acvm::{ - acir::native_types::{Witness, WitnessMap}, AcirField, FieldElement, + acir::native_types::{Witness, WitnessMap}, }; use js_sys::{JsString, Map}; -use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; +use wasm_bindgen::prelude::{JsValue, wasm_bindgen}; // WitnessMap #[wasm_bindgen] @@ -65,21 +65,21 @@ pub(crate) fn field_element_to_js_string(field_element: &FieldElement) -> JsStri format!("0x{}", field_element.to_hex()).into() } -#[cfg(test)] +#[cfg(all(test, any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))] mod test { - use wasm_bindgen_test::wasm_bindgen_test as test; + use wasm_bindgen_test::*; use std::collections::BTreeMap; use acvm::{ - acir::native_types::{Witness, WitnessMap}, AcirField, FieldElement, + acir::native_types::{Witness, WitnessMap}, }; use wasm_bindgen::JsValue; use crate::JsWitnessMap; - #[test] + #[wasm_bindgen_test] fn test_witness_map_to_js() { let witness_map = BTreeMap::from([ (Witness(1), FieldElement::one()), diff --git a/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs b/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs index 79aecafa7426..390f4353bb2d 100644 --- a/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs @@ -6,24 +6,23 @@ use getrandom as _; use acvm::{ + FieldElement, acir::{ circuit::RawAssertionPayload, native_types::{WitnessMap, WitnessStack}, }, - FieldElement, }; use iter_extended::try_btree_map; use noirc_abi::{ - decode_value, display_abi_error, + Abi, AbiErrorType, MAIN_RETURN_NAME, decode_value, display_abi_error, errors::InputParserError, - input_parser::{json::JsonTypes, InputValue}, - Abi, AbiErrorType, MAIN_RETURN_NAME, + input_parser::{InputValue, json::JsonTypes}, }; use serde::Serialize; use std::collections::BTreeMap; use gloo_utils::format::JsValueSerdeExt; -use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; +use wasm_bindgen::{JsValue, prelude::wasm_bindgen}; mod errors; mod js_witness_map; diff --git a/noir/noir-repo/tooling/noirc_artifacts/src/contract.rs b/noir/noir-repo/tooling/noirc_artifacts/src/contract.rs index 0649a34e6255..9f8f7019ff13 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/src/contract.rs +++ b/noir/noir-repo/tooling/noirc_artifacts/src/contract.rs @@ -1,6 +1,6 @@ -use acvm::{acir::circuit::Program, FieldElement}; +use acvm::{FieldElement, acir::circuit::Program}; use noirc_abi::{Abi, AbiType, AbiValue}; -use noirc_driver::{CompiledContract, CompiledContractOutputs, ContractFunction}; +use noirc_driver::{CompiledContract, CompiledContractOutputs, CompiledProgram, ContractFunction}; use serde::{Deserialize, Serialize}; use noirc_driver::DebugFile; @@ -9,6 +9,8 @@ use std::collections::{BTreeMap, HashMap}; use fm::FileId; +use super::{deserialize_hash, serialize_hash}; + #[derive(Clone, Serialize, Deserialize, Debug)] pub struct ContractOutputsArtifact { pub structs: HashMap>, @@ -47,6 +49,14 @@ impl From for ContractArtifact { } } +impl ContractArtifact { + pub fn function_as_compiled_program(&self, function_name: &str) -> Option { + self.functions.iter().find(|f| f.name == function_name).map(|f| { + f.clone().into_compiled_program(self.noir_version.clone(), self.file_map.clone()) + }) + } +} + /// Each function in the contract will be compiled as a separate noir program. /// /// A contract function unlike a regular Noir program however can have additional properties. @@ -55,6 +65,12 @@ impl From for ContractArtifact { pub struct ContractFunctionArtifact { pub name: String, + /// Hash of the [`Program`][noirc_frontend::monomorphization::ast::Program] from which the [`ContractFunction`] + /// was compiled. + #[serde(default)] // For backwards compatibility (it was missing). + #[serde(serialize_with = "serialize_hash", deserialize_with = "deserialize_hash")] + pub hash: u64, + pub is_unconstrained: bool, pub custom_attributes: Vec, @@ -72,18 +88,41 @@ pub struct ContractFunctionArtifact { deserialize_with = "ProgramDebugInfo::deserialize_compressed_base64_json" )] pub debug_symbols: ProgramDebugInfo, - + #[serde(default)] // For backwards compatibility (it was missing). + pub names: Vec, pub brillig_names: Vec, } +impl ContractFunctionArtifact { + pub fn into_compiled_program( + self, + noir_version: String, + file_map: BTreeMap, + ) -> CompiledProgram { + CompiledProgram { + noir_version, + hash: self.hash, + program: self.bytecode, + abi: self.abi, + debug: self.debug_symbols.debug_infos, + file_map, + warnings: Vec::new(), + names: self.names, + brillig_names: self.brillig_names, + } + } +} + impl From for ContractFunctionArtifact { fn from(func: ContractFunction) -> Self { ContractFunctionArtifact { name: func.name, + hash: func.hash, is_unconstrained: func.is_unconstrained, custom_attributes: func.custom_attributes, abi: func.abi, bytecode: func.bytecode, + names: func.names, brillig_names: func.brillig_names, debug_symbols: ProgramDebugInfo { debug_infos: func.debug }, } diff --git a/noir/noir-repo/tooling/noirc_artifacts/src/debug.rs b/noir/noir-repo/tooling/noirc_artifacts/src/debug.rs index 5c47f1f2652c..4924a9f1ca19 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/src/debug.rs +++ b/noir/noir-repo/tooling/noirc_artifacts/src/debug.rs @@ -1,6 +1,6 @@ use codespan_reporting::files::{Error, Files, SimpleFile}; use noirc_driver::{CompiledContract, CompiledProgram, DebugFile}; -use noirc_errors::{debug_info::DebugInfo, Location}; +use noirc_errors::{Location, debug_info::DebugInfo}; use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeMap, BTreeSet}, @@ -204,12 +204,12 @@ mod tests { use crate::debug::DebugArtifact; use acvm::acir::circuit::OpcodeLocation; use fm::FileManager; - use noirc_errors::{debug_info::DebugInfo, Location, Span}; + use noirc_errors::{Location, Span, debug_info::DebugInfo}; use std::collections::BTreeMap; use std::ops::Range; use std::path::Path; use std::path::PathBuf; - use tempfile::{tempdir, TempDir}; + use tempfile::{TempDir, tempdir}; // Returns the absolute path to the file fn create_dummy_file(dir: &TempDir, file_name: &Path) -> PathBuf { diff --git a/noir/noir-repo/tooling/noirc_artifacts/src/lib.rs b/noir/noir-repo/tooling/noirc_artifacts/src/lib.rs index 77873ed94098..337d5d8550da 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_artifacts/src/lib.rs @@ -9,7 +9,48 @@ //! Should any projects require/desire a different artifact format, it's expected that they will write a transformer //! to generate them using these artifacts as a starting point. +use serde::{Deserializer, Serializer, de::Visitor}; + pub mod contract; pub mod debug; mod debug_vars; pub mod program; + +/// Serialize `hash` as `String`, so that it doesn't get truncated in Javascript. +fn serialize_hash(hash: &u64, s: S) -> Result +where + S: Serializer, +{ + s.serialize_str(&hash.to_string()) +} + +/// Deserialize `hash` from `String` in JSON. +fn deserialize_hash<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + use serde::de::Error; + + // Backwards compatible with `hash` serialized as a number. + struct StringOrU64; + + impl Visitor<'_> for StringOrU64 { + type Value = u64; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("String or u64") + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + value.parse().map_err(E::custom) + } + + fn visit_u64(self, value: u64) -> Result { + Ok(value) + } + } + deserializer.deserialize_any(StringOrU64) +} diff --git a/noir/noir-repo/tooling/noirc_artifacts/src/program.rs b/noir/noir-repo/tooling/noirc_artifacts/src/program.rs index ffffc6345d58..fc452fcb2afd 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/src/program.rs +++ b/noir/noir-repo/tooling/noirc_artifacts/src/program.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; -use acvm::acir::circuit::Program; use acvm::FieldElement; +use acvm::acir::circuit::Program; use fm::FileId; use noirc_abi::Abi; use noirc_driver::CompiledProgram; @@ -9,6 +9,8 @@ use noirc_driver::DebugFile; use noirc_errors::debug_info::ProgramDebugInfo; use serde::{Deserialize, Serialize}; +use super::{deserialize_hash, serialize_hash}; + #[derive(Clone, Serialize, Deserialize, Debug)] pub struct ProgramArtifact { pub noir_version: String, @@ -17,6 +19,7 @@ pub struct ProgramArtifact { /// was compiled. /// /// Used to short-circuit compilation in the case of the source code not changing since the last compilation. + #[serde(serialize_with = "serialize_hash", deserialize_with = "deserialize_hash")] pub hash: u64, pub abi: Abi, diff --git a/noir/noir-repo/tooling/noirc_artifacts_info/src/lib.rs b/noir/noir-repo/tooling/noirc_artifacts_info/src/lib.rs index 6f4c80accbdd..b1db98f21579 100644 --- a/noir/noir-repo/tooling/noirc_artifacts_info/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_artifacts_info/src/lib.rs @@ -1,7 +1,7 @@ use acvm::acir::circuit::ExpressionWidth; use iter_extended::vecmap; use noirc_artifacts::program::ProgramArtifact; -use prettytable::{row, table, Row}; +use prettytable::{Row, row, table}; use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; use serde::Serialize; diff --git a/noir/noir-repo/tooling/profiler/Cargo.toml b/noir/noir-repo/tooling/profiler/Cargo.toml index 798a21ea0d6c..b61271e03ef9 100644 --- a/noir/noir-repo/tooling/profiler/Cargo.toml +++ b/noir/noir-repo/tooling/profiler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "noir_profiler" -description = "Profiler for noir circuits" +description = "Profiler for Noir circuits" version.workspace = true authors.workspace = true edition.workspace = true @@ -32,8 +32,9 @@ im.workspace = true acir.workspace = true nargo.workspace = true noirc_errors.workspace = true -noirc_abi.workspace = true noirc_evaluator.workspace = true +noir_artifact_cli.workspace = true +thiserror.workspace = true # Logs tracing-subscriber.workspace = true @@ -43,4 +44,3 @@ tracing-appender = "0.2.3" noirc_abi.workspace = true noirc_driver.workspace = true tempfile.workspace = true - diff --git a/noir/noir-repo/tooling/profiler/src/cli/execution_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/execution_flamegraph_cmd.rs index dbb2a2c38bcd..c9d7eca50f59 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/execution_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/execution_flamegraph_cmd.rs @@ -1,18 +1,23 @@ use std::path::{Path, PathBuf}; +use acir::circuit::Opcode; use acir::circuit::OpcodeLocation; use clap::Args; use color_eyre::eyre::{self, Context}; -use nargo::foreign_calls::DefaultForeignCallBuilder; use nargo::PrintOutput; +use nargo::errors::try_to_diagnose_runtime_error; +use nargo::foreign_calls::DefaultForeignCallBuilder; +use noir_artifact_cli::fs::artifact::read_program_from_file; +use noir_artifact_cli::fs::inputs::read_inputs_from_file; +use noirc_artifacts::program::ProgramArtifact; +use crate::errors::{CliError, report_error}; use crate::flamegraph::{BrilligExecutionSample, FlamegraphGenerator, InfernoFlamegraphGenerator}; -use crate::fs::{read_inputs_from_file, read_program_from_file}; use crate::opcode_formatter::format_brillig_opcode; use bn254_blackbox_solver::Bn254BlackBoxSolver; -use noirc_abi::input_parser::Format; use noirc_artifacts::debug::DebugArtifact; +/// Generates a flamegraph mapping unconstrained Noir execution to source code. #[derive(Debug, Clone, Args)] pub(crate) struct ExecutionFlamegraphCommand { /// The path to the artifact JSON file @@ -54,17 +59,41 @@ fn run_with_generator( let program = read_program_from_file(artifact_path).context("Error reading program from file")?; - let (inputs_map, _) = read_inputs_from_file(prover_toml_path, Format::Toml, &program.abi)?; + ensure_brillig_entry_point(&program)?; + + let (inputs_map, _) = + read_inputs_from_file(&prover_toml_path.with_extension("toml"), &program.abi)?; let initial_witness = program.abi.encode(&inputs_map, None)?; - println!("Executing"); - let (_, mut profiling_samples) = nargo::ops::execute_program_with_profiling( + println!("Executing..."); + + let solved_witness_stack_err = nargo::ops::execute_program_with_profiling( &program.bytecode, initial_witness, &Bn254BlackBoxSolver(pedantic_solving), &mut DefaultForeignCallBuilder::default().with_output(PrintOutput::Stdout).build(), - )?; + ); + let mut profiling_samples = match solved_witness_stack_err { + Ok((_, profiling_samples)) => profiling_samples, + Err(err) => { + let debug_artifact = DebugArtifact { + debug_symbols: program.debug_symbols.debug_infos.clone(), + file_map: program.file_map.clone(), + }; + + if let Some(diagnostic) = try_to_diagnose_runtime_error( + &err, + &program.abi, + &program.debug_symbols.debug_infos, + ) { + diagnostic.report(&debug_artifact, false); + } + + return Err(CliError::Generic.into()); + } + }; + println!("Executed"); println!("Collecting {} samples", profiling_samples.len()); @@ -99,8 +128,27 @@ fn run_with_generator( &debug_artifact, artifact_path.to_str().unwrap(), "main", - &Path::new(&output_path).join(Path::new(&format!("{}.svg", "main"))), + &Path::new(&output_path).join(Path::new(&format!("{}_brillig_trace.svg", "main"))), )?; Ok(()) } + +fn ensure_brillig_entry_point(artifact: &ProgramArtifact) -> Result<(), CliError> { + let err_msg = "Command only supports fully unconstrained Noir programs e.g. `unconstrained fn main() { .. }".to_owned(); + let program = &artifact.bytecode; + if program.functions.len() != 1 || program.unconstrained_functions.len() != 1 { + return report_error(err_msg); + } + + let main_function = &program.functions[0]; + let Opcode::BrilligCall { id, .. } = main_function.opcodes[0] else { + return report_error(err_msg); + }; + + if id.as_usize() != 0 { + return report_error(err_msg); + } + + Ok(()) +} diff --git a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs index e68a8cd5bd27..736cddf7cf4a 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs @@ -4,33 +4,35 @@ use acir::circuit::OpcodeLocation; use clap::Args; use color_eyre::eyre::{self, Context}; +use noir_artifact_cli::fs::artifact::read_program_from_file; use noirc_artifacts::debug::DebugArtifact; use crate::flamegraph::{CompilationSample, FlamegraphGenerator, InfernoFlamegraphGenerator}; -use crate::fs::read_program_from_file; use crate::gates_provider::{BackendGatesProvider, GatesProvider}; use crate::opcode_formatter::format_acir_opcode; +/// Generates a flamegraph mapping backend opcodes to their associated locations in the source code. #[derive(Debug, Clone, Args)] pub(crate) struct GatesFlamegraphCommand { /// The path to the artifact JSON file #[clap(long, short)] - artifact_path: String, + artifact_path: PathBuf, - /// Path to the noir backend binary + /// Path to the Noir backend binary #[clap(long, short)] - backend_path: String, + backend_path: PathBuf, /// Command to get a gates report from the backend. Defaults to "gates" #[clap(long, short = 'g', default_value = "gates")] backend_gates_command: String, + /// Optional arguments for the backend gates command #[arg(trailing_var_arg = true, allow_hyphen_values = true)] backend_extra_args: Vec, /// The output folder for the flamegraph svg files #[clap(long, short)] - output: String, + output: PathBuf, /// The output name for the flamegraph svg files #[clap(long, short = 'f')] @@ -39,14 +41,14 @@ pub(crate) struct GatesFlamegraphCommand { pub(crate) fn run(args: GatesFlamegraphCommand) -> eyre::Result<()> { run_with_provider( - &PathBuf::from(args.artifact_path), + &args.artifact_path, &BackendGatesProvider { - backend_path: PathBuf::from(args.backend_path), + backend_path: args.backend_path, gates_command: args.backend_gates_command, extra_args: args.backend_extra_args, }, &InfernoFlamegraphGenerator { count_name: "gates".to_string() }, - &PathBuf::from(args.output), + &args.output, args.output_filename, ) } @@ -64,19 +66,24 @@ fn run_with_provider( let backend_gates_response = gates_provider.get_gates(artifact_path).context("Error querying backend for gates")?; - let function_names = program.names.clone(); + let function_names = std::mem::take(&mut program.names); let bytecode = std::mem::take(&mut program.bytecode); let debug_artifact: DebugArtifact = program.into(); - for (func_idx, ((func_gates, func_name), bytecode)) in backend_gates_response - .functions - .into_iter() - .zip(function_names) - .zip(bytecode.functions) - .enumerate() + let num_functions = bytecode.functions.len(); + for (func_idx, (func_gates, circuit)) in + backend_gates_response.functions.into_iter().zip(bytecode.functions).enumerate() { + // We can have repeated names if there are functions with the same name in different + // modules or functions that use generics. Thus, add the unique function index as a suffix. + let function_name = if num_functions > 1 { + format!("{}_{}", function_names[func_idx].as_str(), func_idx) + } else { + function_names[func_idx].to_owned() + }; + println!( "Opcode count: {}, Total gates by opcodes: {}, Circuit size: {}", func_gates.acir_opcodes, @@ -87,7 +94,7 @@ fn run_with_provider( let samples = func_gates .gates_per_opcode .into_iter() - .zip(bytecode.opcodes) + .zip(circuit.opcodes) .enumerate() .map(|(index, (gates, opcode))| CompilationSample { opcode: Some(format_acir_opcode(&opcode)), @@ -98,16 +105,16 @@ fn run_with_provider( .collect(); let output_filename = if let Some(output_filename) = &output_filename { - format!("{}::{}::gates.svg", output_filename, func_name) + format!("{}_{}_gates.svg", output_filename, function_name) } else { - format!("{}::gates.svg", func_name) + format!("{}_gates.svg", function_name) }; flamegraph_generator.generate_flamegraph( samples, &debug_artifact.debug_symbols[func_idx], &debug_artifact, artifact_path.to_str().unwrap(), - &func_name, + &function_name, &Path::new(&output_path).join(Path::new(&output_filename)), )?; } @@ -118,7 +125,7 @@ fn run_with_provider( #[cfg(test)] mod tests { use acir::circuit::{Circuit, Program}; - use color_eyre::eyre::{self}; + use color_eyre::eyre; use fm::codespan_files::Files; use noirc_artifacts::program::ProgramArtifact; use noirc_errors::debug_info::{DebugInfo, ProgramDebugInfo}; @@ -210,7 +217,7 @@ mod tests { .expect("should run without errors"); // Check that the output file was written to - let output_file = temp_dir.path().join("test_filename::main::gates.svg"); + let output_file = temp_dir.path().join("test_filename_main_gates.svg"); assert!(output_file.exists()); } } diff --git a/noir/noir-repo/tooling/profiler/src/cli/mod.rs b/noir/noir-repo/tooling/profiler/src/cli/mod.rs index 80c6bceb3ce2..b91dd6990aa0 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/mod.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/mod.rs @@ -32,5 +32,7 @@ pub(crate) fn start_cli() -> eyre::Result<()> { ProfilerCommand::Gates(args) => gates_flamegraph_cmd::run(args), ProfilerCommand::Opcodes(args) => opcodes_flamegraph_cmd::run(args), ProfilerCommand::ExecutionOpcodes(args) => execution_flamegraph_cmd::run(args), - } + }?; + + Ok(()) } diff --git a/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs index 4e271c9f8385..8ce9ba1de398 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs @@ -5,21 +5,22 @@ use acir::circuit::{Circuit, Opcode, OpcodeLocation}; use clap::Args; use color_eyre::eyre::{self, Context}; +use noir_artifact_cli::fs::artifact::read_program_from_file; use noirc_artifacts::debug::DebugArtifact; use crate::flamegraph::{CompilationSample, FlamegraphGenerator, InfernoFlamegraphGenerator}; -use crate::fs::read_program_from_file; use crate::opcode_formatter::{format_acir_opcode, format_brillig_opcode}; +/// Generates a flamegraph mapping ACIR opcodes to their associated locations in the source code. #[derive(Debug, Clone, Args)] pub(crate) struct OpcodesFlamegraphCommand { /// The path to the artifact JSON file #[clap(long, short)] - artifact_path: String, + artifact_path: PathBuf, /// The output folder for the flamegraph svg files #[clap(long, short)] - output: String, + output: PathBuf, /// Whether to skip brillig functions #[clap(long, short, action)] @@ -28,9 +29,9 @@ pub(crate) struct OpcodesFlamegraphCommand { pub(crate) fn run(args: OpcodesFlamegraphCommand) -> eyre::Result<()> { run_with_generator( - &PathBuf::from(args.artifact_path), + &args.artifact_path, &InfernoFlamegraphGenerator { count_name: "opcodes".to_string() }, - &PathBuf::from(args.output), + &args.output, args.skip_brillig, ) } @@ -44,18 +45,25 @@ fn run_with_generator( let mut program = read_program_from_file(artifact_path).context("Error reading program from file")?; - let function_names = program.names.clone(); + let acir_names = std::mem::take(&mut program.names); + let brillig_names = std::mem::take(&mut program.brillig_names); let bytecode = std::mem::take(&mut program.bytecode); let debug_artifact: DebugArtifact = program.into(); - for (func_idx, (func_name, bytecode)) in - function_names.into_iter().zip(bytecode.functions.iter()).enumerate() - { - println!("Opcode count for {}: {}", func_name, bytecode.opcodes.len()); + for (func_idx, circuit) in bytecode.functions.iter().enumerate() { + // We can have repeated names if there are functions with the same name in different + // modules or functions that use generics. Thus, add the unique function index as a suffix. + let function_name = if bytecode.functions.len() > 1 { + format!("{}_{}", acir_names[func_idx].as_str(), func_idx) + } else { + acir_names[func_idx].to_owned() + }; - let samples = bytecode + println!("Opcode count for {}: {}", function_name, circuit.opcodes.len()); + + let samples = circuit .opcodes .iter() .enumerate() @@ -72,51 +80,55 @@ fn run_with_generator( &debug_artifact.debug_symbols[func_idx], &debug_artifact, artifact_path.to_str().unwrap(), - &func_name, - &Path::new(&output_path).join(Path::new(&format!("{}_acir_opcodes.svg", &func_name))), + &function_name, + &Path::new(&output_path) + .join(Path::new(&format!("{}_acir_opcodes.svg", &function_name))), )?; } - if !skip_brillig { - for (brillig_fn_index, brillig_bytecode) in - bytecode.unconstrained_functions.into_iter().enumerate() - { - let acir_location = locate_brillig_call(brillig_fn_index, &bytecode.functions); - let Some((acir_fn_index, acir_opcode_index)) = acir_location else { - continue; - }; - - println!( - "Opcode count for brillig_{}: {}", - brillig_fn_index, - brillig_bytecode.bytecode.len() - ); - - let samples = brillig_bytecode - .bytecode - .into_iter() - .enumerate() - .map(|(brillig_index, opcode)| CompilationSample { - opcode: Some(format_brillig_opcode(&opcode)), - call_stack: vec![OpcodeLocation::Brillig { - acir_index: acir_opcode_index, - brillig_index, - }], - count: 1, - brillig_function_id: Some(BrilligFunctionId(brillig_fn_index as u32)), - }) - .collect(); - - flamegraph_generator.generate_flamegraph( - samples, - &debug_artifact.debug_symbols[acir_fn_index], - &debug_artifact, - artifact_path.to_str().unwrap(), - &format!("brillig_{}", brillig_fn_index), - &Path::new(&output_path) - .join(Path::new(&format!("{}_brillig_opcodes.svg", &brillig_fn_index))), - )?; - } + if skip_brillig { + return Ok(()); + } + + for (brillig_fn_index, brillig_bytecode) in + bytecode.unconstrained_functions.into_iter().enumerate() + { + let acir_location = locate_brillig_call(brillig_fn_index, &bytecode.functions); + let Some((acir_fn_index, acir_opcode_index)) = acir_location else { + continue; + }; + + // We can have repeated names if there are functions with the same name in different + // modules or functions that use generics. Thus, add the unique function index as a suffix. + let function_name = + format!("{}_{}", brillig_names[brillig_fn_index].as_str(), brillig_fn_index); + + println!("Opcode count for {}_brillig: {}", function_name, brillig_bytecode.bytecode.len()); + + let samples = brillig_bytecode + .bytecode + .into_iter() + .enumerate() + .map(|(brillig_index, opcode)| CompilationSample { + opcode: Some(format_brillig_opcode(&opcode)), + call_stack: vec![OpcodeLocation::Brillig { + acir_index: acir_opcode_index, + brillig_index, + }], + count: 1, + brillig_function_id: Some(BrilligFunctionId(brillig_fn_index as u32)), + }) + .collect(); + + flamegraph_generator.generate_flamegraph( + samples, + &debug_artifact.debug_symbols[acir_fn_index], + &debug_artifact, + artifact_path.to_str().unwrap(), + &function_name, + &Path::new(&output_path) + .join(Path::new(&format!("{}_brillig_opcodes.svg", function_name))), + )?; } Ok(()) @@ -130,7 +142,7 @@ fn locate_brillig_call( for (acir_opcode_index, acir_opcode) in acir_fn.opcodes.iter().enumerate() { match acir_opcode { Opcode::BrilligCall { id, .. } if id.as_usize() == brillig_fn_index => { - return Some((acir_fn_index, acir_opcode_index)) + return Some((acir_fn_index, acir_opcode_index)); } _ => {} } @@ -142,13 +154,13 @@ fn locate_brillig_call( #[cfg(test)] mod tests { use acir::{ + FieldElement, circuit::{ - brillig::{BrilligBytecode, BrilligFunctionId}, Circuit, Opcode, Program, + brillig::{BrilligBytecode, BrilligFunctionId}, }, - FieldElement, }; - use color_eyre::eyre::{self}; + use color_eyre::eyre; use fm::codespan_files::Files; use noirc_artifacts::program::ProgramArtifact; use noirc_errors::debug_info::{DebugInfo, ProgramDebugInfo}; @@ -214,12 +226,26 @@ mod tests { let artifact_path = temp_dir.path().join("test.json"); - let acir: Vec> = vec![Opcode::BrilligCall { - id: BrilligFunctionId(0), - inputs: vec![], - outputs: vec![], - predicate: None, - }]; + let acir: Vec> = vec![ + Opcode::BrilligCall { + id: BrilligFunctionId(0), + inputs: vec![], + outputs: vec![], + predicate: None, + }, + Opcode::BrilligCall { + id: BrilligFunctionId(1), + inputs: vec![], + outputs: vec![], + predicate: None, + }, + Opcode::BrilligCall { + id: BrilligFunctionId(2), + inputs: vec![], + outputs: vec![], + predicate: None, + }, + ]; let artifact = ProgramArtifact { noir_version: "0.0.0".to_string(), @@ -227,12 +253,16 @@ mod tests { abi: noirc_abi::Abi::default(), bytecode: Program { functions: vec![Circuit { opcodes: acir, ..Circuit::default() }], - unconstrained_functions: vec![BrilligBytecode::default()], + unconstrained_functions: vec![ + BrilligBytecode::default(), + BrilligBytecode::default(), + BrilligBytecode::default(), + ], }, debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], - brillig_names: vec!["main".to_string()], + brillig_names: vec!["main".to_string(), "main".to_string(), "main_1".to_string()], }; // Write the artifact to a file @@ -244,11 +274,17 @@ mod tests { super::run_with_generator(&artifact_path, &flamegraph_generator, temp_dir.path(), false) .expect("should run without errors"); - // Check that the output files ware written to + // Check that the output files were written let output_file = temp_dir.path().join("main_acir_opcodes.svg"); assert!(output_file.exists()); - let output_file = temp_dir.path().join("0_brillig_opcodes.svg"); + let output_file = temp_dir.path().join("main_0_brillig_opcodes.svg"); + assert!(output_file.exists()); + + let output_file = temp_dir.path().join("main_1_brillig_opcodes.svg"); + assert!(output_file.exists()); + + let output_file = temp_dir.path().join("main_1_2_brillig_opcodes.svg"); assert!(output_file.exists()); } } diff --git a/noir/noir-repo/tooling/profiler/src/errors.rs b/noir/noir-repo/tooling/profiler/src/errors.rs new file mode 100644 index 000000000000..6a028931f5e4 --- /dev/null +++ b/noir/noir-repo/tooling/profiler/src/errors.rs @@ -0,0 +1,16 @@ +use fm::FileMap; +use noirc_errors::{CustomDiagnostic, Location}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub(crate) enum CliError { + #[error("Failed to run profiler command")] + Generic, +} + +/// Report an error from the CLI that is not reliant on a stack trace. +pub(crate) fn report_error(message: String) -> Result<(), CliError> { + let error = CustomDiagnostic::simple_error(message.clone(), String::new(), Location::dummy()); + noirc_errors::reporter::report(&FileMap::default(), &error, false); + Err(CliError::Generic) +} diff --git a/noir/noir-repo/tooling/profiler/src/flamegraph.rs b/noir/noir-repo/tooling/profiler/src/flamegraph.rs index 38966902314e..16857eb2454c 100644 --- a/noir/noir-repo/tooling/profiler/src/flamegraph.rs +++ b/noir/noir-repo/tooling/profiler/src/flamegraph.rs @@ -1,15 +1,15 @@ use std::path::Path; use std::{collections::BTreeMap, io::BufWriter}; -use acir::circuit::brillig::BrilligFunctionId; use acir::circuit::OpcodeLocation; -use color_eyre::eyre::{self}; +use acir::circuit::brillig::BrilligFunctionId; +use color_eyre::eyre; use fm::codespan_files::Files; use fxhash::FxHashMap as HashMap; -use inferno::flamegraph::{from_lines, Options, TextTruncateDirection}; +use inferno::flamegraph::{Options, TextTruncateDirection, from_lines}; +use noirc_errors::Location; use noirc_errors::debug_info::DebugInfo; use noirc_errors::reporter::line_and_column_from_span; -use noirc_errors::Location; use noirc_evaluator::brillig::ProcedureId; pub(crate) trait Sample { @@ -112,7 +112,7 @@ impl FlamegraphGenerator for InfernoFlamegraphGenerator { let mut options = Options::default(); options.hash = true; options.deterministic = true; - options.title = format!("{}-{}", artifact_name, function_name); + options.title = format!("Artifact: {}, Function: {}", artifact_name, function_name); options.frame_height = 24; options.color_diffusion = true; options.min_width = 0.0; @@ -293,12 +293,12 @@ fn to_folded_sorted_lines( #[cfg(test)] mod tests { use acir::{ - circuit::{opcodes::BlockId, Opcode as AcirOpcode, OpcodeLocation}, - native_types::Expression, FieldElement, + circuit::{Opcode as AcirOpcode, OpcodeLocation, opcodes::BlockId}, + native_types::Expression, }; use fm::FileManager; - use noirc_errors::{debug_info::DebugInfo, Location, Span}; + use noirc_errors::{Location, Span, debug_info::DebugInfo}; use std::{collections::BTreeMap, path::Path}; use crate::{flamegraph::CompilationSample, opcode_formatter::format_acir_opcode}; diff --git a/noir/noir-repo/tooling/profiler/src/fs.rs b/noir/noir-repo/tooling/profiler/src/fs.rs deleted file mode 100644 index 43848504a7ff..000000000000 --- a/noir/noir-repo/tooling/profiler/src/fs.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::{collections::BTreeMap, path::Path}; - -use color_eyre::eyre; -use noirc_abi::{ - input_parser::{Format, InputValue}, - Abi, InputMap, MAIN_RETURN_NAME, -}; -use noirc_artifacts::program::ProgramArtifact; - -pub(crate) fn read_program_from_file>( - circuit_path: P, -) -> eyre::Result { - let file_path = circuit_path.as_ref().with_extension("json"); - - let input_string = std::fs::read(file_path)?; - let program = serde_json::from_slice(&input_string)?; - - Ok(program) -} - -/// Returns the circuit's parameters and its return value, if one exists. -/// # Examples -/// -/// ```ignore -/// let (input_map, return_value): (InputMap, Option) = -/// read_inputs_from_file(path, "Verifier", Format::Toml, &abi)?; -/// ``` -pub(crate) fn read_inputs_from_file( - file_path: &Path, - format: Format, - abi: &Abi, -) -> eyre::Result<(InputMap, Option)> { - if abi.is_empty() { - return Ok((BTreeMap::new(), None)); - } - - let input_string = std::fs::read_to_string(file_path)?; - let mut input_map = format.parse(&input_string, abi)?; - let return_value = input_map.remove(MAIN_RETURN_NAME); - - Ok((input_map, return_value)) -} diff --git a/noir/noir-repo/tooling/profiler/src/gates_provider.rs b/noir/noir-repo/tooling/profiler/src/gates_provider.rs index 3f07f3e4be6f..044e2a3642cd 100644 --- a/noir/noir-repo/tooling/profiler/src/gates_provider.rs +++ b/noir/noir-repo/tooling/profiler/src/gates_provider.rs @@ -1,7 +1,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; -use color_eyre::eyre::{self}; +use color_eyre::eyre; use serde::{Deserialize, Serialize}; pub(crate) trait GatesProvider { diff --git a/noir/noir-repo/tooling/profiler/src/main.rs b/noir/noir-repo/tooling/profiler/src/main.rs index b0b42e6ee41a..e4a5bc153d2e 100644 --- a/noir/noir-repo/tooling/profiler/src/main.rs +++ b/noir/noir-repo/tooling/profiler/src/main.rs @@ -4,15 +4,15 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies, unused_extern_crates))] mod cli; +mod errors; mod flamegraph; -mod fs; mod gates_provider; mod opcode_formatter; use std::env; use tracing_appender::rolling; -use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter}; +use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan}; fn main() { // Setup tracing @@ -33,7 +33,7 @@ fn main() { } if let Err(report) = cli::start_cli() { - eprintln!("{report:?}"); + eprintln!("{report}"); std::process::exit(1); } } diff --git a/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs b/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs index d1081de6c8f2..2276bcb4403f 100644 --- a/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs +++ b/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs @@ -1,6 +1,6 @@ -use acir::brillig::{BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpcode}; -use acir::circuit::{opcodes::BlackBoxFuncCall, Opcode as AcirOpcode}; use acir::AcirField; +use acir::brillig::{BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpcode}; +use acir::circuit::{Opcode as AcirOpcode, opcodes::BlackBoxFuncCall}; fn format_blackbox_function(call: &BlackBoxFuncCall) -> String { match call { diff --git a/noir/noir-repo/yarn.lock b/noir/noir-repo/yarn.lock index 56088fedd94b..568e7e487d1e 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,9 +221,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@portal:../../../../barretenberg/ts::locator=integration-tests%40workspace%3Acompiler%2Fintegration-tests": - version: 0.0.0-use.local - resolution: "@aztec/bb.js@portal:../../../../barretenberg/ts::locator=integration-tests%40workspace%3Acompiler%2Fintegration-tests" +"@aztec/bb.js@npm:0.72.1": + version: 0.72.1 + resolution: "@aztec/bb.js@npm:0.72.1" dependencies: comlink: ^4.4.1 commander: ^12.1.0 @@ -233,8 +233,9 @@ __metadata: tslib: ^2.4.0 bin: bb.js: dest/node/main.js + checksum: 143f0062a31e262ceff6e454d31e2a2672afd8dcbff0dafb17d5308f8c5312048e5d3503d80e7ad9ff4b17b6c3d36e8ffbff8d2deda2cdb684f19fa64d5a04ab 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 @@ -16608,7 +16609,7 @@ __metadata: version: 0.0.0-use.local resolution: "integration-tests@workspace:compiler/integration-tests" dependencies: - "@aztec/bb.js": "portal:../../../../barretenberg/ts" + "@aztec/bb.js": 0.72.1 "@noir-lang/noir_js": "workspace:*" "@noir-lang/noir_wasm": "workspace:*" "@nomicfoundation/hardhat-chai-matchers": ^2.0.0 From dfdcae9a3c42ae483b121e1e5b2d383acdf82640 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Tue, 4 Mar 2025 13:01:41 +0000 Subject: [PATCH 02/13] chore: apply sync fixes --- noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../noir-repo/compiler/integration-tests/package.json | 2 +- noir/noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- noir/noir-repo/yarn.lock | 11 +++++------ 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/noir/noir-repo/acvm-repo/acvm_js/build.sh b/noir/noir-repo/acvm-repo/acvm_js/build.sh index 16fb26e55db1..c07d2d8a4c1d 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/compiler/integration-tests/package.json b/noir/noir-repo/compiler/integration-tests/package.json index 053e9efeed20..e33179f31e7f 100644 --- a/noir/noir-repo/compiler/integration-tests/package.json +++ b/noir/noir-repo/compiler/integration-tests/package.json @@ -13,7 +13,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "0.72.1", + "@aztec/bb.js": "portal:../../../../barretenberg/ts", "@noir-lang/noir_js": "workspace:*", "@noir-lang/noir_wasm": "workspace:*", "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", diff --git a/noir/noir-repo/tooling/noirc_abi_wasm/build.sh b/noir/noir-repo/tooling/noirc_abi_wasm/build.sh index 16fb26e55db1..c07d2d8a4c1d 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 568e7e487d1e..56088fedd94b 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,9 +221,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.72.1": - version: 0.72.1 - resolution: "@aztec/bb.js@npm:0.72.1" +"@aztec/bb.js@portal:../../../../barretenberg/ts::locator=integration-tests%40workspace%3Acompiler%2Fintegration-tests": + version: 0.0.0-use.local + resolution: "@aztec/bb.js@portal:../../../../barretenberg/ts::locator=integration-tests%40workspace%3Acompiler%2Fintegration-tests" dependencies: comlink: ^4.4.1 commander: ^12.1.0 @@ -233,9 +233,8 @@ __metadata: tslib: ^2.4.0 bin: bb.js: dest/node/main.js - checksum: 143f0062a31e262ceff6e454d31e2a2672afd8dcbff0dafb17d5308f8c5312048e5d3503d80e7ad9ff4b17b6c3d36e8ffbff8d2deda2cdb684f19fa64d5a04ab languageName: node - linkType: hard + linkType: soft "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.11, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5, @babel/code-frame@npm:^7.8.3": version: 7.23.5 @@ -16609,7 +16608,7 @@ __metadata: version: 0.0.0-use.local resolution: "integration-tests@workspace:compiler/integration-tests" dependencies: - "@aztec/bb.js": 0.72.1 + "@aztec/bb.js": "portal:../../../../barretenberg/ts" "@noir-lang/noir_js": "workspace:*" "@noir-lang/noir_wasm": "workspace:*" "@nomicfoundation/hardhat-chai-matchers": ^2.0.0 From 3a4bc61f163f431e0441388762554d8753183ef7 Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 4 Mar 2025 13:22:12 +0000 Subject: [PATCH 03/13] empty commit From 3ac953babdd224247b74e40cf8df032655c6449d Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 4 Mar 2025 13:26:33 +0000 Subject: [PATCH 04/13] . --- noir/noir-repo/Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index ae0cbb04e4c5..7961f8ecc801 100644 --- a/noir/noir-repo/Cargo.lock +++ b/noir/noir-repo/Cargo.lock @@ -5326,7 +5326,7 @@ checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap 2.7.1", "toml_datetime", - "winnow 0.7.3", + "winnow 0.6.26", ] [[package]] @@ -5760,7 +5760,7 @@ checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.96", ] [[package]] @@ -5987,9 +5987,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.3" +version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" +checksum = "1e90edd2ac1aa278a5c4599b1d89cf03074b610800f866d4026dc199d7929a28" dependencies = [ "memchr", ] From 9c9e17a9c512b406f03754689f1582a8f9adb6dd Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 4 Mar 2025 13:30:58 +0000 Subject: [PATCH 05/13] . --- .../src/hir/def_collector/errors.rs | 37 -------- .../src/hir/resolution/errors.rs | 87 ------------------- .../tooling/nargo_cli/src/cli/execute_cmd.rs | 1 - 3 files changed, 125 deletions(-) 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 501637a29e6e..7f17b1e30430 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 @@ -111,43 +111,6 @@ impl DefCollectorErrorKind { ) => unsupported_numeric_generic_type.ident.location(), } } - - pub fn location(&self) -> Location { - match self { - DefCollectorErrorKind::Duplicate { first_def: ident, .. } - | DefCollectorErrorKind::UnresolvedModuleDecl { mod_name: ident, .. } - | DefCollectorErrorKind::CannotReexportItemWithLessVisibility { - item_name: ident, - .. - } - | DefCollectorErrorKind::MethodNotInTrait { impl_method: ident, .. } - | DefCollectorErrorKind::OverlappingModuleDecls { mod_name: ident, .. } => { - ident.location() - } - DefCollectorErrorKind::PathResolutionError(path_resolution_error) => { - path_resolution_error.location() - } - DefCollectorErrorKind::ImplIsStricterThanTrait { - trait_method_location: location, - .. - } - | DefCollectorErrorKind::TestOnAssociatedFunction { location } - | DefCollectorErrorKind::ExportOnAssociatedFunction { location } - | DefCollectorErrorKind::NonStructTypeInImpl { location } - | DefCollectorErrorKind::MutableReferenceInTraitImpl { location } - | DefCollectorErrorKind::OverlappingImpl { location, .. } - | DefCollectorErrorKind::ModuleAlreadyPartOfCrate { location, .. } - | DefCollectorErrorKind::ModuleOriginallyDefined { location, .. } - | DefCollectorErrorKind::TraitImplOrphaned { location } - | DefCollectorErrorKind::TraitMissingMethod { trait_impl_location: location, .. } - | DefCollectorErrorKind::ForeignImpl { location, .. } => *location, - DefCollectorErrorKind::NotATrait { not_a_trait_name: path } - | DefCollectorErrorKind::TraitNotFound { trait_path: path } => path.location, - DefCollectorErrorKind::UnsupportedNumericGenericType( - unsupported_numeric_generic_type, - ) => unsupported_numeric_generic_type.ident.location(), - } - } } impl<'a> From<&'a UnsupportedNumericGenericType> for Diagnostic { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs index 236046b186fb..bc1c519ed5d8 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -279,93 +279,6 @@ impl ResolverError { } } } - - pub fn location(&self) -> Location { - match self { - ResolverError::DuplicateDefinition { first_location: location, .. } - | ResolverError::UnconditionalRecursion { location, .. } - | ResolverError::PathIsNotIdent { location } - | ResolverError::Expected { location, .. } - | ResolverError::VariableNotDeclared { location, .. } - | ResolverError::MissingFields { location, .. } - | ResolverError::UnnecessaryMut { second_mut: location, .. } - | ResolverError::TypeIsMorePrivateThenItem { location, .. } - | ResolverError::UnableToParseAttribute { location, .. } - | ResolverError::AttributeFunctionIsNotAPath { location, .. } - | ResolverError::AttributeFunctionNotInScope { location, .. } - | ResolverError::TraitNotImplemented { location, .. } - | ResolverError::LoopNotYetSupported { location } - | ResolverError::ExpectedTrait { location, .. } - | ResolverError::MissingRhsExpr { location, .. } - | ResolverError::InvalidArrayLengthExpr { location } - | ResolverError::IntegerTooLarge { location } - | ResolverError::CapturedMutableVariable { location } - | ResolverError::TestFunctionHasParameters { location } - | ResolverError::NonStructUsedInConstructor { location, .. } - | ResolverError::NonStructWithGenerics { location } - | ResolverError::GenericsOnSelfType { location } - | ResolverError::GenericsOnAssociatedType { location } - | ResolverError::MutableReferenceToImmutableVariable { location, .. } - | ResolverError::MutableReferenceToArrayElement { location } - | ResolverError::InvalidClosureEnvironment { location, .. } - | ResolverError::NestedSlices { location } - | ResolverError::AbiAttributeOutsideContract { location } - | ResolverError::UnconstrainedOracleReturnToConstrained { location } - | ResolverError::DependencyCycle { location, .. } - | ResolverError::JumpInConstrainedFn { location, .. } - | ResolverError::LoopInConstrainedFn { location } - | ResolverError::LoopWithoutBreak { location } - | ResolverError::WhileInConstrainedFn { location } - | ResolverError::JumpOutsideLoop { location, .. } - | ResolverError::MutableGlobal { location } - | ResolverError::UnspecifiedGlobalType { location, .. } - | ResolverError::UnevaluatedGlobalType { location } - | ResolverError::NegativeGlobalType { location, .. } - | ResolverError::NonIntegralGlobalType { location, .. } - | ResolverError::GlobalLargerThanKind { location, .. } - | ResolverError::SelfReferentialType { location } - | ResolverError::NumericGenericUsedForType { location, .. } - | ResolverError::UnquoteUsedOutsideQuote { location } - | ResolverError::AsTraitPathNotYetImplemented { location } - | ResolverError::InvalidSyntaxInMacroCall { location } - | ResolverError::MacroIsNotComptime { location } - | ResolverError::NonFunctionInAnnotation { location } - | ResolverError::MacroResultInGenericsListNotAGeneric { location, .. } - | ResolverError::NamedTypeArgs { location, .. } - | ResolverError::AssociatedConstantsMustBeNumeric { location } - | ResolverError::BinaryOpError { location, .. } - | ResolverError::QuoteInRuntimeCode { location } - | ResolverError::ComptimeTypeInRuntimeCode { location, .. } - | ResolverError::MutatingComptimeInNonComptimeContext { location, .. } - | ResolverError::InvalidInternedStatementInExpr { location, .. } - | ResolverError::InvalidSyntaxInPattern { location } - | ResolverError::NonIntegerGlobalUsedInPattern { location, .. } - | ResolverError::TypeUnsupportedInMatch { location, .. } - | ResolverError::UnexpectedItemInPattern { location, .. } - | ResolverError::VariableAlreadyDefinedInPattern { new_location: location, .. } => { - *location - } - ResolverError::UnusedVariable { ident } - | ResolverError::UnusedItem { ident, .. } - | ResolverError::DuplicateField { field: ident } - | ResolverError::NoSuchField { field: ident, .. } - | ResolverError::UnnecessaryPub { ident, .. } - | ResolverError::NecessaryPub { ident } - | ResolverError::LowLevelFunctionOutsideOfStdlib { ident } - | ResolverError::OracleMarkedAsConstrained { ident } - | ResolverError::NoPredicatesAttributeOnUnconstrained { ident } - | ResolverError::FoldAttributeOnUnconstrained { ident } => ident.location(), - ResolverError::ArrayLengthInterpreter { error } => error.location(), - ResolverError::PathResolutionError(path_resolution_error) => { - path_resolution_error.location() - } - ResolverError::NoSuchNumericTypeVariable { path } => path.location, - ResolverError::ParserError(parser_error) => parser_error.location(), - ResolverError::UnsupportedNumericGenericType(unsupported_numeric_generic_type) => { - unsupported_numeric_generic_type.ident.location() - } - } - } } impl<'a> From<&'a ResolverError> for Diagnostic { diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs index 7947e6f34ce1..07a7c1195af7 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -1,6 +1,5 @@ use clap::Args; -use nargo::PrintOutput; use nargo::constants::PROVER_INPUT_FILE; use nargo::workspace::Workspace; use nargo_toml::PackageSelection; From af266fde4d0ac04853050f543e731e8d717d155b Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 4 Mar 2025 13:59:35 +0000 Subject: [PATCH 06/13] . --- .../codegen_verifier/codegen_verifier.sh | 8 +++---- .../prove_and_verify/prove_and_verify.sh | 6 ++--- .../recursion/generate_recursive_proof.sh | 24 +++++++++---------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh b/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh index 30dc06e47127..89df3ced1814 100755 --- a/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh +++ b/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh @@ -6,18 +6,18 @@ BACKEND=${BACKEND:-bb} nargo compile # TODO: backend should automatically generate vk if necessary. -$BACKEND write_vk -b ./target/hello_world.json -$BACKEND contract -o ./src/contract.sol +$BACKEND OLD_API write_vk -b ./target/hello_world.json +$BACKEND OLD_API contract -o ./src/contract.sol # We now generate a proof and check whether the verifier contract will verify it. nargo execute --pedantic-solving witness PROOF_PATH=./target/proof -$BACKEND prove -b ./target/hello_world.json -w ./target/witness.gz -o $PROOF_PATH +$BACKEND OLD_API prove -b ./target/hello_world.json -w ./target/witness.gz -o $PROOF_PATH # Sanity check that proof is valid. -$BACKEND verify -k ./target/vk -p ./target/proof +$BACKEND OLD_API verify -k ./target/vk -p ./target/proof NUM_PUBLIC_INPUTS=2 PUBLIC_INPUT_BYTES=$((32 * $NUM_PUBLIC_INPUTS)) diff --git a/noir/noir-repo/examples/prove_and_verify/prove_and_verify.sh b/noir/noir-repo/examples/prove_and_verify/prove_and_verify.sh index 411f5258caf4..92288bc92e02 100755 --- a/noir/noir-repo/examples/prove_and_verify/prove_and_verify.sh +++ b/noir/noir-repo/examples/prove_and_verify/prove_and_verify.sh @@ -7,8 +7,8 @@ nargo execute --pedantic-solving witness # TODO: `bb` should create `proofs` directory if it doesn't exist. mkdir -p proofs -$BACKEND prove -b ./target/hello_world.json -w ./target/witness.gz +$BACKEND OLD_API prove -b ./target/hello_world.json -w ./target/witness.gz # TODO: backend should automatically generate vk if necessary. -$BACKEND write_vk -b ./target/hello_world.json -$BACKEND verify -k ./target/vk -p ./proofs/proof +$BACKEND OLD_API write_vk -b ./target/hello_world.json +$BACKEND OLD_API verify -k ./target/vk -p ./proofs/proof diff --git a/noir/noir-repo/examples/recursion/generate_recursive_proof.sh b/noir/noir-repo/examples/recursion/generate_recursive_proof.sh index 09b01d547b68..e05dd854d69f 100755 --- a/noir/noir-repo/examples/recursion/generate_recursive_proof.sh +++ b/noir/noir-repo/examples/recursion/generate_recursive_proof.sh @@ -4,16 +4,16 @@ set -eu BACKEND=${BACKEND:-bb} nargo execute sum_witness --package sum -$BACKEND prove -b ./target/sum.json -w ./target/sum_witness.gz -o ./target/sum_proof --recursive +$BACKEND OLD_API prove -b ./target/sum.json -w ./target/sum_witness.gz -o ./target/sum_proof --recursive # Once we have generated our inner proof, we must use this to generate inputs to `recurse_leaf`` -$BACKEND write_vk -b ./target/sum.json -o ./target/sum_key --recursive -$BACKEND vk_as_fields -k ./target/sum_key -o ./target/sum_vk_as_fields +$BACKEND OLD_API write_vk -b ./target/sum.json -o ./target/sum_key --recursive +$BACKEND OLD_API vk_as_fields -k ./target/sum_key -o ./target/sum_vk_as_fields VK_HASH=$(jq -r '.[0]' ./target/sum_vk_as_fields) VK_AS_FIELDS=$(jq -r '.[1:]' ./target/sum_vk_as_fields) -FULL_PROOF_AS_FIELDS="$($BACKEND proof_as_fields -p ./target/sum_proof -k ./target/sum_key -o -)" +FULL_PROOF_AS_FIELDS="$($BACKEND OLD_API proof_as_fields -p ./target/sum_proof -k ./target/sum_key -o -)" # sum has 3 public inputs PUBLIC_INPUTS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[:3]') PROOF_AS_FIELDS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[3:]') @@ -28,19 +28,19 @@ echo "public_inputs = $PUBLIC_INPUTS" >> $RECURSE_LEAF_PROVER_TOML # We can now execute and prove `recurse_leaf` nargo execute recurse_leaf_witness --package recurse_leaf -$BACKEND prove -b ./target/recurse_leaf.json -w ./target/recurse_leaf_witness.gz -o ./target/recurse_leaf_proof --recursive +$BACKEND OLD_API prove -b ./target/recurse_leaf.json -w ./target/recurse_leaf_witness.gz -o ./target/recurse_leaf_proof --recursive # Let's do a sanity check that the proof we've generated so far is valid. -$BACKEND write_vk -b ./target/recurse_leaf.json -o ./target/recurse_leaf_key --recursive -$BACKEND verify -p ./target/recurse_leaf_proof -k ./target/recurse_leaf_key +$BACKEND OLD_API write_vk -b ./target/recurse_leaf.json -o ./target/recurse_leaf_key --recursive +$BACKEND OLD_API verify -p ./target/recurse_leaf_proof -k ./target/recurse_leaf_key # Now we generate the final `recurse_node` proof similarly to how we did for `recurse_leaf`. -$BACKEND vk_as_fields -k ./target/recurse_leaf_key -o ./target/recurse_leaf_vk_as_fields +$BACKEND OLD_API vk_as_fields -k ./target/recurse_leaf_key -o ./target/recurse_leaf_vk_as_fields VK_HASH=$(jq -r '.[0]' ./target/recurse_leaf_vk_as_fields) VK_AS_FIELDS=$(jq -r '.[1:]' ./target/recurse_leaf_vk_as_fields) -FULL_PROOF_AS_FIELDS="$($BACKEND proof_as_fields -p ./target/recurse_leaf_proof -k ./target/recurse_leaf_key -o -)" +FULL_PROOF_AS_FIELDS="$($BACKEND OLD_API proof_as_fields -p ./target/recurse_leaf_proof -k ./target/recurse_leaf_key -o -)" # recurse_leaf has 4 public inputs (excluding aggregation object) PUBLIC_INPUTS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[:4]') PROOF_AS_FIELDS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[4:]') @@ -54,8 +54,8 @@ echo "public_inputs = $PUBLIC_INPUTS" >> $RECURSE_NODE_PROVER_TOML # We can now execute and prove `recurse_node` nargo execute recurse_node_witness --package recurse_node -$BACKEND prove -b ./target/recurse_node.json -w ./target/recurse_node_witness.gz -o ./target/recurse_node_proof +$BACKEND OLD_API prove -b ./target/recurse_node.json -w ./target/recurse_node_witness.gz -o ./target/recurse_node_proof # We finally verify that the generated recursive proof is valid. -$BACKEND write_vk -b ./target/recurse_node.json -o ./target/recurse_node_key -$BACKEND verify -p ./target/recurse_node_proof -k ./target/recurse_node_key +$BACKEND OLD_API write_vk -b ./target/recurse_node.json -o ./target/recurse_node_key +$BACKEND OLD_API verify -p ./target/recurse_node_proof -k ./target/recurse_node_key From 09328125f1d0c7edd023bbcc7bb29e41fe316d44 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Wed, 5 Mar 2025 08:08:32 +0000 Subject: [PATCH 07/13] [1 changes] chore: bump external pinned commits (https://github.com/noir-lang/noir/pull/7581) chore!: remove deprecated hash functions from stdlib (https://github.com/noir-lang/noir/pull/7477) fix(frontend)!: Restrict capturing mutable variable in lambdas (https://github.com/noir-lang/noir/pull/7488) feat: perform constant sha256 compressions at compile-time (https://github.com/noir-lang/noir/pull/7566) chore: bump external pinned commits (https://github.com/noir-lang/noir/pull/7565) chore(ssa): Turn the Brillig constraints check back on by default (https://github.com/noir-lang/noir/pull/7404) chore: bump external pinned commits (https://github.com/noir-lang/noir/pull/7561) chore: address some frontend tests TODOs (https://github.com/noir-lang/noir/pull/7554) fix: shift right overflow in ACIR with unknown var now returns zero (https://github.com/noir-lang/noir/pull/7509) chore(cli): Forward `nargo execute` to `noir_artifact_cli` (https://github.com/noir-lang/noir/pull/7406) feat: Support `::method` in expressions (https://github.com/noir-lang/noir/pull/7551) chore: remove FileDiagnostic (https://github.com/noir-lang/noir/pull/7546) chore: add some extra tests (https://github.com/noir-lang/noir/pull/7544) fix: fix a few cases where safety comment wasn't correctly identified (https://github.com/noir-lang/noir/pull/7548) chore!: remove U128 struct from stdlib (https://github.com/noir-lang/noir/pull/7529) feat: simplify simple conditionals for brillig (https://github.com/noir-lang/noir/pull/7205) chore: put RcTracker as part of the DIE context (https://github.com/noir-lang/noir/pull/7309) --- .noir-sync-commit | 2 +- noir/noir-repo/.github/benchmark_projects.yml | 28 +- .../.github/scripts/wasm-bindgen-install.sh | 4 +- .../workflows/bump-aztec-packages-commit.yml | 50 + noir/noir-repo/.github/workflows/docs-pr.yml | 10 +- .../.github/workflows/formatting.yml | 8 +- .../.github/workflows/publish-acvm.yml | 2 +- .../.github/workflows/publish-docs.yml | 4 +- .../.github/workflows/publish-es-packages.yml | 16 +- .../.github/workflows/publish-nargo.yml | 101 +- noir/noir-repo/.github/workflows/release.yml | 16 +- noir/noir-repo/.github/workflows/reports.yml | 5 +- .../.github/workflows/test-js-packages.yml | 8 +- .../workflows/test-rust-workspace-msrv.yml | 20 +- .../.github/workflows/test-rust-workspace.yml | 14 +- noir/noir-repo/.gitignore | 8 +- noir/noir-repo/.release-please-manifest.json | 2 +- noir/noir-repo/.vscode/settings.json | 6 + noir/noir-repo/CHANGELOG.md | 57 + noir/noir-repo/Cargo.lock | 270 ++- noir/noir-repo/Cargo.toml | 26 +- noir/noir-repo/EXTERNAL_NOIR_LIBRARIES.yml | 8 +- noir/noir-repo/README.md | 2 +- noir/noir-repo/acvm-repo/acir/Cargo.toml | 2 +- .../acvm-repo/acir/benches/serialization.rs | 4 +- .../acvm-repo/acir/src/circuit/mod.rs | 8 +- noir/noir-repo/acvm-repo/acir/src/lib.rs | 4 +- .../src/native_types/expression/ordering.rs | 6 +- .../acir/src/native_types/witness_map.rs | 4 +- .../acir/src/native_types/witness_stack.rs | 22 +- .../acir/tests/test_program_serialization.rs | 2 +- .../noir-repo/acvm-repo/acir_field/Cargo.toml | 8 +- .../acir_field/benches/field_element.rs | 11 + .../acvm-repo/acir_field/src/field_element.rs | 82 +- noir/noir-repo/acvm-repo/acvm/Cargo.toml | 2 +- .../acvm-repo/acvm/src/compiler/mod.rs | 4 +- .../acvm/src/compiler/optimizers/general.rs | 2 +- .../compiler/optimizers/merge_expressions.rs | 10 +- .../acvm/src/compiler/optimizers/mod.rs | 4 +- .../compiler/optimizers/redundant_range.rs | 8 +- .../src/compiler/optimizers/unused_memory.rs | 2 +- .../acvm-repo/acvm/src/compiler/simulator.rs | 6 +- .../acvm/src/compiler/transformers/csat.rs | 4 +- .../acvm/src/compiler/transformers/mod.rs | 8 +- .../acvm-repo/acvm/src/pwg/arithmetic.rs | 51 +- .../acvm-repo/acvm/src/pwg/blackbox/aes128.rs | 4 +- .../acvm-repo/acvm/src/pwg/blackbox/bigint.rs | 2 +- .../src/pwg/blackbox/embedded_curve_ops.rs | 4 +- .../acvm-repo/acvm/src/pwg/blackbox/hash.rs | 25 +- .../acvm-repo/acvm/src/pwg/blackbox/logic.rs | 4 +- .../acvm-repo/acvm/src/pwg/blackbox/mod.rs | 14 +- .../acvm-repo/acvm/src/pwg/blackbox/range.rs | 37 +- .../acvm/src/pwg/blackbox/signature/ecdsa.rs | 4 +- .../acvm-repo/acvm/src/pwg/blackbox/utils.rs | 4 +- .../acvm-repo/acvm/src/pwg/brillig.rs | 12 +- .../acvm-repo/acvm/src/pwg/memory_op.rs | 6 +- noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs | 17 +- noir/noir-repo/acvm-repo/acvm/tests/solver.rs | 8 +- noir/noir-repo/acvm-repo/acvm_js/Cargo.toml | 4 +- noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- noir/noir-repo/acvm-repo/acvm_js/package.json | 2 +- .../acvm_js/src/black_box_solvers.rs | 2 +- .../acvm-repo/acvm_js/src/execute.rs | 10 +- .../acvm_js/src/foreign_call/inputs.rs | 2 +- .../acvm-repo/acvm_js/src/foreign_call/mod.rs | 4 +- .../acvm_js/src/foreign_call/outputs.rs | 2 +- .../acvm_js/src/js_execution_error.rs | 4 +- .../acvm-repo/acvm_js/src/js_witness_map.rs | 14 +- .../acvm-repo/acvm_js/src/js_witness_stack.rs | 4 +- .../acvm-repo/acvm_js/src/logging.rs | 2 +- .../acvm-repo/acvm_js/src/public_witness.rs | 20 +- .../acvm-repo/blackbox_solver/Cargo.toml | 2 +- .../acvm-repo/blackbox_solver/src/bigint.rs | 7 +- .../src/curve_specific_solver.rs | 4 +- .../blackbox_solver/src/ecdsa/secp256k1.rs | 8 +- .../blackbox_solver/src/ecdsa/secp256r1.rs | 8 +- .../bn254_blackbox_solver/Cargo.toml | 2 +- .../benches/criterion.rs | 2 +- .../src/generator/hash_to_curve.rs | 6 +- .../bn254_blackbox_solver/src/lib.rs | 4 +- .../bn254_blackbox_solver/src/poseidon2.rs | 4 +- noir/noir-repo/acvm-repo/brillig/Cargo.toml | 2 +- .../acvm-repo/brillig/src/black_box.rs | 2 +- .../noir-repo/acvm-repo/brillig_vm/Cargo.toml | 2 +- .../acvm-repo/brillig_vm/src/arithmetic.rs | 16 +- .../acvm-repo/brillig_vm/src/black_box.rs | 6 +- .../noir-repo/acvm-repo/brillig_vm/src/lib.rs | 144 +- .../acvm-repo/brillig_vm/src/memory.rs | 6 +- .../compiler/integration-tests/package.json | 2 +- .../compiler/noirc_driver/src/abi_gen.rs | 22 +- .../compiler/noirc_driver/src/contract.rs | 4 +- .../compiler/noirc_driver/src/lib.rs | 97 +- .../compiler/noirc_driver/src/program.rs | 2 +- .../compiler/noirc_driver/tests/contracts.rs | 10 +- .../noirc_driver/tests/stdlib_warnings.rs | 4 +- .../compiler/noirc_errors/src/debug_info.rs | 8 +- .../compiler/noirc_errors/src/lib.rs | 20 +- .../compiler/noirc_errors/src/position.rs | 85 +- .../compiler/noirc_errors/src/reporter.rs | 86 +- .../compiler/noirc_evaluator/Cargo.toml | 1 + .../noirc_evaluator/src/acir/acir_variable.rs | 66 +- .../noirc_evaluator/src/acir/black_box.rs | 14 +- .../noirc_evaluator/src/acir/brillig_call.rs | 6 +- .../src/acir/brillig_directive.rs | 2 +- .../src/acir/generated_acir.rs | 16 +- .../compiler/noirc_evaluator/src/acir/mod.rs | 83 +- .../src/brillig/brillig_gen.rs | 6 +- .../brillig/brillig_gen/brillig_black_box.rs | 68 +- .../src/brillig/brillig_gen/brillig_block.rs | 120 +- .../brillig_gen/brillig_block_variables.rs | 6 +- .../src/brillig/brillig_gen/brillig_fn.rs | 2 +- .../brillig/brillig_gen/brillig_globals.rs | 14 +- .../brillig/brillig_gen/brillig_slice_ops.rs | 8 +- .../noirc_evaluator/src/brillig/brillig_ir.rs | 6 +- .../src/brillig/brillig_ir/artifact.rs | 22 +- .../brillig/brillig_ir/brillig_variable.rs | 4 +- .../src/brillig/brillig_ir/codegen_binary.rs | 6 +- .../src/brillig/brillig_ir/codegen_calls.rs | 4 +- .../brillig_ir/codegen_control_flow.rs | 4 +- .../brillig/brillig_ir/codegen_intrinsic.rs | 4 +- .../src/brillig/brillig_ir/codegen_memory.rs | 4 +- .../src/brillig/brillig_ir/codegen_stack.rs | 8 +- .../src/brillig/brillig_ir/debug_show.rs | 20 +- .../src/brillig/brillig_ir/entry_point.rs | 4 +- .../src/brillig/brillig_ir/instructions.rs | 28 +- .../brillig_ir/procedures/array_copy.rs | 4 +- .../brillig_ir/procedures/array_reverse.rs | 4 +- .../procedures/check_max_stack_depth.rs | 2 +- .../brillig/brillig_ir/procedures/mem_copy.rs | 4 +- .../src/brillig/brillig_ir/procedures/mod.rs | 4 +- .../procedures/prepare_vector_insert.rs | 6 +- .../procedures/prepare_vector_push.rs | 4 +- .../procedures/revert_with_string.rs | 2 +- .../brillig_ir/procedures/vector_copy.rs | 4 +- .../brillig_ir/procedures/vector_pop_back.rs | 4 +- .../brillig_ir/procedures/vector_pop_front.rs | 4 +- .../brillig_ir/procedures/vector_remove.rs | 4 +- .../src/brillig/brillig_ir/registers.rs | 2 +- .../compiler/noirc_evaluator/src/errors.rs | 48 +- .../compiler/noirc_evaluator/src/ssa.rs | 25 +- .../check_for_underconstrained_values.rs | 68 +- .../src/ssa/function_builder/mod.rs | 10 +- .../noirc_evaluator/src/ssa/ir/dfg.rs | 47 +- .../noirc_evaluator/src/ssa/ir/dom.rs | 9 +- .../src/ssa/ir/function_inserter.rs | 25 + .../noirc_evaluator/src/ssa/ir/instruction.rs | 53 +- .../src/ssa/ir/instruction/binary.rs | 54 +- .../src/ssa/ir/instruction/call.rs | 20 +- .../src/ssa/ir/instruction/call/blackbox.rs | 85 +- .../src/ssa/ir/instruction/cast.rs | 2 +- .../src/ssa/ir/instruction/constrain.rs | 24 +- .../noirc_evaluator/src/ssa/ir/printer.rs | 8 +- .../noirc_evaluator/src/ssa/ir/types.rs | 41 +- .../noirc_evaluator/src/ssa/opt/array_set.rs | 8 +- .../src/ssa/opt/basic_conditional.rs | 526 +++++ .../src/ssa/opt/brillig_array_gets.rs | 168 ++ .../src/ssa/opt/brillig_entry_points.rs | 2 +- .../src/ssa/opt/check_u128_mul_overflow.rs | 285 +++ .../src/ssa/opt/constant_folding.rs | 15 +- .../noirc_evaluator/src/ssa/opt/die.rs | 113 +- .../src/ssa/opt/flatten_cfg.rs | 81 +- .../ssa/opt/flatten_cfg/branch_analysis.rs | 4 +- .../src/ssa/opt/flatten_cfg/value_merger.rs | 2 +- .../noirc_evaluator/src/ssa/opt/hint.rs | 6 +- .../noirc_evaluator/src/ssa/opt/inlining.rs | 18 +- .../src/ssa/opt/inlining/inline_info.rs | 6 +- .../src/ssa/opt/loop_invariant.rs | 8 +- .../src/ssa/opt/make_constrain_not_equal.rs | 2 +- .../noirc_evaluator/src/ssa/opt/mem2reg.rs | 6 +- .../src/ssa/opt/mem2reg/block.rs | 6 +- .../noirc_evaluator/src/ssa/opt/mod.rs | 3 + .../src/ssa/opt/preprocess_fns.rs | 4 +- .../noirc_evaluator/src/ssa/opt/pure.rs | 2 +- .../src/ssa/opt/remove_bit_shifts.rs | 50 +- .../src/ssa/opt/remove_enable_side_effects.rs | 4 +- .../src/ssa/opt/remove_if_else.rs | 4 +- .../src/ssa/opt/simplify_cfg.rs | 6 +- .../noirc_evaluator/src/ssa/opt/unrolling.rs | 24 +- .../src/ssa/parser/into_ssa.rs | 4 +- .../noirc_evaluator/src/ssa/parser/lexer.rs | 2 +- .../noirc_evaluator/src/ssa/parser/mod.rs | 51 +- .../noirc_evaluator/src/ssa/parser/tests.rs | 2 +- .../noirc_evaluator/src/ssa/parser/token.rs | 2 +- .../src/ssa/ssa_gen/context.rs | 32 +- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 12 +- .../compiler/noirc_frontend/Cargo.toml | 1 - .../noirc_frontend/src/ast/enumeration.rs | 4 +- .../noirc_frontend/src/ast/expression.rs | 118 +- .../noirc_frontend/src/ast/function.rs | 11 +- .../compiler/noirc_frontend/src/ast/mod.rs | 63 +- .../noirc_frontend/src/ast/statement.rs | 285 +-- .../noirc_frontend/src/ast/structure.rs | 4 +- .../compiler/noirc_frontend/src/ast/traits.rs | 16 +- .../noirc_frontend/src/ast/type_alias.rs | 4 +- .../noirc_frontend/src/ast/visitor.rs | 192 +- .../compiler/noirc_frontend/src/debug/mod.rs | 243 +- .../noirc_frontend/src/elaborator/comptime.rs | 157 +- .../noirc_frontend/src/elaborator/enums.rs | 410 +++- .../src/elaborator/expressions.rs | 507 ++-- .../noirc_frontend/src/elaborator/lints.rs | 68 +- .../noirc_frontend/src/elaborator/mod.rs | 424 ++-- .../noirc_frontend/src/elaborator/options.rs | 62 + .../src/elaborator/path_resolution.rs | 18 +- .../noirc_frontend/src/elaborator/patterns.rs | 158 +- .../noirc_frontend/src/elaborator/scope.rs | 28 +- .../src/elaborator/statements.rs | 175 +- .../src/elaborator/trait_impls.rs | 39 +- .../noirc_frontend/src/elaborator/traits.rs | 52 +- .../noirc_frontend/src/elaborator/types.rs | 526 +++-- .../noirc_frontend/src/elaborator/unquote.rs | 13 +- .../compiler/noirc_frontend/src/graph/mod.rs | 4 +- .../src/hir/comptime/display.rs | 75 +- .../noirc_frontend/src/hir/comptime/errors.rs | 208 +- .../src/hir/comptime/hir_to_display_ast.rs | 159 +- .../src/hir/comptime/interpreter.rs | 151 +- .../src/hir/comptime/interpreter/builtin.rs | 225 +- .../interpreter/builtin/builtin_helpers.rs | 46 +- .../src/hir/comptime/interpreter/foreign.rs | 22 +- .../src/hir/comptime/interpreter/unquote.rs | 12 +- .../noirc_frontend/src/hir/comptime/mod.rs | 2 +- .../noirc_frontend/src/hir/comptime/tests.rs | 12 +- .../noirc_frontend/src/hir/comptime/value.rs | 224 +- .../src/hir/def_collector/dc_crate.rs | 123 +- .../src/hir/def_collector/dc_mod.rs | 184 +- .../src/hir/def_collector/errors.rs | 191 +- .../src/hir/def_map/item_scope.rs | 10 +- .../noirc_frontend/src/hir/def_map/mod.rs | 19 +- .../compiler/noirc_frontend/src/hir/mod.rs | 17 +- .../src/hir/resolution/errors.rs | 529 +++-- .../src/hir/resolution/import.rs | 81 +- .../src/hir/resolution/visibility.rs | 4 +- .../src/hir/type_check/errors.rs | 552 +++-- .../src/hir/type_check/generics.rs | 2 +- .../noirc_frontend/src/hir_def/expr.rs | 58 +- .../noirc_frontend/src/hir_def/function.rs | 2 +- .../noirc_frontend/src/hir_def/stmt.rs | 2 +- .../noirc_frontend/src/hir_def/traits.rs | 16 +- .../noirc_frontend/src/hir_def/types.rs | 156 +- .../src/hir_def/types/arithmetic.rs | 46 +- .../noirc_frontend/src/lexer/errors.rs | 136 +- .../noirc_frontend/src/lexer/lexer.rs | 220 +- .../noirc_frontend/src/lexer/token.rs | 117 +- .../compiler/noirc_frontend/src/lib.rs | 5 +- .../compiler/noirc_frontend/src/locations.rs | 10 +- .../src/monomorphization/ast.rs | 6 +- .../src/monomorphization/debug.rs | 39 +- .../src/monomorphization/errors.rs | 31 +- .../src/monomorphization/mod.rs | 87 +- .../src/monomorphization/printer.rs | 2 +- .../noirc_frontend/src/node_interner.rs | 133 +- .../noirc_frontend/src/parser/errors.rs | 82 +- .../compiler/noirc_frontend/src/parser/mod.rs | 8 +- .../noirc_frontend/src/parser/parser.rs | 163 +- .../src/parser/parser/arguments.rs | 4 +- .../src/parser/parser/attributes.rs | 144 +- .../src/parser/parser/doc_comments.rs | 27 +- .../noirc_frontend/src/parser/parser/enums.rs | 47 +- .../src/parser/parser/expression.rs | 287 +-- .../src/parser/parser/function.rs | 157 +- .../src/parser/parser/generics.rs | 22 +- .../src/parser/parser/global.rs | 44 +- .../noirc_frontend/src/parser/parser/impls.rs | 59 +- .../noirc_frontend/src/parser/parser/infix.rs | 18 +- .../noirc_frontend/src/parser/parser/item.rs | 30 +- .../src/parser/parser/item_visibility.rs | 16 +- .../src/parser/parser/lambda.rs | 4 +- .../src/parser/parser/modifiers.rs | 31 +- .../src/parser/parser/module.rs | 18 +- .../src/parser/parser/parse_many.rs | 10 +- .../noirc_frontend/src/parser/parser/path.rs | 65 +- .../src/parser/parser/pattern.rs | 60 +- .../src/parser/parser/statement.rs | 205 +- .../statement_or_expression_or_lvalue.rs | 8 +- .../src/parser/parser/structs.rs | 48 +- .../src/parser/parser/traits.rs | 58 +- .../src/parser/parser/type_alias.rs | 28 +- .../src/parser/parser/type_expression.rs | 111 +- .../noirc_frontend/src/parser/parser/types.rs | 109 +- .../src/parser/parser/use_tree.rs | 59 +- .../src/parser/parser/where_clause.rs | 20 +- .../noirc_frontend/src/signed_field.rs | 175 ++ .../compiler/noirc_frontend/src/tests.rs | 2048 +++++++---------- .../src/tests/arithmetic_generics.rs | 24 +- .../noirc_frontend/src/tests/bound_checks.rs | 68 +- .../noirc_frontend/src/tests/enums.rs | 201 ++ .../noirc_frontend/src/tests/imports.rs | 66 +- .../src/tests/metaprogramming.rs | 78 +- .../noirc_frontend/src/tests/references.rs | 58 +- .../noirc_frontend/src/tests/traits.rs | 345 +-- .../noirc_frontend/src/tests/turbofish.rs | 155 +- .../noirc_frontend/src/tests/unused_items.rs | 109 +- .../noirc_frontend/src/tests/visibility.rs | 170 +- noir/noir-repo/compiler/wasm/package.json | 2 +- noir/noir-repo/compiler/wasm/src/compile.rs | 49 +- .../compiler/wasm/src/compile_new.rs | 14 +- noir/noir-repo/compiler/wasm/src/errors.rs | 17 +- noir/noir-repo/compiler/wasm/src/lib.rs | 6 +- .../compiler/wasm/src/types/noir_artifact.ts | 4 +- .../wasm/test/compiler/shared/compile.test.ts | 18 +- noir/noir-repo/cspell.json | 5 +- .../docs/how_to/how-to-solidity-verifier.mdx | 17 +- .../docs/noir/concepts/data_types/integers.md | 48 - .../docs/docs/noir/concepts/traits.md | 31 + .../modules_packages_crates/dependencies.md | 4 +- .../noir/standard_library/black_box_fns.md | 2 +- .../cryptographic_primitives/hashes.mdx | 33 +- .../docs/docs/noir/standard_library/traits.md | 2 +- noir/noir-repo/docs/docs/tooling/profiler.md | 135 ++ noir/noir-repo/docs/docs/tooling/security.md | 2 +- .../profiler/acir-flamegraph-optimized.png | Bin 0 -> 90387 bytes .../profiler/acir-flamegraph-unoptimized.png | Bin 0 -> 90830 bytes .../profiler/brillig-trace-initial-32.png | Bin 0 -> 98463 bytes .../tooling/profiler/brillig-trace-opt-32.png | Bin 0 -> 114318 bytes .../gates-flamegraph-optimized-2048.png | Bin 0 -> 85893 bytes .../profiler/gates-flamegraph-optimized.png | Bin 0 -> 34504 bytes .../gates-flamegraph-unoptimized-2048.png | Bin 0 -> 75614 bytes .../profiler/gates-flamegraph-unoptimized.png | Bin 0 -> 67875 bytes .../how_to/how-to-solidity-verifier.mdx | 15 +- .../how_to/using-devcontainers.mdx | 10 +- .../noir/concepts/traits.md | 56 +- .../explainers}/cspell.json | 2 +- .../explainers/explainer-oracle.md | 57 + .../explainers/explainer-recursion.md | 176 ++ .../explainers/explainer-writing-noir.md | 208 ++ .../getting_started/noir_installation.md | 106 + .../getting_started/project_breakdown.md | 159 ++ .../getting_started/quick_start.md | 127 + .../setting_up_shell_completions.md | 87 + .../how_to/_category_.json | 5 + .../how_to/debugger/_category_.json | 6 + .../debugger/debugging_with_the_repl.md | 164 ++ .../how_to/debugger/debugging_with_vs_code.md | 68 + .../how_to/how-to-oracles.md | 275 +++ .../how_to/how-to-recursion.md | 172 ++ .../how_to/how-to-solidity-verifier.mdx | 305 +++ .../how_to/merkle-proof.mdx | 48 + .../how_to/using-devcontainers.mdx | 91 + .../version-v1.0.0-beta.3/index.mdx | 76 + .../version-v1.0.0-beta.3/migration_notes.md | 105 + .../noir/concepts/_category_.json | 6 + .../noir/concepts/assert.md | 79 + .../noir/concepts/comments.md | 33 + .../noir/concepts/comptime.md | 565 +++++ .../noir/concepts/control_flow.md | 111 + .../noir/concepts/data_bus.mdx | 23 + .../noir/concepts/data_types/_category_.json | 5 + .../noir/concepts/data_types/arrays.md | 276 +++ .../noir/concepts/data_types/booleans.md | 28 + .../noir/concepts/data_types/fields.md | 260 +++ .../concepts/data_types/function_types.md | 26 + .../noir/concepts/data_types/index.md | 126 + .../noir/concepts/data_types/integers.md | 178 ++ .../noir/concepts/data_types/references.md | 23 + .../noir/concepts/data_types/slices.mdx | 358 +++ .../noir/concepts/data_types/strings.md | 114 + .../noir/concepts/data_types/structs.md | 96 + .../noir/concepts/data_types/tuples.md | 48 + .../noir/concepts/functions.md | 226 ++ .../noir/concepts/generics.md | 262 +++ .../noir/concepts/globals.md | 82 + .../noir/concepts/lambdas.md | 81 + .../noir/concepts/mutability.md | 121 + .../noir/concepts/ops.md | 98 + .../noir/concepts/oracles.mdx | 29 + .../noir/concepts/shadowing.md | 44 + .../noir/concepts/traits.md | 611 +++++ .../noir/concepts/unconstrained.md | 104 + .../modules_packages_crates/_category_.json | 6 + .../crates_and_packages.md | 43 + .../modules_packages_crates/dependencies.md | 122 + .../noir/modules_packages_crates/modules.md | 221 ++ .../modules_packages_crates/workspaces.md | 46 + .../noir/standard_library/_category_.json | 6 + .../noir/standard_library/black_box_fns.md | 31 + .../noir/standard_library/bn254.md | 46 + .../standard_library/containers/boundedvec.md | 479 ++++ .../standard_library/containers/hashmap.md | 587 +++++ .../noir/standard_library/containers/index.md | 5 + .../noir/standard_library/containers/vec.mdx | 170 ++ .../cryptographic_primitives/_category_.json | 5 + .../cryptographic_primitives/ciphers.mdx | 36 + .../ecdsa_sig_verification.mdx | 98 + .../embedded_curve_ops.mdx | 95 + .../cryptographic_primitives/hashes.mdx | 228 ++ .../cryptographic_primitives/index.md | 14 + .../noir/standard_library/fmtstr.md | 17 + .../noir/standard_library/is_unconstrained.md | 69 + .../noir/standard_library/logging.md | 78 + .../noir/standard_library/mem.md | 82 + .../noir/standard_library/merkle_trees.md | 58 + .../noir/standard_library/meta/ctstring.md | 100 + .../noir/standard_library/meta/expr.md | 380 +++ .../standard_library/meta/function_def.md | 181 ++ .../noir/standard_library/meta/index.md | 224 ++ .../noir/standard_library/meta/module.md | 82 + .../noir/standard_library/meta/op.md | 244 ++ .../noir/standard_library/meta/quoted.md | 140 ++ .../noir/standard_library/meta/struct_def.md | 199 ++ .../standard_library/meta/trait_constraint.md | 17 + .../noir/standard_library/meta/trait_def.md | 26 + .../noir/standard_library/meta/trait_impl.md | 62 + .../noir/standard_library/meta/typ.md | 264 +++ .../noir/standard_library/meta/typed_expr.md | 27 + .../standard_library/meta/unresolved_type.md | 57 + .../noir/standard_library/options.md | 101 + .../noir/standard_library/recursion.mdx | 67 + .../noir/standard_library/traits.md | 655 ++++++ .../reference/NoirJS/noir_js/.nojekyll | 1 + .../reference/NoirJS/noir_js/classes/Noir.md | 52 + .../reference/NoirJS/noir_js/functions/and.md | 22 + .../NoirJS/noir_js/functions/blake2s256.md | 21 + .../functions/ecdsa_secp256k1_verify.md | 28 + .../functions/ecdsa_secp256r1_verify.md | 28 + .../reference/NoirJS/noir_js/functions/xor.md | 22 + .../reference/NoirJS/noir_js/index.md | 47 + .../noir_js/type-aliases/ErrorWithPayload.md | 15 + .../type-aliases/ForeignCallHandler.md | 24 + .../noir_js/type-aliases/ForeignCallInput.md | 9 + .../noir_js/type-aliases/ForeignCallOutput.md | 9 + .../NoirJS/noir_js/type-aliases/WitnessMap.md | 9 + .../NoirJS/noir_js/typedoc-sidebar.cjs | 4 + .../reference/NoirJS/noir_wasm/.nojekyll | 1 + .../NoirJS/noir_wasm/functions/compile.md | 51 + .../noir_wasm/functions/compile_contract.md | 51 + .../noir_wasm/functions/createFileManager.md | 21 + .../functions/inflateDebugSymbols.md | 21 + .../reference/NoirJS/noir_wasm/index.md | 49 + .../NoirJS/noir_wasm/typedoc-sidebar.cjs | 4 + .../reference/_category_.json | 5 + .../reference/debugger/_category_.json | 6 + .../debugger/debugger_known_limitations.md | 59 + .../reference/debugger/debugger_repl.md | 360 +++ .../reference/debugger/debugger_vscode.md | 82 + .../reference/nargo_commands.md | 580 +++++ .../reference/noir_codegen.md | 116 + .../version-v1.0.0-beta.3/tooling/cspell.json | 8 + .../version-v1.0.0-beta.3/tooling/debugger.md | 26 + .../tooling/language_server.md | 43 + .../version-v1.0.0-beta.3/tooling/profiler.md | 115 + .../version-v1.0.0-beta.3/tooling/security.md | 61 + .../version-v1.0.0-beta.3/tooling/testing.md | 79 + .../tutorials/noirjs_app.md | 304 +++ .../version-v1.0.0-beta.3-sidebars.json | 93 + .../codegen_verifier/codegen_verifier.sh | 8 +- .../prove_and_verify/prove_and_verify.sh | 6 +- .../recursion/generate_recursive_proof.sh | 24 +- noir/noir-repo/noir_stdlib/src/cmp.nr | 16 + noir/noir-repo/noir_stdlib/src/convert.nr | 23 + noir/noir-repo/noir_stdlib/src/default.nr | 6 + .../noir_stdlib/src/ecdsa_secp256k1.nr | 1 - noir/noir-repo/noir_stdlib/src/hash/keccak.nr | 155 -- noir/noir-repo/noir_stdlib/src/hash/mod.nr | 48 +- noir/noir-repo/noir_stdlib/src/hash/sha256.nr | 845 ------- noir/noir-repo/noir_stdlib/src/hash/sha512.nr | 165 -- noir/noir-repo/noir_stdlib/src/lib.nr | 41 +- noir/noir-repo/noir_stdlib/src/meta/op.nr | 1 - noir/noir-repo/noir_stdlib/src/ops/arith.nr | 26 +- noir/noir-repo/noir_stdlib/src/ops/bit.nr | 35 +- noir/noir-repo/noir_stdlib/src/prelude.nr | 1 - noir/noir-repo/noir_stdlib/src/sha256.nr | 10 - noir/noir-repo/noir_stdlib/src/sha512.nr | 5 - noir/noir-repo/noir_stdlib/src/uint128.nr | 572 ----- noir/noir-repo/rust-toolchain.toml | 4 +- ...ommit.sh => bump-aztec-packages-commit.sh} | 0 .../benchmarks/bench_sha256/Prover.toml | 1 - .../benchmarks/bench_sha256/src/main.nr | 4 - .../benchmarks/bench_sha256_100/Prover.toml | 102 - .../benchmarks/bench_sha256_100/src/main.nr | 10 - .../benchmarks/bench_sha256_30/Nargo.toml | 7 - .../benchmarks/bench_sha256_30/Prover.toml | 32 - .../benchmarks/bench_sha256_30/src/main.nr | 10 - .../benchmarks/bench_sha256_long/Prover.toml | 191 -- .../benchmarks/bench_sha256_long/src/main.nr | 7 - .../test_programs/compilation_report.sh | 2 +- .../broken_impl}/Nargo.toml | 4 +- .../compile_failure/broken_impl/src/main.nr | 2 + .../src/main.nr | 1 - .../comptime_as_field/src/main.nr | 6 - .../Nargo.toml | 0 .../comptime_as_primitive/src/main.nr | 7 + .../comptime_fmt_strings/src/main.nr | 1 - .../comptime_from_field/Nargo.toml | 6 - .../comptime_from_field/src/main.nr | 6 - .../comptime_function_definition/src/main.nr | 6 +- .../comptime_keccak/Nargo.toml | 7 - .../comptime_keccak/src/main.nr | 32 - .../comptime_traits/src/main.nr | 1 - .../src/main.nr | 1 - .../compile_success_empty/enums/src/main.nr | 39 +- .../src/main.nr | 1 - .../trait_override_implementation/src/main.nr | 1 - .../unquote_struct/src/main.nr | 15 + .../src/main.nr | 1 - .../u128_multiplication_overflow/Nargo.toml | 7 + .../u128_multiplication_overflow/Prover.toml | 2 + .../u128_multiplication_overflow/src/main.nr | 7 + .../test_programs/execution_report.sh | 2 +- .../execution_success/6/Prover.toml | 35 +- .../execution_success/6/src/main.nr | 8 +- .../array_dynamic_blackbox_input/Prover.toml | 2 +- .../array_dynamic_blackbox_input/src/main.nr | 2 +- .../Prover.toml | 4 +- .../src/main.nr | 2 +- .../execution_success/bit_and/src/main.nr | 1 - .../execution_success/bool_not/src/main.nr | 1 - .../execution_success/bool_or/src/main.nr | 1 - .../brillig_calls_conditionals/src/main.nr | 1 - .../brillig_cow_regression/src/main.nr | 4 +- .../brillig_nested_arrays/src/main.nr | 1 - .../brillig_pedersen/src/main.nr | 1 - .../execution_success/cast_bool/src/main.nr | 1 - .../conditional_1/Prover.toml | 2 +- .../conditional_1/src/main.nr | 2 +- .../conditional_regression_661/src/main.nr | 1 - .../Prover.toml | 35 +- .../src/main.nr | 4 +- .../execution_success/databus/src/main.nr | 1 - .../ecdsa_secp256k1/Prover.toml | 40 - .../ecdsa_secp256k1/src/main.nr | 12 +- .../fold_complex_outputs/src/main.nr | 1 - .../src/main.nr | 1 - .../if_else_chain/src/main.nr | 1 - .../execution_success/keccak256/Prover.toml | 35 - .../execution_success/keccak256/src/main.nr | 20 - .../nested_array_dynamic/src/main.nr | 1 - .../nested_arrays_from_brillig/src/main.nr | 1 - .../pedersen_check/src/main.nr | 1 - .../pedersen_commitment/src/main.nr | 1 - .../pedersen_hash/src/main.nr | 1 - .../ram_blowup_regression/src/main.nr | 10 +- .../reference_only_used_as_alias/src/main.nr | 1 - .../regression_4449/Prover.toml | 2 +- .../regression_4449/src/main.nr | 2 +- .../{sha256 => regression_7195}/Nargo.toml | 4 +- .../regression_7195/Prover.toml | 2 + .../regression_7195/src/main.nr | 21 + .../{sha2_byte => regression_7451}/Nargo.toml | 2 +- .../regression_7451/Prover.toml | 1 + .../regression_7451/src/main.nr | 12 + .../execution_success/sha256/Prover.toml | 38 - .../execution_success/sha256/src/main.nr | 27 - .../Nargo.toml | 7 - .../Prover.toml | 16 - .../src/main.nr | 104 - .../sha256_regression/Nargo.toml | 7 - .../sha256_regression/Prover.toml | 14 - .../sha256_regression/src/main.nr | 39 - .../sha256_var_padding_regression/Nargo.toml | 7 - .../sha256_var_padding_regression/Prover.toml | 2 - .../sha256_var_padding_regression/src/main.nr | 29 - .../sha256_var_size_regression/Nargo.toml | 7 - .../sha256_var_size_regression/Prover.toml | 3 - .../sha256_var_size_regression/src/main.nr | 17 - .../Nargo.toml | 7 - .../Prover.toml | 2 - .../src/main.nr | 9 - .../execution_success/sha2_byte/Prover.toml | 5 - .../execution_success/sha2_byte/src/main.nr | 8 - .../Nargo.toml | 3 +- .../shift_right_overflow/Prover.toml | 1 + .../shift_right_overflow/src/main.nr | 5 + .../slice_dynamic_index/src/main.nr | 1 - .../execution_success/slice_regex/src/main.nr | 1 - .../struct_fields_ordering/src/main.nr | 1 - .../execution_success/submodules/src/main.nr | 1 - .../execution_success/u128/Nargo.toml | 6 - .../execution_success/u128/Prover.toml | 7 - .../execution_success/u128/src/main.nr | 42 - .../u128_type}/Nargo.toml | 4 +- .../execution_success/u128_type/Prover.toml | 3 + .../execution_success/u128_type/src/main.nr | 22 + .../wrapping_operations/src/main.nr | 1 - noir/noir-repo/test_programs/memory_report.sh | 2 +- .../noir_test_success/bounded_vec/src/main.nr | 1 - .../brillig_overflow_checks/src/main.nr | 1 - .../comptime_blackbox/src/main.nr | 13 +- .../noir_test_success/global_eval/src/main.nr | 11 - .../noir_test_success/mock_oracle/src/main.nr | 1 - .../u128_type}/Nargo.toml | 4 +- .../noir_test_success/u128_type/src/main.nr | 49 + .../tooling/acvm_cli/src/cli/execute_cmd.rs | 9 +- .../tooling/acvm_cli/src/fs/witness.rs | 4 +- noir/noir-repo/tooling/acvm_cli/src/main.rs | 2 +- .../tooling/artifact_cli/src/bin/execute.rs | 4 +- .../artifact_cli/src/commands/execute_cmd.rs | 178 +- .../tooling/artifact_cli/src/commands/mod.rs | 1 + .../tooling/artifact_cli/src/errors.rs | 18 +- .../tooling/artifact_cli/src/execution.rs | 135 ++ .../tooling/artifact_cli/src/fs/artifact.rs | 60 +- .../tooling/artifact_cli/src/fs/inputs.rs | 2 +- .../tooling/artifact_cli/src/fs/witness.rs | 8 +- .../noir-repo/tooling/artifact_cli/src/lib.rs | 1 + .../noir-repo/tooling/debugger/src/context.rs | 20 +- noir/noir-repo/tooling/debugger/src/dap.rs | 6 +- .../tooling/debugger/src/foreign_calls.rs | 8 +- noir/noir-repo/tooling/debugger/src/lib.rs | 3 +- noir/noir-repo/tooling/debugger/src/repl.rs | 50 +- .../debugger/src/source_code_printer.rs | 40 +- .../noir-repo/tooling/debugger/tests/debug.rs | 111 + .../tooling/fuzzer/src/dictionary/mod.rs | 4 +- noir/noir-repo/tooling/fuzzer/src/lib.rs | 2 +- .../tooling/fuzzer/src/strategies/int.rs | 12 +- .../tooling/fuzzer/src/strategies/mod.rs | 2 +- .../tooling/fuzzer/src/strategies/uint.rs | 6 +- noir/noir-repo/tooling/inspector/Cargo.toml | 1 + .../tooling/inspector/src/cli/info_cmd.rs | 50 +- .../tooling/inspector/src/cli/mod.rs | 2 +- .../inspector/src/cli/print_acir_cmd.rs | 28 +- noir/noir-repo/tooling/lsp/Cargo.toml | 1 + .../lsp/src/attribute_reference_finder.rs | 6 +- noir/noir-repo/tooling/lsp/src/lib.rs | 41 +- noir/noir-repo/tooling/lsp/src/modules.rs | 7 +- .../tooling/lsp/src/notifications/mod.rs | 44 +- .../tooling/lsp/src/requests/code_action.rs | 24 +- .../code_action/fill_struct_fields.rs | 6 +- .../code_action/implement_missing_members.rs | 2 +- .../requests/code_action/import_or_qualify.rs | 4 +- .../src/requests/code_action/import_trait.rs | 6 +- .../code_action/remove_bang_from_call.rs | 4 +- .../code_action/remove_unused_import.rs | 19 +- .../lsp/src/requests/code_action/tests.rs | 6 +- .../lsp/src/requests/code_lens_request.rs | 5 +- .../tooling/lsp/src/requests/completion.rs | 81 +- .../src/requests/completion/auto_import.rs | 6 +- .../lsp/src/requests/completion/builtins.rs | 9 +- .../requests/completion/completion_items.rs | 10 +- .../lsp/src/requests/completion/kinds.rs | 2 +- .../lsp/src/requests/completion/tests.rs | 18 +- .../lsp/src/requests/document_symbol.rs | 40 +- .../lsp/src/requests/goto_declaration.rs | 4 +- .../lsp/src/requests/goto_definition.rs | 8 +- .../tooling/lsp/src/requests/hover.rs | 2 +- .../lsp/src/requests/hover/from_reference.rs | 30 +- .../lsp/src/requests/hover/from_visitor.rs | 43 +- .../tooling/lsp/src/requests/inlay_hint.rs | 52 +- .../noir-repo/tooling/lsp/src/requests/mod.rs | 22 +- .../tooling/lsp/src/requests/references.rs | 2 +- .../tooling/lsp/src/requests/rename.rs | 9 +- .../lsp/src/requests/signature_help.rs | 27 +- .../tooling/lsp/src/requests/test_run.rs | 15 +- .../tooling/lsp/src/requests/tests.rs | 15 +- .../src/trait_impl_method_stub_generator.rs | 4 +- noir/noir-repo/tooling/lsp/src/types.rs | 2 +- .../tooling/lsp/src/use_segment_positions.rs | 8 +- noir/noir-repo/tooling/lsp/src/utils.rs | 6 +- noir/noir-repo/tooling/lsp/src/with_file.rs | 1022 ++++++++ noir/noir-repo/tooling/nargo/src/errors.rs | 18 +- .../nargo/src/foreign_calls/default.rs | 8 +- .../tooling/nargo/src/foreign_calls/layers.rs | 2 +- .../tooling/nargo/src/foreign_calls/mocker.rs | 2 +- .../tooling/nargo/src/foreign_calls/print.rs | 2 +- .../tooling/nargo/src/foreign_calls/rpc.rs | 6 +- noir/noir-repo/tooling/nargo/src/lib.rs | 6 +- noir/noir-repo/tooling/nargo/src/ops/check.rs | 13 +- .../tooling/nargo/src/ops/compile.rs | 8 +- .../tooling/nargo/src/ops/execute.rs | 6 +- noir/noir-repo/tooling/nargo/src/ops/mod.rs | 2 +- .../tooling/nargo/src/ops/optimize.rs | 2 +- noir/noir-repo/tooling/nargo/src/ops/test.rs | 22 +- .../tooling/nargo/src/ops/transform.rs | 2 +- noir/noir-repo/tooling/nargo/src/workspace.rs | 2 +- noir/noir-repo/tooling/nargo_cli/Cargo.toml | 1 + .../tooling/nargo_cli/benches/criterion.rs | 56 +- .../tooling/nargo_cli/benches/iai.rs | 2 +- noir/noir-repo/tooling/nargo_cli/build.rs | 5 +- .../tooling/nargo_cli/src/cli/check_cmd.rs | 9 +- .../tooling/nargo_cli/src/cli/compile_cmd.rs | 19 +- .../tooling/nargo_cli/src/cli/dap_cmd.rs | 28 +- .../tooling/nargo_cli/src/cli/debug_cmd.rs | 45 +- .../tooling/nargo_cli/src/cli/execute_cmd.rs | 145 +- .../tooling/nargo_cli/src/cli/export_cmd.rs | 10 +- .../tooling/nargo_cli/src/cli/fmt_cmd.rs | 9 +- .../tooling/nargo_cli/src/cli/fs/inputs.rs | 42 - .../tooling/nargo_cli/src/cli/fs/mod.rs | 30 - .../tooling/nargo_cli/src/cli/fs/program.rs | 51 - .../tooling/nargo_cli/src/cli/fs/witness.rs | 22 - .../src/cli/generate_completion_script_cmd.rs | 2 +- .../tooling/nargo_cli/src/cli/info_cmd.rs | 15 +- .../tooling/nargo_cli/src/cli/init_cmd.rs | 15 +- .../tooling/nargo_cli/src/cli/mod.rs | 29 +- .../tooling/nargo_cli/src/cli/new_cmd.rs | 2 +- .../tooling/nargo_cli/src/cli/test_cmd.rs | 134 +- .../nargo_cli/src/cli/test_cmd/formatters.rs | 13 +- .../noir-repo/tooling/nargo_cli/src/errors.rs | 46 +- noir/noir-repo/tooling/nargo_cli/src/main.rs | 2 +- .../tooling/nargo_cli/tests/stdlib-props.rs | 137 +- .../tooling/nargo_cli/tests/stdlib-tests.rs | 6 +- noir/noir-repo/tooling/nargo_fmt/Cargo.toml | 1 + noir/noir-repo/tooling/nargo_fmt/build.rs | 4 +- .../noir-repo/tooling/nargo_fmt/src/chunks.rs | 8 +- .../noir-repo/tooling/nargo_fmt/src/config.rs | 2 +- .../tooling/nargo_fmt/src/formatter.rs | 9 +- .../tooling/nargo_fmt/src/formatter/alias.rs | 2 +- .../nargo_fmt/src/formatter/attribute.rs | 2 +- .../tooling/nargo_fmt/src/formatter/buffer.rs | 7 + .../src/formatter/comments_and_whitespace.rs | 9 +- .../nargo_fmt/src/formatter/doc_comments.rs | 2 +- .../tooling/nargo_fmt/src/formatter/enums.rs | 2 +- .../nargo_fmt/src/formatter/expression.rs | 33 +- .../nargo_fmt/src/formatter/function.rs | 3 +- .../nargo_fmt/src/formatter/generics.rs | 2 +- .../tooling/nargo_fmt/src/formatter/global.rs | 4 +- .../tooling/nargo_fmt/src/formatter/impls.rs | 2 +- .../tooling/nargo_fmt/src/formatter/item.rs | 20 +- .../tooling/nargo_fmt/src/formatter/lvalue.rs | 6 +- .../tooling/nargo_fmt/src/formatter/module.rs | 6 +- .../tooling/nargo_fmt/src/formatter/path.rs | 2 +- .../nargo_fmt/src/formatter/pattern.rs | 8 +- .../nargo_fmt/src/formatter/statement.rs | 13 +- .../nargo_fmt/src/formatter/structs.rs | 2 +- .../nargo_fmt/src/formatter/trait_impl.rs | 2 +- .../tooling/nargo_fmt/src/formatter/traits.rs | 24 +- .../src/formatter/type_expression.rs | 2 +- .../tooling/nargo_fmt/src/formatter/types.rs | 6 +- .../nargo_fmt/src/formatter/use_tree.rs | 6 +- .../nargo_fmt/src/formatter/use_tree_merge.rs | 10 +- .../nargo_fmt/src/formatter/visibility.rs | 2 +- .../nargo_fmt/src/formatter/where_clause.rs | 9 +- noir/noir-repo/tooling/nargo_fmt/src/lib.rs | 4 +- .../nargo_fmt/tests/expected/databus.nr | 1 - .../tooling/nargo_fmt/tests/expected/fn.nr | 1 - .../nargo_fmt/tests/expected/ignore.nr | 1 - .../tooling/nargo_fmt/tests/expected/trait.nr | 1 - .../nargo_fmt/tests/expected/trait_alias.nr | 1 - .../nargo_fmt/tests/expected/unsafe.nr | 1 - .../tooling/nargo_toml/src/errors.rs | 20 +- .../noir-repo/tooling/nargo_toml/src/flock.rs | 7 +- noir/noir-repo/tooling/nargo_toml/src/lib.rs | 17 +- .../tooling/nargo_toml/src/semver.rs | 30 +- .../tooling/noir_codegen/package.json | 2 +- noir/noir-repo/tooling/noir_js/package.json | 2 +- .../tooling/noir_js_types/package.json | 2 +- .../input_parser/json.txt | 1 + .../tooling/noirc_abi/src/arbitrary.rs | 10 +- .../noir-repo/tooling/noirc_abi/src/errors.rs | 20 +- .../noirc_abi/src/input_parser/json.rs | 10 +- .../tooling/noirc_abi/src/input_parser/mod.rs | 41 +- .../noirc_abi/src/input_parser/toml.rs | 10 +- noir/noir-repo/tooling/noirc_abi/src/lib.rs | 6 +- .../tooling/noirc_abi_wasm/Cargo.toml | 2 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- .../tooling/noirc_abi_wasm/package.json | 2 +- .../noirc_abi_wasm/src/js_witness_map.rs | 12 +- .../tooling/noirc_abi_wasm/src/lib.rs | 9 +- .../tooling/noirc_artifacts/src/contract.rs | 45 +- .../tooling/noirc_artifacts/src/debug.rs | 6 +- .../tooling/noirc_artifacts/src/lib.rs | 41 + .../tooling/noirc_artifacts/src/program.rs | 5 +- .../tooling/noirc_artifacts_info/src/lib.rs | 2 +- noir/noir-repo/tooling/profiler/Cargo.toml | 6 +- .../src/cli/execution_flamegraph_cmd.rs | 64 +- .../profiler/src/cli/gates_flamegraph_cmd.rs | 49 +- .../noir-repo/tooling/profiler/src/cli/mod.rs | 4 +- .../src/cli/opcodes_flamegraph_cmd.rs | 170 +- noir/noir-repo/tooling/profiler/src/errors.rs | 16 + .../tooling/profiler/src/flamegraph.rs | 16 +- noir/noir-repo/tooling/profiler/src/fs.rs | 42 - .../tooling/profiler/src/gates_provider.rs | 2 +- noir/noir-repo/tooling/profiler/src/main.rs | 6 +- .../tooling/profiler/src/opcode_formatter.rs | 4 +- noir/noir-repo/yarn.lock | 11 +- 761 files changed, 27300 insertions(+), 11990 deletions(-) create mode 100644 noir/noir-repo/.github/workflows/bump-aztec-packages-commit.yml create mode 100644 noir/noir-repo/acvm-repo/acir_field/benches/field_element.rs create mode 100644 noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/basic_conditional.rs create mode 100644 noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_array_gets.rs create mode 100644 noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/check_u128_mul_overflow.rs create mode 100644 noir/noir-repo/compiler/noirc_frontend/src/elaborator/options.rs create mode 100644 noir/noir-repo/compiler/noirc_frontend/src/signed_field.rs create mode 100644 noir/noir-repo/compiler/noirc_frontend/src/tests/enums.rs create mode 100644 noir/noir-repo/docs/docs/tooling/profiler.md create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/acir-flamegraph-optimized.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/acir-flamegraph-unoptimized.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/brillig-trace-initial-32.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/brillig-trace-opt-32.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/gates-flamegraph-optimized-2048.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/gates-flamegraph-optimized.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/gates-flamegraph-unoptimized-2048.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/gates-flamegraph-unoptimized.png rename noir/noir-repo/docs/{docs/tooling => versioned_docs/version-v1.0.0-beta.3/explainers}/cspell.json (55%) create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-oracle.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-recursion.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-writing-noir.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/noir_installation.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/project_breakdown.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/quick_start.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/setting_up_shell_completions.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_the_repl.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_vs_code.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-oracles.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-recursion.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-solidity-verifier.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/merkle-proof.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/using-devcontainers.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/index.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/migration_notes.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/assert.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comments.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comptime.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/control_flow.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_bus.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/arrays.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/booleans.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/fields.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/function_types.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/index.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/integers.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/references.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/slices.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/strings.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/structs.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/tuples.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/functions.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/generics.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/globals.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/lambdas.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/mutability.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/ops.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/oracles.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/shadowing.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/traits.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/unconstrained.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/crates_and_packages.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/dependencies.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/modules.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/workspaces.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/black_box_fns.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/bn254.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/boundedvec.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/hashmap.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/index.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/vec.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ciphers.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/hashes.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/index.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/fmtstr.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/is_unconstrained.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/logging.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/mem.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/merkle_trees.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/ctstring.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/expr.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/function_def.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/index.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/module.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/op.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/quoted.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/struct_def.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_constraint.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_def.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_impl.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typ.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typed_expr.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/unresolved_type.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/options.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/recursion.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/traits.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/.nojekyll create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/classes/Noir.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/and.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/blake2s256.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/xor.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/index.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/WitnessMap.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/typedoc-sidebar.cjs create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/.nojekyll create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile_contract.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/createFileManager.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/index.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_known_limitations.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_repl.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_vscode.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/nargo_commands.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/noir_codegen.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/cspell.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/debugger.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/language_server.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/profiler.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/security.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/testing.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tutorials/noirjs_app.md create mode 100644 noir/noir-repo/docs/versioned_sidebars/version-v1.0.0-beta.3-sidebars.json delete mode 100644 noir/noir-repo/noir_stdlib/src/hash/keccak.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/hash/sha256.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/hash/sha512.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/sha256.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/sha512.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/uint128.nr rename noir/noir-repo/scripts/{bump-aztec-packges-commit.sh => bump-aztec-packages-commit.sh} (100%) delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256/Prover.toml delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256/src/main.nr delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Prover.toml delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_100/src/main.nr delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Prover.toml delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_30/src/main.nr delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Prover.toml delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_long/src/main.nr rename noir/noir-repo/test_programs/{benchmarks/bench_sha256_100 => compile_failure/broken_impl}/Nargo.toml (51%) create mode 100644 noir/noir-repo/test_programs/compile_failure/broken_impl/src/main.nr delete mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_as_field/src/main.nr rename noir/noir-repo/test_programs/compile_success_empty/{comptime_as_field => comptime_as_primitive}/Nargo.toml (100%) create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_as_primitive/src/main.nr delete mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/src/main.nr delete mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/keccak256/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/keccak256/src/main.nr rename noir/noir-repo/test_programs/execution_success/{sha256 => regression_7195}/Nargo.toml (50%) create mode 100644 noir/noir-repo/test_programs/execution_success/regression_7195/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/regression_7195/src/main.nr rename noir/noir-repo/test_programs/execution_success/{sha2_byte => regression_7451}/Nargo.toml (68%) create mode 100644 noir/noir-repo/test_programs/execution_success/regression_7451/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/regression_7451/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_regression/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_regression/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_regression/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha2_byte/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha2_byte/src/main.nr rename noir/noir-repo/test_programs/execution_success/{keccak256 => shift_right_overflow}/Nargo.toml (63%) create mode 100644 noir/noir-repo/test_programs/execution_success/shift_right_overflow/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/shift_right_overflow/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/u128/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/u128/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/u128/src/main.nr rename noir/noir-repo/test_programs/{benchmarks/bench_sha256 => execution_success/u128_type}/Nargo.toml (52%) create mode 100644 noir/noir-repo/test_programs/execution_success/u128_type/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/u128_type/src/main.nr rename noir/noir-repo/test_programs/{benchmarks/bench_sha256_long => noir_test_success/u128_type}/Nargo.toml (52%) create mode 100644 noir/noir-repo/test_programs/noir_test_success/u128_type/src/main.nr create mode 100644 noir/noir-repo/tooling/artifact_cli/src/execution.rs create mode 100644 noir/noir-repo/tooling/lsp/src/with_file.rs delete mode 100644 noir/noir-repo/tooling/nargo_cli/src/cli/fs/inputs.rs delete mode 100644 noir/noir-repo/tooling/nargo_cli/src/cli/fs/mod.rs delete mode 100644 noir/noir-repo/tooling/nargo_cli/src/cli/fs/program.rs delete mode 100644 noir/noir-repo/tooling/nargo_cli/src/cli/fs/witness.rs create mode 100644 noir/noir-repo/tooling/profiler/src/errors.rs delete mode 100644 noir/noir-repo/tooling/profiler/src/fs.rs diff --git a/.noir-sync-commit b/.noir-sync-commit index 58cd9057d55a..78d7fb59fd89 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -fdfe2bf752771b9611dc71953d50423b4ae7ec44 +46e8043c19cf721acb4a9cdd0f792ea280347f41 diff --git a/noir/noir-repo/.github/benchmark_projects.yml b/noir/noir-repo/.github/benchmark_projects.yml index e1bcb080829b..b913f3a76af6 100644 --- a/noir/noir-repo/.github/benchmark_projects.yml +++ b/noir/noir-repo/.github/benchmark_projects.yml @@ -1,4 +1,4 @@ -define: &AZ_COMMIT 1350f93c3e9af8f601ca67ca3e67d0127c9767b6 +define: &AZ_COMMIT 5f141167d16f1450348a4bbe55d94a462b5b62eb projects: private-kernel-inner: repo: AztecProtocol/aztec-packages @@ -7,7 +7,7 @@ projects: num_runs: 5 compilation-timeout: 2.5 execution-timeout: 0.08 - compilation-memory-limit: 300 + compilation-memory-limit: 350 execution-memory-limit: 250 private-kernel-tail: repo: AztecProtocol/aztec-packages @@ -18,7 +18,7 @@ projects: compilation-timeout: 1.2 execution-timeout: 0.02 compilation-memory-limit: 250 - execution-memory-limit: 200 + execution-memory-limit: 230 private-kernel-reset: repo: AztecProtocol/aztec-packages ref: *AZ_COMMIT @@ -35,19 +35,19 @@ projects: path: noir-projects/noir-protocol-circuits/crates/rollup-base-private num_runs: 5 timeout: 15 - compilation-timeout: 10 - execution-timeout: 0.5 - compilation-memory-limit: 1100 - execution-memory-limit: 500 + compilation-timeout: 20 + execution-timeout: 1 + compilation-memory-limit: 1500 + execution-memory-limit: 650 rollup-base-public: repo: AztecProtocol/aztec-packages ref: *AZ_COMMIT path: noir-projects/noir-protocol-circuits/crates/rollup-base-public num_runs: 5 timeout: 15 - compilation-timeout: 8 - execution-timeout: 0.4 - compilation-memory-limit: 1000 + compilation-timeout: 15 + execution-timeout: 0.75 + compilation-memory-limit: 1300 execution-memory-limit: 500 rollup-block-root-empty: repo: AztecProtocol/aztec-packages @@ -65,17 +65,17 @@ projects: cannot_execute: true num_runs: 1 timeout: 60 - compilation-timeout: 100 - compilation-memory-limit: 7000 + compilation-timeout: 135 + compilation-memory-limit: 8000 rollup-block-root: repo: AztecProtocol/aztec-packages ref: *AZ_COMMIT path: noir-projects/noir-protocol-circuits/crates/rollup-block-root num_runs: 1 timeout: 60 - compilation-timeout: 110 + compilation-timeout: 135 execution-timeout: 40 - compilation-memory-limit: 7000 + compilation-memory-limit: 8000 execution-memory-limit: 1500 rollup-merge: repo: AztecProtocol/aztec-packages diff --git a/noir/noir-repo/.github/scripts/wasm-bindgen-install.sh b/noir/noir-repo/.github/scripts/wasm-bindgen-install.sh index 209080036934..bb48062cb43f 100755 --- a/noir/noir-repo/.github/scripts/wasm-bindgen-install.sh +++ b/noir/noir-repo/.github/scripts/wasm-bindgen-install.sh @@ -6,8 +6,8 @@ cd $(dirname "$0") ./cargo-binstall-install.sh # Install wasm-bindgen-cli. -if [ "$(wasm-bindgen --version &> /dev/null | cut -d' ' -f2)" != "0.2.86" ]; then +if [ "$(wasm-bindgen --version &> /dev/null | cut -d' ' -f2)" != "0.2.100" ]; then echo "Building wasm-bindgen..." - cargo binstall wasm-bindgen-cli@0.2.86 --force --no-confirm + cargo binstall wasm-bindgen-cli@0.2.100 --force --no-confirm fi diff --git a/noir/noir-repo/.github/workflows/bump-aztec-packages-commit.yml b/noir/noir-repo/.github/workflows/bump-aztec-packages-commit.yml new file mode 100644 index 000000000000..6b7c1a80a0dd --- /dev/null +++ b/noir/noir-repo/.github/workflows/bump-aztec-packages-commit.yml @@ -0,0 +1,50 @@ +name: Bump external repos pinned commits + +on: + workflow_dispatch: + schedule: + # Trigger at 8am on Mondays + - cron: '0 8 * * 1' + + +jobs: + bump-commit: + name: Update external repo pinned commits + runs-on: ubuntu-22.04 + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + ref: master + + - name: Check for existing PR + id: pr-check + run: | + set -xue # print commands + PR_URL=$(gh pr list --repo noir-lang/noir --head bump-aztec-packages --json url --jq ".[0].url") + echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT + env: + GH_TOKEN: ${{ github.token }} + + - name: Configure git + run: | + git config user.name noirwhal + git config user.email tomfrench@aztecprotocol.com + + - name: Update commit + run: | + git checkout bump-aztec-packages || git checkout -b bump-aztec-packages + ./scripts/bump-aztec-packages-commit.sh + git add . + git commit -m 'chore: Update pinned commit of aztec-packages' + git push --set-upstream origin bump-aztec-packages --force + + - name: Create PR + if: ${{ steps.pr-check.outputs.pr_url == '' }} + run: | + PR_BODY=""" + Automated update of the pinned commit of [aztec-packages](https://github.com/AztecProtocol/aztec-packages) repository against which we run benchmarks. + """ + gh pr create --repo noir-lang/noir --title "chore: bump external pinned commits" --body "$PR_BODY" --base master --head bump-aztec-packages + env: + GH_TOKEN: ${{ secrets.NOIR_REPO_TOKEN }} diff --git a/noir/noir-repo/.github/workflows/docs-pr.yml b/noir/noir-repo/.github/workflows/docs-pr.yml index c123def6ba39..d60bc8b841eb 100644 --- a/noir/noir-repo/.github/workflows/docs-pr.yml +++ b/noir/noir-repo/.github/workflows/docs-pr.yml @@ -33,7 +33,7 @@ jobs: // Check if any file is within the 'docs' folder const docsChanged = files.some(file => file.filename.startsWith('docs/')); return docsChanged; - + - name: Add label if not present if: steps.check-labels.outputs.result == 'true' uses: actions/github-script@v7.0.1 @@ -57,7 +57,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -71,7 +71,7 @@ jobs: - name: Install wasm-bindgen-cli uses: taiki-e/install-action@v2 with: - tool: wasm-bindgen-cli@0.2.86 + tool: wasm-bindgen-cli@0.2.100 - name: Install wasm-opt run: | @@ -102,13 +102,13 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - + - name: Download built docs uses: actions/download-artifact@v4 with: name: docs path: ./docs/build - + - name: Deploy to Netlify uses: nwtgck/actions-netlify@v2.1 with: diff --git a/noir/noir-repo/.github/workflows/formatting.yml b/noir/noir-repo/.github/workflows/formatting.yml index 34216c22e01a..007fd89b0aeb 100644 --- a/noir/noir-repo/.github/workflows/formatting.yml +++ b/noir/noir-repo/.github/workflows/formatting.yml @@ -25,7 +25,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: x86_64-unknown-linux-gnu components: clippy, rustfmt @@ -51,7 +51,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: x86_64-unknown-linux-gnu components: clippy, rustfmt @@ -89,7 +89,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -121,7 +121,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - + - name: Download nargo binary uses: ./.github/actions/download-nargo diff --git a/noir/noir-repo/.github/workflows/publish-acvm.yml b/noir/noir-repo/.github/workflows/publish-acvm.yml index 27d927a67d13..60e523777e80 100644 --- a/noir/noir-repo/.github/workflows/publish-acvm.yml +++ b/noir/noir-repo/.github/workflows/publish-acvm.yml @@ -18,7 +18,7 @@ jobs: ref: ${{ inputs.noir-ref }} - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 # These steps are in a specific order so crate dependencies are updated first - name: Publish acir_field diff --git a/noir/noir-repo/.github/workflows/publish-docs.yml b/noir/noir-repo/.github/workflows/publish-docs.yml index 16959256d2aa..f949576900f2 100644 --- a/noir/noir-repo/.github/workflows/publish-docs.yml +++ b/noir/noir-repo/.github/workflows/publish-docs.yml @@ -22,12 +22,12 @@ jobs: - name: Install wasm-bindgen-cli uses: taiki-e/install-action@v2 with: - tool: wasm-bindgen-cli@0.2.86 + tool: wasm-bindgen-cli@0.2.100 - name: Install wasm-opt run: | npm i wasm-opt -g - + - name: Query active docs versions run: yarn workspace docs version::stables diff --git a/noir/noir-repo/.github/workflows/publish-es-packages.yml b/noir/noir-repo/.github/workflows/publish-es-packages.yml index 76c6fce6d5e9..8186213effda 100644 --- a/noir/noir-repo/.github/workflows/publish-es-packages.yml +++ b/noir/noir-repo/.github/workflows/publish-es-packages.yml @@ -22,9 +22,9 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ inputs.noir-ref }} - + - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -44,7 +44,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: noirc_abi_wasm - path: | + path: | ./tooling/noirc_abi_wasm/nodejs ./tooling/noirc_abi_wasm/web retention-days: 10 @@ -58,7 +58,7 @@ jobs: ref: ${{ inputs.noir-ref }} - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -95,7 +95,7 @@ jobs: ref: ${{ inputs.noir-ref }} - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -119,7 +119,7 @@ jobs: ./acvm-repo/acvm_js/nodejs ./acvm-repo/acvm_js/web retention-days: 3 - + publish-es-packages: runs-on: ubuntu-22.04 needs: [build-acvm_js, build-noirc_abi_wasm, build-noir_wasm] @@ -133,12 +133,12 @@ jobs: with: name: acvm-js path: acvm-repo/acvm_js - + - uses: actions/download-artifact@v4 with: name: noir_wasm path: compiler/wasm - + - uses: actions/download-artifact@v4 with: name: noirc_abi_wasm diff --git a/noir/noir-repo/.github/workflows/publish-nargo.yml b/noir/noir-repo/.github/workflows/publish-nargo.yml index d7d9c1ea03eb..e18dac52ca48 100644 --- a/noir/noir-repo/.github/workflows/publish-nargo.yml +++ b/noir/noir-repo/.github/workflows/publish-nargo.yml @@ -38,15 +38,8 @@ jobs: with: ref: ${{ inputs.tag || env.GITHUB_REF }} - - name: Setup for Apple Silicon - if: matrix.target == 'aarch64-apple-darwin' - run: | - sudo xcode-select -s /Applications/Xcode_15.4.0.app/Contents/Developer/ - echo "SDKROOT=$(xcrun -sdk macosx$(sw_vers -productVersion) --show-sdk-path)" >> $GITHUB_ENV - echo "MACOSX_DEPLOYMENT_TARGET=$(xcrun -sdk macosx$(sw_vers -productVersion) --show-sdk-platform-version)" >> $GITHUB_ENV - - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: ${{ matrix.target }} @@ -59,21 +52,28 @@ jobs: - name: Build environment and Compile run: | cargo build --package nargo_cli --release --target ${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" + cargo build --package noir_profiler --release --target ${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" + cargo build --package noir_inspector --release --target ${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" - name: Package artifacts run: | mkdir dist cp ./target/${{ matrix.target }}/release/nargo ./dist/nargo - 7z a -ttar -so -an ./dist/* | 7z a -si ./nargo-${{ matrix.target }}.tar.gz + cp ./target/${{ matrix.target }}/release/noir-profiler ./dist/noir-profiler + cp ./target/${{ matrix.target }}/release/noir-inspector ./dist/noir-inspector + + # TODO(https://github.com/noir-lang/noir/issues/7445): Remove the separate nargo binary + 7z a -ttar -so -an ./dist/nargo | 7z a -si ./nargo-${{ matrix.target }}.tar.gz + 7z a -ttar -so -an ./dist/* | 7z a -si ./noir-${{ matrix.target }}.tar.gz - - name: Upload artifact + - name: Upload Noir binaries artifact uses: actions/upload-artifact@v4 with: - name: nargo-${{ matrix.target }} + name: noir-${{ matrix.target }} path: ./dist/* retention-days: 3 - - name: Upload binaries to release tag + - name: Upload nargo binary to release tag uses: svenstaro/upload-release-action@v2 if: ${{ inputs.publish || github.event_name == 'schedule' }} with: @@ -84,12 +84,23 @@ jobs: overwrite: true tag: ${{ inputs.tag || 'nightly' }} # This will fail if `inputs.tag` is not a tag (e.g. testing a branch) + - name: Upload Noir binaries to release tag + uses: svenstaro/upload-release-action@v2 + if: ${{ inputs.publish || github.event_name == 'schedule' }} + with: + repo_name: noir-lang/noir + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ./noir-${{ matrix.target }}.tar.gz + asset_name: noir-${{ matrix.target }}.tar.gz + overwrite: true + tag: ${{ inputs.tag || 'nightly' }} # This will fail if `inputs.tag` is not a tag (e.g. testing a branch) + - name: Get formatted date id: date if: ${{ inputs.tag == '' && inputs.publish || github.event_name == 'schedule' }} run: echo "date=$(date '+%Y-%m-%d')" >> $GITHUB_OUTPUT - - name: Upload binaries to release with date tag + - name: Upload nargo binary to release with date tag uses: svenstaro/upload-release-action@v2 if: ${{ inputs.tag == '' && inputs.publish || github.event_name == 'schedule' }} with: @@ -102,6 +113,19 @@ jobs: overwrite: true tag: ${{ format('{0}-{1}', 'nightly', steps.date.outputs.date) }} + - name: Upload Noir binaries to release with date tag + uses: svenstaro/upload-release-action@v2 + if: ${{ inputs.tag == '' && inputs.publish || github.event_name == 'schedule' }} + with: + repo_name: noir-lang/noir + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ./noir-${{ matrix.target }}.tar.gz + asset_name: noir-${{ matrix.target }}.tar.gz + prerelease: true + make_latest: false + overwrite: true + tag: ${{ format('{0}-{1}', 'nightly', steps.date.outputs.date) }} + build-linux: runs-on: ubuntu-22.04 env: @@ -120,7 +144,7 @@ jobs: ref: ${{ inputs.tag || env.GITHUB_REF }} - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: ${{ matrix.target }} @@ -135,23 +159,31 @@ jobs: with: tool: cross@0.2.5 - - name: Build Nargo - run: cross build --package nargo_cli --release --target=${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" + - name: Build binaries + run: | + cross build --package nargo_cli --release --target=${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" + cross build --package noir_profiler --release --target=${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" + cross build --package noir_inspector --release --target ${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" - name: Package artifacts run: | mkdir dist cp ./target/${{ matrix.target }}/release/nargo ./dist/nargo - 7z a -ttar -so -an ./dist/* | 7z a -si ./nargo-${{ matrix.target }}.tar.gz + cp ./target/${{ matrix.target }}/release/noir-profiler ./dist/noir-profiler + cp ./target/${{ matrix.target }}/release/noir-inspector ./dist/noir-inspector - - name: Upload artifact + # TODO(https://github.com/noir-lang/noir/issues/7445): Remove the separate nargo binary + tar -czf nargo-${{ matrix.target }}.tar.gz -C dist nargo + tar -czf noir-${{ matrix.target }}.tar.gz -C dist . + + - name: Upload Noir binaries artifact uses: actions/upload-artifact@v4 with: - name: nargo-${{ matrix.target }} + name: noir-${{ matrix.target }} path: ./dist/* retention-days: 3 - - name: Upload binaries to release tag + - name: Upload nargo binary to release tag uses: svenstaro/upload-release-action@v2 if: ${{ inputs.publish }} with: @@ -163,12 +195,24 @@ jobs: overwrite: true tag: ${{ inputs.tag || 'nightly' }} # This will fail if `inputs.tag` is not a tag (e.g. testing a branch) + - name: Upload Noir binaries to release tag + uses: svenstaro/upload-release-action@v2 + if: ${{ inputs.publish }} + with: + repo_name: noir-lang/noir + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ./noir-${{ matrix.target }}.tar.gz + asset_name: noir-${{ matrix.target }}.tar.gz + prerelease: true + overwrite: true + tag: ${{ inputs.tag || 'nightly' }} # This will fail if `inputs.tag` is not a tag (e.g. testing a branch) + - name: Get formatted date id: date if: ${{ env.NIGHTLY_RELEASE && inputs.publish }} run: echo "date=$(date '+%Y-%m-%d')" >> $GITHUB_OUTPUT - - name: Upload binaries to release with date tag + - name: Upload nargo binary to release with date tag uses: svenstaro/upload-release-action@v2 if: ${{ env.NIGHTLY_RELEASE && inputs.publish }} with: @@ -181,4 +225,17 @@ jobs: overwrite: true tag: ${{ format('{0}-{1}', 'nightly', steps.date.outputs.date) }} - + - name: Upload Noir binaries to release with date tag + uses: svenstaro/upload-release-action@v2 + if: ${{ env.NIGHTLY_RELEASE && inputs.publish }} + with: + repo_name: noir-lang/noir + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ./noir-${{ matrix.target }}.tar.gz + asset_name: noir-${{ matrix.target }}.tar.gz + prerelease: true + make_latest: false + overwrite: true + tag: ${{ format('{0}-{1}', 'nightly', steps.date.outputs.date) }} + + diff --git a/noir/noir-repo/.github/workflows/release.yml b/noir/noir-repo/.github/workflows/release.yml index bbe9a7fff620..ea1f1eba6197 100644 --- a/noir/noir-repo/.github/workflows/release.yml +++ b/noir/noir-repo/.github/workflows/release.yml @@ -47,10 +47,10 @@ jobs: node-version: 18.19.0 cache: 'yarn' cache-dependency-path: 'yarn.lock' - + - name: Update yarn.lock run: yarn - + - name: Configure git run: | git config user.name noirwhal @@ -61,13 +61,13 @@ jobs: git add . git commit -m 'chore: Update root workspace acvm versions and lockfile' git push - + update-docs: name: Update docs needs: [release-please, update-acvm-workspace-package-versions] if: ${{ needs.release-please.outputs.release-pr }} runs-on: ubuntu-22.04 - + steps: - name: Checkout release branch uses: actions/checkout@v4 @@ -81,7 +81,7 @@ jobs: - name: Install wasm-bindgen-cli uses: taiki-e/install-action@v2 with: - tool: wasm-bindgen-cli@0.2.86 + tool: wasm-bindgen-cli@0.2.100 - name: Install wasm-opt run: | @@ -113,7 +113,7 @@ jobs: runs-on: ubuntu-22.04 # We want this job to always run (even if the dependant jobs fail) as we need apply changes to the sticky comment. if: ${{ always() }} - + needs: - release-please - update-acvm-workspace-package-versions @@ -123,7 +123,7 @@ jobs: # We treat any skipped or failing jobs as a failure for the workflow as a whole. FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }} - steps: + steps: - name: Add warning to sticky comment uses: marocchino/sticky-pull-request-comment@v2 with: @@ -175,7 +175,7 @@ jobs: needs: [release-please] if: ${{ needs.release-please.outputs.tag-name }} runs-on: ubuntu-22.04 - + steps: - name: Dispatch to publish-acvm uses: benc-uk/workflow-dispatch@v1 diff --git a/noir/noir-repo/.github/workflows/reports.yml b/noir/noir-repo/.github/workflows/reports.yml index 37f313259ad3..d458f8998f00 100644 --- a/noir/noir-repo/.github/workflows/reports.yml +++ b/noir/noir-repo/.github/workflows/reports.yml @@ -32,7 +32,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -417,9 +417,6 @@ jobs: - name: Download nargo binary uses: ./scripts/.github/actions/download-nargo - - name: Download nargo binary - uses: ./scripts/.github/actions/download-nargo - - name: Checkout uses: actions/checkout@v4 with: diff --git a/noir/noir-repo/.github/workflows/test-js-packages.yml b/noir/noir-repo/.github/workflows/test-js-packages.yml index 0af0e41d46ae..f88e64fa1a5a 100644 --- a/noir/noir-repo/.github/workflows/test-js-packages.yml +++ b/noir/noir-repo/.github/workflows/test-js-packages.yml @@ -54,7 +54,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -87,7 +87,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -119,7 +119,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -154,7 +154,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: diff --git a/noir/noir-repo/.github/workflows/test-rust-workspace-msrv.yml b/noir/noir-repo/.github/workflows/test-rust-workspace-msrv.yml index 38bc3cba153e..8f061bcad64e 100644 --- a/noir/noir-repo/.github/workflows/test-rust-workspace-msrv.yml +++ b/noir/noir-repo/.github/workflows/test-rust-workspace-msrv.yml @@ -3,7 +3,7 @@ name: Test (MSRV check) # TL;DR https://github.com/noir-lang/noir/issues/4384 # # This workflow acts to ensure that we can publish to crates.io, we need this extra check as libraries don't respect the Cargo.lock file committed in this repository. -# We must then always be able to build the workspace using the latest versions of all of our dependencies, so we explicitly update them and build in this workflow. +# We must then always be able to build the workspace using the latest versions of all of our dependencies, so we explicitly update them and build in this workflow. on: schedule: @@ -29,12 +29,12 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: x86_64-unknown-linux-gnu # We force the ACVM crate and all of its dependencies to update their dependencies - # This ensures that we'll be able to build the crates when they're being published. + # This ensures that we'll be able to build the crates when they're being published. - name: Update Cargo.lock run: | cargo update --package acvm --aggressive @@ -53,7 +53,7 @@ jobs: - name: Build and archive tests run: cargo nextest archive --workspace --archive-file nextest-archive.tar.zst - + - name: Upload archive to workflow uses: actions/upload-artifact@v4 with: @@ -70,9 +70,9 @@ jobs: partition: [1, 2, 3, 4] steps: - uses: actions/checkout@v4 - + - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: x86_64-unknown-linux-gnu @@ -80,7 +80,7 @@ jobs: uses: taiki-e/install-action@v2 with: tool: nextest@0.9.67 - + - name: Download archive uses: actions/download-artifact@v4 with: @@ -99,9 +99,9 @@ jobs: runs-on: ubuntu-22.04 # We want this job to always run (even if the dependant jobs fail) as we want this job to fail rather than skipping. if: ${{ always() }} - needs: + needs: - run-tests - + steps: - name: Report overall success run: | @@ -113,7 +113,7 @@ jobs: env: # We treat any cancelled, skipped or failing jobs as a failure for the workflow as a whole. FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'skipped') }} - + - name: Checkout if: ${{ failure() }} uses: actions/checkout@v4 diff --git a/noir/noir-repo/.github/workflows/test-rust-workspace.yml b/noir/noir-repo/.github/workflows/test-rust-workspace.yml index fe4213610723..91809a98a26a 100644 --- a/noir/noir-repo/.github/workflows/test-rust-workspace.yml +++ b/noir/noir-repo/.github/workflows/test-rust-workspace.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: x86_64-unknown-linux-gnu @@ -40,7 +40,7 @@ jobs: - name: Build and archive tests run: cargo nextest archive --workspace --archive-file nextest-archive.tar.zst - + - name: Upload archive to workflow uses: actions/upload-artifact@v4 with: @@ -57,9 +57,9 @@ jobs: partition: [1, 2, 3, 4] steps: - uses: actions/checkout@v4 - + - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: x86_64-unknown-linux-gnu @@ -67,7 +67,7 @@ jobs: uses: taiki-e/install-action@v2 with: tool: nextest@0.9.67 - + - name: Download archive uses: actions/download-artifact@v4 with: @@ -86,9 +86,9 @@ jobs: runs-on: ubuntu-22.04 # We want this job to always run (even if the dependant jobs fail) as we want this job to fail rather than skipping. if: ${{ always() }} - needs: + needs: - run-tests - + steps: - name: Report overall success run: | diff --git a/noir/noir-repo/.gitignore b/noir/noir-repo/.gitignore index 49d9e2dbb2ff..334901860d91 100644 --- a/noir/noir-repo/.gitignore +++ b/noir/noir-repo/.gitignore @@ -52,4 +52,10 @@ tooling/noir_js/lib # docs autogen build /docs/docs/noir_js/reference/ -codegen \ No newline at end of file +codegen + +**/cspell.json +!./cspell.json + +mutants.out +mutants.out.old diff --git a/noir/noir-repo/.release-please-manifest.json b/noir/noir-repo/.release-please-manifest.json index e3112f8e85e1..52d52e73e5a8 100644 --- a/noir/noir-repo/.release-please-manifest.json +++ b/noir/noir-repo/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.0.0-beta.2" + ".": "1.0.0-beta.3" } diff --git a/noir/noir-repo/.vscode/settings.json b/noir/noir-repo/.vscode/settings.json index cd1c5f886dfa..7568da6a1af5 100644 --- a/noir/noir-repo/.vscode/settings.json +++ b/noir/noir-repo/.vscode/settings.json @@ -8,5 +8,11 @@ "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, + "[markdown]": { + "files.trimTrailingWhitespace": true + }, + "[yaml]": { + "files.trimTrailingWhitespace": true + }, "rust-analyzer.server.extraEnv": { "RUSTUP_TOOLCHAIN": "stable" } } diff --git a/noir/noir-repo/CHANGELOG.md b/noir/noir-repo/CHANGELOG.md index 31d87763a150..0c6662010c2a 100644 --- a/noir/noir-repo/CHANGELOG.md +++ b/noir/noir-repo/CHANGELOG.md @@ -1,5 +1,62 @@ # Changelog +## [1.0.0-beta.3](https://github.com/noir-lang/noir/compare/v1.0.0-beta.2...v1.0.0-beta.3) (2025-02-20) + + +### ⚠ BREAKING CHANGES + +* make `ResolverError::OracleMarkedAsConstrained` into a full error ([#7426](https://github.com/noir-lang/noir/issues/7426)) +* remove bigint from stdlib ([#7411](https://github.com/noir-lang/noir/issues/7411)) +* Only decrement the counter of an array if its address has not changed ([#7297](https://github.com/noir-lang/noir/issues/7297)) + +### Features + +* `FunctionDefinition::as_typed_expr` ([#7358](https://github.com/noir-lang/noir/issues/7358)) ([97afa52](https://github.com/noir-lang/noir/commit/97afa52f5212be2d05af26b9e8dde9c3ea7a1d2e)) +* **acir_field:** Add little-endian byte serialization for FieldElement ([#7258](https://github.com/noir-lang/noir/issues/7258)) ([f37eedc](https://github.com/noir-lang/noir/commit/f37eedca7f286187c71587797e08da189c4eee70)) +* Add native `u128` type ([#7301](https://github.com/noir-lang/noir/issues/7301)) ([8783e48](https://github.com/noir-lang/noir/commit/8783e480a39efb47c7783c68b0e09c1c65b1f33a)) +* Allow unquoting TraitConstraint in trait impl position ([#7395](https://github.com/noir-lang/noir/issues/7395)) ([40d2763](https://github.com/noir-lang/noir/commit/40d276328e568712fcb742c1c2ecdd975e5123b6)) +* **brillig:** Hoist shared constants across functions to the global space ([#7216](https://github.com/noir-lang/noir/issues/7216)) ([8652072](https://github.com/noir-lang/noir/commit/8652072920b3abc5990be5d57db27a7e66221252)) +* **ci:** Publish binaries for noir-profiler ([#7443](https://github.com/noir-lang/noir/issues/7443)) ([af5e4cd](https://github.com/noir-lang/noir/commit/af5e4cd1f200da6f21c543616dea9c188190d833)) +* **ci:** Release noir-inspector in binaries ([#7464](https://github.com/noir-lang/noir/issues/7464)) ([7a9c8c1](https://github.com/noir-lang/noir/commit/7a9c8c13f626575f23d25f9cb8689e44dc1c6116)) +* **cli:** Add `--target-dir` option ([#7350](https://github.com/noir-lang/noir/issues/7350)) ([1b6ba5d](https://github.com/noir-lang/noir/commit/1b6ba5d960239f8fa934d9543699eb86edd3c43b)) +* **cli:** Add noir-execute binary ([#7384](https://github.com/noir-lang/noir/issues/7384)) ([fdfe2bf](https://github.com/noir-lang/noir/commit/fdfe2bf752771b9611dc71953d50423b4ae7ec44)) +* **experimental:** Compile match expressions ([#7312](https://github.com/noir-lang/noir/issues/7312)) ([4c3dee1](https://github.com/noir-lang/noir/commit/4c3dee165f400124ba727a884455d83948f2006e)) +* **experimental:** Show macro errors where they happen ([#7333](https://github.com/noir-lang/noir/issues/7333)) ([04dd6d9](https://github.com/noir-lang/noir/commit/04dd6d9cdc906210e77cf755c711d45d04cb29aa)) +* LSP hover for integer literals ([#7368](https://github.com/noir-lang/noir/issues/7368)) ([967ab5f](https://github.com/noir-lang/noir/commit/967ab5f6e23b7e9f4503fe01d05b8a7a94ca70f6)) +* **LSP:** Auto-import via visible reexport ([#7409](https://github.com/noir-lang/noir/issues/7409)) ([9b03217](https://github.com/noir-lang/noir/commit/9b03217ff4b7f8eb13a43466bf9b2d761944c6f7)) +* Optimize FieldElement::num_bits ([#7147](https://github.com/noir-lang/noir/issues/7147)) ([44c35dc](https://github.com/noir-lang/noir/commit/44c35dc3ed5cceb34131bcb90622b0e3e0156b9c)) +* **performance:** Check sub operations against induction variables ([#7356](https://github.com/noir-lang/noir/issues/7356)) ([7cdce1f](https://github.com/noir-lang/noir/commit/7cdce1fef7e0fd63355fe6dc0993415bbb210ebf)) +* **performance:** Use unchecked ops based upon known induction variables ([#7344](https://github.com/noir-lang/noir/issues/7344)) ([10b377f](https://github.com/noir-lang/noir/commit/10b377fb4eb9284df66f5c0bd830f6d20ab2c003)) +* Remove bigint from stdlib ([#7411](https://github.com/noir-lang/noir/issues/7411)) ([31cc6a1](https://github.com/noir-lang/noir/commit/31cc6a1cf9ea0a02931ef60c71d4d41524f8b84c)) +* Require safety comments instead of safety doc comments ([#7295](https://github.com/noir-lang/noir/issues/7295)) ([e895feb](https://github.com/noir-lang/noir/commit/e895feb4e7b25530a22668bca597dfc78be92584)) +* Simplify assertions that squared values are equal to zero ([#7432](https://github.com/noir-lang/noir/issues/7432)) ([5d19109](https://github.com/noir-lang/noir/commit/5d19109b6a5738a97b745c6117cf0a1e9f1552bb)) +* While statement ([#7280](https://github.com/noir-lang/noir/issues/7280)) ([582f56e](https://github.com/noir-lang/noir/commit/582f56e6b6ea43ab79b08aacfe7f1ba67a097f26)) + + +### Bug Fixes + +* **brillig:** Brillig entry point analysis and function specialization through duplication ([#7277](https://github.com/noir-lang/noir/issues/7277)) ([119bf62](https://github.com/noir-lang/noir/commit/119bf620005b52362e3aca9321b69e96e8a42fc0)) +* **cli:** Only lock the packages selected in the workspace ([#7345](https://github.com/noir-lang/noir/issues/7345)) ([f0ce5c5](https://github.com/noir-lang/noir/commit/f0ce5c5a57bc4cd8b3b482a3b682e8d5c2605d5c)) +* Do not discard negative sign from field literals in comptime interpreter ([#7439](https://github.com/noir-lang/noir/issues/7439)) ([1d04f8b](https://github.com/noir-lang/noir/commit/1d04f8ba0292e493e4e64cf8caf6df15c51b3346)) +* Don't let nargo fmt produce multiple trailing newlines ([#7444](https://github.com/noir-lang/noir/issues/7444)) ([093a8ec](https://github.com/noir-lang/noir/commit/093a8ec60eaacd1b307447545e166a40e037436d)) +* Field zero division in brillig ([#7386](https://github.com/noir-lang/noir/issues/7386)) ([e73f8cd](https://github.com/noir-lang/noir/commit/e73f8cd669c13cdb792313b46dd4aa012c40a0ad)) +* Format global attributes ([#7401](https://github.com/noir-lang/noir/issues/7401)) ([b7ace68](https://github.com/noir-lang/noir/commit/b7ace682af1ab8a43308457302f08b151af342db)) +* Give "correct" error when trying to use AsTraitPath ([#7360](https://github.com/noir-lang/noir/issues/7360)) ([8f20392](https://github.com/noir-lang/noir/commit/8f20392cab7cca4abf0f1811204ce1a4229f827a)) +* Incorrect secondary file in LSP errors ([#7347](https://github.com/noir-lang/noir/issues/7347)) ([5d782f0](https://github.com/noir-lang/noir/commit/5d782f020f6aec6aaa8a445c3a6a5fb9b275e3c6)) +* Let LSP read `noirfmt.toml` for formatting files ([#7355](https://github.com/noir-lang/noir/issues/7355)) ([81b86e2](https://github.com/noir-lang/noir/commit/81b86e2a9bfe991bc0385118094656648a125587)) +* Only decrement the counter of an array if its address has not changed ([#7297](https://github.com/noir-lang/noir/issues/7297)) ([93d1740](https://github.com/noir-lang/noir/commit/93d17407f7170abbab7a6e9c8df6b39fb478ec18)) +* **performance:** Remove redundant slice access check from brillig ([#7434](https://github.com/noir-lang/noir/issues/7434)) ([49a095d](https://github.com/noir-lang/noir/commit/49a095ded5cd33795bcdac60cbd98ce7c5ab9198)) +* Prevent incorrect ACIRgen caused by noop truncations ([#7456](https://github.com/noir-lang/noir/issues/7456)) ([1fa9b33](https://github.com/noir-lang/noir/commit/1fa9b33aab796dbc2a61f3062bf80e120831b462)) +* Require loop/for/while body to be unit ([#7437](https://github.com/noir-lang/noir/issues/7437)) ([13a7309](https://github.com/noir-lang/noir/commit/13a7309e6e2aec58cce3e12d4dc5f9ce8eb08a67)) +* **ssa:** Accurately mark binary ops for hoisting and check Div/Mod against induction variable lower bound ([#7396](https://github.com/noir-lang/noir/issues/7396)) ([64890c0](https://github.com/noir-lang/noir/commit/64890c0d7420adb32d3867e51dd194e48b87bb32)) +* **ssa:** Do not deduplicate division by a zero constant ([#7393](https://github.com/noir-lang/noir/issues/7393)) ([38eeee3](https://github.com/noir-lang/noir/commit/38eeee39a98a62747dcca3b31b409151761d4ef1)) +* **ssa:** Make the lookback feature opt-in ([#7190](https://github.com/noir-lang/noir/issues/7190)) ([31becc6](https://github.com/noir-lang/noir/commit/31becc6863688dc9cadf15d2e9726aab9f2a0150)) + + +### Miscellaneous Chores + +* Make `ResolverError::OracleMarkedAsConstrained` into a full error ([#7426](https://github.com/noir-lang/noir/issues/7426)) ([40184eb](https://github.com/noir-lang/noir/commit/40184eb75d69153fb7849700ad10c53bf19cacf3)) + ## [1.0.0-beta.2](https://github.com/noir-lang/noir/compare/v1.0.0-beta.1...v1.0.0-beta.2) (2025-02-10) diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index 8b6912452f04..48474cc79b1a 100644 --- a/noir/noir-repo/Cargo.lock +++ b/noir/noir-repo/Cargo.lock @@ -1,10 +1,10 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "acir" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir_field", "base64 0.21.7", @@ -26,12 +26,14 @@ dependencies = [ [[package]] name = "acir_field" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "ark-bls12-381", "ark-bn254 0.5.0", "ark-ff 0.5.0", + "ark-std 0.5.0", "cfg-if", + "criterion", "hex", "num-bigint", "proptest", @@ -40,7 +42,7 @@ dependencies = [ [[package]] name = "acvm" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "acvm_blackbox_solver", @@ -63,7 +65,7 @@ dependencies = [ [[package]] name = "acvm_blackbox_solver" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "blake2", @@ -101,7 +103,7 @@ dependencies = [ [[package]] name = "acvm_js" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "bn254_blackbox_solver", @@ -251,9 +253,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" [[package]] name = "ark-bls12-381" @@ -730,15 +732,16 @@ dependencies = [ [[package]] name = "blake3" -version = "1.5.5" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e" +checksum = "1230237285e3e10cde447185e8975408ae24deaa67205ce684805c25bc0c7937" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", + "memmap2", ] [[package]] @@ -765,7 +768,7 @@ dependencies = [ [[package]] name = "bn254_blackbox_solver" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "acvm_blackbox_solver", @@ -783,7 +786,7 @@ dependencies = [ [[package]] name = "brillig" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir_field", "serde", @@ -791,7 +794,7 @@ dependencies = [ [[package]] name = "brillig_vm" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "acvm_blackbox_solver", @@ -854,9 +857,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.13" +version = "1.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" +checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" dependencies = [ "shlex", ] @@ -875,9 +878,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.39" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" dependencies = [ "android-tzdata", "iana-time-zone", @@ -885,7 +888,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -917,9 +920,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.28" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff" +checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" dependencies = [ "clap_builder", "clap_derive", @@ -935,9 +938,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.27" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" +checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" dependencies = [ "anstream", "anstyle", @@ -947,9 +950,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.44" +version = "4.5.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375f9d8255adeeedd51053574fd8d4ba875ea5fa558e86617b07f09f1680c8b6" +checksum = "f5c5508ea23c5366f77e53f5a0070e5a84e51687ec3ef9e0464c86dc8d13ce98" dependencies = [ "clap", ] @@ -1288,9 +1291,9 @@ dependencies = [ [[package]] name = "csv-core" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" dependencies = [ "memchr", ] @@ -1511,9 +1514,9 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" [[package]] name = "elliptic-curve" @@ -1608,9 +1611,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" @@ -1731,12 +1734,12 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.35" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" dependencies = [ "crc32fast", - "miniz_oxide 0.8.3", + "miniz_oxide 0.8.5", ] [[package]] @@ -1750,7 +1753,7 @@ dependencies = [ [[package]] name = "fm" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "codespan-reporting", "iter-extended", @@ -1998,9 +2001,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" dependencies = [ "atomic-waker", "bytes", @@ -2554,7 +2557,7 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "iter-extended" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" [[package]] name = "itertools" @@ -2602,10 +2605,11 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.63" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -2796,9 +2800,9 @@ checksum = "82903360c009b816f5ab72a9b68158c27c301ee2c3f20655b55c5e589e7d3bb7" [[package]] name = "libc" -version = "0.2.169" +version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" [[package]] name = "libredox" @@ -2819,7 +2823,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.8.0", "libc", - "redox_syscall 0.5.8", + "redox_syscall 0.5.9", ] [[package]] @@ -2858,9 +2862,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.25" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "louds-rs" @@ -2945,6 +2949,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d97bbf43eb4f088f8ca469930cde17fa036207c9a5e02ccc5107c4e8b17c964" +[[package]] +name = "minicov" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" +dependencies = [ + "cc", + "walkdir", +] + [[package]] name = "miniz_oxide" version = "0.7.4" @@ -2956,9 +2970,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", ] @@ -2988,7 +3002,7 @@ dependencies = [ [[package]] name = "nargo" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "fm", @@ -3013,7 +3027,7 @@ dependencies = [ [[package]] name = "nargo_cli" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "ark-bn254 0.4.0", @@ -3042,6 +3056,7 @@ dependencies = [ "nargo", "nargo_fmt", "nargo_toml", + "noir_artifact_cli", "noir_debugger", "noir_lsp", "noirc_abi", @@ -3079,8 +3094,9 @@ dependencies = [ [[package]] name = "nargo_fmt" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ + "noirc_errors", "noirc_frontend", "serde", "similar-asserts", @@ -3090,7 +3106,7 @@ dependencies = [ [[package]] name = "nargo_toml" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "dirs", "fm", @@ -3157,7 +3173,7 @@ dependencies = [ [[package]] name = "noir_artifact_cli" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "acvm", @@ -3181,7 +3197,7 @@ dependencies = [ [[package]] name = "noir_debugger" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "assert_cmd", @@ -3205,7 +3221,7 @@ dependencies = [ [[package]] name = "noir_fuzzer" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "noirc_abi", @@ -3216,12 +3232,13 @@ dependencies = [ [[package]] name = "noir_inspector" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "clap", "color-eyre", "const_format", + "noir_artifact_cli", "noirc_artifacts", "noirc_artifacts_info", "serde", @@ -3230,7 +3247,7 @@ dependencies = [ [[package]] name = "noir_lsp" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "async-lsp", @@ -3238,6 +3255,7 @@ dependencies = [ "convert_case", "fm", "fxhash", + "iter-extended", "lsp-types 0.94.1", "nargo", "nargo_fmt", @@ -3258,7 +3276,7 @@ dependencies = [ [[package]] name = "noir_profiler" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "bn254_blackbox_solver", @@ -3270,6 +3288,7 @@ dependencies = [ "im", "inferno", "nargo", + "noir_artifact_cli", "noirc_abi", "noirc_artifacts", "noirc_driver", @@ -3278,13 +3297,14 @@ dependencies = [ "serde", "serde_json", "tempfile", + "thiserror", "tracing-appender", "tracing-subscriber", ] [[package]] name = "noir_wasm" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "build-data", @@ -3308,7 +3328,7 @@ dependencies = [ [[package]] name = "noirc_abi" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "iter-extended", @@ -3327,7 +3347,7 @@ dependencies = [ [[package]] name = "noirc_abi_wasm" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "build-data", @@ -3344,11 +3364,11 @@ dependencies = [ [[package]] name = "noirc_arena" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" [[package]] name = "noirc_artifacts" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "codespan-reporting", @@ -3363,7 +3383,7 @@ dependencies = [ [[package]] name = "noirc_artifacts_info" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "acvm", @@ -3378,7 +3398,7 @@ dependencies = [ [[package]] name = "noirc_driver" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "build-data", @@ -3397,7 +3417,7 @@ dependencies = [ [[package]] name = "noirc_errors" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "base64 0.21.7", @@ -3414,7 +3434,7 @@ dependencies = [ [[package]] name = "noirc_evaluator" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "bn254_blackbox_solver", @@ -3444,7 +3464,7 @@ dependencies = [ [[package]] name = "noirc_frontend" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "base64 0.21.7", @@ -3476,7 +3496,7 @@ dependencies = [ [[package]] name = "noirc_printable_type" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "proptest", @@ -3687,7 +3707,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.8", + "redox_syscall 0.5.9", "smallvec", "windows-targets 0.52.6", ] @@ -4137,9 +4157,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" dependencies = [ "bitflags 2.8.0", ] @@ -4240,15 +4260,14 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.8" +version = "0.17.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "da5349ae27d3887ca812fb375b45a4fbb36d8d12d2df394968cd86e35683fe73" dependencies = [ "cc", "cfg-if", "getrandom 0.2.15", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] @@ -4335,9 +4354,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.22" +version = "0.23.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb9263ab4eb695e42321db096e3b8fbd715a59b154d5c88d82db2175b681ba7" +checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" dependencies = [ "log", "once_cell", @@ -4543,12 +4562,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -4601,9 +4614,9 @@ checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" [[package]] name = "serde" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] @@ -4644,9 +4657,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", @@ -4655,9 +4668,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.138" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" dependencies = [ "itoa", "memchr", @@ -4790,9 +4803,9 @@ dependencies = [ [[package]] name = "similar-asserts" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f08357795f0d604ea7d7130f22c74b03838c959bdb14adde3142aab4d18a293" +checksum = "b5b441962c817e33508847a22bd82f03a30cff43642dc2fae8b050566121eb9a" dependencies = [ "console", "similar", @@ -4834,9 +4847,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" dependencies = [ "serde", ] @@ -5023,9 +5036,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.16.0" +version = "3.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" +checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" dependencies = [ "cfg-if", "fastrand", @@ -5313,7 +5326,7 @@ checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap 2.7.1", "toml_datetime", - "winnow 0.7.2", + "winnow 0.7.3", ] [[package]] @@ -5491,9 +5504,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unarray" @@ -5503,9 +5516,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" [[package]] name = "unicode-linebreak" @@ -5569,9 +5582,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" +checksum = "bd8dcafa1ca14750d8d7a05aa05988c17aab20886e1f3ae33a40223c58d92ef7" [[package]] name = "valuable" @@ -5655,11 +5668,13 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.86" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "serde", "serde_json", "wasm-bindgen-macro", @@ -5667,13 +5682,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.86" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn 2.0.98", @@ -5682,21 +5696,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.36" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.86" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5704,9 +5719,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.86" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -5717,19 +5732,21 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.86" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-bindgen-test" -version = "0.3.36" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e636f3a428ff62b3742ebc3c70e254dfe12b8c2b469d688ea59cdd4abcf502" +checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3" dependencies = [ - "console_error_panic_hook", "js-sys", - "scoped-tls", + "minicov", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", @@ -5737,19 +5754,20 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.36" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f18c1fad2f7c4958e7bcce014fa212f59a65d5e3721d0f77e6c0b27ede936ba3" +checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", + "syn 2.0.98", ] [[package]] name = "web-sys" -version = "0.3.63" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -5804,6 +5822,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-link" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" + [[package]] name = "windows-sys" version = "0.48.0" @@ -5963,9 +5987,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" +checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" dependencies = [ "memchr", ] diff --git a/noir/noir-repo/Cargo.toml b/noir/noir-repo/Cargo.toml index c91eb816083f..9c5bf1351d1c 100644 --- a/noir/noir-repo/Cargo.toml +++ b/noir/noir-repo/Cargo.toml @@ -49,11 +49,11 @@ resolver = "2" [workspace.package] # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors = ["The Noir Team "] -edition = "2021" -rust-version = "1.75.0" +edition = "2024" +rust-version = "1.85.0" license = "MIT OR Apache-2.0" repository = "https://github.com/noir-lang/noir/" @@ -66,13 +66,13 @@ unused_qualifications = "warn" [workspace.dependencies] # ACVM workspace dependencies -acir_field = { version = "1.0.0-beta.2", path = "acvm-repo/acir_field", default-features = false } -acir = { version = "1.0.0-beta.2", path = "acvm-repo/acir", default-features = false } -acvm = { version = "1.0.0-beta.2", path = "acvm-repo/acvm" } -brillig = { version = "1.0.0-beta.2", path = "acvm-repo/brillig", default-features = false } -brillig_vm = { version = "1.0.0-beta.2", path = "acvm-repo/brillig_vm", default-features = false } -acvm_blackbox_solver = { version = "1.0.0-beta.2", path = "acvm-repo/blackbox_solver", default-features = false } -bn254_blackbox_solver = { version = "1.0.0-beta.2", path = "acvm-repo/bn254_blackbox_solver", default-features = false } +acir_field = { version = "1.0.0-beta.3", path = "acvm-repo/acir_field", default-features = false } +acir = { version = "1.0.0-beta.3", path = "acvm-repo/acir", default-features = false } +acvm = { version = "1.0.0-beta.3", path = "acvm-repo/acvm" } +brillig = { version = "1.0.0-beta.3", path = "acvm-repo/brillig", default-features = false } +brillig_vm = { version = "1.0.0-beta.3", path = "acvm-repo/brillig_vm", default-features = false } +acvm_blackbox_solver = { version = "1.0.0-beta.3", path = "acvm-repo/blackbox_solver", default-features = false } +bn254_blackbox_solver = { version = "1.0.0-beta.3", path = "acvm-repo/bn254_blackbox_solver", default-features = false } # Noir compiler workspace dependencies fm = { path = "compiler/fm" } @@ -116,9 +116,9 @@ lsp-types = "0.94.1" tower = "0.4" # Wasm -wasm-bindgen = { version = "=0.2.86", features = ["serde-serialize"] } -wasm-bindgen-test = "0.3.36" -wasm-bindgen-futures = "0.4.36" +wasm-bindgen = { version = "=0.2.100", features = ["serde-serialize"] } +wasm-bindgen-test = "0.3.50" +wasm-bindgen-futures = "0.4.50" console_error_panic_hook = "0.1.7" gloo-utils = { version = "0.1", features = ["serde"] } js-sys = "0.3.62" diff --git a/noir/noir-repo/EXTERNAL_NOIR_LIBRARIES.yml b/noir/noir-repo/EXTERNAL_NOIR_LIBRARIES.yml index fcb6885c1659..1da6b9df35ca 100644 --- a/noir/noir-repo/EXTERNAL_NOIR_LIBRARIES.yml +++ b/noir/noir-repo/EXTERNAL_NOIR_LIBRARIES.yml @@ -1,4 +1,4 @@ -define: &AZ_COMMIT 1350f93c3e9af8f601ca67ca3e67d0127c9767b6 +define: &AZ_COMMIT 5f141167d16f1450348a4bbe55d94a462b5b62eb libraries: noir_check_shuffle: repo: noir-lang/noir_check_shuffle @@ -23,13 +23,13 @@ libraries: timeout: 2 noir-bignum: repo: noir-lang/noir-bignum - timeout: 380 + timeout: 90 noir_bigcurve: repo: noir-lang/noir_bigcurve - timeout: 330 + timeout: 250 noir_base64: repo: noir-lang/noir_base64 - timeout: 2 + timeout: 5 noir_string_search: repo: noir-lang/noir_string_search timeout: 2 diff --git a/noir/noir-repo/README.md b/noir/noir-repo/README.md index c2e41435b660..20f1f80d83ae 100644 --- a/noir/noir-repo/README.md +++ b/noir/noir-repo/README.md @@ -34,7 +34,7 @@ The current focus is to gather as much feedback as possible while in the alpha p ## Minimum Rust version -This workspace's minimum supported rustc version is 1.75.0. +This workspace's minimum supported rustc version is 1.85.0. ## License diff --git a/noir/noir-repo/acvm-repo/acir/Cargo.toml b/noir/noir-repo/acvm-repo/acir/Cargo.toml index de55860762e5..2b15c2abf095 100644 --- a/noir/noir-repo/acvm-repo/acir/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir/Cargo.toml @@ -2,7 +2,7 @@ name = "acir" description = "ACIR is the IR that the VM processes, it is analogous to LLVM IR" # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/acir/benches/serialization.rs b/noir/noir-repo/acvm-repo/acir/benches/serialization.rs index dd6a5c8b1cf3..2725b3e1dd0c 100644 --- a/noir/noir-repo/acvm-repo/acir/benches/serialization.rs +++ b/noir/noir-repo/acvm-repo/acir/benches/serialization.rs @@ -1,10 +1,10 @@ -use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; +use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main}; use std::{collections::BTreeSet, time::Duration}; use acir::{ + FieldElement, circuit::{Circuit, ExpressionWidth, Opcode, Program, PublicInputs}, native_types::{Expression, Witness}, - FieldElement, }; use pprof::criterion::{Output, PProfProfiler}; 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 091a3dcb0e52..68c3c832b5cd 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs @@ -11,7 +11,7 @@ use std::{io::prelude::*, num::ParseIntError, str::FromStr}; use base64::Engine; use flate2::Compression; -use serde::{de::Error as DeserializationError, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as DeserializationError}; use std::collections::BTreeSet; @@ -252,7 +252,7 @@ impl Program { program_bytes } - // Serialize and base64 encode program + /// Serialize and base64 encode program pub fn serialize_program_base64(program: &Self, s: S) -> Result where S: Serializer, @@ -277,7 +277,7 @@ impl Deserialize<'a>> Program { Program::read(serialized_circuit) } - // Deserialize and base64 decode program + /// Deserialize and base64 decode program pub fn deserialize_program_base64<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, @@ -375,8 +375,8 @@ mod tests { use std::collections::BTreeSet; use super::{ - opcodes::{BlackBoxFuncCall, FunctionInput}, Circuit, Compression, Opcode, PublicInputs, + opcodes::{BlackBoxFuncCall, FunctionInput}, }; use crate::{ circuit::{ExpressionWidth, Program}, diff --git a/noir/noir-repo/acvm-repo/acir/src/lib.rs b/noir/noir-repo/acvm-repo/acir/src/lib.rs index bb5b50c0daf2..e49ab60f9e06 100644 --- a/noir/noir-repo/acvm-repo/acir/src/lib.rs +++ b/noir/noir-repo/acvm-repo/acir/src/lib.rs @@ -41,10 +41,10 @@ mod reflection { use crate::{ circuit::{ - brillig::{BrilligInputs, BrilligOutputs}, - opcodes::{BlackBoxFuncCall, BlockType, ConstantOrWitnessEnum, FunctionInput}, AssertionPayload, Circuit, ExpressionOrMemory, ExpressionWidth, Opcode, OpcodeLocation, Program, + brillig::{BrilligInputs, BrilligOutputs}, + opcodes::{BlackBoxFuncCall, BlockType, ConstantOrWitnessEnum, FunctionInput}, }, native_types::{Witness, WitnessMap, WitnessStack}, }; diff --git a/noir/noir-repo/acvm-repo/acir/src/native_types/expression/ordering.rs b/noir/noir-repo/acvm-repo/acir/src/native_types/expression/ordering.rs index f1d217b3bd9d..968dc310bc11 100644 --- a/noir/noir-repo/acvm-repo/acir/src/native_types/expression/ordering.rs +++ b/noir/noir-repo/acvm-repo/acir/src/native_types/expression/ordering.rs @@ -85,11 +85,7 @@ impl Expression { fn cmp_max(m1: Option, m2: Option) -> Ordering { if let Some(m1) = m1 { - if let Some(m2) = m2 { - m1.cmp(&m2) - } else { - Ordering::Greater - } + if let Some(m2) = m2 { m1.cmp(&m2) } else { Ordering::Greater } } else if m2.is_some() { Ordering::Less } else { diff --git a/noir/noir-repo/acvm-repo/acir/src/native_types/witness_map.rs b/noir/noir-repo/acvm-repo/acir/src/native_types/witness_map.rs index e508fe5b1861..77745c714a39 100644 --- a/noir/noir-repo/acvm-repo/acir/src/native_types/witness_map.rs +++ b/noir/noir-repo/acvm-repo/acir/src/native_types/witness_map.rs @@ -1,12 +1,12 @@ use std::{ - collections::{btree_map, BTreeMap}, + collections::{BTreeMap, btree_map}, io::Read, ops::Index, }; +use flate2::Compression; use flate2::bufread::GzDecoder; use flate2::bufread::GzEncoder; -use flate2::Compression; use serde::{Deserialize, Serialize}; use thiserror::Error; diff --git a/noir/noir-repo/acvm-repo/acir/src/native_types/witness_stack.rs b/noir/noir-repo/acvm-repo/acir/src/native_types/witness_stack.rs index 8a4fffa15772..6338ad630d6c 100644 --- a/noir/noir-repo/acvm-repo/acir/src/native_types/witness_stack.rs +++ b/noir/noir-repo/acvm-repo/acir/src/native_types/witness_stack.rs @@ -1,8 +1,8 @@ use std::io::Read; +use flate2::Compression; use flate2::bufread::GzDecoder; use flate2::bufread::GzEncoder; -use flate2::Compression; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -12,6 +12,9 @@ use super::WitnessMap; enum SerializationError { #[error(transparent)] Deflate(#[from] std::io::Error), + + #[error(transparent)] + BincodeError(#[from] bincode::Error), } #[derive(Debug, Error)] @@ -57,11 +60,11 @@ impl From> for WitnessStack { } } -impl TryFrom> for Vec { +impl TryFrom<&WitnessStack> for Vec { type Error = WitnessStackError; - fn try_from(val: WitnessStack) -> Result { - let buf = bincode::serialize(&val).unwrap(); + fn try_from(val: &WitnessStack) -> Result { + let buf = bincode::serialize(val).map_err(|e| WitnessStackError(e.into()))?; let mut deflater = GzEncoder::new(buf.as_slice(), Compression::best()); let mut buf_c = Vec::new(); deflater.read_to_end(&mut buf_c).map_err(|err| WitnessStackError(err.into()))?; @@ -69,6 +72,14 @@ impl TryFrom> for Vec { } } +impl TryFrom> for Vec { + type Error = WitnessStackError; + + fn try_from(val: WitnessStack) -> Result { + Self::try_from(&val) + } +} + impl Deserialize<'a>> TryFrom<&[u8]> for WitnessStack { type Error = WitnessStackError; @@ -76,7 +87,8 @@ impl Deserialize<'a>> TryFrom<&[u8]> for WitnessStack { let mut deflater = GzDecoder::new(bytes); let mut buf_d = Vec::new(); deflater.read_to_end(&mut buf_d).map_err(|err| WitnessStackError(err.into()))?; - let witness_stack = bincode::deserialize(&buf_d).unwrap(); + let witness_stack = + bincode::deserialize(&buf_d).map_err(|e| WitnessStackError(e.into()))?; Ok(witness_stack) } } diff --git a/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs b/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs index 305d94abcee2..4ff571106a1b 100644 --- a/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs +++ b/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs @@ -13,9 +13,9 @@ use std::collections::BTreeSet; use acir::{ circuit::{ + Circuit, Opcode, Program, PublicInputs, brillig::{BrilligBytecode, BrilligFunctionId, BrilligInputs, BrilligOutputs}, opcodes::{AcirFunctionId, BlackBoxFuncCall, BlockId, FunctionInput, MemOp}, - Circuit, Opcode, Program, PublicInputs, }, native_types::{Expression, Witness}, }; diff --git a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml index bc4be250cfd4..719d2585485b 100644 --- a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml @@ -2,7 +2,7 @@ name = "acir_field" description = "The field implementation being used by ACIR." # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true @@ -23,12 +23,18 @@ serde.workspace = true ark-bn254.workspace = true ark-bls12-381 = { workspace = true, optional = true } ark-ff.workspace = true +ark-std.workspace = true cfg-if.workspace = true [dev-dependencies] proptest.workspace = true +criterion.workspace = true [features] bn254 = [] bls12_381 = ["dep:ark-bls12-381"] + +[[bench]] +name = "field_element" +harness = false diff --git a/noir/noir-repo/acvm-repo/acir_field/benches/field_element.rs b/noir/noir-repo/acvm-repo/acir_field/benches/field_element.rs new file mode 100644 index 000000000000..8560c10cd040 --- /dev/null +++ b/noir/noir-repo/acvm-repo/acir_field/benches/field_element.rs @@ -0,0 +1,11 @@ +use acir_field::{AcirField, FieldElement}; +use criterion::{Criterion, criterion_group, criterion_main}; +use std::hint::black_box; + +fn criterion_benchmark(c: &mut Criterion) { + let field_element = FieldElement::from(123456789_u128); + c.bench_function("FieldElement::num_bits", |b| b.iter(|| black_box(field_element).num_bits())); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs index 8afc76da9d89..fdac33836ad8 100644 --- a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs +++ b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs @@ -1,5 +1,6 @@ use ark_ff::PrimeField; use ark_ff::Zero; +use ark_std::io::Write; use num_bigint::BigUint; use serde::{Deserialize, Serialize}; use std::borrow::Cow; @@ -111,11 +112,7 @@ impl From for FieldElement { impl From for FieldElement { fn from(boolean: bool) -> FieldElement { - if boolean { - FieldElement::one() - } else { - FieldElement::zero() - } + if boolean { FieldElement::one() } else { FieldElement::zero() } } } @@ -182,11 +179,7 @@ impl AcirField for FieldElement { /// For example, a max bit size of 254 would give a max byte size of 32. fn max_num_bytes() -> u32 { let num_bytes = Self::max_num_bits() / 8; - if Self::max_num_bits() % 8 == 0 { - num_bytes - } else { - num_bytes + 1 - } + if Self::max_num_bits() % 8 == 0 { num_bytes } else { num_bytes + 1 } } fn modulus() -> BigUint { @@ -195,26 +188,9 @@ impl AcirField for FieldElement { /// This is the number of bits required to represent this specific field element fn num_bits(&self) -> u32 { - let bytes = self.to_be_bytes(); - - // Iterate through the byte decomposition and pop off all leading zeroes - let mut iter = bytes.iter().skip_while(|x| (**x) == 0); - - // The first non-zero byte in the decomposition may have some leading zero-bits. - let Some(head_byte) = iter.next() else { - // If we don't have a non-zero byte then the field element is zero, - // which we consider to require a single bit to represent. - return 1; - }; - let num_bits_for_head_byte = head_byte.ilog2(); - - // Each remaining byte in the byte decomposition requires 8 bits. - // - // Note: count will panic if it goes over usize::MAX. - // This may not be suitable for devices whose usize < u16 - let tail_length = iter.count() as u32; - - 8 * tail_length + num_bits_for_head_byte + 1 + let mut bit_counter = BitCounter::default(); + self.0.serialize_uncompressed(&mut bit_counter).unwrap(); + bit_counter.bits() } fn to_u128(self) -> u128 { @@ -363,6 +339,52 @@ impl SubAssign for FieldElement { } } +#[derive(Default, Debug)] +struct BitCounter { + /// Total number of non-zero bytes we found. + count: usize, + /// Total bytes we found. + total: usize, + /// The last non-zero byte we found. + head_byte: u8, +} + +impl BitCounter { + fn bits(&self) -> u32 { + // If we don't have a non-zero byte then the field element is zero, + // which we consider to require a single bit to represent. + if self.count == 0 { + return 1; + } + + let num_bits_for_head_byte = self.head_byte.ilog2(); + + // Each remaining byte in the byte decomposition requires 8 bits. + // + // Note: count will panic if it goes over usize::MAX. + // This may not be suitable for devices whose usize < u16 + let tail_length = (self.count - 1) as u32; + 8 * tail_length + num_bits_for_head_byte + 1 + } +} + +impl Write for BitCounter { + fn write(&mut self, buf: &[u8]) -> ark_std::io::Result { + for byte in buf { + self.total += 1; + if *byte != 0 { + self.count = self.total; + self.head_byte = *byte; + } + } + Ok(buf.len()) + } + + fn flush(&mut self) -> ark_std::io::Result<()> { + Ok(()) + } +} + #[cfg(test)] mod tests { use super::{AcirField, FieldElement}; diff --git a/noir/noir-repo/acvm-repo/acvm/Cargo.toml b/noir/noir-repo/acvm-repo/acvm/Cargo.toml index 902918475138..53e220db23e5 100644 --- a/noir/noir-repo/acvm-repo/acvm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm" description = "The virtual machine that processes ACIR given a backend/proof system." # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/mod.rs index ee84f7bf60bd..ee503e7e0d05 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/mod.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; use acir::{ - circuit::{AssertionPayload, Circuit, ExpressionWidth, OpcodeLocation}, AcirField, + circuit::{AssertionPayload, Circuit, ExpressionWidth, OpcodeLocation}, }; // The various passes that we can use over ACIR @@ -14,7 +14,7 @@ pub use optimizers::optimize; use optimizers::optimize_internal; pub use simulator::CircuitSimulator; use transformers::transform_internal; -pub use transformers::{transform, MIN_EXPRESSION_WIDTH}; +pub use transformers::{MIN_EXPRESSION_WIDTH, transform}; /// This module moves and decomposes acir opcodes. The transformation map allows consumers of this module to map /// metadata they had about the opcodes to the new opcode structure generated after the transformation. diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/general.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/general.rs index 39a01a38cace..0802f33185cc 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/general.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/general.rs @@ -1,6 +1,6 @@ use acir::{ - native_types::{Expression, Witness}, AcirField, + native_types::{Expression, Witness}, }; use indexmap::IndexMap; diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/merge_expressions.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/merge_expressions.rs index e95c6207c3c3..2590c5f208aa 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/merge_expressions.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/merge_expressions.rs @@ -1,13 +1,13 @@ use std::collections::{BTreeMap, BTreeSet, HashMap}; use acir::{ + AcirField, circuit::{ + Circuit, Opcode, brillig::{BrilligInputs, BrilligOutputs}, opcodes::BlockId, - Circuit, Opcode, }, native_types::{Expression, Witness}, - AcirField, }; use crate::compiler::CircuitSimulator; @@ -258,16 +258,16 @@ impl MergeExpressionsOptimizer { #[cfg(test)] mod tests { - use crate::compiler::{optimizers::MergeExpressionsOptimizer, CircuitSimulator}; + use crate::compiler::{CircuitSimulator, optimizers::MergeExpressionsOptimizer}; use acir::{ + FieldElement, acir_field::AcirField, circuit::{ + Circuit, ExpressionWidth, Opcode, PublicInputs, brillig::{BrilligFunctionId, BrilligOutputs}, opcodes::{BlackBoxFuncCall, FunctionInput}, - Circuit, ExpressionWidth, Opcode, PublicInputs, }, native_types::{Expression, Witness}, - FieldElement, }; use std::collections::BTreeSet; diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/mod.rs index 3531825c709d..3e085e4ba9b5 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/mod.rs @@ -1,6 +1,6 @@ use acir::{ - circuit::{Circuit, Opcode}, AcirField, + circuit::{Circuit, Opcode}, }; // mod constant_backpropagation; @@ -17,7 +17,7 @@ use tracing::info; // use self::constant_backpropagation::ConstantBackpropagationOptimizer; use self::unused_memory::UnusedMemoryOptimizer; -use super::{transform_assert_messages, AcirTransformationMap}; +use super::{AcirTransformationMap, transform_assert_messages}; /// Applies [`ProofSystemCompiler`][crate::ProofSystemCompiler] independent optimizations to a [`Circuit`]. pub fn optimize(acir: Circuit) -> (Circuit, AcirTransformationMap) { diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs index f9c715a277f4..67dce75411e8 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs @@ -1,10 +1,10 @@ use acir::{ + AcirField, circuit::{ - opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum}, Circuit, Opcode, + opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum}, }, native_types::Witness, - AcirField, }; use std::collections::{BTreeMap, HashSet}; @@ -163,12 +163,12 @@ mod tests { use crate::compiler::optimizers::redundant_range::RangeOptimizer; use acir::{ + FieldElement, circuit::{ - opcodes::{BlackBoxFuncCall, FunctionInput}, Circuit, ExpressionWidth, Opcode, PublicInputs, + opcodes::{BlackBoxFuncCall, FunctionInput}, }, native_types::{Expression, Witness}, - FieldElement, }; fn test_circuit(ranges: Vec<(Witness, u32)>) -> Circuit { diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/unused_memory.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/unused_memory.rs index 1325a3b03cdf..8b7e52d66f2c 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/unused_memory.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/unused_memory.rs @@ -1,4 +1,4 @@ -use acir::circuit::{brillig::BrilligInputs, opcodes::BlockId, Circuit, Opcode}; +use acir::circuit::{Circuit, Opcode, brillig::BrilligInputs, opcodes::BlockId}; use std::collections::HashSet; /// `UnusedMemoryOptimizer` will remove initializations of memory blocks which are unused. diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/simulator.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/simulator.rs index 893195f342a9..96134926f5e0 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/simulator.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/simulator.rs @@ -1,11 +1,11 @@ use acir::{ + AcirField, circuit::{ + Circuit, Opcode, brillig::{BrilligInputs, BrilligOutputs}, opcodes::{BlockId, FunctionInput}, - Circuit, Opcode, }, native_types::{Expression, Witness}, - AcirField, }; use std::collections::{BTreeSet, HashMap, HashSet}; @@ -215,10 +215,10 @@ mod tests { use crate::compiler::CircuitSimulator; use acir::{ + FieldElement, acir_field::AcirField, circuit::{Circuit, ExpressionWidth, Opcode, PublicInputs}, native_types::{Expression, Witness}, - FieldElement, }; fn test_circuit( 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 bdd6998835a2..8f890e4ca86b 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 @@ -1,8 +1,8 @@ use std::{cmp::Ordering, collections::HashSet}; use acir::{ - native_types::{Expression, Witness}, AcirField, + native_types::{Expression, Witness}, }; use indexmap::IndexMap; @@ -201,7 +201,7 @@ impl CSatTransformer { // Now we have used up 2 spaces in our assert-zero opcode. The width now dictates, how many more we can add let mut remaining_space = self.width - 2 - 1; // We minus 1 because we need an extra space to contain the intermediate variable - // Keep adding terms until we have no more left, or we reach the width + // Keep adding terms until we have no more left, or we reach the width let mut remaining_linear_terms = Vec::with_capacity(opcode.linear_combinations.len()); while remaining_space > 0 { diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/transformers/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/transformers/mod.rs index 77a5d2da34e0..fe9404b075c8 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/transformers/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/transformers/mod.rs @@ -1,12 +1,11 @@ use acir::{ + AcirField, circuit::{ - self, + self, Circuit, ExpressionWidth, Opcode, brillig::{BrilligInputs, BrilligOutputs}, opcodes::{BlackBoxFuncCall, FunctionInput, MemOp}, - Circuit, ExpressionWidth, Opcode, }, native_types::{Expression, Witness}, - AcirField, }; use indexmap::IndexMap; @@ -17,8 +16,9 @@ pub use csat::MIN_EXPRESSION_WIDTH; use tracing::info; use super::{ + AcirTransformationMap, optimizers::{MergeExpressionsOptimizer, RangeOptimizer}, - transform_assert_messages, AcirTransformationMap, + transform_assert_messages, }; /// We need multiple passes to stabilize the output. diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/arithmetic.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/arithmetic.rs index 5eeabd8a8332..a2921bcbc9b1 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/arithmetic.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/arithmetic.rs @@ -1,9 +1,9 @@ use acir::{ - native_types::{Expression, Witness, WitnessMap}, AcirField, + native_types::{Expression, Witness, WitnessMap}, }; -use super::{insert_value, ErrorLocation, OpcodeNotSolvable, OpcodeResolutionError}; +use super::{ErrorLocation, OpcodeNotSolvable, OpcodeResolutionError, insert_value}; /// An Expression solver will take a Circuit's assert-zero opcodes with witness assignments /// and create the other witness variables @@ -254,7 +254,52 @@ mod tests { use acir::FieldElement; #[test] - fn expression_solver_smoke_test() { + fn solves_simple_assignment() { + let a = Witness(0); + + // a - 1 == 0; + let opcode_a = Expression { + mul_terms: vec![], + linear_combinations: vec![(FieldElement::one(), a)], + q_c: -FieldElement::one(), + }; + + let mut values = WitnessMap::new(); + assert_eq!(ExpressionSolver::solve(&mut values, &opcode_a), Ok(())); + + assert_eq!(values.get(&a).unwrap(), &FieldElement::from(1_i128)); + } + + #[test] + fn solves_unknown_in_mul_term() { + let a = Witness(0); + let b = Witness(1); + let c = Witness(2); + let d = Witness(3); + + // a * b - b - c - d == 0; + let opcode_a = Expression { + mul_terms: vec![(FieldElement::one(), a, b)], + linear_combinations: vec![ + (-FieldElement::one(), b), + (-FieldElement::one(), c), + (-FieldElement::one(), d), + ], + q_c: FieldElement::zero(), + }; + + let mut values = WitnessMap::new(); + values.insert(b, FieldElement::from(2_i128)); + values.insert(c, FieldElement::from(1_i128)); + values.insert(d, FieldElement::from(1_i128)); + + assert_eq!(ExpressionSolver::solve(&mut values, &opcode_a), Ok(())); + + assert_eq!(values.get(&a).unwrap(), &FieldElement::from(2_i128)); + } + + #[test] + fn solves_unknown_in_linear_term() { let a = Witness(0); let b = Witness(1); let c = Witness(2); diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/aes128.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/aes128.rs index e3c8dc78aa62..e241a39f5af7 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/aes128.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/aes128.rs @@ -1,11 +1,11 @@ use acir::{ + AcirField, circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, - AcirField, }; use acvm_blackbox_solver::aes128_encrypt; -use crate::{pwg::insert_value, OpcodeResolutionError}; +use crate::{OpcodeResolutionError, pwg::insert_value}; use super::utils::{to_u8_array, to_u8_vec}; diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/bigint.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/bigint.rs index eb5a6f81e7d5..1c5a436fa110 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/bigint.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/bigint.rs @@ -1,8 +1,8 @@ use crate::pwg::input_to_value; use acir::{ + AcirField, BlackBoxFunc, circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, - AcirField, BlackBoxFunc, }; use acvm_blackbox_solver::BigIntSolver; diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs index 9e5115712751..2c728e77304c 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs @@ -1,11 +1,11 @@ use acir::{ + AcirField, circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, - AcirField, }; use acvm_blackbox_solver::BlackBoxFunctionSolver; -use crate::pwg::{input_to_value, insert_value, OpcodeResolutionError}; +use crate::pwg::{OpcodeResolutionError, input_to_value, insert_value}; pub(super) fn multi_scalar_mul( backend: &impl BlackBoxFunctionSolver, diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/hash.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/hash.rs index 7476b0dc2dcd..a4af9de55cfe 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/hash.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/hash.rs @@ -1,12 +1,12 @@ use acir::{ + AcirField, circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, - AcirField, }; -use acvm_blackbox_solver::{sha256_compression, BlackBoxFunctionSolver, BlackBoxResolutionError}; +use acvm_blackbox_solver::{BlackBoxFunctionSolver, BlackBoxResolutionError, sha256_compression}; -use crate::pwg::{input_to_value, insert_value}; use crate::OpcodeResolutionError; +use crate::pwg::{input_to_value, insert_value}; /// Attempts to solve a 256 bit hash function opcode. /// If successful, `initial_witness` will be mutated to contain the new witness assignment. @@ -49,9 +49,13 @@ fn get_hash_input( // in the message, then we error. if num_bytes_to_take > message_input.len() { return Err(OpcodeResolutionError::BlackBoxFunctionFailed( - acir::BlackBoxFunc::Blake2s, - format!("the number of bytes to take from the message is more than the number of bytes in the message. {} > {}", num_bytes_to_take, message_input.len()), - )); + acir::BlackBoxFunc::Blake2s, + format!( + "the number of bytes to take from the message is more than the number of bytes in the message. {} > {}", + num_bytes_to_take, + message_input.len() + ), + )); } let truncated_message = message_input[0..num_bytes_to_take].to_vec(); Ok(truncated_message) @@ -132,11 +136,10 @@ pub(crate) fn solve_poseidon2_permutation_opcode( } // Read witness assignments - let mut state = Vec::new(); - for input in inputs.iter() { - let witness_assignment = input_to_value(initial_witness, *input, false)?; - state.push(witness_assignment); - } + let state: Vec = inputs + .iter() + .map(|input| input_to_value(initial_witness, *input, false)) + .collect::>()?; let state = backend.poseidon2_permutation(&state, len)?; diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/logic.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/logic.rs index 3fa72d9b215b..587add11b810 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/logic.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/logic.rs @@ -1,9 +1,9 @@ -use crate::pwg::{input_to_value, insert_value}; use crate::OpcodeResolutionError; +use crate::pwg::{input_to_value, insert_value}; use acir::{ + AcirField, circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, - AcirField, }; use acvm_blackbox_solver::{bit_and, bit_xor}; diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs index 2f10e333c24b..ad064fefd7b8 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -1,7 +1,7 @@ use acir::{ + AcirField, circuit::opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput}, native_types::{Witness, WitnessMap}, - AcirField, }; use acvm_blackbox_solver::{blake2s, blake3, keccakf1600}; @@ -10,8 +10,8 @@ use self::{ hash::solve_poseidon2_permutation_opcode, }; -use super::{insert_value, OpcodeNotSolvable, OpcodeResolutionError}; -use crate::{pwg::input_to_value, BlackBoxFunctionSolver}; +use super::{OpcodeNotSolvable, OpcodeResolutionError, insert_value}; +use crate::{BlackBoxFunctionSolver, pwg::input_to_value}; mod aes128; pub(crate) mod bigint; @@ -37,12 +37,8 @@ fn first_missing_assignment( inputs: &[FunctionInput], ) -> Option { inputs.iter().find_map(|input| { - if let ConstantOrWitnessEnum::Witness(ref witness) = input.input_ref() { - if witness_assignments.contains_key(witness) { - None - } else { - Some(*witness) - } + if let ConstantOrWitnessEnum::Witness(witness) = input.input_ref() { + if witness_assignments.contains_key(witness) { None } else { Some(*witness) } } else { None } diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/range.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/range.rs index d7083e4b9c05..039a04b9063d 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/range.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/range.rs @@ -1,8 +1,8 @@ use crate::{ - pwg::{input_to_value, ErrorLocation}, OpcodeResolutionError, + pwg::{ErrorLocation, input_to_value}, }; -use acir::{circuit::opcodes::FunctionInput, native_types::WitnessMap, AcirField}; +use acir::{AcirField, circuit::opcodes::FunctionInput, native_types::WitnessMap}; pub(crate) fn solve_range_opcode( initial_witness: &WitnessMap, @@ -21,3 +21,36 @@ pub(crate) fn solve_range_opcode( } Ok(()) } + +#[cfg(test)] +mod tests { + use std::collections::BTreeMap; + + use acir::{ + FieldElement, + circuit::opcodes::FunctionInput, + native_types::{Witness, WitnessMap}, + }; + + use crate::pwg::blackbox::solve_range_opcode; + + #[test] + fn rejects_too_large_inputs() { + let witness_map = + WitnessMap::from(BTreeMap::from([(Witness(0), FieldElement::from(256u32))])); + let input: FunctionInput = FunctionInput::witness(Witness(0), 8); + assert!(solve_range_opcode(&witness_map, &input, false).is_err()); + } + + #[test] + fn accepts_valid_inputs() { + let values: [u32; 4] = [0, 1, 8, 255]; + + for value in values { + let witness_map = + WitnessMap::from(BTreeMap::from([(Witness(0), FieldElement::from(value))])); + let input: FunctionInput = FunctionInput::witness(Witness(0), 8); + assert!(solve_range_opcode(&witness_map, &input, false).is_ok()); + } + } +} diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/signature/ecdsa.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/signature/ecdsa.rs index db92d27b871e..4b53316e88f5 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/signature/ecdsa.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/signature/ecdsa.rs @@ -1,16 +1,16 @@ use acir::{ + AcirField, circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, - AcirField, }; use acvm_blackbox_solver::{ecdsa_secp256k1_verify, ecdsa_secp256r1_verify}; use crate::{ + OpcodeResolutionError, pwg::{ blackbox::utils::{to_u8_array, to_u8_vec}, insert_value, }, - OpcodeResolutionError, }; pub(crate) fn secp256k1_prehashed( diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/utils.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/utils.rs index b966cb0cc5d2..6c9c5a1025ad 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/utils.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/utils.rs @@ -1,6 +1,6 @@ -use acir::{circuit::opcodes::FunctionInput, native_types::WitnessMap, AcirField}; +use acir::{AcirField, circuit::opcodes::FunctionInput, native_types::WitnessMap}; -use crate::pwg::{input_to_value, OpcodeResolutionError}; +use crate::pwg::{OpcodeResolutionError, input_to_value}; pub(crate) fn to_u8_array( initial_witness: &WitnessMap, diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs index a635cd926159..136f358b9bb6 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs @@ -1,20 +1,20 @@ use std::collections::HashMap; use acir::{ + AcirField, brillig::{ForeignCallParam, ForeignCallResult, Opcode as BrilligOpcode}, circuit::{ + ErrorSelector, OpcodeLocation, RawAssertionPayload, ResolvedAssertionPayload, brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}, opcodes::BlockId, - ErrorSelector, OpcodeLocation, RawAssertionPayload, ResolvedAssertionPayload, }, native_types::WitnessMap, - AcirField, }; use acvm_blackbox_solver::BlackBoxFunctionSolver; -use brillig_vm::{BrilligProfilingSamples, FailureReason, MemoryValue, VMStatus, VM}; +use brillig_vm::{BrilligProfilingSamples, FailureReason, MemoryValue, VM, VMStatus}; use serde::{Deserialize, Serialize}; -use crate::{pwg::OpcodeNotSolvable, OpcodeResolutionError}; +use crate::{OpcodeResolutionError, pwg::OpcodeNotSolvable}; use super::{get_value, insert_value, memory_op::MemoryOpSolver}; @@ -100,7 +100,7 @@ impl<'b, B: BlackBoxFunctionSolver, F: AcirField> BrilligSolver<'b, F, B> { Err(_) => { return Err(OpcodeResolutionError::OpcodeNotSolvable( OpcodeNotSolvable::ExpressionHasTooManyUnknowns(expr.clone()), - )) + )); } }, BrilligInputs::Array(expr_arr) => { @@ -111,7 +111,7 @@ impl<'b, B: BlackBoxFunctionSolver, F: AcirField> BrilligSolver<'b, F, B> { Err(_) => { return Err(OpcodeResolutionError::OpcodeNotSolvable( OpcodeNotSolvable::ExpressionHasTooManyUnknowns(expr.clone()), - )) + )); } } } diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/memory_op.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/memory_op.rs index 2a83bf2531ca..aef18d3957ea 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/memory_op.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/memory_op.rs @@ -1,15 +1,15 @@ use std::collections::HashMap; use acir::{ + AcirField, circuit::opcodes::MemOp, native_types::{Expression, Witness, WitnessMap}, - AcirField, }; +use super::{ErrorLocation, OpcodeResolutionError}; use super::{ arithmetic::ExpressionSolver, get_value, insert_value, is_predicate_false, witness_to_value, }; -use super::{ErrorLocation, OpcodeResolutionError}; type MemoryIndex = u32; @@ -140,9 +140,9 @@ mod tests { use std::collections::BTreeMap; use acir::{ + AcirField, FieldElement, circuit::opcodes::MemOp, native_types::{Expression, Witness, WitnessMap}, - AcirField, FieldElement, }; use super::MemoryOpSolver; diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs index 6e0e28cf81d3..f5d56df17c1e 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs @@ -3,17 +3,17 @@ use std::collections::HashMap; use acir::{ + AcirField, BlackBoxFunc, brillig::ForeignCallResult, circuit::{ + AssertionPayload, ErrorSelector, ExpressionOrMemory, Opcode, OpcodeLocation, + RawAssertionPayload, ResolvedAssertionPayload, brillig::{BrilligBytecode, BrilligFunctionId}, opcodes::{ AcirFunctionId, BlockId, ConstantOrWitnessEnum, FunctionInput, InvalidInputBitSize, }, - AssertionPayload, ErrorSelector, ExpressionOrMemory, Opcode, OpcodeLocation, - RawAssertionPayload, ResolvedAssertionPayload, }, native_types::{Expression, Witness, WitnessMap}, - AcirField, BlackBoxFunc, }; use acvm_blackbox_solver::BlackBoxResolutionError; @@ -73,6 +73,7 @@ impl std::fmt::Display for ACVMStatus { } } +#[expect(clippy::large_enum_variant)] pub enum StepResult<'a, F, B: BlackBoxFunctionSolver> { Status(ACVMStatus), IntoBrillig(BrilligSolver<'a, F, B>), @@ -142,7 +143,9 @@ pub enum OpcodeResolutionError { }, #[error("Attempted to call `main` with a `Call` opcode")] AcirMainCallAttempted { opcode_location: ErrorLocation }, - #[error("{results_size:?} result values were provided for {outputs_size:?} call output witnesses, most likely due to bad ACIR codegen")] + #[error( + "{results_size:?} result values were provided for {outputs_size:?} call output witnesses, most likely due to bad ACIR codegen" + )] AcirCallOutputsMismatch { opcode_location: ErrorLocation, results_size: u32, outputs_size: u32 }, #[error("(--pedantic): Predicates are expected to be 0 or 1, but found: {pred_value}")] PredicateLargerThanOne { opcode_location: ErrorLocation, pred_value: F }, @@ -744,11 +747,7 @@ pub fn insert_value( // The function is used during partial witness generation to report unsolved witness fn any_witness_from_expression(expr: &Expression) -> Option { if expr.linear_combinations.is_empty() { - if expr.mul_terms.is_empty() { - None - } else { - Some(expr.mul_terms[0].1) - } + if expr.mul_terms.is_empty() { None } else { Some(expr.mul_terms[0].1) } } else { Some(expr.linear_combinations[0].1) } diff --git a/noir/noir-repo/acvm-repo/acvm/tests/solver.rs b/noir/noir-repo/acvm-repo/acvm/tests/solver.rs index 2fd610503779..ce75a7d20017 100644 --- a/noir/noir-repo/acvm-repo/acvm/tests/solver.rs +++ b/noir/noir-repo/acvm-repo/acvm/tests/solver.rs @@ -3,20 +3,20 @@ use std::sync::Arc; use acir::brillig::{BitSize, HeapVector, IntegerBitSize}; use acir::{ + AcirField, FieldElement, acir_field::GenericFieldElement, brillig::{BinaryFieldOp, MemoryAddress, Opcode as BrilligOpcode, ValueOrArray}, circuit::{ + Opcode, OpcodeLocation, brillig::{BrilligBytecode, BrilligFunctionId, BrilligInputs, BrilligOutputs}, opcodes::{BlackBoxFuncCall, BlockId, BlockType, FunctionInput, MemOp}, - Opcode, OpcodeLocation, }, native_types::{Expression, Witness, WitnessMap}, - AcirField, FieldElement, }; -use acvm::pwg::{ACVMStatus, ErrorLocation, ForeignCallWaitInfo, OpcodeResolutionError, ACVM}; +use acvm::pwg::{ACVM, ACVMStatus, ErrorLocation, ForeignCallWaitInfo, OpcodeResolutionError}; use acvm_blackbox_solver::{BigIntSolver, StubbedBlackBoxSolver}; -use bn254_blackbox_solver::{field_from_hex, Bn254BlackBoxSolver, POSEIDON2_CONFIG}; +use bn254_blackbox_solver::{Bn254BlackBoxSolver, POSEIDON2_CONFIG, field_from_hex}; use brillig_vm::brillig::HeapValueType; use num_bigint::BigUint; diff --git a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml index 308d0ef516bb..d8416b5d89f8 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm_js" description = "Typescript wrapper around the ACVM allowing execution of ACIR code" # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true @@ -40,7 +40,7 @@ getrandom = { workspace = true, features = ["js"] } build-data.workspace = true pkg-config = "0.3" -[dev-dependencies] +[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dev-dependencies] wasm-bindgen-test.workspace = true [features] 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/acvm-repo/acvm_js/package.json b/noir/noir-repo/acvm-repo/acvm_js/package.json index e54a952698b1..fc842da42360 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/package.json +++ b/noir/noir-repo/acvm-repo/acvm_js/package.json @@ -1,6 +1,6 @@ { "name": "@noir-lang/acvm_js", - "version": "1.0.0-beta.2", + "version": "1.0.0-beta.3", "publishConfig": { "access": "public" }, diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/black_box_solvers.rs b/noir/noir-repo/acvm-repo/acvm_js/src/black_box_solvers.rs index 0e35851ee788..02c026cd26fa 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/black_box_solvers.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/black_box_solvers.rs @@ -2,7 +2,7 @@ use js_sys::JsString; use wasm_bindgen::prelude::*; use crate::js_witness_map::{field_element_to_js_string, js_value_to_field_element}; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; /// Performs a bitwise AND operation between `lhs` and `rhs` #[wasm_bindgen] diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/execute.rs b/noir/noir-repo/acvm-repo/acvm_js/src/execute.rs index e4d52063977f..3efa6900f6ec 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/execute.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/execute.rs @@ -1,22 +1,22 @@ use std::{future::Future, pin::Pin}; -use acvm::acir::circuit::brillig::BrilligBytecode; use acvm::acir::circuit::ResolvedAssertionPayload; +use acvm::acir::circuit::brillig::BrilligBytecode; +use acvm::{BlackBoxFunctionSolver, FieldElement}; use acvm::{ acir::circuit::{Circuit, Program}, acir::native_types::{WitnessMap, WitnessStack}, - pwg::{ACVMStatus, ErrorLocation, OpcodeResolutionError, ACVM}, + pwg::{ACVM, ACVMStatus, ErrorLocation, OpcodeResolutionError}, }; -use acvm::{BlackBoxFunctionSolver, FieldElement}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use js_sys::Error; use wasm_bindgen::prelude::wasm_bindgen; use crate::{ - foreign_call::{resolve_brillig, ForeignCallHandler}, - public_witness::extract_indices, JsExecutionError, JsSolvedAndReturnWitness, JsWitnessMap, JsWitnessStack, + foreign_call::{ForeignCallHandler, resolve_brillig}, + public_witness::extract_indices, }; /// Executes an ACIR circuit to generate the solved witness from the initial witness. diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/inputs.rs b/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/inputs.rs index dd12bc639ece..eee36a0f01a9 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/inputs.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/inputs.rs @@ -1,4 +1,4 @@ -use acvm::{brillig_vm::brillig::ForeignCallParam, FieldElement}; +use acvm::{FieldElement, brillig_vm::brillig::ForeignCallParam}; use crate::js_witness_map::field_element_to_js_string; diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/mod.rs b/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/mod.rs index 4884c1173dc4..8b34b699a856 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/mod.rs @@ -1,7 +1,7 @@ -use acvm::{brillig_vm::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo, FieldElement}; +use acvm::{FieldElement, brillig_vm::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo}; use js_sys::{Error, JsString}; -use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; +use wasm_bindgen::{JsValue, prelude::wasm_bindgen}; mod inputs; mod outputs; diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/outputs.rs b/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/outputs.rs index 2b3f44fe98df..75b9c3aa3113 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/outputs.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/outputs.rs @@ -1,6 +1,6 @@ use acvm::{ - brillig_vm::brillig::{ForeignCallParam, ForeignCallResult}, FieldElement, + brillig_vm::brillig::{ForeignCallParam, ForeignCallResult}, }; use wasm_bindgen::JsValue; diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs b/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs index f6a00af79422..6aa7cf756786 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs @@ -1,10 +1,10 @@ use acvm::{ - acir::circuit::{brillig::BrilligFunctionId, OpcodeLocation, RawAssertionPayload}, FieldElement, + acir::circuit::{OpcodeLocation, RawAssertionPayload, brillig::BrilligFunctionId}, }; use gloo_utils::format::JsValueSerdeExt; use js_sys::{Array, Error, JsString, Reflect}; -use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; +use wasm_bindgen::prelude::{JsValue, wasm_bindgen}; #[wasm_bindgen(typescript_custom_section)] const EXECUTION_ERROR: &'static str = r#" diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_map.rs b/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_map.rs index 8316059e21a6..a09e8e1ade7a 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_map.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_map.rs @@ -1,10 +1,10 @@ use acvm::{ - acir::native_types::{Witness, WitnessMap}, - acir::AcirField, FieldElement, + acir::AcirField, + acir::native_types::{Witness, WitnessMap}, }; use js_sys::{JsString, Map, Object}; -use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; +use wasm_bindgen::prelude::{JsValue, wasm_bindgen}; #[wasm_bindgen(typescript_custom_section)] const WITNESS_MAP: &'static str = r#" @@ -106,21 +106,21 @@ pub(crate) fn field_element_to_js_string(field_element: &FieldElement) -> JsStri format!("0x{}", field_element.to_hex()).into() } -#[cfg(test)] +#[cfg(all(test, any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))] mod test { - use wasm_bindgen_test::wasm_bindgen_test as test; + use wasm_bindgen_test::*; use std::collections::BTreeMap; use acvm::{ - acir::native_types::{Witness, WitnessMap}, AcirField, FieldElement, + acir::native_types::{Witness, WitnessMap}, }; use wasm_bindgen::JsValue; use crate::JsWitnessMap; - #[test] + #[wasm_bindgen_test] fn test_witness_map_to_js() { let witness_map = BTreeMap::from([ (Witness(1), FieldElement::one()), diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_stack.rs b/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_stack.rs index d59ee508086d..e1baa4917274 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_stack.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_stack.rs @@ -1,6 +1,6 @@ -use acvm::{acir::native_types::WitnessStack, FieldElement}; +use acvm::{FieldElement, acir::native_types::WitnessStack}; use js_sys::{Array, Map, Object}; -use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; +use wasm_bindgen::prelude::{JsValue, wasm_bindgen}; use crate::JsWitnessMap; diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/logging.rs b/noir/noir-repo/acvm-repo/acvm_js/src/logging.rs index 483c23ab624e..f40e2fa1bd34 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/logging.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/logging.rs @@ -1,6 +1,6 @@ use js_sys::{Error, JsString}; -use tracing_subscriber::prelude::*; use tracing_subscriber::EnvFilter; +use tracing_subscriber::prelude::*; use tracing_web::MakeWebConsoleWriter; use wasm_bindgen::prelude::*; diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/public_witness.rs b/noir/noir-repo/acvm-repo/acvm_js/src/public_witness.rs index 245d5b4dd0ad..2ca794ba3a50 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/public_witness.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/public_witness.rs @@ -1,9 +1,9 @@ use acvm::{ + FieldElement, acir::{ circuit::Program, native_types::{Witness, WitnessMap}, }, - FieldElement, }; use js_sys::JsString; use wasm_bindgen::prelude::wasm_bindgen; @@ -44,7 +44,11 @@ pub fn get_return_witness( let circuit = match program.functions.len() { 0 => return Ok(JsWitnessMap::from(WitnessMap::new())), 1 => &program.functions[0], - _ => return Err(JsString::from("Program contains multiple circuits however ACVM currently only supports programs containing a single circuit")) + _ => { + return Err(JsString::from( + "Program contains multiple circuits however ACVM currently only supports programs containing a single circuit", + )); + } }; let witness_map = WitnessMap::from(witness_map); @@ -71,7 +75,11 @@ pub fn get_public_parameters_witness( let circuit = match program.functions.len() { 0 => return Ok(JsWitnessMap::from(WitnessMap::new())), 1 => &program.functions[0], - _ => return Err(JsString::from("Program contains multiple circuits however ACVM currently only supports programs containing a single circuit")) + _ => { + return Err(JsString::from( + "Program contains multiple circuits however ACVM currently only supports programs containing a single circuit", + )); + } }; let witness_map = WitnessMap::from(solved_witness); @@ -98,7 +106,11 @@ pub fn get_public_witness( let circuit = match program.functions.len() { 0 => return Ok(JsWitnessMap::from(WitnessMap::new())), 1 => &program.functions[0], - _ => return Err(JsString::from("Program contains multiple circuits however ACVM currently only supports programs containing a single circuit")) + _ => { + return Err(JsString::from( + "Program contains multiple circuits however ACVM currently only supports programs containing a single circuit", + )); + } }; let witness_map = WitnessMap::from(solved_witness); diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml index bcd5be3f43ab..38292dd1924d 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm_blackbox_solver" description = "A solver for the blackbox functions found in ACIR and Brillig" # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs b/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs index f7be1e80a556..d3531007f092 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs +++ b/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs @@ -220,8 +220,8 @@ impl BigIntSolverWithId { #[test] fn all_allowed_bigint_moduli_are_prime() { - use num_prime::nt_funcs::is_prime; use num_prime::Primality; + use num_prime::nt_funcs::is_prime; for modulus in BigIntSolver::allowed_bigint_moduli() { let modulus = BigUint::from_bytes_le(&modulus); @@ -231,7 +231,10 @@ fn all_allowed_bigint_moduli_are_prime() { Primality::No => panic!("not all allowed_bigint_moduli are prime: {modulus}"), Primality::Probable(probability) => { if probability < 0.90 { - panic!("not all allowed_bigint_moduli are prime within the allowed probability: {} < 0.90", probability); + panic!( + "not all allowed_bigint_moduli are prime within the allowed probability: {} < 0.90", + probability + ); } } } diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs b/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs index 37fe5d053633..af0104b54f0a 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs +++ b/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs @@ -25,8 +25,8 @@ pub trait BlackBoxFunctionSolver { ) -> Result<(F, F, F), BlackBoxResolutionError>; fn poseidon2_permutation( &self, - _inputs: &[F], - _len: u32, + inputs: &[F], + len: u32, ) -> Result, BlackBoxResolutionError>; } diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256k1.rs b/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256k1.rs index 17c51353f7fa..d090029b1cd5 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256k1.rs +++ b/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256k1.rs @@ -1,15 +1,15 @@ -use k256::elliptic_curve::sec1::FromEncodedPoint; use k256::elliptic_curve::PrimeField; +use k256::elliptic_curve::sec1::FromEncodedPoint; use blake2::digest::generic_array::GenericArray; -use k256::{ecdsa::Signature, Scalar}; use k256::{ + AffinePoint, EncodedPoint, ProjectivePoint, PublicKey, elliptic_curve::{ - sec1::{Coordinates, ToEncodedPoint}, IsHigh, + sec1::{Coordinates, ToEncodedPoint}, }, - AffinePoint, EncodedPoint, ProjectivePoint, PublicKey, }; +use k256::{Scalar, ecdsa::Signature}; pub(super) fn verify_signature( hashed_msg: &[u8], diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256r1.rs b/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256r1.rs index 54559d7c7744..7a162aa800ab 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256r1.rs +++ b/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256r1.rs @@ -1,15 +1,15 @@ -use p256::elliptic_curve::sec1::FromEncodedPoint; use p256::elliptic_curve::PrimeField; +use p256::elliptic_curve::sec1::FromEncodedPoint; use blake2::digest::generic_array::GenericArray; -use p256::{ecdsa::Signature, Scalar}; use p256::{ + AffinePoint, EncodedPoint, ProjectivePoint, PublicKey, elliptic_curve::{ - sec1::{Coordinates, ToEncodedPoint}, IsHigh, + sec1::{Coordinates, ToEncodedPoint}, }, - AffinePoint, EncodedPoint, ProjectivePoint, PublicKey, }; +use p256::{Scalar, ecdsa::Signature}; pub(super) fn verify_signature( hashed_msg: &[u8], diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml index 5ac93d9d8f68..d9367f6b52af 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml @@ -2,7 +2,7 @@ name = "bn254_blackbox_solver" description = "Solvers for black box functions which are specific for the bn254 curve" # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/benches/criterion.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/benches/criterion.rs index fc566b70a268..05211264beb6 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/benches/criterion.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/benches/criterion.rs @@ -1,4 +1,4 @@ -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{Criterion, criterion_group, criterion_main}; use std::{hint::black_box, time::Duration}; use acir::{AcirField, FieldElement}; diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/hash_to_curve.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/hash_to_curve.rs index 3c284fa811ca..028ca2e5d1cc 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/hash_to_curve.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/hash_to_curve.rs @@ -62,11 +62,7 @@ pub(crate) fn hash_to_curve(seed: &[u8], attempt_count: u8) -> Affine { if let Some(point) = Affine::get_point_from_x_unchecked(x, false) { let parity_bit = hash_hi[0] > 127; let y_bit_set = point.y().unwrap().into_bigint().get_bit(0); - if (parity_bit && !y_bit_set) || (!parity_bit && y_bit_set) { - -point - } else { - point - } + if (parity_bit && !y_bit_set) || (!parity_bit && y_bit_set) { -point } else { point } } else { hash_to_curve(seed, attempt_count + 1) } diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs index fc031faefa26..d0fece86d6f9 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs @@ -11,8 +11,8 @@ mod poseidon2; pub use embedded_curve_ops::{embedded_curve_add, multi_scalar_mul}; pub use generator::generators::derive_generators; pub use poseidon2::{ - field_from_hex, poseidon2_permutation, poseidon_hash, Poseidon2Config, Poseidon2Sponge, - POSEIDON2_CONFIG, + POSEIDON2_CONFIG, Poseidon2Config, Poseidon2Sponge, field_from_hex, poseidon_hash, + poseidon2_permutation, }; // Temporary hack, this ensure that we always use a bn254 field here diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs index 3aa735388ca8..d756f40a236f 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs @@ -430,7 +430,7 @@ lazy_static! { }; } -impl<'a> Poseidon2<'a> { +impl Poseidon2<'_> { pub(crate) fn new() -> Self { Poseidon2 { config: &POSEIDON2_CONFIG } } @@ -626,7 +626,7 @@ impl<'a> Poseidon2Sponge<'a> { mod test { use acir::AcirField; - use super::{field_from_hex, poseidon2_permutation, FieldElement}; + use super::{FieldElement, field_from_hex, poseidon2_permutation}; #[test] fn smoke_test() { diff --git a/noir/noir-repo/acvm-repo/brillig/Cargo.toml b/noir/noir-repo/acvm-repo/brillig/Cargo.toml index d037531b8ed5..5a9720238aca 100644 --- a/noir/noir-repo/acvm-repo/brillig/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig/Cargo.toml @@ -2,7 +2,7 @@ name = "brillig" description = "Brillig is the bytecode ACIR uses for non-determinism." # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/brillig/src/black_box.rs b/noir/noir-repo/acvm-repo/brillig/src/black_box.rs index be9ba20ed499..eb496d0f826c 100644 --- a/noir/noir-repo/acvm-repo/brillig/src/black_box.rs +++ b/noir/noir-repo/acvm-repo/brillig/src/black_box.rs @@ -1,4 +1,4 @@ -use crate::{opcodes::HeapVector, HeapArray, MemoryAddress}; +use crate::{HeapArray, MemoryAddress, opcodes::HeapVector}; use serde::{Deserialize, Serialize}; /// These opcodes provide an equivalent of ACIR blackbox functions. diff --git a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml index 531179f54d1f..55bfe486c9f3 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml @@ -2,7 +2,7 @@ name = "brillig_vm" description = "The virtual machine that processes Brillig bytecode, used to introduce non-determinism to the ACVM" # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/brillig_vm/src/arithmetic.rs b/noir/noir-repo/acvm-repo/brillig_vm/src/arithmetic.rs index c9417faaff30..24013d1fde7a 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/src/arithmetic.rs +++ b/noir/noir-repo/acvm-repo/brillig_vm/src/arithmetic.rs @@ -1,7 +1,7 @@ use std::ops::{BitAnd, BitOr, BitXor, Shl, Shr}; -use acir::brillig::{BinaryFieldOp, BinaryIntOp, BitSize, IntegerBitSize}; use acir::AcirField; +use acir::brillig::{BinaryFieldOp, BinaryIntOp, BitSize, IntegerBitSize}; use num_bigint::BigUint; use num_traits::{CheckedDiv, WrappingAdd, WrappingMul, WrappingSub, Zero}; @@ -231,21 +231,11 @@ fn evaluate_binary_int_op_shifts + Zero + Shl + Shr { let rhs_usize: usize = rhs as usize; - #[allow(unused_qualifications)] - if rhs_usize >= 8 * std::mem::size_of::() { - T::zero() - } else { - lhs << rhs.into() - } + if rhs_usize >= 8 * size_of::() { T::zero() } else { lhs << rhs.into() } } BinaryIntOp::Shr => { let rhs_usize: usize = rhs as usize; - #[allow(unused_qualifications)] - if rhs_usize >= 8 * std::mem::size_of::() { - T::zero() - } else { - lhs >> rhs.into() - } + if rhs_usize >= 8 * size_of::() { T::zero() } else { lhs >> rhs.into() } } _ => unreachable!("Operator not handled by this function: {op:?}"), } diff --git a/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs b/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs index aa3d0de55f47..74ea3568a1a2 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs +++ b/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs @@ -1,14 +1,14 @@ use acir::brillig::{BlackBoxOp, HeapArray, HeapVector}; use acir::{AcirField, BlackBoxFunc}; use acvm_blackbox_solver::{ - aes128_encrypt, blake2s, blake3, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify, keccakf1600, - sha256_compression, BigIntSolverWithId, BlackBoxFunctionSolver, BlackBoxResolutionError, + BigIntSolverWithId, BlackBoxFunctionSolver, BlackBoxResolutionError, aes128_encrypt, blake2s, + blake3, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify, keccakf1600, sha256_compression, }; use num_bigint::BigUint; use num_traits::Zero; -use crate::memory::MemoryValue; use crate::Memory; +use crate::memory::MemoryValue; fn read_heap_vector<'a, F: AcirField>( memory: &'a Memory, diff --git a/noir/noir-repo/acvm-repo/brillig_vm/src/lib.rs b/noir/noir-repo/acvm-repo/brillig_vm/src/lib.rs index 27759012335c..935c296d5ae8 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/src/lib.rs +++ b/noir/noir-repo/acvm-repo/brillig_vm/src/lib.rs @@ -10,19 +10,19 @@ //! [acir]: https://crates.io/crates/acir //! [acvm]: https://crates.io/crates/acvm +use acir::AcirField; use acir::brillig::{ BinaryFieldOp, BinaryIntOp, BitSize, ForeignCallParam, ForeignCallResult, HeapArray, HeapValueType, HeapVector, IntegerBitSize, MemoryAddress, Opcode, ValueOrArray, }; -use acir::AcirField; use acvm_blackbox_solver::BlackBoxFunctionSolver; -use arithmetic::{evaluate_binary_field_op, evaluate_binary_int_op, BrilligArithmeticError}; -use black_box::{evaluate_black_box, BrilligBigIntSolver}; +use arithmetic::{BrilligArithmeticError, evaluate_binary_field_op, evaluate_binary_int_op}; +use black_box::{BrilligBigIntSolver, evaluate_black_box}; // Re-export `brillig`. pub use acir::brillig; use memory::MemoryTypeError; -pub use memory::{Memory, MemoryValue, MEMORY_ADDRESSING_BIT_SIZE}; +pub use memory::{MEMORY_ADDRESSING_BIT_SIZE, Memory, MemoryValue}; mod arithmetic; mod black_box; @@ -548,69 +548,99 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver> VM<'a, F, B> { destinations.iter().zip(destination_value_types).zip(&values) { match (destination, value_type) { - (ValueOrArray::MemoryAddress(value_index), HeapValueType::Simple(bit_size)) => { - match output { - ForeignCallParam::Single(value) => { - self.write_value_to_memory(*value_index, value, *bit_size)?; - } - _ => return Err(format!( - "Function result size does not match brillig bytecode. Expected 1 result but got {output:?}") - ), - } - } - ( - ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }), - HeapValueType::Array { value_types, size: type_size }, - ) if size == type_size => { - if HeapValueType::all_simple(value_types) { + (ValueOrArray::MemoryAddress(value_index), HeapValueType::Simple(bit_size)) => { match output { - ForeignCallParam::Array(values) => { - if values.len() != *size { - // foreign call returning flattened values into a nested type, so the sizes do not match - let destination = self.memory.read_ref(*pointer_index); - let return_type = value_type; - let mut flatten_values_idx = 0; //index of values read from flatten_values - self.write_slice_of_values_to_memory(destination, &output.fields(), &mut flatten_values_idx, return_type)?; - } else { - self.write_values_to_memory_slice(*pointer_index, values, value_types)?; - } + ForeignCallParam::Single(value) => { + self.write_value_to_memory(*value_index, value, *bit_size)?; } _ => { - return Err("Function result size does not match brillig bytecode size".to_string()); + return Err(format!( + "Function result size does not match brillig bytecode. Expected 1 result but got {output:?}" + )); } } - } else { - // foreign call returning flattened values into a nested type, so the sizes do not match - let destination = self.memory.read_ref(*pointer_index); - let return_type = value_type; - let mut flatten_values_idx = 0; //index of values read from flatten_values - self.write_slice_of_values_to_memory(destination, &output.fields(), &mut flatten_values_idx, return_type)?; - } - } - ( - ValueOrArray::HeapVector(HeapVector {pointer: pointer_index, size: size_index }), - HeapValueType::Vector { value_types }, - ) => { - if HeapValueType::all_simple(value_types) { - match output { - ForeignCallParam::Array(values) => { - // Set our size in the size address - self.memory.write(*size_index, values.len().into()); - self.write_values_to_memory_slice(*pointer_index, values, value_types)?; - + } + ( + ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }), + HeapValueType::Array { value_types, size: type_size }, + ) if size == type_size => { + if HeapValueType::all_simple(value_types) { + match output { + ForeignCallParam::Array(values) => { + if values.len() != *size { + // foreign call returning flattened values into a nested type, so the sizes do not match + let destination = self.memory.read_ref(*pointer_index); + let return_type = value_type; + let mut flatten_values_idx = 0; //index of values read from flatten_values + self.write_slice_of_values_to_memory( + destination, + &output.fields(), + &mut flatten_values_idx, + return_type, + )?; + } else { + self.write_values_to_memory_slice( + *pointer_index, + values, + value_types, + )?; + } + } + _ => { + return Err( + "Function result size does not match brillig bytecode size" + .to_string(), + ); + } } - _ => { - return Err("Function result size does not match brillig bytecode size".to_string()); + } else { + // foreign call returning flattened values into a nested type, so the sizes do not match + let destination = self.memory.read_ref(*pointer_index); + let return_type = value_type; + let mut flatten_values_idx = 0; //index of values read from flatten_values + self.write_slice_of_values_to_memory( + destination, + &output.fields(), + &mut flatten_values_idx, + return_type, + )?; + } + } + ( + ValueOrArray::HeapVector(HeapVector { + pointer: pointer_index, + size: size_index, + }), + HeapValueType::Vector { value_types }, + ) => { + if HeapValueType::all_simple(value_types) { + match output { + ForeignCallParam::Array(values) => { + // Set our size in the size address + self.memory.write(*size_index, values.len().into()); + self.write_values_to_memory_slice( + *pointer_index, + values, + value_types, + )?; + } + _ => { + return Err( + "Function result size does not match brillig bytecode size" + .to_string(), + ); + } } + } else { + unimplemented!("deflattening heap vectors from foreign calls"); } - } else { - unimplemented!("deflattening heap vectors from foreign calls"); + } + _ => { + return Err(format!( + "Unexpected value type {value_type:?} for destination {destination:?}" + )); } } - _ => { - return Err(format!("Unexpected value type {value_type:?} for destination {destination:?}")); - } - } } let _ = diff --git a/noir/noir-repo/acvm-repo/brillig_vm/src/memory.rs b/noir/noir-repo/acvm-repo/brillig_vm/src/memory.rs index 73443384efa5..33a760b52f57 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/src/memory.rs +++ b/noir/noir-repo/acvm-repo/brillig_vm/src/memory.rs @@ -1,6 +1,6 @@ use acir::{ - brillig::{BitSize, IntegerBitSize, MemoryAddress}, AcirField, + brillig::{BitSize, IntegerBitSize, MemoryAddress}, }; pub const MEMORY_ADDRESSING_BIT_SIZE: IntegerBitSize = IntegerBitSize::U32; @@ -18,7 +18,9 @@ pub enum MemoryValue { #[derive(Debug, thiserror::Error)] pub enum MemoryTypeError { - #[error("Bit size for value {value_bit_size} does not match the expected bit size {expected_bit_size}")] + #[error( + "Bit size for value {value_bit_size} does not match the expected bit size {expected_bit_size}" + )] MismatchedBitSize { value_bit_size: u32, expected_bit_size: u32 }, } diff --git a/noir/noir-repo/compiler/integration-tests/package.json b/noir/noir-repo/compiler/integration-tests/package.json index e33179f31e7f..053e9efeed20 100644 --- a/noir/noir-repo/compiler/integration-tests/package.json +++ b/noir/noir-repo/compiler/integration-tests/package.json @@ -13,7 +13,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.72.1", "@noir-lang/noir_js": "workspace:*", "@noir-lang/noir_wasm": "workspace:*", "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", 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 9838a8af2102..3bbe2181798e 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs @@ -1,15 +1,15 @@ use std::collections::BTreeMap; -use acvm::acir::circuit::ErrorSelector; use acvm::AcirField; +use acvm::acir::circuit::ErrorSelector; use iter_extended::vecmap; use noirc_abi::{ Abi, AbiErrorType, AbiParameter, AbiReturnType, AbiType, AbiValue, AbiVisibility, Sign, }; -use noirc_errors::Span; +use noirc_errors::Location; use noirc_evaluator::ErrorType; -use noirc_frontend::ast::{Signedness, Visibility}; use noirc_frontend::TypeBinding; +use noirc_frontend::ast::{Signedness, Visibility}; use noirc_frontend::{ hir::Context, hir_def::{ @@ -42,11 +42,11 @@ pub(super) fn gen_abi( } // Get the Span of the root crate's main function, or else a dummy span if that fails -fn get_main_function_span(context: &Context) -> Span { +fn get_main_function_location(context: &Context) -> Location { if let Some(func_id) = context.get_main_function(context.root_crate_id()) { - context.function_meta(&func_id).location.span + context.function_meta(&func_id).location } else { - Span::default() + Location::dummy() } } @@ -54,7 +54,7 @@ fn build_abi_error_type(context: &Context, typ: ErrorType) -> AbiErrorType { match typ { ErrorType::Dynamic(typ) => { if let Type::FmtString(len, item_types) = typ { - let span = get_main_function_span(context); + let span = get_main_function_location(context); let length = len.evaluate_to_u32(span).expect("Cannot evaluate fmt length"); let Type::Tuple(item_types) = item_types.as_ref() else { unreachable!("FmtString items must be a tuple") @@ -74,7 +74,7 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType { match typ { Type::FieldElement => AbiType::Field, Type::Array(size, typ) => { - let span = get_main_function_span(context); + let span = get_main_function_location(context); let length = size .evaluate_to_u32(span) .expect("Cannot have variable sized arrays as a parameter to main"); @@ -103,7 +103,7 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType { } Type::Bool => AbiType::Boolean, Type::String(size) => { - let span = get_main_function_span(context); + let span = get_main_function_location(context); let size = size .evaluate_to_u32(span) .expect("Cannot have variable sized strings as a parameter to main"); @@ -226,7 +226,9 @@ pub(super) fn value_from_hir_expression(context: &Context, expression: HirExpres }, HirLiteral::Bool(value) => AbiValue::Boolean { value }, HirLiteral::Str(value) => AbiValue::String { value }, - HirLiteral::Integer(field, sign) => AbiValue::Integer { value: field.to_hex(), sign }, + HirLiteral::Integer(value) => { + AbiValue::Integer { value: value.field.to_hex(), sign: value.is_negative } + } _ => unreachable!("Literal cannot be used in the abi"), }, _ => unreachable!("Type cannot be used in the abi {:?}", expression), diff --git a/noir/noir-repo/compiler/noirc_driver/src/contract.rs b/noir/noir-repo/compiler/noirc_driver/src/contract.rs index 6253f3e1814e..b4c5e9a3fcae 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/contract.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/contract.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, HashMap}; -use acvm::{acir::circuit::Program, FieldElement}; +use acvm::{FieldElement, acir::circuit::Program}; use fm::FileId; use noirc_abi::{Abi, AbiType, AbiValue}; use noirc_errors::debug_info::DebugInfo; @@ -41,6 +41,8 @@ pub struct CompiledContract { pub struct ContractFunction { pub name: String, + pub hash: u64, + pub is_unconstrained: bool, pub custom_attributes: Vec, diff --git a/noir/noir-repo/compiler/noirc_driver/src/lib.rs b/noir/noir-repo/compiler/noirc_driver/src/lib.rs index 807ee7385a37..3712634d707f 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/lib.rs @@ -10,14 +10,15 @@ use clap::Args; use fm::{FileId, FileManager}; use iter_extended::vecmap; use noirc_abi::{AbiParameter, AbiType, AbiValue}; -use noirc_errors::{CustomDiagnostic, DiagnosticKind, FileDiagnostic}; +use noirc_errors::{CustomDiagnostic, DiagnosticKind}; use noirc_evaluator::brillig::BrilligOptions; use noirc_evaluator::create_program; use noirc_evaluator::errors::RuntimeError; use noirc_evaluator::ssa::{SsaLogging, SsaProgramArtifact}; use noirc_frontend::debug::build_debug_crate_file; -use noirc_frontend::hir::def_map::{Contract, CrateDefMap}; +use noirc_frontend::elaborator::{FrontendOptions, UnstableFeature}; use noirc_frontend::hir::Context; +use noirc_frontend::hir::def_map::{Contract, CrateDefMap}; use noirc_frontend::monomorphization::{ errors::MonomorphizationError, monomorphize, monomorphize_debug, }; @@ -105,10 +106,6 @@ pub struct CompileOptions { #[arg(long, conflicts_with = "deny_warnings")] pub silence_warnings: bool, - /// Disables the builtin Aztec macros being used in the compiler - #[arg(long, hide = true)] - pub disable_macros: bool, - /// Outputs the monomorphized IR to stdout for debugging #[arg(long, hide = true)] pub show_monomorphized: bool, @@ -136,20 +133,16 @@ pub struct CompileOptions { #[arg(long)] pub skip_underconstrained_check: bool, - /// Flag to turn on the compiler check for missing Brillig call constraints. - /// Warning: This can degrade compilation speed but will also find some correctness errors. + /// Flag to turn off the compiler check for missing Brillig call constraints. + /// Warning: This can improve compilation speed but can also lead to correctness errors. /// This check should always be run on production code. #[arg(long)] - pub enable_brillig_constraints_check: bool, + pub skip_brillig_constraints_check: bool, /// Flag to turn on extra Brillig bytecode to be generated to guard against invalid states in testing. #[arg(long, hide = true)] pub enable_brillig_debug_assertions: bool, - /// Hidden Brillig call check flag to maintain CI compatibility (currently ignored) - #[arg(long, hide = true)] - pub skip_brillig_constraints_check: bool, - /// Flag to turn on the lookback feature of the Brillig call constraints /// check, allowing tracking argument values before the call happens preventing /// certain rare false positives (leads to a slowdown on large rollout functions) @@ -179,6 +172,11 @@ pub struct CompileOptions { /// Used internally to test for non-determinism in the compiler. #[clap(long, hide = true)] pub check_non_determinism: bool, + + /// Unstable features to enable for this current build + #[arg(value_parser = clap::value_parser!(UnstableFeature))] + #[clap(long, short = 'Z', value_delimiter = ',')] + pub unstable_features: Vec, } pub fn parse_expression_width(input: &str) -> Result { @@ -197,6 +195,16 @@ pub fn parse_expression_width(input: &str) -> Result FrontendOptions { + FrontendOptions { + debug_comptime_in_file: self.debug_comptime_in_file.as_deref(), + pedantic_solving: self.pedantic_solving, + enabled_unstable_features: &self.unstable_features, + } + } +} + #[derive(Debug)] pub enum CompileError { MonomorphizationError(MonomorphizationError), @@ -215,8 +223,8 @@ impl From for CompileError { } } -impl From for FileDiagnostic { - fn from(error: CompileError) -> FileDiagnostic { +impl From for CustomDiagnostic { + fn from(error: CompileError) -> CustomDiagnostic { match error { CompileError::RuntimeError(err) => err.into(), CompileError::MonomorphizationError(err) => err.into(), @@ -225,10 +233,10 @@ impl From for FileDiagnostic { } /// Helper type used to signify where only warnings are expected in file diagnostics -pub type Warnings = Vec; +pub type Warnings = Vec; /// Helper type used to signify where errors or warnings are expected in file diagnostics -pub type ErrorsAndWarnings = Vec; +pub type ErrorsAndWarnings = Vec; /// Helper type for connecting a compilation artifact to the errors or warnings which were produced during compilation. pub type CompilationResult = Result<(T, Warnings), ErrorsAndWarnings>; @@ -336,30 +344,18 @@ pub fn check_crate( crate_id: CrateId, options: &CompileOptions, ) -> CompilationResult<()> { - let diagnostics = CrateDefMap::collect_defs( - crate_id, - context, - options.debug_comptime_in_file.as_deref(), - options.pedantic_solving, - ); + let diagnostics = CrateDefMap::collect_defs(crate_id, context, options.frontend_options()); let crate_files = context.crate_files(&crate_id); - let warnings_and_errors: Vec = diagnostics - .into_iter() - .map(|(error, file_id)| { - let diagnostic = CustomDiagnostic::from(&error); - diagnostic.in_file(file_id) - }) + let warnings_and_errors: Vec = diagnostics + .iter() + .map(CustomDiagnostic::from) .filter(|diagnostic| { // We filter out any warnings if they're going to be ignored later on to free up memory. - !options.silence_warnings || diagnostic.diagnostic.kind != DiagnosticKind::Warning + !options.silence_warnings || diagnostic.kind != DiagnosticKind::Warning }) .filter(|error| { // Only keep warnings from the crate we are checking - if error.diagnostic.is_warning() { - crate_files.contains(&error.file_id) - } else { - true - } + if error.is_warning() { crate_files.contains(&error.file) } else { true } }) .collect(); @@ -397,16 +393,16 @@ pub fn compile_main( // TODO(#2155): This error might be a better to exist in Nargo let err = CustomDiagnostic::from_message( "cannot compile crate into a program as it does not contain a `main` function", - ) - .in_file(FileId::default()); + FileId::default(), + ); vec![err] })?; let compiled_program = compile_no_check(context, options, main, cached_program, options.force_compile) - .map_err(FileDiagnostic::from)?; + .map_err(|error| vec![CustomDiagnostic::from(error)])?; - let compilation_warnings = vecmap(compiled_program.warnings.clone(), FileDiagnostic::from); + let compilation_warnings = vecmap(compiled_program.warnings.clone(), CustomDiagnostic::from); if options.deny_warnings && !compilation_warnings.is_empty() { return Err(compilation_warnings); } @@ -435,14 +431,16 @@ pub fn compile_contract( let mut errors = warnings; if contracts.len() > 1 { - let err = CustomDiagnostic::from_message("Packages are limited to a single contract") - .in_file(FileId::default()); + let err = CustomDiagnostic::from_message( + "Packages are limited to a single contract", + FileId::default(), + ); return Err(vec![err]); } else if contracts.is_empty() { let err = CustomDiagnostic::from_message( "cannot compile crate into a contract as it does not contain any contracts", - ) - .in_file(FileId::default()); + FileId::default(), + ); return Err(vec![err]); }; @@ -479,12 +477,8 @@ pub fn compile_contract( } /// True if there are (non-warning) errors present and we should halt compilation -fn has_errors(errors: &[FileDiagnostic], deny_warnings: bool) -> bool { - if deny_warnings { - !errors.is_empty() - } else { - errors.iter().any(|error| error.diagnostic.is_error()) - } +fn has_errors(errors: &[CustomDiagnostic], deny_warnings: bool) -> bool { + if deny_warnings { !errors.is_empty() } else { errors.iter().any(|error| error.is_error()) } } /// Compile all of the functions associated with a Noir contract. @@ -521,7 +515,7 @@ fn compile_contract_inner( let function = match compile_no_check(context, &options, function_id, None, true) { Ok(function) => function, Err(new_error) => { - errors.push(FileDiagnostic::from(new_error)); + errors.push(new_error.into()); continue; } }; @@ -541,6 +535,7 @@ fn compile_contract_inner( functions.push(ContractFunction { name, + hash: function.hash, custom_attributes, abi: function.abi, bytecode: function.program, @@ -699,7 +694,7 @@ pub fn compile_no_check( skip_underconstrained_check: options.skip_underconstrained_check, enable_brillig_constraints_check_lookback: options .enable_brillig_constraints_check_lookback, - enable_brillig_constraints_check: options.enable_brillig_constraints_check, + skip_brillig_constraints_check: options.skip_brillig_constraints_check, inliner_aggressiveness: options.inliner_aggressiveness, max_bytecode_increase_percent: options.max_bytecode_increase_percent, }; diff --git a/noir/noir-repo/compiler/noirc_driver/src/program.rs b/noir/noir-repo/compiler/noirc_driver/src/program.rs index 4b4d6662e8e7..b3d545339fc3 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/program.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/program.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use acvm::{acir::circuit::Program, FieldElement}; +use acvm::{FieldElement, acir::circuit::Program}; use fm::FileId; use noirc_errors::debug_info::DebugInfo; diff --git a/noir/noir-repo/compiler/noirc_driver/tests/contracts.rs b/noir/noir-repo/compiler/noirc_driver/tests/contracts.rs index c30412923527..0732a7728ca5 100644 --- a/noir/noir-repo/compiler/noirc_driver/tests/contracts.rs +++ b/noir/noir-repo/compiler/noirc_driver/tests/contracts.rs @@ -1,9 +1,9 @@ use std::path::Path; use fm::FileId; -use noirc_driver::{file_manager_with_stdlib, prepare_crate, CompileOptions, ErrorsAndWarnings}; +use noirc_driver::{CompileOptions, ErrorsAndWarnings, file_manager_with_stdlib, prepare_crate}; use noirc_errors::CustomDiagnostic; -use noirc_frontend::hir::{def_map::parse_file, Context}; +use noirc_frontend::hir::{Context, def_map::parse_file}; #[test] fn reject_crates_containing_multiple_contracts() -> Result<(), ErrorsAndWarnings> { @@ -33,8 +33,10 @@ contract Bar {}"; assert_eq!( errors, - vec![CustomDiagnostic::from_message("Packages are limited to a single contract") - .in_file(FileId::default())], + vec![CustomDiagnostic::from_message( + "Packages are limited to a single contract", + FileId::default() + )], "stdlib is producing warnings" ); diff --git a/noir/noir-repo/compiler/noirc_driver/tests/stdlib_warnings.rs b/noir/noir-repo/compiler/noirc_driver/tests/stdlib_warnings.rs index e290842480d0..411ea68fea7c 100644 --- a/noir/noir-repo/compiler/noirc_driver/tests/stdlib_warnings.rs +++ b/noir/noir-repo/compiler/noirc_driver/tests/stdlib_warnings.rs @@ -1,7 +1,7 @@ use std::path::Path; -use noirc_driver::{file_manager_with_stdlib, prepare_crate, ErrorsAndWarnings}; -use noirc_frontend::hir::{def_map::parse_file, Context}; +use noirc_driver::{ErrorsAndWarnings, file_manager_with_stdlib, prepare_crate}; +use noirc_frontend::hir::{Context, def_map::parse_file}; #[test] fn stdlib_does_not_produce_constant_warnings() -> Result<(), ErrorsAndWarnings> { diff --git a/noir/noir-repo/compiler/noirc_errors/src/debug_info.rs b/noir/noir-repo/compiler/noirc_errors/src/debug_info.rs index a5e12b37712d..4d25973835db 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/debug_info.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/debug_info.rs @@ -1,16 +1,16 @@ -use acvm::acir::circuit::brillig::BrilligFunctionId; use acvm::acir::circuit::BrilligOpcodeLocation; use acvm::acir::circuit::OpcodeLocation; +use acvm::acir::circuit::brillig::BrilligFunctionId; use acvm::compiler::AcirTransformationMap; use base64::Engine; +use flate2::Compression; use flate2::read::DeflateDecoder; use flate2::write::DeflateEncoder; -use flate2::Compression; use serde::Deserializer; use serde::Serializer; -use serde_with::serde_as; use serde_with::DisplayFromStr; +use serde_with::serde_as; use std::collections::BTreeMap; use std::io::Read; use std::io::Write; @@ -19,7 +19,7 @@ use std::mem; use crate::Location; use noirc_printable_type::PrintableType; use serde::{ - de::Error as DeserializationError, ser::Error as SerializationError, Deserialize, Serialize, + Deserialize, Serialize, de::Error as DeserializationError, ser::Error as SerializationError, }; #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, Deserialize, Serialize)] diff --git a/noir/noir-repo/compiler/noirc_errors/src/lib.rs b/noir/noir-repo/compiler/noirc_errors/src/lib.rs index bcdc0684099c..91d121603baa 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/lib.rs @@ -6,23 +6,5 @@ pub mod debug_info; mod position; pub mod reporter; -pub use position::{Location, Position, Span, Spanned}; +pub use position::{Located, Location, Position, Span, Spanned}; pub use reporter::{CustomDiagnostic, DiagnosticKind}; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct FileDiagnostic { - pub file_id: fm::FileId, - pub diagnostic: CustomDiagnostic, -} - -impl FileDiagnostic { - pub fn new(file_id: fm::FileId, diagnostic: CustomDiagnostic) -> FileDiagnostic { - FileDiagnostic { file_id, diagnostic } - } -} - -impl From for Vec { - fn from(value: FileDiagnostic) -> Self { - vec![value] - } -} diff --git a/noir/noir-repo/compiler/noirc_errors/src/position.rs b/noir/noir-repo/compiler/noirc_errors/src/position.rs index c7a64c4f4224..f87f78e1f309 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/position.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/position.rs @@ -2,12 +2,68 @@ use codespan::Span as ByteSpan; use fm::FileId; use serde::{Deserialize, Serialize}; use std::{ + cmp::Ordering, hash::{Hash, Hasher}, ops::Range, }; pub type Position = u32; +#[derive(Eq, Debug, Clone)] +pub struct Located { + pub contents: T, + location: Location, +} + +/// This is important for tests. Two Located objects are equal if their content is equal +/// They may not have the same location. Use `.location()` to test for Location being equal specifically +impl PartialEq> for Located { + fn eq(&self, other: &Located) -> bool { + self.contents == other.contents + } +} + +impl PartialOrd> for Located { + fn partial_cmp(&self, other: &Located) -> Option { + self.contents.partial_cmp(&other.contents) + } +} + +impl Ord for Located { + fn cmp(&self, other: &Located) -> Ordering { + self.contents.cmp(&other.contents) + } +} + +impl Default for Located { + fn default() -> Self { + Self { contents: Default::default(), location: Location::dummy() } + } +} + +/// Hash-based data structures (HashMap, HashSet) rely on the inverse of Hash +/// being injective, i.e. x.eq(y) => hash(x, H) == hash(y, H), we hence align +/// this with the above +impl Hash for Located { + fn hash(&self, state: &mut H) { + self.contents.hash(state); + } +} + +impl Located { + pub fn from(location: Location, contents: T) -> Located { + Located { location, contents } + } + + pub fn span(&self) -> Span { + self.location.span + } + + pub fn location(&self) -> Location { + self.location + } +} + #[derive(PartialOrd, Eq, Ord, Debug, Clone, Default)] pub struct Spanned { pub contents: T, @@ -36,17 +92,13 @@ impl Spanned { Spanned { span: Span::inclusive(start, end), contents } } - pub const fn from(t_span: Span, contents: T) -> Spanned { + pub fn from(t_span: Span, contents: T) -> Spanned { Spanned { span: t_span, contents } } pub fn span(&self) -> Span { self.span } - - pub fn set_span(&mut self, span: Span) { - self.span = span; - } } impl std::borrow::Borrow for Spanned { @@ -135,12 +187,33 @@ impl Location { } pub fn dummy() -> Self { - Self { span: Span::single_char(0), file: FileId::dummy() } + Self { span: Span::default(), file: FileId::dummy() } } pub fn contains(&self, other: &Location) -> bool { self.file == other.file && self.span.contains(&other.span) } + + #[must_use] + pub fn merge(self, other: Location) -> Location { + if self.file == other.file { + Location::new(self.span.merge(other.span), self.file) + } else { + self + } + } +} + +impl Ord for Location { + fn cmp(&self, other: &Self) -> Ordering { + (self.file, self.span).cmp(&(other.file, other.span)) + } +} + +impl PartialOrd for Location { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } } #[cfg(test)] diff --git a/noir/noir-repo/compiler/noirc_errors/src/reporter.rs b/noir/noir-repo/compiler/noirc_errors/src/reporter.rs index 30bf49348b6a..d406e897d656 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/reporter.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/reporter.rs @@ -1,6 +1,6 @@ use std::io::IsTerminal; -use crate::{FileDiagnostic, Location, Span}; +use crate::{Location, Span}; use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::Files; use codespan_reporting::term; @@ -8,6 +8,7 @@ use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct CustomDiagnostic { + pub file: fm::FileId, pub message: String, pub secondaries: Vec, pub notes: Vec, @@ -35,8 +36,9 @@ pub struct ReportedErrors { } impl CustomDiagnostic { - pub fn from_message(msg: &str) -> CustomDiagnostic { + pub fn from_message(msg: &str, file: fm::FileId) -> CustomDiagnostic { Self { + file, message: msg.to_owned(), secondaries: Vec::new(), notes: Vec::new(), @@ -50,12 +52,13 @@ impl CustomDiagnostic { fn simple_with_kind( primary_message: String, secondary_message: String, - secondary_span: Span, + secondary_location: Location, kind: DiagnosticKind, ) -> CustomDiagnostic { CustomDiagnostic { + file: secondary_location.file, message: primary_message, - secondaries: vec![CustomLabel::new(secondary_message, secondary_span, None)], + secondaries: vec![CustomLabel::new(secondary_message, secondary_location)], notes: Vec::new(), kind, deprecated: false, @@ -67,12 +70,12 @@ impl CustomDiagnostic { pub fn simple_error( primary_message: String, secondary_message: String, - secondary_span: Span, + secondary_location: Location, ) -> CustomDiagnostic { Self::simple_with_kind( primary_message, secondary_message, - secondary_span, + secondary_location, DiagnosticKind::Error, ) } @@ -80,12 +83,12 @@ impl CustomDiagnostic { pub fn simple_warning( primary_message: String, secondary_message: String, - secondary_span: Span, + secondary_location: Location, ) -> CustomDiagnostic { Self::simple_with_kind( primary_message, secondary_message, - secondary_span, + secondary_location, DiagnosticKind::Warning, ) } @@ -93,12 +96,12 @@ impl CustomDiagnostic { pub fn simple_info( primary_message: String, secondary_message: String, - secondary_span: Span, + secondary_location: Location, ) -> CustomDiagnostic { Self::simple_with_kind( primary_message, secondary_message, - secondary_span, + secondary_location, DiagnosticKind::Info, ) } @@ -106,11 +109,12 @@ impl CustomDiagnostic { pub fn simple_bug( primary_message: String, secondary_message: String, - secondary_span: Span, + secondary_location: Location, ) -> CustomDiagnostic { CustomDiagnostic { + file: secondary_location.file, message: primary_message, - secondaries: vec![CustomLabel::new(secondary_message, secondary_span, None)], + secondaries: vec![CustomLabel::new(secondary_message, secondary_location)], notes: Vec::new(), kind: DiagnosticKind::Bug, deprecated: false, @@ -119,10 +123,6 @@ impl CustomDiagnostic { } } - pub fn in_file(self, file_id: fm::FileId) -> FileDiagnostic { - FileDiagnostic::new(file_id, self) - } - pub fn with_call_stack(mut self, call_stack: Vec) -> Self { self.call_stack = call_stack; self @@ -132,12 +132,8 @@ impl CustomDiagnostic { self.notes.push(message); } - pub fn add_secondary(&mut self, message: String, span: Span) { - self.secondaries.push(CustomLabel::new(message, span, None)); - } - - pub fn add_secondary_with_file(&mut self, message: String, span: Span, file: fm::FileId) { - self.secondaries.push(CustomLabel::new(message, span, Some(file))); + pub fn add_secondary(&mut self, message: String, location: Location) { + self.secondaries.push(CustomLabel::new(message, location)); } pub fn is_error(&self) -> bool { @@ -176,13 +172,12 @@ impl std::fmt::Display for CustomDiagnostic { #[derive(Debug, Clone, PartialEq, Eq)] pub struct CustomLabel { pub message: String, - pub span: Span, - pub file: Option, + pub location: Location, } impl CustomLabel { - fn new(message: String, span: Span, file: Option) -> CustomLabel { - CustomLabel { message, span, file } + fn new(message: String, location: Location) -> CustomLabel { + CustomLabel { message, location } } } @@ -190,16 +185,16 @@ impl CustomLabel { /// of diagnostics that were errors. pub fn report_all<'files>( files: &'files impl Files<'files, FileId = fm::FileId>, - diagnostics: &[FileDiagnostic], + diagnostics: &[CustomDiagnostic], deny_warnings: bool, silence_warnings: bool, ) -> ReportedErrors { // Report warnings before any errors let (warnings_and_bugs, mut errors): (Vec<_>, _) = - diagnostics.iter().partition(|item| !item.diagnostic.is_error()); + diagnostics.iter().partition(|item| !item.is_error()); let (warnings, mut bugs): (Vec<_>, _) = - warnings_and_bugs.iter().partition(|item| item.diagnostic.is_warning()); + warnings_and_bugs.iter().partition(|item| item.is_warning()); let mut diagnostics = if silence_warnings { Vec::new() } else { warnings }; diagnostics.append(&mut bugs); diagnostics.append(&mut errors); @@ -210,14 +205,14 @@ pub fn report_all<'files>( ReportedErrors { error_count } } -impl FileDiagnostic { +impl CustomDiagnostic { /// Print the report; return true if it was an error. pub fn report<'files>( &self, files: &'files impl Files<'files, FileId = fm::FileId>, deny_warnings: bool, ) -> bool { - report(files, &self.diagnostic, Some(self.file_id), deny_warnings) + report(files, self, deny_warnings) } } @@ -225,7 +220,6 @@ impl FileDiagnostic { pub fn report<'files>( files: &'files impl Files<'files, FileId = fm::FileId>, custom_diagnostic: &CustomDiagnostic, - file: Option, deny_warnings: bool, ) -> bool { let color_choice = @@ -234,7 +228,7 @@ pub fn report<'files>( let config = term::Config::default(); let stack_trace = stack_trace(files, &custom_diagnostic.call_stack); - let diagnostic = convert_diagnostic(custom_diagnostic, file, stack_trace, deny_warnings); + let diagnostic = convert_diagnostic(custom_diagnostic, stack_trace, deny_warnings); term::emit(&mut writer.lock(), &config, files, &diagnostic).unwrap(); deny_warnings || custom_diagnostic.is_error() @@ -242,7 +236,6 @@ pub fn report<'files>( fn convert_diagnostic( cd: &CustomDiagnostic, - file: Option, stack_trace: String, deny_warnings: bool, ) -> Diagnostic { @@ -253,19 +246,18 @@ fn convert_diagnostic( _ => Diagnostic::error(), }; - let secondary_labels = if let Some(file_id) = file { - cd.secondaries - .iter() - .map(|sl| { - let start_span = sl.span.start() as usize; - let end_span = sl.span.end() as usize; - let file = sl.file.unwrap_or(file_id); - Label::secondary(file, start_span..end_span).with_message(&sl.message) - }) - .collect() - } else { - vec![] - }; + let secondary_labels = cd + .secondaries + .iter() + .map(|custom_label| { + let location = custom_label.location; + let span = location.span; + let start_span = span.start() as usize; + let end_span = span.end() as usize; + let file = location.file; + Label::secondary(file, start_span..end_span).with_message(&custom_label.message) + }) + .collect(); let mut notes = cd.notes.clone(); notes.push(stack_trace); diff --git a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml index 76aa0f50e8a2..f9cc2e7b3bf5 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml @@ -20,6 +20,7 @@ fxhash.workspace = true iter-extended.workspace = true thiserror.workspace = true num-bigint = "0.4" +num-traits.workspace = true im.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/acir_variable.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/acir_variable.rs index a4e81de3d5cd..b8f5edaa3cd6 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/acir_variable.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/acir_variable.rs @@ -1,13 +1,13 @@ use acvm::{ + BlackBoxFunctionSolver, acir::{ + AcirField, BlackBoxFunc, circuit::{ - opcodes::{AcirFunctionId, BlockId, BlockType, MemOp}, AssertionPayload, ExpressionOrMemory, ExpressionWidth, Opcode, + opcodes::{AcirFunctionId, BlockId, BlockType, MemOp}, }, native_types::{Expression, Witness}, - AcirField, BlackBoxFunc, }, - BlackBoxFunctionSolver, }; use fxhash::FxHashMap as HashMap; use iter_extended::{try_vecmap, vecmap}; @@ -22,7 +22,7 @@ use crate::ssa::ir::{ use super::big_int::BigIntContext; use super::generated_acir::{BrilligStdlibFunc, GeneratedAcir, PLACEHOLDER_BRILLIG_INDEX}; -use super::{brillig_directive, AcirDynamicArray, AcirValue}; +use super::{AcirDynamicArray, AcirValue, brillig_directive}; #[derive(Clone, Debug, PartialEq, Eq, Hash)] /// High level Type descriptor for Variables. @@ -80,7 +80,7 @@ impl From for AcirType { } } -impl<'a> From<&'a SsaType> for AcirType { +impl From<&SsaType> for AcirType { fn from(value: &SsaType) -> Self { match value { SsaType::Numeric(numeric_type) => AcirType::NumericType(*numeric_type), @@ -278,7 +278,7 @@ impl> AcirContext { let var_data = match self.vars.get(&var) { Some(var_data) => var_data, None => { - return Err(InternalError::UndeclaredAcirVar { call_stack: self.get_call_stack() }) + return Err(InternalError::UndeclaredAcirVar { call_stack: self.get_call_stack() }); } }; Ok(var_data.to_expression().into_owned()) @@ -777,7 +777,8 @@ impl> AcirContext { pub(crate) fn not_var(&mut self, x: AcirVar, typ: AcirType) -> Result { let bit_size = typ.bit_size::(); // Subtracting from max flips the bits - let max = self.add_constant((1_u128 << bit_size) - 1); + let max = power_of_two::(bit_size) - F::one(); + let max = self.add_constant(max); self.sub_var(max, x) } @@ -841,18 +842,19 @@ impl> AcirContext { } // maximum bit size for q and for [r and rhs] - let mut max_q_bits = bit_size; - let mut max_rhs_bits = bit_size; - // when rhs is constant, we can better estimate the maximum bit sizes - if let Some(rhs_const) = rhs_expr.to_const() { - max_rhs_bits = rhs_const.num_bits(); - if max_rhs_bits != 0 { - if max_rhs_bits > bit_size { - return Ok((zero, zero)); - } - max_q_bits = bit_size - max_rhs_bits + 1; - } - } + let (max_q_bits, max_rhs_bits) = if let Some(rhs_const) = rhs_expr.to_const() { + // when rhs is constant, we can better estimate the maximum bit sizes + let max_rhs_bits = rhs_const.num_bits(); + assert!( + max_rhs_bits <= bit_size, + "attempted to divide by constant larger than operand type" + ); + + let max_q_bits = bit_size - max_rhs_bits + 1; + (max_q_bits, max_rhs_bits) + } else { + (bit_size, bit_size) + }; let [q_value, r_value]: [AcirValue; 2] = self .brillig_call( @@ -908,19 +910,9 @@ impl> AcirContext { self.assert_eq_var(lhs_constraint, rhs_constraint, None)?; // Avoids overflow: 'q*b+r < 2^max_q_bits*2^max_rhs_bits' - let mut avoid_overflow = false; if max_q_bits + max_rhs_bits >= F::max_num_bits() - 1 { // q*b+r can overflow; we avoid this when b is constant - if rhs_expr.is_const() { - avoid_overflow = true; - } else { - // we do not support unbounded division - unreachable!("overflow in unbounded division"); - } - } - - if let Some(rhs_const) = rhs_expr.to_const() { - if avoid_overflow { + if let Some(rhs_const) = rhs_expr.to_const() { // we compute q0 = p/rhs let rhs_big = BigUint::from_bytes_be(&rhs_const.to_be_bytes()); let q0_big = F::modulus() / &rhs_big; @@ -944,6 +936,20 @@ impl> AcirContext { predicate, rhs_const.num_bits(), )?; + } else if bit_size == 128 { + // q and b are u128 and q*b could overflow so we check that either q or b are less than 2^64 + let two_pow_64: F = power_of_two(64); + let two_pow_64 = self.add_constant(two_pow_64); + + let (q_upper, _) = + self.euclidean_division_var(quotient_var, two_pow_64, bit_size, predicate)?; + let (rhs_upper, _) = + self.euclidean_division_var(rhs, two_pow_64, bit_size, predicate)?; + let mul_uppers = self.mul_var(q_upper, rhs_upper)?; + self.assert_eq_var(mul_uppers, zero, None)?; + } else { + // we do not support unbounded division + unreachable!("overflow in unbounded division"); } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/black_box.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/black_box.rs index 7b386d6c1886..a75d53cf10a3 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/black_box.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/black_box.rs @@ -1,9 +1,9 @@ use acvm::{ + BlackBoxFunctionSolver, acir::{ - circuit::opcodes::{ConstantOrWitnessEnum, FunctionInput}, AcirField, BlackBoxFunc, + circuit::opcodes::{ConstantOrWitnessEnum, FunctionInput}, }, - BlackBoxFunctionSolver, }; use iter_extended::vecmap; use num_bigint::BigUint; @@ -11,8 +11,8 @@ use num_bigint::BigUint; use crate::errors::{InternalError, RuntimeError}; use super::{ - acir_variable::{AcirContext, AcirVar}, AcirValue, + acir_variable::{AcirContext, AcirVar}, }; impl> AcirContext { @@ -35,7 +35,7 @@ impl> AcirContext { name: "poseidon_2_permutation call".to_string(), arg: "length".to_string(), call_stack: self.get_call_stack(), - })) + })); } }; @@ -45,7 +45,7 @@ impl> AcirContext { return Err(RuntimeError::InternalError(InternalError::NotAConstant { name: "length".to_string(), call_stack: self.get_call_stack(), - })) + })); } }; @@ -160,7 +160,7 @@ impl> AcirContext { name: "verify proof".to_string(), arg: "proof type".to_string(), call_stack: self.get_call_stack(), - })) + })); } }; @@ -170,7 +170,7 @@ impl> AcirContext { return Err(RuntimeError::InternalError(InternalError::NotAConstant { name: "proof type".to_string(), call_stack: self.get_call_stack(), - })) + })); } }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_call.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_call.rs index f027a7b62f80..020c60968a13 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_call.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_call.rs @@ -1,12 +1,12 @@ use acvm::{ + BlackBoxFunctionSolver, acir::{ + AcirField, brillig::Opcode as BrilligOpcode, circuit::brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}, native_types::{Expression, Witness}, - AcirField, }, - brillig_vm::{MemoryValue, VMStatus, VM}, - BlackBoxFunctionSolver, + brillig_vm::{MemoryValue, VM, VMStatus}, }; use iter_extended::{try_vecmap, vecmap}; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_directive.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_directive.rs index 89fc7f1eda5d..5fab9e345231 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_directive.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_directive.rs @@ -1,9 +1,9 @@ use acvm::acir::{ + AcirField, brillig::{ BinaryFieldOp, BinaryIntOp, BitSize, HeapVector, IntegerBitSize, MemoryAddress, Opcode as BrilligOpcode, }, - AcirField, }; use crate::brillig::brillig_ir::artifact::GeneratedBrillig; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/generated_acir.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/generated_acir.rs index 141c9f367c2c..25b4771048c9 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/generated_acir.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/generated_acir.rs @@ -3,21 +3,21 @@ use std::collections::BTreeMap; use acvm::acir::{ + AcirField, BlackBoxFunc, circuit::{ + AssertionPayload, BrilligOpcodeLocation, ErrorSelector, OpcodeLocation, brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}, opcodes::{BlackBoxFuncCall, FunctionInput, Opcode as AcirOpcode}, - AssertionPayload, BrilligOpcodeLocation, ErrorSelector, OpcodeLocation, }, native_types::{Expression, Witness}, - AcirField, BlackBoxFunc, }; use super::brillig_directive; use crate::{ + ErrorType, brillig::brillig_ir::artifact::GeneratedBrillig, errors::{InternalError, RuntimeError, SsaReport}, ssa::ir::call_stack::CallStack, - ErrorType, }; use iter_extended::vecmap; @@ -788,7 +788,10 @@ fn intrinsics_check_inputs(name: BlackBoxFunc, input_count: usize) { None => return, }; - assert_eq!(expected_num_inputs,input_count,"Tried to call black box function {name} with {input_count} inputs, but this function's definition requires {expected_num_inputs} inputs"); + assert_eq!( + expected_num_inputs, input_count, + "Tried to call black box function {name} with {input_count} inputs, but this function's definition requires {expected_num_inputs} inputs" + ); } /// Checks that the number of outputs being used to call the blackbox function @@ -818,5 +821,8 @@ fn intrinsics_check_outputs(name: BlackBoxFunc, output_count: usize) { None => return, }; - assert_eq!(expected_num_outputs,output_count,"Tried to call black box function {name} with {output_count} outputs, but this function's definition requires {expected_num_outputs} outputs"); + assert_eq!( + expected_num_outputs, output_count, + "Tried to call black box function {name} with {output_count} outputs, but this function's definition requires {expected_num_outputs} outputs" + ); } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/mod.rs index dc6b3826b60e..a2c4913b5e7a 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/mod.rs @@ -5,15 +5,15 @@ use std::collections::{BTreeMap, HashSet}; use std::fmt::Debug; use acvm::acir::{ + BlackBoxFunc, circuit::{ + AssertionPayload, ErrorSelector, ExpressionWidth, OpcodeLocation, brillig::{BrilligBytecode, BrilligFunctionId}, opcodes::{AcirFunctionId, BlockType}, - AssertionPayload, ErrorSelector, ExpressionWidth, OpcodeLocation, }, native_types::Witness, - BlackBoxFunc, }; -use acvm::{acir::circuit::opcodes::BlockId, acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField, acir::circuit::opcodes::BlockId}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use iter_extended::{try_vecmap, vecmap}; use noirc_frontend::monomorphization::ast::InlineType; @@ -25,12 +25,12 @@ mod brillig_call; mod brillig_directive; mod generated_acir; -use crate::brillig::brillig_gen::gen_brillig_for; use crate::brillig::BrilligOptions; +use crate::brillig::brillig_gen::gen_brillig_for; use crate::brillig::{ + Brillig, brillig_gen::brillig_fn::FunctionContext as BrilligFunctionContext, brillig_ir::artifact::{BrilligParameter, GeneratedBrillig}, - Brillig, }; use crate::errors::{InternalError, InternalWarning, RuntimeError, SsaReport}; use crate::ssa::ir::instruction::Hint; @@ -51,7 +51,7 @@ use crate::ssa::{ }, ssa_gen::Ssa, }; -use acir_variable::{power_of_two, AcirContext, AcirType, AcirVar}; +use acir_variable::{AcirContext, AcirType, AcirVar, power_of_two}; use generated_acir::BrilligStdlibFunc; pub(crate) use generated_acir::GeneratedAcir; use noirc_frontend::hir_def::types::Type as HirType; @@ -404,11 +404,15 @@ impl<'a> Context<'a> { match inline_type { InlineType::Inline | InlineType::InlineAlways => { if function.id() != ssa.main_id { - panic!("ACIR function should have been inlined earlier if not marked otherwise"); + panic!( + "ACIR function should have been inlined earlier if not marked otherwise" + ); } } InlineType::NoPredicates => { - panic!("All ACIR functions marked with #[no_predicates] should be inlined before ACIR gen. This is an SSA exclusive codegen attribute"); + panic!( + "All ACIR functions marked with #[no_predicates] should be inlined before ACIR gen. This is an SSA exclusive codegen attribute" + ); } InlineType::Fold => {} } @@ -863,7 +867,11 @@ impl<'a> Context<'a> { let func = &ssa.functions[id]; match func.runtime() { RuntimeType::Acir(inline_type) => { - assert!(!matches!(inline_type, InlineType::Inline), "ICE: Got an ACIR function named {} that should have already been inlined", func.name()); + assert!( + !matches!(inline_type, InlineType::Inline), + "ICE: Got an ACIR function named {} that should have already been inlined", + func.name() + ); let inputs = vecmap(arguments, |arg| self.convert_value(*arg, dfg)); let output_count = result_ids @@ -874,7 +882,9 @@ impl<'a> Context<'a> { .sum(); let Some(acir_function_id) = ssa.get_entry_point_index(id) else { - unreachable!("Expected an associated final index for call to acir function {id} with args {arguments:?}"); + unreachable!( + "Expected an associated final index for call to acir function {id} with args {arguments:?}" + ); }; let output_vars = self.acir_context.call_acir_function( @@ -956,7 +966,11 @@ impl<'a> Context<'a> { }; // Compiler sanity check - assert_eq!(result_ids.len(), output_values.len(), "ICE: The number of Brillig output values should match the result ids in SSA"); + assert_eq!( + result_ids.len(), + output_values.len(), + "ICE: The number of Brillig output values should match the result ids in SSA" + ); self.handle_ssa_call_outputs(result_ids, output_values, dfg)?; } @@ -1076,7 +1090,7 @@ impl<'a> Context<'a> { found: format!("Instead got {:?}", dfg[instruction]), call_stack: self.acir_context.get_call_stack(), } - .into()) + .into()); } }; // Ensure that array id is fully resolved. @@ -1477,7 +1491,7 @@ impl<'a> Context<'a> { found: format!("Instead got {:?}", dfg[instruction]), call_stack: self.acir_context.get_call_stack(), } - .into()) + .into()); } }; @@ -1525,7 +1539,11 @@ impl<'a> Context<'a> { let value_types = self.convert_value(array, dfg).flat_numeric_types(); // Compiler sanity check - assert_eq!(value_types.len(), array_len, "ICE: The length of the flattened type array should match the length of the dynamic array"); + assert_eq!( + value_types.len(), + array_len, + "ICE: The length of the flattened type array should match the length of the dynamic array" + ); let result_value = AcirValue::DynamicArray(AcirDynamicArray { block_id: result_block_id, @@ -1675,7 +1693,7 @@ impl<'a> Context<'a> { found: format!("{:?}", array_acir_value), call_stack: self.acir_context.get_call_stack(), } - .into()) + .into()); } } } @@ -1685,7 +1703,7 @@ impl<'a> Context<'a> { found: format!("{:?}", &dfg[array_id]), call_stack: self.acir_context.get_call_stack(), } - .into()) + .into()); } }; } @@ -1973,26 +1991,7 @@ impl<'a> Context<'a> { ) -> Result { let lhs = self.convert_numeric_value(binary.lhs, dfg)?; let rhs = self.convert_numeric_value(binary.rhs, dfg)?; - let binary_type = self.type_of_binary_operation(binary, dfg); - match &binary_type { - Type::Numeric(NumericType::Unsigned { bit_size }) - | Type::Numeric(NumericType::Signed { bit_size }) => { - // Conservative max bit size that is small enough such that two operands can be - // multiplied and still fit within the field modulus. This is necessary for the - // truncation technique: result % 2^bit_size to be valid. - let max_integer_bit_size = FieldElement::max_num_bits() / 2; - if *bit_size > max_integer_bit_size { - return Err(RuntimeError::UnsupportedIntegerSize { - num_bits: *bit_size, - max_num_bits: max_integer_bit_size, - call_stack: self.acir_context.get_call_stack(), - }); - } - } - _ => {} - } - let binary_type = AcirType::from(binary_type); let bit_count = binary_type.bit_size::(); let num_type = binary_type.to_numeric_type(); @@ -2113,6 +2112,12 @@ impl<'a> Context<'a> { max_bit_size: u32, dfg: &DataFlowGraph, ) -> Result { + assert_ne!(bit_size, max_bit_size, "Attempted to generate a noop truncation"); + assert!( + bit_size < max_bit_size, + "Attempted to generate a truncation into size larger than max input" + ); + let mut var = self.convert_numeric_value(value_id, dfg)?; match &dfg[value_id] { Value::Instruction { instruction, .. } => { @@ -2192,7 +2197,9 @@ impl<'a> Context<'a> { Ok(self.convert_vars_to_values(vars, dfg, result_ids)) } Intrinsic::ApplyRangeConstraint => { - unreachable!("ICE: `Intrinsic::ApplyRangeConstraint` calls should be transformed into an `Instruction::RangeCheck`"); + unreachable!( + "ICE: `Intrinsic::ApplyRangeConstraint` calls should be transformed into an `Instruction::RangeCheck`" + ); } Intrinsic::ToRadix(endian) => { let field = self.convert_value(arguments[0], dfg).into_var()?; @@ -2895,15 +2902,15 @@ fn can_omit_element_sizes_array(array_typ: &Type) -> bool { mod test { use acvm::{ + FieldElement, acir::{ circuit::{ + ExpressionWidth, Opcode, OpcodeLocation, brillig::BrilligFunctionId, opcodes::{AcirFunctionId, BlackBoxFuncCall}, - ExpressionWidth, Opcode, OpcodeLocation, }, native_types::Witness, }, - FieldElement, }; use noirc_errors::Location; use noirc_frontend::monomorphization::ast::InlineType; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs index 957ebc2b0695..64160528148d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs @@ -12,11 +12,11 @@ use fxhash::FxHashMap as HashMap; use self::{brillig_block::BrilligBlock, brillig_fn::FunctionContext}; use super::{ + Brillig, BrilligOptions, BrilligVariable, ValueId, brillig_ir::{ - artifact::{BrilligArtifact, BrilligParameter, GeneratedBrillig, Label}, BrilligContext, + artifact::{BrilligArtifact, BrilligParameter, GeneratedBrillig, Label}, }, - Brillig, BrilligOptions, BrilligVariable, ValueId, }; use crate::{ errors::InternalError, @@ -88,7 +88,7 @@ pub(crate) fn gen_brillig_for( return Err(InternalError::General { message: format!("Cannot find linked fn {unresolved_fn_label}"), call_stack: CallStack::new(), - }) + }); } }; entry_point.link_with(artifact); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs index 1fc39b582232..cdc6df262402 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs @@ -1,14 +1,14 @@ use acvm::{ + AcirField, acir::{ - brillig::{BlackBoxOp, HeapVector, ValueOrArray}, BlackBoxFunc, + brillig::{BlackBoxOp, HeapVector, ValueOrArray}, }, - AcirField, }; use crate::brillig::brillig_ir::{ - brillig_variable::BrilligVariable, debug_show::DebugToString, registers::RegisterAllocator, - BrilligContext, + BrilligContext, brillig_variable::BrilligVariable, debug_show::DebugToString, + registers::RegisterAllocator, }; /// Transforms SSA's black box function calls into the corresponding brillig instructions @@ -83,7 +83,12 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::BrilligArray(public_key_x), BrilligVariable::BrilligArray(public_key_y), BrilligVariable::BrilligArray(signature), message], + [ + BrilligVariable::BrilligArray(public_key_x), + BrilligVariable::BrilligArray(public_key_y), + BrilligVariable::BrilligArray(signature), + message, + ], [BrilligVariable::SingleAddr(result_register)], ) = (function_arguments, function_results) { @@ -114,7 +119,12 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::BrilligArray(public_key_x), BrilligVariable::BrilligArray(public_key_y), BrilligVariable::BrilligArray(signature), message], + [ + BrilligVariable::BrilligArray(public_key_x), + BrilligVariable::BrilligArray(public_key_y), + BrilligVariable::BrilligArray(signature), + message, + ], [BrilligVariable::SingleAddr(result_register)], ) = (function_arguments, function_results) { @@ -168,7 +178,14 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::SingleAddr(input1_x), BrilligVariable::SingleAddr(input1_y), BrilligVariable::SingleAddr(input1_infinite), BrilligVariable::SingleAddr(input2_x), BrilligVariable::SingleAddr(input2_y), BrilligVariable::SingleAddr(input2_infinite)], + [ + BrilligVariable::SingleAddr(input1_x), + BrilligVariable::SingleAddr(input1_y), + BrilligVariable::SingleAddr(input1_infinite), + BrilligVariable::SingleAddr(input2_x), + BrilligVariable::SingleAddr(input2_y), + BrilligVariable::SingleAddr(input2_infinite), + ], [BrilligVariable::BrilligArray(result_array)], ) = (function_arguments, function_results) { @@ -202,7 +219,12 @@ pub(crate) fn convert_black_box_call {} BlackBoxFunc::BigIntAdd => { if let ( - [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(_lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(_rhs_modulus)], + [ + BrilligVariable::SingleAddr(lhs), + BrilligVariable::SingleAddr(_lhs_modulus), + BrilligVariable::SingleAddr(rhs), + BrilligVariable::SingleAddr(_rhs_modulus), + ], [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(_modulus_id)], ) = (function_arguments, function_results) { @@ -219,7 +241,12 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(_lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(_rhs_modulus)], + [ + BrilligVariable::SingleAddr(lhs), + BrilligVariable::SingleAddr(_lhs_modulus), + BrilligVariable::SingleAddr(rhs), + BrilligVariable::SingleAddr(_rhs_modulus), + ], [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(_modulus_id)], ) = (function_arguments, function_results) { @@ -236,7 +263,12 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(_lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(_rhs_modulus)], + [ + BrilligVariable::SingleAddr(lhs), + BrilligVariable::SingleAddr(_lhs_modulus), + BrilligVariable::SingleAddr(rhs), + BrilligVariable::SingleAddr(_rhs_modulus), + ], [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(_modulus_id)], ) = (function_arguments, function_results) { @@ -253,7 +285,12 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(_lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(_rhs_modulus)], + [ + BrilligVariable::SingleAddr(lhs), + BrilligVariable::SingleAddr(_lhs_modulus), + BrilligVariable::SingleAddr(rhs), + BrilligVariable::SingleAddr(_rhs_modulus), + ], [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(_modulus_id)], ) = (function_arguments, function_results) { @@ -326,12 +363,17 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::BrilligArray(input_array), BrilligVariable::BrilligArray(hash_values)], + [ + BrilligVariable::BrilligArray(input_array), + BrilligVariable::BrilligArray(hash_values), + ], [BrilligVariable::BrilligArray(result_array)], ) = (function_arguments, function_results) { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 40dd825be35d..d8f1f9d0997e 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -1,11 +1,11 @@ use crate::brillig::brillig_ir::artifact::Label; use crate::brillig::brillig_ir::brillig_variable::{ - type_to_heap_value_type, BrilligArray, BrilligVariable, SingleAddrVariable, + BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable, type_to_heap_value_type, }; use crate::brillig::brillig_ir::registers::RegisterAllocator; use crate::brillig::brillig_ir::{ - BrilligBinaryOp, BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, + BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, BrilligBinaryOp, BrilligContext, ReservedRegisters, }; use crate::ssa::ir::call_stack::CallStack; use crate::ssa::ir::instruction::{ConstrainError, Hint}; @@ -21,7 +21,7 @@ use crate::ssa::ir::{ }; use acvm::acir::brillig::{MemoryAddress, ValueOrArray}; use acvm::brillig_vm::MEMORY_ADDRESSING_BIT_SIZE; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use iter_extended::vecmap; use num_bigint::BigUint; @@ -29,7 +29,7 @@ use std::collections::BTreeSet; use std::sync::Arc; use super::brillig_black_box::convert_black_box_call; -use super::brillig_block_variables::{allocate_value_with_type, BlockVariables}; +use super::brillig_block_variables::{BlockVariables, allocate_value_with_type}; use super::brillig_fn::FunctionContext; use super::brillig_globals::HoistedConstantsToBrilligGlobals; use super::constant_allocation::InstructionLocation; @@ -460,7 +460,9 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { element_size, ); } else { - unreachable!("ICE: a vector must be preceded by a register containing its length"); + unreachable!( + "ICE: a vector must be preceded by a register containing its length" + ); } self.brillig_context.deallocate_heap_vector(*heap_vector); } @@ -769,21 +771,28 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { // Slice access checks are generated separately against the slice's dynamic length field. if matches!(dfg.type_of_value(*array), Type::Array(..)) - && !dfg.is_safe_index(*index, *array) + && !dfg.is_safe_brillig_index(*index, *array) { self.validate_array_index(array_variable, index_variable); } - let items_pointer = - self.brillig_context.codegen_make_array_or_vector_items_pointer(array_variable); - - self.brillig_context.codegen_load_with_offset( - items_pointer, - index_variable, - destination_variable.extract_register(), - ); - - self.brillig_context.deallocate_register(items_pointer); + if dfg.is_constant(*index) { + self.brillig_context.codegen_load_with_offset( + array_variable.extract_register(), + index_variable, + destination_variable.extract_register(), + ); + } else { + let items_pointer = self + .brillig_context + .codegen_make_array_or_vector_items_pointer(array_variable); + self.brillig_context.codegen_load_with_offset( + items_pointer, + index_variable, + destination_variable.extract_register(), + ); + self.brillig_context.deallocate_register(items_pointer); + } } Instruction::ArraySet { array, index, value, mutable } => { let source_variable = self.convert_ssa_value(*array, dfg); @@ -926,8 +935,64 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { Instruction::EnableSideEffectsIf { .. } => { unreachable!("enable_side_effects not supported by brillig") } - Instruction::IfElse { .. } => { - unreachable!("IfElse instructions should not be possible in brillig") + Instruction::IfElse { then_condition, then_value, else_condition: _, else_value } => { + let then_condition = self.convert_ssa_single_addr_value(*then_condition, dfg); + let then_value = self.convert_ssa_value(*then_value, dfg); + let else_value = self.convert_ssa_value(*else_value, dfg); + let result = self.variables.define_variable( + self.function_context, + self.brillig_context, + dfg.instruction_results(instruction_id)[0], + dfg, + ); + match (then_value, else_value) { + ( + BrilligVariable::SingleAddr(then_address), + BrilligVariable::SingleAddr(else_address), + ) => { + self.brillig_context.conditional_move_instruction( + then_condition, + then_address, + else_address, + result.extract_single_addr(), + ); + } + ( + BrilligVariable::BrilligArray(then_array), + BrilligVariable::BrilligArray(else_array), + ) => { + // Pointer to the array which result from the if-else + let pointer = self.brillig_context.allocate_register(); + self.brillig_context.conditional_move_instruction( + then_condition, + SingleAddrVariable::new_usize(then_array.pointer), + SingleAddrVariable::new_usize(else_array.pointer), + SingleAddrVariable::new_usize(pointer), + ); + let if_else_array = BrilligArray { pointer, size: then_array.size }; + // Copy the if-else array to the result + self.brillig_context + .call_array_copy_procedure(if_else_array, result.extract_array()); + } + ( + BrilligVariable::BrilligVector(then_vector), + BrilligVariable::BrilligVector(else_vector), + ) => { + // Pointer to the vector which result from the if-else + let pointer = self.brillig_context.allocate_register(); + self.brillig_context.conditional_move_instruction( + then_condition, + SingleAddrVariable::new_usize(then_vector.pointer), + SingleAddrVariable::new_usize(else_vector.pointer), + SingleAddrVariable::new_usize(pointer), + ); + let if_else_vector = BrilligVector { pointer }; + // Copy the if-else vector to the result + self.brillig_context + .call_vector_copy_procedure(if_else_vector, result.extract_vector()); + } + _ => unreachable!("ICE - then and else values must have the same type"), + } } Instruction::MakeArray { elements: array, typ } => { let value_id = dfg.instruction_results(instruction_id)[0]; @@ -1432,7 +1497,9 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { NumericType::Unsigned { .. } => (false, false), NumericType::NativeField => (true, false), }, - _ => unreachable!("only numeric types are allowed in binary operations. References are handled separately"), + _ => unreachable!( + "only numeric types are allowed in binary operations. References are handled separately" + ), }; let brillig_binary_op = match binary.operator { @@ -1991,14 +2058,21 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { self.allocate_foreign_call_result_array(element_type, inner_array); // We add one since array.pointer points to [RC, ...items] - let idx = - self.brillig_context.make_usize_constant_instruction((index + 1).into() ); - self.brillig_context.codegen_store_with_offset(array.pointer, idx, inner_array.pointer); + let idx = self + .brillig_context + .make_usize_constant_instruction((index + 1).into()); + self.brillig_context.codegen_store_with_offset( + array.pointer, + idx, + inner_array.pointer, + ); self.brillig_context.deallocate_single_addr(idx); self.brillig_context.deallocate_register(inner_array.pointer); } - Type::Slice(_) => unreachable!("ICE: unsupported slice type in allocate_nested_array(), expects an array or a numeric type"), + Type::Slice(_) => unreachable!( + "ICE: unsupported slice type in allocate_nested_array(), expects an array or a numeric type" + ), _ => (), } index += 1; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs index 4cf8e9214833..027994a11de4 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs @@ -3,12 +3,12 @@ use fxhash::FxHashSet as HashSet; use crate::{ brillig::brillig_ir::{ + BrilligContext, brillig_variable::{ - get_bit_size_from_ssa_type, BrilligArray, BrilligVariable, BrilligVector, - SingleAddrVariable, + BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable, + get_bit_size_from_ssa_type, }, registers::RegisterAllocator, - BrilligContext, }, ssa::ir::{ dfg::DataFlowGraph, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs index d2656354b566..6ff85b9ebe8e 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs @@ -3,7 +3,7 @@ use iter_extended::vecmap; use crate::{ brillig::brillig_ir::{ artifact::BrilligParameter, - brillig_variable::{get_bit_size_from_ssa_type, BrilligVariable}, + brillig_variable::{BrilligVariable, get_bit_size_from_ssa_type}, }, ssa::ir::{ basic_block::BasicBlockId, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_globals.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_globals.rs index 66f0980ac639..317f763078e1 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_globals.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_globals.rs @@ -8,7 +8,7 @@ use super::{ }; use crate::brillig::{Brillig, BrilligOptions, FunctionId}; use crate::{ - brillig::{brillig_ir::BrilligContext, ConstantAllocation, DataFlowGraph}, + brillig::{ConstantAllocation, DataFlowGraph, brillig_ir::BrilligContext}, ssa::ir::types::NumericType, ssa::opt::brillig_entry_points::{build_inner_call_to_entry_points, get_brillig_entry_points}, }; @@ -147,11 +147,7 @@ impl BrilligGlobals { .iter() .filter_map( |(&value, &num_occurrences)| { - if num_occurrences > 1 { - Some(value) - } else { - None - } + if num_occurrences > 1 { Some(value) } else { None } }, ) .collect(); @@ -280,12 +276,12 @@ pub(crate) fn convert_ssa_globals( #[cfg(test)] mod tests { use acvm::{ - acir::brillig::{BitSize, IntegerBitSize, Opcode}, FieldElement, + acir::brillig::{BitSize, IntegerBitSize, Opcode}, }; use crate::brillig::{ - brillig_ir::registers::RegisterAllocator, BrilligOptions, GlobalSpace, LabelType, Ssa, + BrilligOptions, GlobalSpace, LabelType, Ssa, brillig_ir::registers::RegisterAllocator, }; use super::ConstantAllocation; @@ -434,6 +430,8 @@ mod tests { "; let ssa = Ssa::from_str(src).unwrap(); + // Need to run SSA pass that sets up Brillig array gets + let ssa = ssa.brillig_array_gets(); // Need to run DIE to generate the used globals map, which is necessary for Brillig globals generation. let mut ssa = ssa.dead_instruction_elimination(); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index 99645f84ed3d..6387dc3dd371 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -1,14 +1,14 @@ use acvm::acir::brillig::MemoryAddress; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, brillig_variable::{BrilligVariable, BrilligVector, SingleAddrVariable}, registers::RegisterAllocator, - BrilligBinaryOp, }; use super::brillig_block::BrilligBlock; -impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { +impl BrilligBlock<'_, Registers> { fn write_variables(&mut self, write_pointer: MemoryAddress, variables: &[BrilligVariable]) { for (index, variable) in variables.iter().enumerate() { self.brillig_context.store_instruction(write_pointer, variable.extract_register()); @@ -163,6 +163,7 @@ mod tests { use fxhash::FxHashMap as HashMap; use noirc_frontend::monomorphization::ast::InlineType; + use crate::brillig::ValueId; use crate::brillig::brillig_gen::brillig_block::BrilligBlock; use crate::brillig::brillig_gen::brillig_block_variables::BlockVariables; use crate::brillig::brillig_gen::brillig_fn::FunctionContext; @@ -174,8 +175,7 @@ mod tests { use crate::brillig::brillig_ir::tests::{ create_and_run_vm, create_context, create_entry_point_bytecode, }; - use crate::brillig::brillig_ir::{BrilligContext, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE}; - use crate::brillig::ValueId; + use crate::brillig::brillig_ir::{BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, BrilligContext}; use crate::ssa::function_builder::FunctionBuilder; use crate::ssa::ir::function::RuntimeType; use crate::ssa::ir::map::Id; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index 95e6c5175b2d..a0637e8e3c25 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -32,8 +32,8 @@ use registers::{RegisterAllocator, ScratchSpace}; use self::{artifact::BrilligArtifact, debug_show::DebugToString, registers::Stack}; use crate::ssa::ir::call_stack::CallStack; use acvm::{ - acir::brillig::{MemoryAddress, Opcode as BrilligOpcode}, AcirField, + acir::brillig::{MemoryAddress, Opcode as BrilligOpcode}, }; use debug_show::DebugShow; @@ -281,11 +281,11 @@ pub(crate) mod tests { ValueOrArray, }; use acvm::brillig_vm::brillig::HeapValueType; - use acvm::brillig_vm::{VMStatus, VM}; + use acvm::brillig_vm::{VM, VMStatus}; use acvm::{BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement}; - use crate::brillig::brillig_ir::{BrilligBinaryOp, BrilligContext}; use crate::brillig::BrilligOptions; + use crate::brillig::brillig_ir::{BrilligBinaryOp, BrilligContext}; use crate::ssa::ir::function::FunctionId; use super::artifact::{BrilligParameter, GeneratedBrillig, Label, LabelType}; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs index c9223715042f..42052c092306 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs @@ -2,8 +2,8 @@ use acvm::acir::brillig::Opcode as BrilligOpcode; use acvm::acir::circuit::ErrorSelector; use std::collections::{BTreeMap, HashMap}; -use crate::ssa::ir::{basic_block::BasicBlockId, call_stack::CallStack, function::FunctionId}; use crate::ErrorType; +use crate::ssa::ir::{basic_block::BasicBlockId, call_stack::CallStack, function::FunctionId}; use super::procedures::ProcedureId; @@ -304,25 +304,37 @@ impl BrilligArtifact { let jump_instruction = self.byte_code[*location_of_jump].clone(); match jump_instruction { BrilligOpcode::Jump { location } => { - assert_eq!(location, 0, "location is not zero, which means that the jump label does not need resolving"); + assert_eq!( + location, 0, + "location is not zero, which means that the jump label does not need resolving" + ); self.byte_code[*location_of_jump] = BrilligOpcode::Jump { location: resolved_location }; } BrilligOpcode::JumpIfNot { condition, location } => { - assert_eq!(location, 0, "location is not zero, which means that the jump label does not need resolving"); + assert_eq!( + location, 0, + "location is not zero, which means that the jump label does not need resolving" + ); self.byte_code[*location_of_jump] = BrilligOpcode::JumpIfNot { condition, location: resolved_location }; } BrilligOpcode::JumpIf { condition, location } => { - assert_eq!(location, 0, "location is not zero, which means that the jump label does not need resolving"); + assert_eq!( + location, 0, + "location is not zero, which means that the jump label does not need resolving" + ); self.byte_code[*location_of_jump] = BrilligOpcode::JumpIf { condition, location: resolved_location }; } BrilligOpcode::Call { location } => { - assert_eq!(location, 0, "location is not zero, which means that the call label does not need resolving"); + assert_eq!( + location, 0, + "location is not zero, which means that the call label does not need resolving" + ); self.byte_code[*location_of_jump] = BrilligOpcode::Call { location: resolved_location }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs index 0bb18448670a..2a46cae2d241 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs @@ -1,7 +1,7 @@ use acvm::{ - acir::{brillig::BitSize, AcirField}, - brillig_vm::brillig::{HeapValueType, MemoryAddress}, FieldElement, + acir::{AcirField, brillig::BitSize}, + brillig_vm::brillig::{HeapValueType, MemoryAddress}, }; use serde::{Deserialize, Serialize}; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs index 9f573543e2a8..1fc37882e482 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs @@ -1,8 +1,8 @@ -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::{ - debug_show::DebugToString, instructions::BrilligBinaryOp, registers::RegisterAllocator, - BrilligContext, ReservedRegisters, + BrilligContext, ReservedRegisters, debug_show::DebugToString, instructions::BrilligBinaryOp, + registers::RegisterAllocator, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs index 4da3aa4d6d2e..5036326fbc97 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs @@ -1,12 +1,12 @@ -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use crate::ssa::ir::function::FunctionId; use super::{ + BrilligBinaryOp, BrilligContext, ReservedRegisters, brillig_variable::{BrilligVariable, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, Stack}, - BrilligBinaryOp, BrilligContext, ReservedRegisters, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs index 9512a60e6998..388c5f7a343e 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs @@ -1,19 +1,19 @@ use acvm::{ + AcirField, acir::{ brillig::{HeapVector, MemoryAddress}, circuit::ErrorSelector, }, - AcirField, }; use crate::ssa::ir::instruction::ErrorType; use super::{ + BrilligBinaryOp, BrilligContext, ReservedRegisters, artifact::BrilligParameter, brillig_variable::{BrilligArray, BrilligVariable, SingleAddrVariable}, debug_show::DebugToString, registers::RegisterAllocator, - BrilligBinaryOp, BrilligContext, ReservedRegisters, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs index 42c13a3c2355..3daf04861107 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs @@ -1,15 +1,15 @@ use acvm::acir::{ - brillig::{BlackBoxOp, IntegerBitSize}, AcirField, + brillig::{BlackBoxOp, IntegerBitSize}, }; use crate::brillig::brillig_ir::BrilligBinaryOp; use super::{ + BrilligContext, brillig_variable::{BrilligArray, SingleAddrVariable}, debug_show::DebugToString, registers::RegisterAllocator, - BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs index a34034bb5505..2936aea486ed 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs @@ -1,15 +1,15 @@ use acvm::{ - acir::brillig::{HeapArray, HeapVector, MemoryAddress, ValueOrArray}, AcirField, + acir::brillig::{HeapArray, HeapVector, MemoryAddress, ValueOrArray}, }; use crate::brillig::brillig_ir::BrilligBinaryOp; use super::{ + BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, BrilligContext, ReservedRegisters, brillig_variable::{BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::RegisterAllocator, - BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs index f609c3422886..a5dbc2811c3c 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs @@ -1,7 +1,7 @@ -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; -use super::{debug_show::DebugToString, registers::RegisterAllocator, BrilligContext}; +use super::{BrilligContext, debug_show::DebugToString, registers::RegisterAllocator}; impl BrilligContext { /// This function moves values from a set of registers to another set of registers. @@ -126,15 +126,15 @@ impl LoopDetector { #[cfg(test)] mod tests { use acvm::{ - acir::brillig::{MemoryAddress, Opcode}, FieldElement, + acir::brillig::{MemoryAddress, Opcode}, }; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::{ brillig::{ - brillig_ir::{artifact::Label, registers::Stack, BrilligContext}, BrilligOptions, + brillig_ir::{BrilligContext, artifact::Label, registers::Stack}, }, ssa::ir::function::FunctionId, }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index 283c0d67eb8b..c0b6492618c1 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -3,8 +3,8 @@ use super::BrilligBinaryOp; use crate::brillig::brillig_ir::ReservedRegisters; use acvm::{ - acir::brillig::{BlackBoxOp, HeapArray, HeapVector, MemoryAddress, ValueOrArray}, FieldElement, + acir::brillig::{BlackBoxOp, HeapArray, HeapVector, MemoryAddress, ValueOrArray}, }; /// Trait for converting values into debug-friendly strings. @@ -126,6 +126,24 @@ impl DebugShow { debug_println!(self.enable_debug_trace, " MOV {}, {}", destination, source); } + /// Emits a `conditional mov` instruction. + pub(crate) fn conditional_mov_instruction( + &self, + destination: MemoryAddress, + source_then: MemoryAddress, + source_else: MemoryAddress, + condition: MemoryAddress, + ) { + debug_println!( + self.enable_debug_trace, + " {} = MOV if {} then {}, else {}", + destination, + condition, + source_then, + source_else + ); + } + /// Emits a `cast` instruction. pub(crate) fn cast_instruction( &self, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs index 4a5be8bb19c6..e1450f0f1188 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs @@ -1,15 +1,15 @@ use crate::{brillig::BrilligOptions, ssa::ir::function::FunctionId}; use super::{ + BrilligBinaryOp, BrilligContext, ReservedRegisters, artifact::{BrilligArtifact, BrilligParameter}, brillig_variable::{BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::Stack, - BrilligBinaryOp, BrilligContext, ReservedRegisters, }; use acvm::acir::{ - brillig::{HeapVector, MemoryAddress}, AcirField, + brillig::{HeapVector, MemoryAddress}, }; pub(crate) const MAX_STACK_SIZE: usize = 16 * MAX_STACK_FRAME_SIZE; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs index 9dd541c71807..fad9892cfb16 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs @@ -1,23 +1,23 @@ use acvm::{ + FieldElement, acir::{ + AcirField, brillig::{ BinaryFieldOp, BinaryIntOp, BitSize, BlackBoxOp, HeapValueType, HeapVector, MemoryAddress, Opcode as BrilligOpcode, ValueOrArray, }, - AcirField, }, - FieldElement, }; use crate::ssa::ir::function::FunctionId; use super::{ + BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, BrilligContext, ReservedRegisters, artifact::{Label, UnresolvedJumpLocation}, brillig_variable::SingleAddrVariable, debug_show::DebugToString, procedures::ProcedureId, registers::RegisterAllocator, - BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; /// Low level instructions of the brillig IR, used by the brillig ir codegens and brillig_gen @@ -75,6 +75,28 @@ impl BrilligContext< ); } + /// Insert a conditional move instruction + pub(crate) fn conditional_move_instruction( + &mut self, + condition: SingleAddrVariable, + then_address: SingleAddrVariable, + else_address: SingleAddrVariable, + destination: SingleAddrVariable, + ) { + self.debug_show.conditional_mov_instruction( + destination.address, + then_address.address, + else_address.address, + condition.address, + ); + self.push_opcode(BrilligOpcode::ConditionalMov { + destination: destination.address, + source_a: then_address.address, + source_b: else_address.address, + condition: condition.address, + }); + } + fn binary( &mut self, lhs: SingleAddrVariable, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_copy.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_copy.rs index 0a6e8824223e..cccd08fe2d50 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_copy.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_copy.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligArray, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_reverse.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_reverse.rs index a5a11d61bef7..aa5d1cfeece8 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_reverse.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_reverse.rs @@ -1,12 +1,12 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligContext, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/check_max_stack_depth.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/check_max_stack_depth.rs index 4d5abe934209..fd8552903c71 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/check_max_stack_depth.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/check_max_stack_depth.rs @@ -2,11 +2,11 @@ use acvm::AcirField; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, BrilligContext, ReservedRegisters, brillig_variable::SingleAddrVariable, debug_show::DebugToString, entry_point::{MAX_STACK_FRAME_SIZE, MAX_STACK_SIZE}, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, ReservedRegisters, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mem_copy.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mem_copy.rs index cdd995424836..9f3f1daa2ac9 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mem_copy.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mem_copy.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligContext, brillig_variable::SingleAddrVariable, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mod.rs index 8e1761d0eee4..a2d00786ff8c 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mod.rs @@ -25,12 +25,12 @@ use vector_pop_back::compile_vector_pop_back_procedure; use vector_pop_front::compile_vector_pop_front_procedure; use vector_remove::compile_vector_remove_procedure; -use crate::brillig::{brillig_ir::AcirField, BrilligOptions}; +use crate::brillig::{BrilligOptions, brillig_ir::AcirField}; use super::{ + BrilligContext, artifact::{BrilligArtifact, Label}, debug_show::DebugToString, - BrilligContext, }; /// Procedures are a set of complex operations that are common in the noir language. diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_insert.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_insert.rs index 1c1a738509cb..dced68922ef3 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_insert.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_insert.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; -use super::{prepare_vector_push::reallocate_vector_for_insertion, ProcedureId}; +use super::{ProcedureId, prepare_vector_push::reallocate_vector_for_insertion}; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_push.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_push.rs index 798cf2385e5b..4d985ccb12b7 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_push.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_push.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/revert_with_string.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/revert_with_string.rs index 83d7dfc618d9..1601451ef50e 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/revert_with_string.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/revert_with_string.rs @@ -2,9 +2,9 @@ use acvm::AcirField; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligContext, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_copy.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_copy.rs index 6d2f9c4afb4f..8e53fb8d01cd 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_copy.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_copy.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_back.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_back.rs index bfc9d5128526..bd71b39f9e71 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_back.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_back.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_front.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_front.rs index 49123ca2f500..8df8f1c6b12f 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_front.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_front.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_remove.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_remove.rs index 7abc43286ee7..686fc74210a6 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_remove.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_remove.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs index 093c99dec3b5..2fc7f6fc7115 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs @@ -6,9 +6,9 @@ use iter_extended::vecmap; use crate::brillig::brillig_ir::entry_point::MAX_STACK_SIZE; use super::{ + BrilligContext, ReservedRegisters, brillig_variable::SingleAddrVariable, entry_point::{MAX_SCRATCH_SPACE, MAX_STACK_FRAME_SIZE}, - BrilligContext, ReservedRegisters, }; pub(crate) trait RegisterAllocator { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs b/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs index 94c0e0554a4d..deaefd40ae32 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs @@ -7,9 +7,9 @@ //! An Error of the former is a user Error //! //! An Error of the latter is an error in the implementation of the compiler -use acvm::FieldElement; use iter_extended::vecmap; -use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic}; +use noirc_errors::{CustomDiagnostic, Location}; +use noirc_frontend::signed_field::SignedField; use thiserror::Error; use crate::ssa::ir::{call_stack::CallStack, types::NumericType}; @@ -23,7 +23,7 @@ pub enum RuntimeError { InvalidRangeConstraint { num_bits: u32, call_stack: CallStack }, #[error("The value `{value:?}` cannot fit into `{typ}` which has range `{range}`")] IntegerOutOfBounds { - value: FieldElement, + value: SignedField, typ: NumericType, range: String, call_stack: CallStack, @@ -34,7 +34,9 @@ pub enum RuntimeError { UnInitialized { name: String, call_stack: CallStack }, #[error("Integer sized {num_bits:?} is over the max supported size of {max_num_bits:?}")] UnsupportedIntegerSize { num_bits: u32, max_num_bits: u32, call_stack: CallStack }, - #[error("Integer {value}, sized {num_bits:?}, is over the max supported size of {max_num_bits:?} for the blackbox function's inputs")] + #[error( + "Integer {value}, sized {num_bits:?}, is over the max supported size of {max_num_bits:?} for the blackbox function's inputs" + )] InvalidBlackBoxInputBitSize { value: String, num_bits: u32, @@ -59,7 +61,9 @@ pub enum RuntimeError { UnconstrainedSliceReturnToConstrained { call_stack: CallStack }, #[error("All `oracle` methods should be wrapped in an unconstrained fn")] UnconstrainedOracleReturnToConstrained { call_stack: CallStack }, - #[error("Could not resolve some references to the array. All references must be resolved at compile time")] + #[error( + "Could not resolve some references to the array. All references must be resolved at compile time" + )] UnknownReference { call_stack: CallStack }, } @@ -69,8 +73,8 @@ pub enum SsaReport { Bug(InternalBug), } -impl From for FileDiagnostic { - fn from(error: SsaReport) -> FileDiagnostic { +impl From for CustomDiagnostic { + fn from(error: SsaReport) -> CustomDiagnostic { match error { SsaReport::Warning(warning) => { let message = warning.to_string(); @@ -83,11 +87,10 @@ impl From for FileDiagnostic { }, }; let call_stack = vecmap(call_stack, |location| location); - let file_id = call_stack.last().map(|location| location.file).unwrap_or_default(); let location = call_stack.last().expect("Expected RuntimeError to have a location"); let diagnostic = - Diagnostic::simple_warning(message, secondary_message, location.span); - diagnostic.with_call_stack(call_stack).in_file(file_id) + CustomDiagnostic::simple_warning(message, secondary_message, *location); + diagnostic.with_call_stack(call_stack) } SsaReport::Bug(bug) => { let message = bug.to_string(); @@ -101,10 +104,10 @@ impl From for FileDiagnostic { InternalBug::AssertFailed { call_stack } => ("As a result, the compiled circuit is ensured to fail. Other assertions may also fail during execution".to_string(), call_stack) }; let call_stack = vecmap(call_stack, |location| location); - let file_id = call_stack.last().map(|location| location.file).unwrap_or_default(); let location = call_stack.last().expect("Expected RuntimeError to have a location"); - let diagnostic = Diagnostic::simple_bug(message, secondary_message, location.span); - diagnostic.with_call_stack(call_stack).in_file(file_id) + let diagnostic = + CustomDiagnostic::simple_bug(message, secondary_message, *location); + diagnostic.with_call_stack(call_stack) } } } @@ -178,24 +181,23 @@ impl RuntimeError { } } -impl From for FileDiagnostic { - fn from(error: RuntimeError) -> FileDiagnostic { +impl From for CustomDiagnostic { + fn from(error: RuntimeError) -> CustomDiagnostic { let call_stack = vecmap(error.call_stack(), |location| *location); - let file_id = call_stack.last().map(|location| location.file).unwrap_or_default(); let diagnostic = error.into_diagnostic(); - diagnostic.with_call_stack(call_stack).in_file(file_id) + diagnostic.with_call_stack(call_stack) } } impl RuntimeError { - fn into_diagnostic(self) -> Diagnostic { + fn into_diagnostic(self) -> CustomDiagnostic { match self { RuntimeError::InternalError(cause) => { - Diagnostic::simple_error( + CustomDiagnostic::simple_error( "Internal Consistency Evaluators Errors: \n This is likely a bug. Consider opening an issue at https://github.com/noir-lang/noir/issues".to_owned(), cause.to_string(), - noirc_errors::Span::inclusive(0, 0) + Location::dummy(), ) } RuntimeError::UnknownLoopBound { .. } => { @@ -203,10 +205,10 @@ impl RuntimeError { let location = self.call_stack().last().expect("Expected RuntimeError to have a location"); - Diagnostic::simple_error( + CustomDiagnostic::simple_error( primary_message, "If attempting to fetch the length of a slice, try converting to an array. Slices only use dynamic lengths.".to_string(), - location.span, + *location, ) } _ => { @@ -214,7 +216,7 @@ impl RuntimeError { let location = self.call_stack().last().unwrap_or_else(|| panic!("Expected RuntimeError to have a location. Error message: {message}")); - Diagnostic::simple_error(message, String::new(), location.span) + CustomDiagnostic::simple_error(message, String::new(), *location) } } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs index 74196d9a7666..935918c6b7e8 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs @@ -18,14 +18,14 @@ use crate::{ errors::{RuntimeError, SsaReport}, }; use acvm::{ + FieldElement, acir::{ circuit::{ - brillig::BrilligBytecode, Circuit, ErrorSelector, ExpressionWidth, - Program as AcirProgram, PublicInputs, + Circuit, ErrorSelector, ExpressionWidth, Program as AcirProgram, PublicInputs, + brillig::BrilligBytecode, }, native_types::Witness, }, - FieldElement, }; use ir::instruction::ErrorType; @@ -34,7 +34,7 @@ use noirc_errors::debug_info::{DebugFunctions, DebugInfo, DebugTypes, DebugVaria use noirc_frontend::ast::Visibility; use noirc_frontend::{hir_def::function::FunctionSignature, monomorphization::ast::Program}; use ssa_gen::Ssa; -use tracing::{span, Level}; +use tracing::{Level, span}; use crate::acir::{Artifacts, GeneratedAcir}; @@ -72,8 +72,8 @@ pub struct SsaEvaluatorOptions { /// Skip the check for under constrained values pub skip_underconstrained_check: bool, - /// Enable the missing Brillig call constraints check - pub enable_brillig_constraints_check: bool, + /// Skip the missing Brillig call constraints check + pub skip_brillig_constraints_check: bool, /// Enable the lookback feature of the Brillig call constraints /// check (prevents some rare false positives, leads to a slowdown @@ -132,7 +132,7 @@ pub(crate) fn optimize_into_acir( // It could happen that we inlined all calls to a given brillig function. // In that case it's unused so we can remove it. This is what we check next. .run_pass(Ssa::remove_unreachable_functions, "Removing Unreachable Functions (4th)") - .run_pass(Ssa::dead_instruction_elimination, "Dead Instruction Elimination (3rd)") + .run_pass(Ssa::dead_instruction_elimination_acir, "Dead Instruction Elimination (3rd)") .finish(); if !options.skip_underconstrained_check { @@ -143,7 +143,7 @@ pub(crate) fn optimize_into_acir( )); } - if options.enable_brillig_constraints_check { + if !options.skip_brillig_constraints_check { ssa_level_warnings.extend(time( "After Check for Missing Brillig Call Constraints", options.print_codegen_timings, @@ -211,9 +211,11 @@ fn optimize_all(builder: SsaBuilder, options: &SsaEvaluatorOptions) -> Result Result, // Map of argument value ids to the Brillig call ids employing them call_arguments: HashMap>, - // Maintains count of calls being tracked - tracking_count: usize, + // The set of calls currently being tracked + tracking: HashSet, // Opt-in to use the lookback feature (tracking the argument values // of a Brillig call before the call happens if their usage precedes // it). Can prevent certain false positives, at the cost of @@ -138,8 +138,6 @@ struct BrilligTaintedIds { array_elements: HashMap>, // Initial result value ids, along with element ids for arrays root_results: HashSet, - // The flag signaling that the call should be now tracked - tracking: bool, } #[derive(Clone, Debug)] @@ -195,7 +193,6 @@ impl BrilligTaintedIds { results: results_status, array_elements, root_results: HashSet::from_iter(results.iter().copied()), - tracking: false, } } @@ -327,7 +324,7 @@ impl DependencyContext { function.dfg[block].instructions().iter().for_each(|instruction| { if let Instruction::Call { func, arguments } = &function.dfg[*instruction] { if let Value::Function(callee) = &function.dfg[*func] { - if all_functions[&callee].runtime().is_brillig() { + if all_functions[callee].runtime().is_brillig() { // Skip already visited locations (happens often in unrolled functions) let call_stack = function.dfg.get_instruction_call_stack(*instruction); let location = call_stack.last(); @@ -394,23 +391,19 @@ impl DependencyContext { for argument in &arguments { if let Some(calls) = self.call_arguments.get(argument) { for call in calls { - if let Some(tainted_ids) = self.tainted.get_mut(call) { - tainted_ids.tracking = true; - self.tracking_count += 1; + if self.tainted.contains_key(call) { + self.tracking.insert(*call); } } } } } - if let Some(tainted_ids) = self.tainted.get_mut(instruction) { - if !tainted_ids.tracking { - tainted_ids.tracking = true; - self.tracking_count += 1; - } + if self.tainted.contains_key(instruction) { + self.tracking.insert(*instruction); } // We can skip over instructions while nothing is being tracked - if self.tracking_count > 0 { + if !self.tracking.is_empty() { let mut results = Vec::new(); // Collect non-constant instruction results @@ -431,8 +424,10 @@ impl DependencyContext { if let Some(value_id) = self.memory_slots.get(address) { self.update_children(&[*value_id], &results); } else { - panic!("load instruction {} has attempted to access previously unused memory location", - instruction); + panic!( + "load instruction {} has attempted to access previously unused memory location", + instruction + ); } } // Record the condition to set as future parent for the following values @@ -502,7 +497,10 @@ impl DependencyContext { RuntimeType::Brillig(..) => {} }, Value::ForeignFunction(..) => { - panic!("should not be able to reach foreign function from non-Brillig functions, {func_id} in function {}", function.name()); + panic!( + "should not be able to reach foreign function from non-Brillig functions, {func_id} in function {}", + function.name() + ); } Value::Instruction { .. } | Value::NumericConstant { .. } @@ -519,7 +517,7 @@ impl DependencyContext { // results involving the array in question, to properly // populate the array element tainted sets Instruction::ArrayGet { array, index } => { - self.process_array_get(function, *array, *index, &results); + self.process_array_get(*array, *index, &results, function); // Record all the used arguments as parents of the results self.update_children(&arguments, &results); } @@ -558,7 +556,10 @@ impl DependencyContext { .tainted .keys() .map(|brillig_call| { - trace!("tainted structure for {}: {:?}", brillig_call, self.tainted[brillig_call]); + trace!( + "tainted structure for {:?}: {:?}", + brillig_call, self.tainted[brillig_call] + ); SsaReport::Bug(InternalBug::UncheckedBrilligCall { call_stack: function.dfg.get_instruction_call_stack(*brillig_call), }) @@ -582,8 +583,8 @@ impl DependencyContext { self.side_effects_condition.map(|v| parents.insert(v)); // Don't update sets for the calls not yet being tracked - for (_, tainted_ids) in self.tainted.iter_mut() { - if tainted_ids.tracking { + for call in &self.tracking { + if let Some(tainted_ids) = self.tainted.get_mut(call) { tainted_ids.update_children(&parents, children); } } @@ -600,15 +601,15 @@ impl DependencyContext { .collect(); // Skip untracked calls - for (_, tainted_ids) in self.tainted.iter_mut() { - if tainted_ids.tracking { + for call in &self.tracking { + if let Some(tainted_ids) = self.tainted.get_mut(call) { tainted_ids.store_partial_constraints(&constrained_values); } } - self.tainted.retain(|_, tainted_ids| { + self.tainted.retain(|call, tainted_ids| { if tainted_ids.check_constrained() { - self.tracking_count -= 1; + self.tracking.remove(call); false } else { true @@ -619,10 +620,10 @@ impl DependencyContext { /// Process ArrayGet instruction for tracked Brillig calls fn process_array_get( &mut self, - function: &Function, array: ValueId, index: ValueId, element_results: &[ValueId], + function: &Function, ) { use acvm::acir::AcirField; @@ -630,8 +631,8 @@ impl DependencyContext { if let Some(value) = function.dfg.get_numeric_constant(index) { if let Some(index) = value.try_to_u32() { // Skip untracked calls - for (_, tainted_ids) in self.tainted.iter_mut() { - if tainted_ids.tracking { + for call in &self.tracking { + if let Some(tainted_ids) = self.tainted.get_mut(call) { tainted_ids.process_array_get(array, index as usize, element_results); } } @@ -826,13 +827,18 @@ impl Context { } }, Value::ForeignFunction(..) => { - panic!("Should not be able to reach foreign function from non-Brillig functions, {func_id} in function {}", function.name()); + panic!( + "Should not be able to reach foreign function from non-Brillig functions, {func_id} in function {}", + function.name() + ); } Value::Instruction { .. } | Value::NumericConstant { .. } | Value::Param { .. } | Value::Global(_) => { - panic!("At the point we are running disconnect there shouldn't be any other values as arguments") + panic!( + "At the point we are running disconnect there shouldn't be any other values as arguments" + ) } } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index e4c00358c8c0..95944129c4eb 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -2,7 +2,7 @@ pub(crate) mod data_bus; use std::{borrow::Cow, collections::BTreeMap, sync::Arc}; -use acvm::{acir::circuit::ErrorSelector, FieldElement}; +use acvm::{FieldElement, acir::circuit::ErrorSelector}; use noirc_errors::Location; use noirc_frontend::hir_def::types::Type as HirType; use noirc_frontend::monomorphization::ast::InlineType; @@ -85,7 +85,11 @@ impl FunctionBuilder { /// This should only be used immediately following construction of a FunctionBuilder /// and will panic if there are any already finished functions. pub(crate) fn set_runtime(&mut self, runtime: RuntimeType) { - assert_eq!(self.finished_functions.len(), 0, "Attempted to set runtime on a FunctionBuilder with finished functions. A FunctionBuilder's runtime should only be set on its initial function"); + assert_eq!( + self.finished_functions.len(), + 0, + "Attempted to set runtime on a FunctionBuilder with finished functions. A FunctionBuilder's runtime should only be set on its initial function" + ); self.current_function.set_runtime(runtime); } @@ -584,7 +588,7 @@ impl std::ops::Index for FunctionBuilder { mod tests { use std::sync::Arc; - use acvm::{acir::AcirField, FieldElement}; + use acvm::{FieldElement, acir::AcirField}; use crate::ssa::ir::{ instruction::{Endian, Intrinsic}, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index aa517d431047..132985830ebf 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -18,12 +18,12 @@ use super::{ value::{Value, ValueId}, }; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use fxhash::FxHashMap as HashMap; use iter_extended::vecmap; use serde::{Deserialize, Serialize}; -use serde_with::serde_as; use serde_with::DisplayFromStr; +use serde_with::serde_as; /// The DataFlowGraph contains most of the actual data in a function including /// its blocks, instructions, and values. This struct is largely responsible for @@ -239,10 +239,9 @@ impl DataFlowGraph { instruction, Instruction::IncrementRc { .. } | Instruction::DecrementRc { .. } ), - RuntimeType::Brillig(_) => !matches!( - instruction, - Instruction::EnableSideEffectsIf { .. } | Instruction::IfElse { .. } - ), + RuntimeType::Brillig(_) => { + !matches!(instruction, Instruction::EnableSideEffectsIf { .. }) + } } } @@ -340,14 +339,19 @@ impl DataFlowGraph { } } let mut instructions = instructions.unwrap_or(vec![instruction]); - assert!(!instructions.is_empty(), "`SimplifyResult::SimplifiedToInstructionMultiple` must not return empty vector"); + assert!( + !instructions.is_empty(), + "`SimplifyResult::SimplifiedToInstructionMultiple` must not return empty vector" + ); if instructions.len() > 1 { // There's currently no way to pass results from one instruction in `instructions` on to the next. // We then restrict this to only support multiple instructions if they're all `Instruction::Constrain` // as this instruction type does not have any results. assert!( - instructions.iter().all(|instruction| matches!(instruction, Instruction::Constrain(..))), + instructions + .iter() + .all(|instruction| matches!(instruction, Instruction::Constrain(..))), "`SimplifyResult::SimplifiedToInstructionMultiple` only supports `Constrain` instructions" ); } @@ -372,6 +376,11 @@ impl DataFlowGraph { } } + /// Replace an existing instruction with a new one. + pub(crate) fn set_instruction(&mut self, id: InstructionId, instruction: Instruction) { + self.instructions[id] = instruction; + } + /// Set the value of value_to_replace to refer to the value referred to by new_value. /// /// This is the preferred method to call for optimizations simplifying @@ -658,10 +667,28 @@ impl DataFlowGraph { pub(crate) fn is_safe_index(&self, index: ValueId, array: ValueId) -> bool { #[allow(clippy::match_like_matches_macro)] match (self.type_of_value(array), self.get_numeric_constant(index)) { - (Type::Array(_, len), Some(index)) if index.to_u128() < (len as u128) => true, + (Type::Array(elements, len), Some(index)) + if index.to_u128() < (len as u128 * elements.len() as u128) => + { + true + } + _ => false, + } + } + + /// Arrays are represented as `[RC, ...items]` where RC stands for reference count. + /// By the time of Brillig generation we expect all constant indices + /// to already account for the extra offset from the RC. + pub(crate) fn is_safe_brillig_index(&self, index: ValueId, array: ValueId) -> bool { + #[allow(clippy::match_like_matches_macro)] + match (self.type_of_value(array), self.get_numeric_constant(index)) { + (Type::Array(elements, len), Some(index)) => { + (index.to_u128() - 1) < (len as u128 * elements.len() as u128) + } _ => false, } } + /// Sets the terminator instruction for the given basic block pub(crate) fn set_block_terminator( &mut self, @@ -880,7 +907,7 @@ impl<'dfg> InsertInstructionResult<'dfg> { } } -impl<'dfg> std::ops::Index for InsertInstructionResult<'dfg> { +impl std::ops::Index for InsertInstructionResult<'_> { type Output = ValueId; fn index(&self, index: usize) -> &Self::Output { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dom.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dom.rs index 3dde6240e183..a6f878dafbf0 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dom.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dom.rs @@ -136,10 +136,7 @@ impl DominatorTree { if let Some(value) = f(block_id) { return Some(value); } - block_id = match self.immediate_dominator(block_id) { - Some(immediate_dominator) => immediate_dominator, - None => return None, - } + block_id = self.immediate_dominator(block_id)?; } } @@ -295,8 +292,8 @@ mod tests { } // Testing setup for a function with an unreachable block2 - fn unreachable_node_setup( - ) -> (DominatorTree, BasicBlockId, BasicBlockId, BasicBlockId, BasicBlockId) { + fn unreachable_node_setup() + -> (DominatorTree, BasicBlockId, BasicBlockId, BasicBlockId, BasicBlockId) { // func() { // block0(cond: u1): // jmpif v0 block2() block3() diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs index 9e4557e06a66..13b5ead5eb61 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs @@ -79,6 +79,13 @@ impl<'f> FunctionInserter<'f> { (instruction, self.function.dfg.get_instruction_call_stack_id(id)) } + /// Get an instruction, map all its values, and replace it with the resolved instruction. + pub(crate) fn map_instruction_in_place(&mut self, id: InstructionId) { + let mut instruction = self.function.dfg[id].clone(); + instruction.map_values_mut(|id| self.resolve(id)); + self.function.dfg.set_instruction(id, instruction); + } + /// Maps a terminator in place, replacing any ValueId in the terminator with the /// resolved version of that value id from this FunctionInserter's internal value mapping. pub(crate) fn map_terminator_in_place(&mut self, block: BasicBlockId) { @@ -251,4 +258,22 @@ impl<'f> FunctionInserter<'f> { self.values.entry(*param).or_insert(*new_param); } } + + /// Merge the internal mapping into the given mapping + /// The merge is guaranteed to be coherent because ambiguous cases are prevented + pub(crate) fn extract_mapping(&self, mapping: &mut HashMap) { + for (k, v) in &self.values { + if mapping.contains_key(k) { + unreachable!("cannot merge key"); + } + if mapping.contains_key(v) { + unreachable!("cannot merge value"); + } + mapping.insert(*k, *v); + } + } + + pub(crate) fn set_mapping(&mut self, mapping: HashMap) { + self.values = mapping; + } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 178e15e4e322..1bef9079eb82 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -1,11 +1,11 @@ -use binary::truncate_field; +use binary::{truncate, truncate_field}; use serde::{Deserialize, Serialize}; use std::hash::{Hash, Hasher}; use acvm::{ - acir::AcirField, - acir::{circuit::ErrorSelector, BlackBoxFunc}, FieldElement, + acir::AcirField, + acir::{BlackBoxFunc, circuit::ErrorSelector}, }; use fxhash::FxHasher64; use iter_extended::vecmap; @@ -911,8 +911,10 @@ impl Instruction { // would be incorrect however since the extra bits on the field would not be flipped. Value::NumericConstant { constant, typ } if typ.is_unsigned() => { // As we're casting to a `u128`, we need to clear out any upper bits that the NOT fills. - let value = !constant.to_u128() % (1 << typ.bit_size()); - SimplifiedTo(dfg.make_constant(value.into(), *typ)) + let bit_size = typ.bit_size(); + assert!(bit_size <= 128); + let not_value: u128 = truncate(!constant.to_u128(), bit_size); + SimplifiedTo(dfg.make_constant(not_value.into(), *typ)) } Value::Instruction { instruction, .. } => { // !!v => v @@ -1001,11 +1003,7 @@ impl Instruction { // // In order for the truncation to be a noop, we then require `max_quotient_bits < bit_size`. let max_quotient_bits = max_numerator_bits - divisor_bits; - if max_quotient_bits < *bit_size { - SimplifiedTo(*value) - } else { - None - } + if max_quotient_bits < *bit_size { SimplifiedTo(*value) } else { None } } _ => None, @@ -1034,11 +1032,7 @@ impl Instruction { Instruction::DecrementRc { .. } => None, Instruction::RangeCheck { value, max_bit_size, .. } => { let max_potential_bits = dfg.get_value_max_num_bits(*value); - if max_potential_bits < *max_bit_size { - Remove - } else { - None - } + if max_potential_bits <= *max_bit_size { Remove } else { None } } Instruction::IfElse { then_condition, then_value, else_condition, else_value } => { let then_condition = dfg.resolve(*then_condition); @@ -1470,3 +1464,32 @@ impl SimplifyResult { } } } + +#[cfg(test)] +mod tests { + use crate::ssa::{opt::assert_normalized_ssa_equals, ssa_gen::Ssa}; + + #[test] + fn removes_range_constraints_on_constants() { + let src = " + acir(inline) fn main f0 { + b0(v0: Field): + range_check Field 0 to 1 bits + range_check Field 1 to 1 bits + range_check Field 255 to 8 bits + range_check Field 256 to 8 bits + return + } + "; + let ssa = Ssa::from_str_simplifying(src).unwrap(); + + let expected = " + acir(inline) fn main f0 { + b0(v0: Field): + range_check Field 256 to 8 bits + return + } + "; + assert_normalized_ssa_equals(ssa, expected); + } +} diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs index 1550c9ea050e..1ec2e33ee8d6 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs @@ -1,4 +1,5 @@ -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; +use num_traits::ToPrimitive as _; use serde::{Deserialize, Serialize}; use super::{ @@ -139,11 +140,11 @@ impl Binary { }; } - let lhs_is_zero = lhs_value.map_or(false, |lhs| lhs.is_zero()); - let rhs_is_zero = rhs_value.map_or(false, |rhs| rhs.is_zero()); + let lhs_is_zero = lhs_value.is_some_and(|lhs| lhs.is_zero()); + let rhs_is_zero = rhs_value.is_some_and(|rhs| rhs.is_zero()); - let lhs_is_one = lhs_value.map_or(false, |lhs| lhs.is_one()); - let rhs_is_one = rhs_value.map_or(false, |rhs| rhs.is_one()); + let lhs_is_one = lhs_value.is_some_and(|lhs| lhs.is_one()); + let rhs_is_one = rhs_value.is_some_and(|rhs| rhs.is_one()); match self.operator { BinaryOp::Add { .. } => { @@ -306,14 +307,18 @@ impl Binary { let bitmask_plus_one = bitmask.to_u128() + 1; if bitmask_plus_one.is_power_of_two() { let value = if lhs_value.is_some() { rhs } else { lhs }; - let num_bits = bitmask_plus_one.ilog2(); - return SimplifyResult::SimplifiedToInstruction( - Instruction::Truncate { - value, - bit_size: num_bits, - max_bit_size: lhs_type.bit_size(), - }, - ); + let bit_size = bitmask_plus_one.ilog2(); + let max_bit_size = lhs_type.bit_size(); + + if bit_size == max_bit_size { + // If we're truncating a value into the full size of its type then + // the truncation is a noop. + return SimplifyResult::SimplifiedTo(value); + } else { + return SimplifyResult::SimplifiedToInstruction( + Instruction::Truncate { value, bit_size, max_bit_size }, + ); + } } } @@ -509,7 +514,7 @@ fn convert_signed_integer_to_field_element(int: i128, bit_size: u32) -> FieldEle } /// Truncates `int` to fit within `bit_size` bits. -fn truncate(int: u128, bit_size: u32) -> u128 { +pub(super) fn truncate(int: u128, bit_size: u32) -> u128 { if bit_size == 128 { int } else { @@ -574,8 +579,8 @@ impl BinaryOp { BinaryOp::Xor => |x, y| Some(x ^ y), BinaryOp::Eq => |x, y| Some((x == y) as u128), BinaryOp::Lt => |x, y| Some((x < y) as u128), - BinaryOp::Shl => |x, y| Some(x << y), - BinaryOp::Shr => |x, y| Some(x >> y), + BinaryOp::Shl => |x, y| y.to_u32().and_then(|y| x.checked_shl(y)), + BinaryOp::Shr => |x, y| y.to_u32().and_then(|y| x.checked_shr(y)), } } @@ -591,8 +596,8 @@ impl BinaryOp { BinaryOp::Xor => |x, y| Some(x ^ y), BinaryOp::Eq => |x, y| Some((x == y) as i128), BinaryOp::Lt => |x, y| Some((x < y) as i128), - BinaryOp::Shl => |x, y| Some(x << y), - BinaryOp::Shr => |x, y| Some(x >> y), + BinaryOp::Shl => |x, y| y.to_u32().and_then(|y| x.checked_shl(y)), + BinaryOp::Shr => |x, y| y.to_u32().and_then(|y| x.checked_shr(y)), } } @@ -620,7 +625,7 @@ mod test { use proptest::prelude::*; use super::{ - convert_signed_integer_to_field_element, truncate_field, + BinaryOp, convert_signed_integer_to_field_element, truncate_field, try_convert_field_element_to_signed_integer, }; use acvm::{AcirField, FieldElement}; @@ -649,6 +654,17 @@ mod test { let truncated_as_bigint = FieldElement::from_be_bytes_reduce(&truncated_as_bigint.to_bytes_be()); prop_assert_eq!(truncated_as_field, truncated_as_bigint); } + } + + #[test] + fn get_u128_function_shift_works_with_values_larger_than_127() { + assert!(BinaryOp::Shr.get_u128_function()(1, 128).is_none()); + assert!(BinaryOp::Shl.get_u128_function()(1, 128).is_none()); + } + #[test] + fn get_i128_function_shift_works_with_values_larger_than_127() { + assert!(BinaryOp::Shr.get_i128_function()(1, 128).is_none()); + assert!(BinaryOp::Shl.get_i128_function()(1, 128).is_none()); } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index 6ee7aa0192ce..d32a562a037e 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -2,8 +2,8 @@ use fxhash::FxHashMap as HashMap; use std::{collections::VecDeque, sync::Arc}; use acvm::{ - acir::{AcirField, BlackBoxFunc}, FieldElement, + acir::{AcirField, BlackBoxFunc}, }; use bn254_blackbox_solver::derive_generators; use iter_extended::vecmap; @@ -170,7 +170,7 @@ pub(super) fn simplify_call( } Intrinsic::SlicePopBack => { let length = dfg.get_numeric_constant(arguments[0]); - if length.map_or(true, |length| length.is_zero()) { + if length.is_none_or(|length| length.is_zero()) { // If the length is zero then we're trying to pop the last element from an empty slice. // Defer the error to acir_gen. return SimplifyResult::None; @@ -185,7 +185,7 @@ pub(super) fn simplify_call( } Intrinsic::SlicePopFront => { let length = dfg.get_numeric_constant(arguments[0]); - if length.map_or(true, |length| length.is_zero()) { + if length.is_none_or(|length| length.is_zero()) { // If the length is zero then we're trying to pop the first element from an empty slice. // Defer the error to acir_gen. return SimplifyResult::None; @@ -243,7 +243,7 @@ pub(super) fn simplify_call( } Intrinsic::SliceRemove => { let length = dfg.get_numeric_constant(arguments[0]); - if length.map_or(true, |length| length.is_zero()) { + if length.is_none_or(|length| length.is_zero()) { // If the length is zero then we're trying to remove an element from an empty slice. // Defer the error to acir_gen. return SimplifyResult::None; @@ -610,7 +610,9 @@ fn simplify_black_box_func( "ICE: `BlackBoxFunc::RANGE` calls should be transformed into a `Instruction::Cast`" ) } - BlackBoxFunc::Sha256Compression => SimplifyResult::None, //TODO(Guillaume) + BlackBoxFunc::Sha256Compression => { + blackbox::simplify_sha256_compression(dfg, arguments, block, call_stack) + } BlackBoxFunc::AES128Encrypt => SimplifyResult::None, } } @@ -718,10 +720,10 @@ fn simplify_derive_generators( ); let is_infinite = dfg.make_constant(FieldElement::zero(), NumericType::bool()); let mut results = Vec::new(); - for gen in generators { - let x_big: BigUint = gen.x.into(); + for generator in generators { + let x_big: BigUint = generator.x.into(); let x = FieldElement::from_be_bytes_reduce(&x_big.to_bytes_be()); - let y_big: BigUint = gen.y.into(); + let y_big: BigUint = generator.y.into(); let y = FieldElement::from_be_bytes_reduce(&y_big.to_bytes_be()); results.push(dfg.make_constant(x, NumericType::NativeField)); results.push(dfg.make_constant(y, NumericType::NativeField)); @@ -742,7 +744,7 @@ fn simplify_derive_generators( #[cfg(test)] mod tests { - use crate::ssa::{opt::assert_normalized_ssa_equals, Ssa}; + use crate::ssa::{Ssa, opt::assert_normalized_ssa_equals}; #[test] fn simplify_derive_generators_has_correct_type() { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs index fac2e8b4d5a1..e2aabc62fa61 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs @@ -1,6 +1,7 @@ use std::sync::Arc; -use acvm::{acir::AcirField, BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement}; +use acvm::blackbox_solver::sha256_compression; +use acvm::{BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement, acir::AcirField}; use crate::ssa::ir::call_stack::CallStackId; use crate::ssa::ir::instruction::BlackBoxFunc; @@ -233,6 +234,55 @@ pub(super) fn simplify_poseidon2_permutation( } } +pub(super) fn simplify_sha256_compression( + dfg: &mut DataFlowGraph, + arguments: &[ValueId], + block: BasicBlockId, + call_stack: CallStackId, +) -> SimplifyResult { + match (dfg.get_array_constant(arguments[0]), dfg.get_array_constant(arguments[1])) { + (Some((state, _)), Some((msg_blocks, _))) + if array_is_constant(dfg, &state) && array_is_constant(dfg, &msg_blocks) => + { + let state: Option> = state + .iter() + .map(|id| { + dfg.get_numeric_constant(*id) + .expect("value id from array should point at constant") + .try_to_u32() + }) + .collect(); + + let Some(mut state) = state.and_then(|vec| <[u32; 8]>::try_from(vec).ok()) else { + return SimplifyResult::None; + }; + + let msg_blocks: Option> = msg_blocks + .iter() + .map(|id| { + dfg.get_numeric_constant(*id) + .expect("value id from array should point at constant") + .try_to_u32() + }) + .collect(); + + let Some(msg_blocks) = msg_blocks.and_then(|vec| <[u32; 16]>::try_from(vec).ok()) + else { + return SimplifyResult::None; + }; + + sha256_compression(&mut state, &msg_blocks); + + let new_state = state.into_iter().map(FieldElement::from); + let typ = NumericType::Unsigned { bit_size: 32 }; + let result_array = make_constant_array(dfg, new_state, typ, block, call_stack); + + SimplifyResult::SimplifiedTo(result_array) + } + _ => SimplifyResult::None, + } +} + pub(super) fn simplify_hash( dfg: &mut DataFlowGraph, arguments: &[ValueId], @@ -308,9 +358,9 @@ pub(super) fn simplify_signature( #[cfg(feature = "bn254")] #[cfg(test)] -mod test { - use crate::ssa::opt::assert_normalized_ssa_equals; +mod multi_scalar_mul { use crate::ssa::Ssa; + use crate::ssa::opt::assert_normalized_ssa_equals; #[cfg(feature = "bn254")] #[test] @@ -395,3 +445,32 @@ mod test { assert_normalized_ssa_equals(ssa, expected_src); } } + +#[cfg(test)] +mod sha256_compression { + use crate::ssa::Ssa; + use crate::ssa::opt::assert_normalized_ssa_equals; + + #[test] + fn is_optimized_out_with_constant_arguments() { + let src = r#" + acir(inline) fn main f0 { + b0(): + v0 = make_array [u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0] : [u32; 8] + v1 = make_array [u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0] : [u32; 16] + v2 = call sha256_compression(v0, v1) -> [u32; 8] + return v2 + }"#; + let ssa = Ssa::from_str_simplifying(src).unwrap(); + let expected_src = r#" + acir(inline) fn main f0 { + b0(): + v1 = make_array [u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0] : [u32; 8] + v2 = make_array [u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0] : [u32; 16] + v11 = make_array [u32 2091193876, u32 1113340840, u32 3461668143, u32 3254913767, u32 3068490961, u32 2551409935, u32 2927503052, u32 3205228454] : [u32; 8] + return v11 + } + "#; + assert_normalized_ssa_equals(ssa, expected_src); + } +} diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs index ee2ab43aa5d5..189fd20ca531 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs @@ -1,4 +1,4 @@ -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use num_bigint::BigUint; use super::{DataFlowGraph, Instruction, NumericType, SimplifyResult, Type, Value, ValueId}; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs index 8b3b897088a2..48587cb4b7bc 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs @@ -1,4 +1,4 @@ -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use crate::ssa::ir::types::NumericType; @@ -204,4 +204,26 @@ mod tests { "; assert_normalized_ssa_equals(ssa, expected); } + + #[test] + fn simplifies_out_noop_bitwise_ands() { + // Regression test for https://github.com/noir-lang/noir/issues/7451 + let src = " + acir(inline) predicate_pure fn main f0 { + b0(v0: u8): + v1 = and u8 255, v0 + return v1 + } + "; + + let ssa = Ssa::from_str_simplifying(src).unwrap(); + + let expected = " + acir(inline) fn main f0 { + b0(v0: u8): + return v0 + } + "; + assert_normalized_ssa_equals(ssa, expected); + } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/printer.rs index 06f2ecd70eaf..0fac2242abd7 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -6,8 +6,8 @@ use im::Vector; use iter_extended::vecmap; use crate::ssa::{ - ir::types::{NumericType, Type}, Ssa, + ir::types::{NumericType, Type}, }; use super::{ @@ -344,11 +344,7 @@ pub(crate) fn try_to_extract_string_from_error_payload( values: &[ValueId], dfg: &DataFlowGraph, ) -> Option { - if is_string_type && values.len() == 1 { - dfg.get_string(values[0]) - } else { - None - } + if is_string_type && values.len() == 1 { dfg.get_string(values[0]) } else { None } } fn display_constrain_error( diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/types.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/types.rs index 0dd7fd92ee53..ca6242d51c21 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/types.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/types.rs @@ -1,7 +1,8 @@ +use noirc_frontend::signed_field::SignedField; use serde::{Deserialize, Serialize}; use std::sync::Arc; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use iter_extended::vecmap; use crate::ssa::ssa_gen::SSA_WORD_SIZE; @@ -58,28 +59,20 @@ impl NumericType { /// Returns None if the given Field value is within the numeric limits /// for the current NumericType. Otherwise returns a string describing /// the limits, as a range. - pub(crate) fn value_is_outside_limits( - self, - field: FieldElement, - negative: bool, - ) -> Option { + pub(crate) fn value_is_outside_limits(self, value: SignedField) -> Option { match self { NumericType::Unsigned { bit_size } => { - let max = 2u128.pow(bit_size) - 1; - if negative { + let max = if bit_size == 128 { u128::MAX } else { 2u128.pow(bit_size) - 1 }; + if value.is_negative { return Some(format!("0..={}", max)); } - if field <= max.into() { - None - } else { - Some(format!("0..={}", max)) - } + if value.field <= max.into() { None } else { Some(format!("0..={}", max)) } } NumericType::Signed { bit_size } => { let min = 2u128.pow(bit_size - 1); let max = 2u128.pow(bit_size - 1) - 1; - let target_max = if negative { min } else { max }; - if field <= target_max.into() { + let target_max = if value.is_negative { min } else { max }; + if value.field <= target_max.into() { None } else { Some(format!("-{}..={}", min, max)) @@ -307,19 +300,19 @@ mod tests { #[test] fn test_u8_value_is_outside_limits() { let u8 = NumericType::Unsigned { bit_size: 8 }; - assert!(u8.value_is_outside_limits(FieldElement::from(1_i128), true).is_some()); - assert!(u8.value_is_outside_limits(FieldElement::from(0_i128), false).is_none()); - assert!(u8.value_is_outside_limits(FieldElement::from(255_i128), false).is_none()); - assert!(u8.value_is_outside_limits(FieldElement::from(256_i128), false).is_some()); + assert!(u8.value_is_outside_limits(SignedField::negative(1_i128)).is_some()); + assert!(u8.value_is_outside_limits(SignedField::positive(0_i128)).is_none()); + assert!(u8.value_is_outside_limits(SignedField::positive(255_i128)).is_none()); + assert!(u8.value_is_outside_limits(SignedField::positive(256_i128)).is_some()); } #[test] fn test_i8_value_is_outside_limits() { let i8 = NumericType::Signed { bit_size: 8 }; - assert!(i8.value_is_outside_limits(FieldElement::from(129_i128), true).is_some()); - assert!(i8.value_is_outside_limits(FieldElement::from(128_i128), true).is_none()); - assert!(i8.value_is_outside_limits(FieldElement::from(0_i128), false).is_none()); - assert!(i8.value_is_outside_limits(FieldElement::from(127_i128), false).is_none()); - assert!(i8.value_is_outside_limits(FieldElement::from(128_i128), false).is_some()); + assert!(i8.value_is_outside_limits(SignedField::negative(129_i128)).is_some()); + assert!(i8.value_is_outside_limits(SignedField::negative(128_i128)).is_none()); + assert!(i8.value_is_outside_limits(SignedField::positive(0_i128)).is_none()); + assert!(i8.value_is_outside_limits(SignedField::positive(127_i128)).is_none()); + assert!(i8.value_is_outside_limits(SignedField::positive(128_i128)).is_some()); } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/array_set.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/array_set.rs index 05ceafcf4509..7bef8e7b1ae2 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/array_set.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/array_set.rs @@ -39,7 +39,11 @@ impl Function { let reachable_blocks = self.reachable_blocks(); if !self.runtime().is_entry_point() { - assert_eq!(reachable_blocks.len(), 1, "Expected there to be 1 block remaining in Acir function for array_set optimization"); + assert_eq!( + reachable_blocks.len(), + 1, + "Expected there to be 1 block remaining in Acir function for array_set optimization" + ); } let mut context = Context::new(&self.dfg); @@ -187,7 +191,7 @@ fn make_mutable( #[cfg(test)] mod tests { - use crate::ssa::{opt::assert_normalized_ssa_equals, Ssa}; + use crate::ssa::{Ssa, opt::assert_normalized_ssa_equals}; #[test] fn array_set_in_loop_with_conditional_clone() { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/basic_conditional.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/basic_conditional.rs new file mode 100644 index 000000000000..cbc36787a254 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/basic_conditional.rs @@ -0,0 +1,526 @@ +use std::collections::HashSet; + +use acvm::AcirField; +use fxhash::FxHashMap as HashMap; +use iter_extended::vecmap; + +use crate::ssa::{ + Ssa, + ir::{ + basic_block::BasicBlockId, + cfg::ControlFlowGraph, + dfg::DataFlowGraph, + function::{Function, FunctionId}, + function_inserter::FunctionInserter, + instruction::{BinaryOp, Instruction, TerminatorInstruction}, + post_order::PostOrder, + value::ValueId, + }, +}; + +use super::flatten_cfg::Context; +#[derive(Debug, Clone)] +struct BasicConditional { + block_entry: BasicBlockId, + block_then: Option, + block_else: Option, + block_exit: BasicBlockId, +} + +impl Ssa { + #[tracing::instrument(level = "trace", skip(self))] + /// This pass flatten simple IF-THEN-ELSE statements + /// This optimization pass identifies simple conditional control flow patterns in unconstrained code + /// and flattens them to reduce the number of basic blocks and improve performance. + /// + /// e.g: if c {a} else {b} would be flattened to c*(a-b)+b + /// A simple conditional pattern is defined as an IF-THEN (with optional ELSE) statement, with no nested conditional nor loop statements + /// Performance improvement is based on a simple execution cost metric + pub(crate) fn flatten_basic_conditionals(mut self) -> Ssa { + // Retrieve the 'no_predicates' attribute of the functions in a map, to avoid problems with borrowing + let mut no_predicates = HashMap::default(); + for function in self.functions.values() { + no_predicates.insert(function.id(), function.is_no_predicates()); + } + for function in self.functions.values_mut() { + flatten_function(function, &mut no_predicates); + } + self + } +} + +/// Returns the blocks of the simple conditional sub-graph whose input block is the entry. +/// Returns None if the input block is not the entry block of a simple conditional. +fn is_conditional( + block: BasicBlockId, + cfg: &ControlFlowGraph, + function: &Function, +) -> Option { + // jump overhead is the cost for doing the conditional and jump around the blocks + // We use 10 as a rough estimate, the real cost is less. + let jump_overhead = 10; + let mut successors = cfg.successors(block); + let mut result = None; + // a conditional must have 2 branches + if successors.len() != 2 { + return None; + } + let left = successors.next().unwrap(); + let right = successors.next().unwrap(); + let mut left_successors = cfg.successors(left); + let mut right_successors = cfg.successors(right); + let left_successors_len = left_successors.len(); + let right_successors_len = right_successors.len(); + let next_left = left_successors.next(); + let next_right = right_successors.next(); + if next_left == Some(block) || next_right == Some(block) { + // this is a loop, not a conditional + return None; + } + if left_successors_len == 1 && right_successors_len == 1 && next_left == next_right { + // The branches join on one block so it is a non-nested conditional + let cost_left = block_cost(left, &function.dfg); + let cost_right = block_cost(right, &function.dfg); + // For the flattening to be valuable, we compare the cost of the flattened code with the average cost of the 2 branches, + // including an overhead to take into account the jumps between the blocks. + let cost = cost_right.saturating_add(cost_left); + if cost < cost / 2 + jump_overhead { + if let Some(TerminatorInstruction::JmpIf { + condition: _, + then_destination, + else_destination, + call_stack: _, + }) = function.dfg[block].terminator() + { + result = Some(BasicConditional { + block_entry: block, + block_then: Some(*then_destination), + block_else: Some(*else_destination), + block_exit: next_left.unwrap(), + }); + } + } + } else if left_successors_len == 1 && next_left == Some(right) { + // Left branch joins the right branch, e.g if/then statement with no else + // This case may not happen (i.e not generated), but it is safer to handle it (e.g in case it happens due to some optimizations) + let cost = block_cost(left, &function.dfg); + if cost < cost / 2 + jump_overhead { + if let Some(TerminatorInstruction::JmpIf { + condition: _, + then_destination, + else_destination, + call_stack: _, + }) = function.dfg[block].terminator() + { + let (block_then, block_else) = if left == *then_destination { + (Some(left), None) + } else if left == *else_destination { + (None, Some(left)) + } else { + return None; + }; + + result = Some(BasicConditional { + block_entry: block, + block_then, + block_else, + block_exit: right, + }); + } + } + } else if right_successors_len == 1 && next_right == Some(left) { + // Right branch joins the left branch, e.g if/else statement with no then + // This case may not happen (i.e not generated), but it is safer to handle it (e.g in case it happens due to some optimizations) + let cost = block_cost(right, &function.dfg); + if cost < cost / 2 + jump_overhead { + if let Some(TerminatorInstruction::JmpIf { + condition: _, + then_destination, + else_destination, + call_stack: _, + }) = function.dfg[block].terminator() + { + let (block_then, block_else) = if right == *then_destination { + (Some(right), None) + } else if right == *else_destination { + (None, Some(right)) + } else { + return None; + }; + result = Some(BasicConditional { + block_entry: block, + block_then, + block_else, + block_exit: right, + }); + } + } + } + // A conditional exit would have exactly 2 predecessors + result.filter(|result| cfg.predecessors(result.block_exit).len() == 2) +} + +/// Computes a cost estimate of a basic block +/// returns u32::MAX if the block has side-effect instructions +/// WARNING: these are estimates of the runtime cost of each instruction, +/// 1 being the cost of the simplest instruction. These numbers can be improved. +fn block_cost(block: BasicBlockId, dfg: &DataFlowGraph) -> u32 { + let mut cost: u32 = 0; + for instruction in dfg[block].instructions() { + let instruction_cost = match &dfg[*instruction] { + Instruction::Binary(binary) => { + match binary.operator { + BinaryOp::Add { unchecked } + | BinaryOp::Sub { unchecked } + | BinaryOp::Mul { unchecked } => if unchecked { 3 } else { return u32::MAX }, + BinaryOp::Div + | BinaryOp::Mod => return u32::MAX, + BinaryOp::Eq => 1, + BinaryOp::Lt => 5, + BinaryOp::And + | BinaryOp::Or + | BinaryOp::Xor => 1, + BinaryOp::Shl + | BinaryOp::Shr => return u32::MAX, + } + }, + // A Cast can be either simplified, or lead to a truncate + Instruction::Cast(_, _) => 3, + Instruction::Not(_) => 1, + Instruction::Truncate { .. } => 7, + + Instruction::Constrain(_,_,_) + | Instruction::ConstrainNotEqual(_,_,_) + | Instruction::RangeCheck { .. } + // Calls with no-predicate set to true could be supported, but + // they are likely to be too costly anyways. Simple calls would + // have been inlined already. + | Instruction::Call { .. } + | Instruction::Load { .. } + | Instruction::Store { .. } + | Instruction::ArraySet { .. } => return u32::MAX, + + Instruction::ArrayGet { array, index } => { + // A get can fail because of out-of-bound index + let mut in_bound = false; + // check if index is in bound + if let (Some(index), Some(len)) = (dfg.get_numeric_constant(*index), dfg.try_get_array_length(*array)) { + // The index is in-bounds + if index.to_u128() < len as u128 { + in_bound = true; + } + } + if !in_bound { + return u32::MAX; + } + 1 + }, + // if less than 10 elements, it is translated into a store for each element + // if more than 10, it is a loop, so 20 should be a good estimate, worst case being 10 stores and ~10 index increments + Instruction::MakeArray { .. } => 20, + + Instruction::Allocate + | Instruction::EnableSideEffectsIf { .. } + | Instruction::IncrementRc { .. } + | Instruction::DecrementRc { .. } + | Instruction::Noop => 0, + Instruction::IfElse { .. } => 1, + }; + cost += instruction_cost; + } + cost +} + +/// Identifies all simple conditionals in the function and flattens them +fn flatten_function(function: &mut Function, no_predicates: &mut HashMap) { + // This pass is dedicated to brillig functions + if !function.runtime().is_brillig() { + return; + } + let cfg = ControlFlowGraph::with_function(function); + let mut stack = vec![function.entry_block()]; + let mut processed = HashSet::new(); + let mut conditionals = Vec::new(); + + // 1. Process all blocks of the cfg, starting from the root and following the successors + while let Some(block) = stack.pop() { + // Avoid cycles + if processed.contains(&block) { + continue; + } + processed.insert(block); + + // Identify the simple conditionals + if let Some(conditional) = is_conditional(block, &cfg, function) { + // no need to check the branches, process the join block directly + stack.push(conditional.block_exit); + conditionals.push(conditional); + } else { + stack.extend(cfg.successors(block)); + } + } + + // 2. Flatten all simple conditionals + // process basic conditionals in reverse order so that + // a conditional does not impact the previous ones + conditionals.reverse(); + flatten_multiple(&conditionals, function, no_predicates); +} + +fn flatten_multiple( + conditionals: &Vec, + function: &mut Function, + no_predicates: &mut HashMap, +) { + // 1. process each basic conditional, using a new context per conditional + let post_order = PostOrder::with_function(function); + + let mut mapping = HashMap::default(); + for conditional in conditionals { + let cfg = ControlFlowGraph::with_function(function); + let cfg_root = function.entry_block(); + let mut branch_ends = HashMap::default(); + branch_ends.insert(conditional.block_entry, conditional.block_exit); + let mut context = Context::new(function, cfg, branch_ends, cfg_root); + context.flatten_single_conditional(conditional, no_predicates); + // extract the mapping into 'mapping + context.inserter.extract_mapping(&mut mapping); + } + // 2. re-map the full program for values that may been simplified. + if !mapping.is_empty() { + for block in post_order.as_slice() { + Context::map_block_with_mapping(mapping.clone(), function, *block); + } + } +} + +impl Context<'_> { + fn flatten_single_conditional( + &mut self, + conditional: &BasicConditional, + no_predicates: &mut HashMap, + ) { + // Manually inline 'then', 'else' and 'exit' into the entry block + //0. initialize the context for flattening a 'single conditional' + let old_target = self.target_block; + let old_no_predicate = self.no_predicate; + let mut queue = vec![]; + self.target_block = conditional.block_entry; + self.no_predicate = true; + //1. process 'then' branch + self.inline_block(conditional.block_entry, no_predicates); + let to_process = self.handle_terminator(conditional.block_entry, &queue); + queue.extend(to_process); + if let Some(then) = conditional.block_then { + assert_eq!(queue.pop(), conditional.block_then); + self.inline_block(then, no_predicates); + let to_process = self.handle_terminator(then, &queue); + + for incoming_block in to_process { + if !queue.contains(&incoming_block) { + queue.push(incoming_block); + } + } + } + + //2. process 'else' branch, in case there is no 'then' + let next = queue.pop(); + if next == conditional.block_else { + let next = next.unwrap(); + self.inline_block(next, no_predicates); + let _ = self.handle_terminator(next, &queue); + } else { + assert_eq!(next, Some(conditional.block_exit)); + } + + //3. process 'exit' block + self.inline_block(conditional.block_exit, no_predicates); + // Manually set the terminator of the entry block to the one of the exit block + let terminator = + self.inserter.function.dfg[conditional.block_exit].terminator().unwrap().clone(); + let new_terminator = match terminator { + TerminatorInstruction::JmpIf { + condition, + then_destination, + else_destination, + call_stack, + } => { + let condition = self.inserter.resolve(condition); + TerminatorInstruction::JmpIf { + condition, + then_destination, + else_destination, + call_stack, + } + } + TerminatorInstruction::Jmp { destination, arguments, call_stack } => { + let arguments = vecmap(arguments, |value| self.inserter.resolve(value)); + TerminatorInstruction::Jmp { destination, arguments, call_stack } + } + TerminatorInstruction::Return { return_values, call_stack } => { + let return_values = vecmap(return_values, |value| self.inserter.resolve(value)); + TerminatorInstruction::Return { return_values, call_stack } + } + }; + self.inserter.function.dfg.set_block_terminator(conditional.block_entry, new_terminator); + self.inserter.map_data_bus_in_place(); + //4. restore the context, in case it is re-used. + self.target_block = old_target; + self.no_predicate = old_no_predicate; + } + + fn map_block_with_mapping( + mapping: HashMap, + func: &mut Function, + block: BasicBlockId, + ) { + // Map all instructions in the block + let mut inserter = FunctionInserter::new(func); + inserter.set_mapping(mapping); + let instructions = inserter.function.dfg[block].instructions().to_vec(); + for instruction in instructions { + inserter.map_instruction_in_place(instruction); + } + inserter.map_terminator_in_place(block); + } +} + +#[cfg(test)] +mod test { + use crate::ssa::{Ssa, opt::assert_normalized_ssa_equals}; + + #[test] + fn basic_jmpif() { + let src = " + brillig(inline) fn foo f0 { + b0(v0: u32): + v3 = eq v0, u32 0 + jmpif v3 then: b2, else: b1 + b1(): + jmp b3(u32 5) + b2(): + jmp b3(u32 3) + b3(v1: u32): + return v1 + } + "; + let ssa = Ssa::from_str(src).unwrap(); + assert_eq!(ssa.main().reachable_blocks().len(), 4); + + let expected = " + brillig(inline) fn foo f0 { + b0(v0: u32): + v2 = eq v0, u32 0 + v3 = not v2 + v4 = cast v2 as u32 + v5 = cast v3 as u32 + v7 = unchecked_mul v4, u32 3 + v9 = unchecked_mul v5, u32 5 + v10 = unchecked_add v7, v9 + return v10 + } + "; + + let ssa = ssa.flatten_basic_conditionals(); + assert_normalized_ssa_equals(ssa, expected); + } + + #[test] + fn array_jmpif() { + let src = r#" + brillig(inline) fn foo f0 { + b0(v0: u32): + v3 = eq v0, u32 5 + jmpif v3 then: b2, else: b1 + b1(): + v6 = make_array b"foo" + jmp b3(v6) + b2(): + v10 = make_array b"bar" + jmp b3(v10) + b3(v1: [u8; 3]): + return v1 + } + "#; + let ssa = Ssa::from_str(src).unwrap(); + assert_eq!(ssa.main().reachable_blocks().len(), 4); + let ssa = ssa.flatten_basic_conditionals(); + // make_array is not simplified + assert_normalized_ssa_equals(ssa, src); + } + + #[test] + fn nested_jmpifs() { + let src = " + brillig(inline) fn foo f0 { + b0(v0: u32): + v5 = eq v0, u32 5 + v6 = not v5 + jmpif v5 then: b5, else: b1 + b1(): + v8 = lt v0, u32 3 + jmpif v8 then: b3, else: b2 + b2(): + v9 = truncate v0 to 2 bits, max_bit_size: 32 + jmp b4(v9) + b3(): + v10 = truncate v0 to 1 bits, max_bit_size: 32 + jmp b4(v10) + b4(v1: u32): + jmp b9(v1) + b5(): + v12 = lt u32 2, v0 + jmpif v12 then: b7, else: b6 + b6(): + v13 = truncate v0 to 3 bits, max_bit_size: 32 + jmp b8(v13) + b7(): + v14 = and v0, u32 2 + jmp b8(v14) + b8(v2: u32): + jmp b9(v2) + b9(v3: u32): + return v3 + } + "; + let ssa = Ssa::from_str(src).unwrap(); + assert_eq!(ssa.main().reachable_blocks().len(), 10); + + let expected = " + brillig(inline) fn foo f0 { + b0(v0: u32): + v3 = eq v0, u32 5 + v4 = not v3 + jmpif v3 then: b2, else: b1 + b1(): + v6 = lt v0, u32 3 + v7 = truncate v0 to 1 bits, max_bit_size: 32 + v8 = not v6 + v9 = truncate v0 to 2 bits, max_bit_size: 32 + v10 = cast v6 as u32 + v11 = cast v8 as u32 + v12 = unchecked_mul v10, v7 + v13 = unchecked_mul v11, v9 + v14 = unchecked_add v12, v13 + jmp b3(v14) + b2(): + v16 = lt u32 2, v0 + v17 = and v0, u32 2 + v18 = not v16 + v19 = truncate v0 to 3 bits, max_bit_size: 32 + v20 = cast v16 as u32 + v21 = cast v18 as u32 + v22 = unchecked_mul v20, v17 + v23 = unchecked_mul v21, v19 + v24 = unchecked_add v22, v23 + jmp b3(v24) + b3(v1: u32): + return v1 + } + "; + + let ssa = ssa.flatten_basic_conditionals(); + assert_eq!(ssa.main().reachable_blocks().len(), 4); + assert_normalized_ssa_equals(ssa, expected); + } +} diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_array_gets.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_array_gets.rs new file mode 100644 index 000000000000..c5d485dd1613 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_array_gets.rs @@ -0,0 +1,168 @@ +//! In the Brillig runtime arrays are represented as [RC, ...items], +//! Certain operations such as array gets only utilize the items pointer. +//! Without handling the items pointer offset in SSA, it is left to Brillig generation +//! to offset the array pointer. +//! +//! Slices are represented as Brillig vectors, where the items pointer instead starts at three rather than one. +//! A Brillig vector is represented as [RC, Size, Capacity, ...items]. +//! +//! For array operations with constant indices adding an instruction to offset the pointer +//! is unnecessary as we already know the index. This pass looks for such array operations +//! with constant indices and replaces their index with the appropriate offset. + +use fxhash::FxHashMap as HashMap; + +use crate::{ + brillig::brillig_ir::BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, + ssa::{ + Ssa, + ir::{ + function::Function, + instruction::Instruction, + types::{NumericType, Type}, + }, + }, +}; + +impl Ssa { + #[tracing::instrument(level = "trace", skip(self))] + pub(crate) fn brillig_array_gets(mut self) -> Ssa { + let brillig_functions = + self.functions.values_mut().filter(|function| function.runtime().is_brillig()); + for function in brillig_functions { + function.brillig_array_gets(); + } + + self + } +} + +impl Function { + pub(super) fn brillig_array_gets(&mut self) { + let reachable_blocks = self.reachable_blocks(); + + let mut instructions_to_update = HashMap::default(); + for block_id in reachable_blocks.into_iter() { + for instruction_id in self.dfg[block_id].instructions() { + if let Instruction::ArrayGet { array, index } = self.dfg[*instruction_id] { + if self.dfg.is_constant(index) { + instructions_to_update.insert( + *instruction_id, + (Instruction::ArrayGet { array, index }, block_id), + ); + } + } + } + } + + for (instruction_id, _) in instructions_to_update { + let new_instruction = match self.dfg[instruction_id] { + Instruction::ArrayGet { array, index } => { + let index_constant = + self.dfg.get_numeric_constant(index).expect("ICE: Expected constant index"); + let offset = if matches!(self.dfg.type_of_value(array), Type::Array(..)) { + // Brillig arrays are [RC, ...items] + 1u128 + } else { + // Brillig vectors are [RC, Size, Capacity, ...items] + 3u128 + }; + let index = self.dfg.make_constant( + index_constant + offset.into(), + NumericType::unsigned(BRILLIG_MEMORY_ADDRESSING_BIT_SIZE), + ); + Instruction::ArrayGet { array, index } + } + _ => { + continue; + } + }; + self.dfg[instruction_id] = new_instruction; + } + } +} + +#[cfg(test)] +mod tests { + use crate::ssa::opt::assert_normalized_ssa_equals; + + use super::Ssa; + + #[test] + fn offset_array_get_constant_index() { + let src = " + brillig(inline) fn main f0 { + b0(v0: [Field; 3]): + v2 = array_get v0, index u32 0 -> Field + return v2 + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.brillig_array_gets(); + + let expected = " + brillig(inline) fn main f0 { + b0(v0: [Field; 3]): + v2 = array_get v0, index u32 1 -> Field + return v2 + } + "; + + assert_normalized_ssa_equals(ssa, expected); + } + + #[test] + fn do_not_offset_dynamic_array_get() { + let src = " + brillig(inline) fn main f0 { + b0(v0: [Field; 3], v1: u32): + v2 = array_get v0, index v1 -> Field + return v2 + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.brillig_array_gets(); + assert_normalized_ssa_equals(ssa, src); + } + + #[test] + fn do_not_offset_array_get_in_acir() { + let src = " + acir(inline) fn main f0 { + b0(v0: [Field; 3]): + v2 = array_get v0, index u32 0 -> Field + return v2 + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.brillig_array_gets(); + assert_normalized_ssa_equals(ssa, src); + } + + #[test] + fn offset_slice_array_get_constant_index() { + let src = " + brillig(inline) fn main f0 { + b0(v0: [Field]): + v2 = array_get v0, index u32 0 -> Field + return v2 + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.brillig_array_gets(); + + let expected = " + brillig(inline) fn main f0 { + b0(v0: [Field]): + v2 = array_get v0, index u32 3 -> Field + return v2 + } + "; + + assert_normalized_ssa_equals(ssa, expected); + } +} diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_entry_points.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_entry_points.rs index e03f14b2721f..380daabee6cf 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_entry_points.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_entry_points.rs @@ -64,12 +64,12 @@ use std::collections::{BTreeMap, BTreeSet}; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::ssa::{ + Ssa, ir::{ function::{Function, FunctionId}, instruction::Instruction, value::Value, }, - Ssa, }; use super::inlining::called_functions_vec; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/check_u128_mul_overflow.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/check_u128_mul_overflow.rs new file mode 100644 index 000000000000..ae0c0f1e1033 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/check_u128_mul_overflow.rs @@ -0,0 +1,285 @@ +use acvm::{AcirField, FieldElement}; + +use crate::ssa::{ + ir::{ + basic_block::BasicBlockId, + call_stack::CallStackId, + dfg::DataFlowGraph, + function::Function, + instruction::{Binary, BinaryOp, ConstrainError, Instruction}, + types::NumericType, + value::ValueId, + }, + ssa_gen::Ssa, +}; + +impl Ssa { + /// An SSA pass that checks that multiplying two u128 doesn't overflow because + /// both operands are greater or equal than 2^64. + /// If both are, then the result is surely greater or equal than 2^128 so it would overflow. + /// The operands can still overflow if just one of them is less than 2^64, but in that case the result + /// will be less than 2^192 so it fits in a Field value, and acir will check that it fits in a u128. + #[tracing::instrument(level = "trace", skip(self))] + pub(crate) fn check_u128_mul_overflow(mut self) -> Ssa { + for function in self.functions.values_mut() { + function.check_u128_mul_overflow(); + } + self + } +} + +impl Function { + pub(crate) fn check_u128_mul_overflow(&mut self) { + if !self.runtime().is_acir() { + return; + } + + for block in self.reachable_blocks() { + let instructions = self.dfg[block].take_instructions(); + + for instruction in instructions { + self.dfg[block].insert_instruction(instruction); + + let Instruction::Binary(Binary { + lhs, + rhs, + operator: BinaryOp::Mul { unchecked: false }, + }) = &self.dfg[instruction] + else { + continue; + }; + + let binary_type = self.dfg.type_of_value(*lhs).unwrap_numeric(); + let NumericType::Unsigned { bit_size: 128 } = binary_type else { + continue; + }; + + let call_stack = self.dfg.get_instruction_call_stack_id(instruction); + check_u128_mul_overflow(*lhs, *rhs, block, &mut self.dfg, call_stack); + } + } + } +} + +fn check_u128_mul_overflow( + lhs: ValueId, + rhs: ValueId, + block: BasicBlockId, + dfg: &mut DataFlowGraph, + call_stack: CallStackId, +) { + let lhs_value = dfg.get_numeric_constant(lhs); + let rhs_value = dfg.get_numeric_constant(rhs); + + let two_pow_64 = 1_u128 << 64; + + // If lhs is less than 2^64 then the condition trivially holds. + if let Some(value) = lhs_value { + if value.to_u128() < two_pow_64 { + return; + } + } + + // Same goes for rhs + if let Some(value) = rhs_value { + if value.to_u128() < two_pow_64 { + return; + } + } + + let u128 = NumericType::unsigned(128); + let two_pow_64 = dfg.make_constant(two_pow_64.into(), u128); + + let res = if lhs_value.is_some() && rhs_value.is_some() { + // If both values are known at compile time, at this point we know it overflows + dfg.make_constant(FieldElement::one(), u128) + } else if lhs_value.is_some() { + // If only the left-hand side is known we just need to check that the right-hand side + // isn't greater than 2^64 + let instruction = + Instruction::Binary(Binary { lhs: rhs, rhs: two_pow_64, operator: BinaryOp::Div }); + dfg.insert_instruction_and_results(instruction, block, None, call_stack).first() + } else if rhs_value.is_some() { + // Same goes for the other side + let instruction = + Instruction::Binary(Binary { lhs, rhs: two_pow_64, operator: BinaryOp::Div }); + dfg.insert_instruction_and_results(instruction, block, None, call_stack).first() + } else { + // Check both sides + let instruction = + Instruction::Binary(Binary { lhs, rhs: two_pow_64, operator: BinaryOp::Div }); + let divided_lhs = + dfg.insert_instruction_and_results(instruction, block, None, call_stack).first(); + + let instruction = + Instruction::Binary(Binary { lhs: rhs, rhs: two_pow_64, operator: BinaryOp::Div }); + let divided_rhs = + dfg.insert_instruction_and_results(instruction, block, None, call_stack).first(); + + // Unchecked as operands are restricted to be less than 2^64 so multiplying them cannot overflow. + let mul = BinaryOp::Mul { unchecked: true }; + let instruction = + Instruction::Binary(Binary { lhs: divided_lhs, rhs: divided_rhs, operator: mul }); + dfg.insert_instruction_and_results(instruction, block, None, call_stack).first() + }; + + let zero = dfg.make_constant(FieldElement::zero(), u128); + let instruction = Instruction::Constrain( + res, + zero, + Some(ConstrainError::StaticString("attempt to multiply with overflow".to_string())), + ); + dfg.insert_instruction_and_results(instruction, block, None, call_stack); +} + +#[cfg(test)] +mod tests { + use crate::ssa::{opt::assert_normalized_ssa_equals, ssa_gen::Ssa}; + + #[test] + fn does_not_insert_check_if_lhs_is_less_than_two_pow_64() { + let src = " + acir(inline) fn main f0 { + b0(v0: u128): + v2 = mul u128 18446744073709551615, v0 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, src); + } + + #[test] + fn does_not_insert_check_if_rhs_is_less_than_two_pow_64() { + let src = " + acir(inline) fn main f0 { + b0(v0: u128): + v2 = mul v0, u128 18446744073709551615 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, src); + } + + #[test] + fn inserts_check_for_lhs() { + let src = " + acir(inline) fn main f0 { + b0(v0: u128): + v2 = mul v0, u128 18446744073709551617 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let expected = r#" + acir(inline) fn main f0 { + b0(v0: u128): + v2 = mul v0, u128 18446744073709551617 + v4 = div v0, u128 18446744073709551616 + constrain v4 == u128 0, "attempt to multiply with overflow" + return + } + "#; + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, expected); + } + + #[test] + fn inserts_check_for_rhs() { + let src = " + acir(inline) fn main f0 { + b0(v0: u128): + v2 = mul u128 18446744073709551617, v0 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let expected = r#" + acir(inline) fn main f0 { + b0(v0: u128): + v2 = mul u128 18446744073709551617, v0 + v4 = div v0, u128 18446744073709551616 + constrain v4 == u128 0, "attempt to multiply with overflow" + return + } + "#; + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, expected); + } + + #[test] + fn inserts_check_for_both_operands() { + let src = " + acir(inline) fn main f0 { + b0(v0: u128, v1: u128): + v2 = mul v0, v1 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let expected = r#" + acir(inline) fn main f0 { + b0(v0: u128, v1: u128): + v2 = mul v0, v1 + v4 = div v0, u128 18446744073709551616 + v5 = div v1, u128 18446744073709551616 + v6 = unchecked_mul v4, v5 + constrain v6 == u128 0, "attempt to multiply with overflow" + return + } + "#; + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, expected); + } + + #[test] + fn inserts_assertion_failure_if_overflow_is_guaranteed() { + let src = " + acir(inline) fn main f0 { + b0(): + v2 = mul u128 18446744073709551617, u128 18446744073709551616 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + // The multiplication remains, but it will be later removed by DIE + let expected = r#" + acir(inline) fn main f0 { + b0(): + v2 = mul u128 18446744073709551617, u128 18446744073709551616 + constrain u128 1 == u128 0, "attempt to multiply with overflow" + return + } + "#; + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, expected); + } + + #[test] + fn does_nothing_for_brillig() { + let src = " + brillig(inline) fn main f0 { + b0(): + v2 = mul u128 18446744073709551617, u128 18446744073709551616 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, src); + } +} diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index 05bd48b88306..3bde1d7530f0 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -22,9 +22,9 @@ use std::collections::{BTreeMap, HashSet, VecDeque}; use acvm::{ - acir::AcirField, - brillig_vm::{MemoryValue, VMStatus, VM}, FieldElement, + acir::AcirField, + brillig_vm::{MemoryValue, VM, VMStatus}, }; use bn254_blackbox_solver::Bn254BlackBoxSolver; use im::Vector; @@ -32,9 +32,9 @@ use iter_extended::vecmap; use crate::{ brillig::{ + Brillig, BrilligOptions, brillig_gen::gen_brillig_for, brillig_ir::{artifact::BrilligParameter, brillig_variable::get_bit_size_from_ssa_type}, - Brillig, BrilligOptions, }, ssa::{ ir::{ @@ -95,6 +95,11 @@ impl Ssa { let brillig_info = Some(BrilligInfo { brillig, brillig_functions: &brillig_functions }); for function in self.functions.values_mut() { + // We have already performed our final Brillig generation, so constant folding + // Brillig functions is unnecessary work. + if function.dfg.runtime().is_brillig() { + continue; + } function.constant_fold(false, brillig_info); } @@ -808,6 +813,7 @@ mod test { use crate::{ brillig::BrilligOptions, ssa::{ + Ssa, function_builder::FunctionBuilder, ir::{ function::RuntimeType, @@ -815,7 +821,6 @@ mod test { types::{NumericType, Type}, }, opt::assert_normalized_ssa_equals, - Ssa, }, }; @@ -1446,6 +1451,8 @@ mod test { } "; let ssa = Ssa::from_str(src).unwrap(); + // Need to run SSA pass that sets up Brillig array gets + let ssa = ssa.brillig_array_gets(); let brillig = ssa.to_brillig(&BrilligOptions::default()); let expected = " 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 d23cfee8a14f..e742ad4aa5d8 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 @@ -17,7 +17,7 @@ use crate::ssa::{ ssa_gen::Ssa, }; -use super::rc::{pop_rc_for, RcInstruction}; +use super::rc::{RcInstruction, pop_rc_for}; impl Ssa { /// Performs Dead Instruction Elimination (DIE) to remove any instructions with @@ -26,20 +26,22 @@ impl Ssa { /// This step should come after the flattening of the CFG and mem2reg. #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn dead_instruction_elimination(self) -> Ssa { - self.dead_instruction_elimination_inner(true) + self.dead_instruction_elimination_inner(true, false) } - fn dead_instruction_elimination_inner(mut self, flattened: bool) -> Ssa { + /// Post the Brillig generation we do not need to run this pass on Brillig functions. + #[tracing::instrument(level = "trace", skip(self))] + pub(crate) fn dead_instruction_elimination_acir(self) -> Ssa { + self.dead_instruction_elimination_inner(true, true) + } + + fn dead_instruction_elimination_inner(mut self, flattened: bool, skip_brillig: bool) -> Ssa { let mut used_globals_map: HashMap<_, _> = self .functions .par_iter_mut() .filter_map(|(id, func)| { - let set = func.dead_instruction_elimination(true, flattened); - if func.runtime().is_brillig() { - Some((*id, set)) - } else { - None - } + let set = func.dead_instruction_elimination(true, flattened, skip_brillig); + if func.runtime().is_brillig() { Some((*id, set)) } else { None } }) .collect(); @@ -79,7 +81,12 @@ impl Function { &mut self, insert_out_of_bounds_checks: bool, flattened: bool, + skip_brillig: bool, ) -> HashSet { + if skip_brillig && self.dfg.runtime().is_brillig() { + return HashSet::default(); + } + let mut context = Context { flattened, ..Default::default() }; context.mark_function_parameter_arrays_as_used(self); @@ -103,7 +110,7 @@ impl Function { // instructions (we don't want to remove those checks, or instructions that are // dependencies of those checks) if inserted_out_of_bounds_checks { - return self.dead_instruction_elimination(false, flattened); + return self.dead_instruction_elimination(false, flattened, skip_brillig); } context.remove_rc_instructions(&mut self.dfg); @@ -128,9 +135,8 @@ struct Context { /// them just yet. flattened: bool, - // When tracking mutations we consider arrays with the same type as all being possibly mutated. - // This we consider to span all blocks of the functions. - mutated_array_types: HashSet, + /// Track IncrementRc instructions per block to determine whether they are useless. + rc_tracker: RcTracker, } impl Context { @@ -160,10 +166,8 @@ impl Context { let block = &function.dfg[block_id]; self.mark_terminator_values_as_used(function, block); - // Lend the shared array type to the tracker. - let mut mutated_array_types = std::mem::take(&mut self.mutated_array_types); - let mut rc_tracker = RcTracker::new(&mut mutated_array_types); - rc_tracker.mark_terminator_arrays_as_used(function, block); + self.rc_tracker.new_block(); + self.rc_tracker.mark_terminator_arrays_as_used(function, block); let instructions_len = block.instructions().len(); @@ -196,12 +200,11 @@ impl Context { } } - rc_tracker.track_inc_rcs_to_remove(*instruction_id, function); + self.rc_tracker.track_inc_rcs_to_remove(*instruction_id, function); } - self.instructions_to_remove.extend(rc_tracker.get_non_mutated_arrays(&function.dfg)); - self.instructions_to_remove.extend(rc_tracker.rc_pairs_to_remove); - + self.instructions_to_remove.extend(self.rc_tracker.get_non_mutated_arrays(&function.dfg)); + self.instructions_to_remove.extend(self.rc_tracker.rc_pairs_to_remove.drain()); // If there are some instructions that might trigger an out of bounds error, // first add constrain checks. Then run the DIE pass again, which will remove those // but leave the constrains (any any value needed by those constrains) @@ -221,9 +224,6 @@ impl Context { .instructions_mut() .retain(|instruction| !self.instructions_to_remove.contains(instruction)); - // Take the mutated array back. - self.mutated_array_types = mutated_array_types; - false } @@ -272,11 +272,15 @@ impl Context { let typ = typ.get_contained_array(); // Want to store the array type which is being referenced, // because it's the underlying array that the `inc_rc` is associated with. - self.mutated_array_types.insert(typ.clone()); + self.add_mutated_array_type(typ.clone()); } } } + fn add_mutated_array_type(&mut self, typ: Type) { + self.rc_tracker.mutated_array_types.insert(typ.get_contained_array().clone()); + } + /// Go through the RC instructions collected when we figured out which values were unused; /// for each RC that refers to an unused value, remove the RC as well. fn remove_rc_instructions(&self, dfg: &mut DataFlowGraph) { @@ -608,8 +612,9 @@ fn apply_side_effects( (lhs, rhs) } +#[derive(Default)] /// Per block RC tracker. -struct RcTracker<'a> { +struct RcTracker { // We can track IncrementRc instructions per block to determine whether they are useless. // IncrementRc and DecrementRc instructions are normally side effectual instructions, but we remove // them if their value is not used anywhere in the function. However, even when their value is used, their existence @@ -624,7 +629,8 @@ struct RcTracker<'a> { // If an array is the same type as one of those non-mutated array types, we can safely remove all IncrementRc instructions on that array. inc_rcs: HashMap>, // Mutated arrays shared across the blocks of the function. - mutated_array_types: &'a mut HashSet, + // When tracking mutations we consider arrays with the same type as all being possibly mutated. + mutated_array_types: HashSet, // The SSA often creates patterns where after simplifications we end up with repeat // IncrementRc instructions on the same value. We track whether the previous instruction was an IncrementRc, // and if the current instruction is also an IncrementRc on the same value we remove the current instruction. @@ -632,15 +638,12 @@ struct RcTracker<'a> { previous_inc_rc: Option, } -impl<'a> RcTracker<'a> { - fn new(mutated_array_types: &'a mut HashSet) -> Self { - Self { - rcs_with_possible_pairs: Default::default(), - rc_pairs_to_remove: Default::default(), - inc_rcs: Default::default(), - previous_inc_rc: Default::default(), - mutated_array_types, - } +impl RcTracker { + fn new_block(&mut self) { + self.rcs_with_possible_pairs.clear(); + self.rc_pairs_to_remove.clear(); + self.inc_rcs.clear(); + self.previous_inc_rc = Default::default(); } fn mark_terminator_arrays_as_used(&mut self, function: &Function, block: &BasicBlock) { @@ -751,6 +754,7 @@ mod test { use noirc_frontend::monomorphization::ast::InlineType; use crate::ssa::{ + Ssa, function_builder::FunctionBuilder, ir::{ function::RuntimeType, @@ -758,7 +762,6 @@ mod test { types::{NumericType, Type}, }, opt::assert_normalized_ssa_equals, - Ssa, }; #[test] @@ -1099,7 +1102,7 @@ mod test { let ssa = Ssa::from_str(src).unwrap(); // Even though these ACIR functions only have 1 block, we have not inlined and flattened anything yet. - let ssa = ssa.dead_instruction_elimination_inner(false); + let ssa = ssa.dead_instruction_elimination_inner(false, false); let expected = " acir(inline) fn main f0 { @@ -1121,4 +1124,38 @@ mod test { "; assert_normalized_ssa_equals(ssa, expected); } + + #[test] + fn do_not_remove_inc_rc_if_mutated_in_other_block() { + let src = " + brillig(inline) fn main f0 { + b0(v0: &mut [Field; 3]): + v1 = load v0 -> [Field; 3] + inc_rc v1 + jmp b1() + b1(): + v2 = load v0 -> [Field; 3] + v3 = array_set v2, index u32 0, value u32 0 + store v3 at v0 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let expected = " + brillig(inline) fn main f0 { + b0(v0: &mut [Field; 3]): + v1 = load v0 -> [Field; 3] + inc_rc v1 + jmp b1() + b1(): + v2 = load v0 -> [Field; 3] + v4 = array_set v2, index u32 0, value u32 0 + store v4 at v0 + return + } + "; + let ssa = ssa.dead_instruction_elimination(); + assert_normalized_ssa_equals(ssa, expected); + } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 76f8495c0094..a25e3db2b080 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -133,7 +133,7 @@ //! store v12 at v5 (new store) use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; -use acvm::{acir::AcirField, acir::BlackBoxFunc, FieldElement}; +use acvm::{FieldElement, acir::AcirField, acir::BlackBoxFunc}; use iter_extended::vecmap; use crate::ssa::{ @@ -176,12 +176,15 @@ impl Ssa { } } -struct Context<'f> { - inserter: FunctionInserter<'f>, +pub(crate) struct Context<'f> { + pub(crate) inserter: FunctionInserter<'f>, /// This ControlFlowGraph is the graph from before the function was modified by this flattening pass. cfg: ControlFlowGraph, + /// Target block of the flattening + pub(crate) target_block: BasicBlockId, + /// Maps start of branch -> end of branch branch_ends: HashMap, @@ -213,6 +216,10 @@ struct Context<'f> { /// us from unnecessarily inserting extra instructions, and keeps ids unique which /// helps simplifications. not_instructions: HashMap, + + /// Flag to tell the context to not issue 'enable_side_effect' instructions during flattening. + /// This should be set to true only by flatten_single(), when no instruction is known to fail. + pub(crate) no_predicate: bool, } #[derive(Clone)] @@ -249,6 +256,7 @@ fn flatten_function_cfg(function: &mut Function, no_predicates: &HashMap Context<'f> { - fn flatten(&mut self, no_predicates: &HashMap) { + //impl Context<'_> { + pub(crate) fn new( + function: &'f mut Function, + cfg: ControlFlowGraph, + branch_ends: HashMap, + target_block: BasicBlockId, + ) -> Self { + Context { + inserter: FunctionInserter::new(function), + cfg, + branch_ends, + condition_stack: Vec::new(), + arguments_stack: Vec::new(), + local_allocations: HashSet::default(), + not_instructions: HashMap::default(), + target_block, + no_predicate: false, + } + } + + pub(crate) fn flatten(&mut self, no_predicates: &HashMap) { // Flatten the CFG by inlining all instructions from the queued blocks // until all blocks have been flattened. // We follow the terminator of each block to determine which blocks to // process next - let mut queue = vec![self.inserter.function.entry_block()]; + let mut queue = vec![self.target_block]; while let Some(block) = queue.pop() { self.inline_block(block, no_predicates); let to_process = self.handle_terminator(block, &queue); @@ -318,10 +348,14 @@ impl<'f> Context<'f> { result } - // Inline all instructions from the given block into the entry block, and track slice capacities - fn inline_block(&mut self, block: BasicBlockId, no_predicates: &HashMap) { - if self.inserter.function.entry_block() == block { - // we do not inline the entry block into itself + // Inline all instructions from the given block into the target block, and track slice capacities + pub(crate) fn inline_block( + &mut self, + block: BasicBlockId, + no_predicates: &HashMap, + ) { + if self.target_block == block { + // we do not inline the target block into itself // for the outer block before we start inlining return; } @@ -354,7 +388,7 @@ impl<'f> Context<'f> { /// For a normal block, it would be its successor /// For blocks related to a conditional statement, we ensure to process /// the 'then-branch', then the 'else-branch' (if it exists), and finally the end block - fn handle_terminator( + pub(crate) fn handle_terminator( &mut self, block: BasicBlockId, work_list: &[BasicBlockId], @@ -388,9 +422,9 @@ impl<'f> Context<'f> { let return_values = vecmap(return_values.clone(), |value| self.inserter.resolve(value)); let new_return = TerminatorInstruction::Return { return_values, call_stack }; - let entry = self.inserter.function.entry_block(); + let target = self.target_block; - self.inserter.function.dfg.set_block_terminator(entry, new_return); + self.inserter.function.dfg.set_block_terminator(target, new_return); vec![] } } @@ -544,7 +578,7 @@ impl<'f> Context<'f> { } else { self.inserter.function.dfg.make_constant(FieldElement::zero(), NumericType::bool()) }; - let block = self.inserter.function.entry_block(); + let block = self.target_block; // Cannot include this in the previous vecmap since it requires exclusive access to self let args = vecmap(args, |(then_arg, else_arg)| { @@ -568,11 +602,11 @@ impl<'f> Context<'f> { destination } - /// Insert a new instruction into the function's entry block. + /// Insert a new instruction into the target block. /// Unlike push_instruction, this function will not map any ValueIds. /// within the given instruction, nor will it modify self.values in any way. fn insert_instruction(&mut self, instruction: Instruction, call_stack: CallStackId) -> ValueId { - let block = self.inserter.function.entry_block(); + let block = self.target_block; self.inserter .function .dfg @@ -580,7 +614,7 @@ impl<'f> Context<'f> { .first() } - /// Inserts a new instruction into the function's entry block, using the given + /// Inserts a new instruction into the target block, using the given /// control type variables to specify result types if needed. /// Unlike push_instruction, this function will not map any ValueIds. /// within the given instruction, nor will it modify self.values in any way. @@ -590,7 +624,7 @@ impl<'f> Context<'f> { ctrl_typevars: Option>, call_stack: CallStackId, ) -> InsertInstructionResult { - let block = self.inserter.function.entry_block(); + let block = self.target_block; self.inserter.function.dfg.insert_instruction_and_results( instruction, block, @@ -600,11 +634,14 @@ impl<'f> Context<'f> { } /// Checks the branch condition on the top of the stack and uses it to build and insert an - /// `EnableSideEffectsIf` instruction into the entry block. + /// `EnableSideEffectsIf` instruction into the target block. /// /// If the stack is empty, a "true" u1 constant is taken to be the active condition. This is /// necessary for re-enabling side-effects when re-emerging to a branch depth of 0. fn insert_current_side_effects_enabled(&mut self) { + if self.no_predicate { + return; + } let condition = match self.get_last_condition() { Some(cond) => cond, None => { @@ -616,7 +653,7 @@ impl<'f> Context<'f> { self.insert_instruction_with_typevars(enable_side_effects, None, call_stack); } - /// Push the given instruction to the end of the entry block of the current function. + /// Push the given instruction to the end of the target block of the current function. /// /// Note that each ValueId of the instruction will be mapped via self.inserter.resolve. /// As a result, the instruction that will be pushed will actually be a new instruction @@ -631,8 +668,8 @@ impl<'f> Context<'f> { let instruction = self.handle_instruction_side_effects(instruction, call_stack); let instruction_is_allocate = matches!(&instruction, Instruction::Allocate); - let entry = self.inserter.function.entry_block(); - let results = self.inserter.push_instruction_value(instruction, id, entry, call_stack); + let results = + self.inserter.push_instruction_value(instruction, id, self.target_block, call_stack); // Remember an allocate was created local to this branch so that we do not try to merge store // values across branches for it later. @@ -816,13 +853,13 @@ mod test { use acvm::acir::AcirField; use crate::ssa::{ + Ssa, ir::{ dfg::DataFlowGraph, instruction::{Instruction, TerminatorInstruction}, value::{Value, ValueId}, }, opt::assert_normalized_ssa_equals, - Ssa, }; #[test] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs index 780912852085..7aad174d327d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs @@ -102,7 +102,9 @@ impl<'cfg> Context<'cfg> { } else if successors.len() == 1 { self.find_join_point(successors.next().unwrap()) } else if successors.len() == 0 { - unreachable!("return encountered before a join point was found. This can only happen if early-return was added to the language without implementing it by jmping to a join block first") + unreachable!( + "return encountered before a join point was found. This can only happen if early-return was added to the language without implementing it by jmping to a join block first" + ) } else { unreachable!("A block can only have 0, 1, or 2 successors"); } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs index f4638cf85e45..6e8c7df2bba5 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs @@ -1,4 +1,4 @@ -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use fxhash::{FxHashMap as HashMap, FxHashSet}; use crate::ssa::ir::{ diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/hint.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/hint.rs index be088c3da948..911554e5d6dd 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/hint.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/hint.rs @@ -6,8 +6,8 @@ mod tests { brillig::BrilligOptions, errors::RuntimeError, ssa::{ - opt::assert_normalized_ssa_equals, optimize_all, Ssa, SsaBuilder, SsaEvaluatorOptions, - SsaLogging, + Ssa, SsaBuilder, SsaEvaluatorOptions, SsaLogging, opt::assert_normalized_ssa_equals, + optimize_all, }, }; @@ -20,7 +20,7 @@ mod tests { emit_ssa: None, skip_underconstrained_check: true, enable_brillig_constraints_check_lookback: false, - enable_brillig_constraints_check: false, + skip_brillig_constraints_check: true, inliner_aggressiveness: 0, max_bytecode_increase_percent: None, }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining.rs index e5753aeba4e8..15414e92eff6 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining.rs @@ -23,7 +23,7 @@ use crate::ssa::{ pub(super) mod inline_info; -pub(super) use inline_info::{compute_inline_infos, InlineInfo, InlineInfos}; +pub(super) use inline_info::{InlineInfo, InlineInfos, compute_inline_infos}; /// An arbitrary limit to the maximum number of recursive call /// frames at any point in time. @@ -278,7 +278,9 @@ impl InlineContext { if self.recursion_level > RECURSION_LIMIT { panic!( - "Attempted to recur more than {RECURSION_LIMIT} times during inlining function '{}':\n{}", source_function.name(), source_function + "Attempted to recur more than {RECURSION_LIMIT} times during inlining function '{}':\n{}", + source_function.name(), + source_function ); } @@ -349,10 +351,14 @@ impl<'function> PerFunctionContext<'function> { return id; } } - unreachable!("All Value::Instructions should already be known during inlining after creating the original inlined instruction. Unknown value {id} = {value:?}") + unreachable!( + "All Value::Instructions should already be known during inlining after creating the original inlined instruction. Unknown value {id} = {value:?}" + ) } value @ Value::Param { .. } => { - unreachable!("All Value::Params should already be known from previous calls to translate_block. Unknown value {id} = {value:?}") + unreachable!( + "All Value::Params should already be known from previous calls to translate_block. Unknown value {id} = {value:?}" + ) } Value::NumericConstant { constant, typ } => { // The dfg indexes a global's inner value directly, so we need to check here @@ -778,10 +784,11 @@ impl<'function> PerFunctionContext<'function> { mod test { use std::cmp::max; - use acvm::{acir::AcirField, FieldElement}; + use acvm::{FieldElement, acir::AcirField}; use noirc_frontend::monomorphization::ast::InlineType; use crate::ssa::{ + Ssa, function_builder::FunctionBuilder, ir::{ basic_block::BasicBlockId, @@ -791,7 +798,6 @@ mod test { types::{NumericType, Type}, }, opt::{assert_normalized_ssa_equals, inlining::inline_info::compute_bottom_up_order}, - Ssa, }; #[test] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining/inline_info.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining/inline_info.rs index 26bb2cad6752..d40baaae6a35 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining/inline_info.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining/inline_info.rs @@ -311,11 +311,7 @@ fn mark_functions_to_retain_recursive( let inlined_function_weights: i64 = called_functions.iter().fold(0, |acc, callee| { let info = &inline_infos[callee]; // If the callee is not going to be inlined then we can ignore its cost. - if info.should_inline { - acc.saturating_add(info.weight) - } else { - acc - } + if info.should_inline { acc.saturating_add(info.weight) } else { acc } }); let this_function_weight = inlined_function_weights diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs index 19fc6a7f5a20..f68afc55efa0 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs @@ -7,22 +7,22 @@ //! - Already marked as loop invariants //! //! We also check that we are not hoisting instructions with side effects. -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::ssa::{ + Ssa, ir::{ basic_block::BasicBlockId, function::Function, function_inserter::FunctionInserter, instruction::{ - binary::eval_constant_binary_op, Binary, BinaryOp, Instruction, InstructionId, + Binary, BinaryOp, Instruction, InstructionId, binary::eval_constant_binary_op, }, post_order::PostOrder, types::Type, value::ValueId, }, - Ssa, }; use super::unrolling::{Loop, Loops}; @@ -373,8 +373,8 @@ impl<'f> LoopInvariantContext<'f> { #[cfg(test)] mod test { - use crate::ssa::opt::assert_normalized_ssa_equals; use crate::ssa::Ssa; + use crate::ssa::opt::assert_normalized_ssa_equals; #[test] fn simple_loop_invariant_code_motion() { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/make_constrain_not_equal.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/make_constrain_not_equal.rs index 21f536eba2d4..28e59f924292 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/make_constrain_not_equal.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/make_constrain_not_equal.rs @@ -39,7 +39,7 @@ impl Function { if self .dfg .get_numeric_constant(*rhs) - .map_or(false, |constant| constant.is_zero()) + .is_some_and(|constant| constant.is_zero()) { if let Value::Instruction { instruction, .. } = &self.dfg[self.dfg.resolve(*lhs)] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index ce76825877a6..9b58a0de3294 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -201,7 +201,7 @@ impl<'f> PerFunctionContext<'f> { let is_dereference = block .expressions .get(store_address) - .map_or(false, |expression| matches!(expression, Expression::Dereference(_))); + .is_some_and(|expression| matches!(expression, Expression::Dereference(_))); if !self.last_loads.contains_key(store_address) && !store_alias_used @@ -668,10 +668,11 @@ impl<'f> PerFunctionContext<'f> { mod tests { use std::sync::Arc; - use acvm::{acir::AcirField, FieldElement}; + use acvm::{FieldElement, acir::AcirField}; use im::vector; use crate::ssa::{ + Ssa, function_builder::FunctionBuilder, ir::{ basic_block::BasicBlockId, @@ -681,7 +682,6 @@ mod tests { types::Type, }, opt::assert_normalized_ssa_equals, - Ssa, }; #[test] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs index 91e27f07b8e9..8c74852b53bf 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs @@ -58,11 +58,7 @@ pub(super) enum ReferenceValue { impl ReferenceValue { fn unify(self, other: Self) -> Self { - if self == other { - self - } else { - ReferenceValue::Unknown - } + if self == other { self } else { ReferenceValue::Unknown } } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mod.rs index 4d8a652b94d9..a9784d4c7cfb 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mod.rs @@ -7,7 +7,10 @@ mod array_set; mod as_slice_length; mod assert_constant; +mod basic_conditional; +mod brillig_array_gets; pub(crate) mod brillig_entry_points; +mod check_u128_mul_overflow; mod constant_folding; mod defunctionalize; mod die; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/preprocess_fns.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/preprocess_fns.rs index 764fb6dd65b5..6fa1e88ee2d4 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/preprocess_fns.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/preprocess_fns.rs @@ -1,8 +1,8 @@ //! Pre-process functions before inlining them into others. use crate::ssa::{ - ir::function::{Function, RuntimeType}, Ssa, + ir::function::{Function, RuntimeType}, }; use super::inlining::{self, InlineInfo}; @@ -59,7 +59,7 @@ impl Ssa { // Try to reduce the number of blocks. function.simplify_function(); // Remove leftover instructions. - function.dead_instruction_elimination(true, false); + function.dead_instruction_elimination(true, false, false); // Put it back into the SSA, so the next functions can pick it up. self.functions.insert(id, function); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/pure.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/pure.rs index d790d035eb05..b975413fc2e5 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/pure.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/pure.rs @@ -171,7 +171,7 @@ impl Function { | Value::Instruction { .. } | Value::Param { .. } | Value::NumericConstant { .. } => { - return (Purity::Impure, BTreeSet::new()) + return (Purity::Impure, BTreeSet::new()); } } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs index e36be71aeea2..b4427a1c91bf 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, sync::Arc}; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use crate::ssa::{ ir::{ @@ -167,6 +167,7 @@ impl Context<'_> { let lhs_typ = self.function.dfg.type_of_value(lhs).unwrap_numeric(); let base = self.field_constant(FieldElement::from(2_u128)); let pow = self.pow(base, rhs); + let pow = self.pow_or_max_for_bit_size(pow, rhs, bit_size, lhs_typ); let pow = self.insert_cast(pow, lhs_typ); if lhs_typ.is_unsigned() { // unsigned right bit shift is just a normal division @@ -205,6 +206,53 @@ impl Context<'_> { } } + /// Returns `pow` or the maximum value allowed for `typ` if 2^rhs is guaranteed to exceed that maximum. + fn pow_or_max_for_bit_size( + &mut self, + pow: ValueId, + rhs: ValueId, + bit_size: u32, + typ: NumericType, + ) -> ValueId { + let max = if typ.is_unsigned() { + if bit_size == 128 { u128::MAX } else { (1_u128 << bit_size) - 1 } + } else { + 1_u128 << (bit_size - 1) + }; + let max = self.field_constant(FieldElement::from(max)); + + // Here we check whether rhs is less than the bit_size: if it's not then it will overflow. + // Then we do: + // + // rhs_is_less_than_bit_size = lt rhs, bit_size + // rhs_is_not_less_than_bit_size = not rhs_is_less_than_bit_size + // pow_when_is_less_than_bit_size = rhs_is_less_than_bit_size * pow + // pow_when_is_not_less_than_bit_size = rhs_is_not_less_than_bit_size * max + // pow = add pow_when_is_less_than_bit_size, pow_when_is_not_less_than_bit_size + // + // All operations here are unchecked because they work on field types. + let rhs_typ = self.function.dfg.type_of_value(rhs).unwrap_numeric(); + let bit_size = self.numeric_constant(bit_size as u128, rhs_typ); + let rhs_is_less_than_bit_size = self.insert_binary(rhs, BinaryOp::Lt, bit_size); + let rhs_is_not_less_than_bit_size = self.insert_not(rhs_is_less_than_bit_size); + let rhs_is_less_than_bit_size = + self.insert_cast(rhs_is_less_than_bit_size, NumericType::NativeField); + let rhs_is_not_less_than_bit_size = + self.insert_cast(rhs_is_not_less_than_bit_size, NumericType::NativeField); + let pow_when_is_less_than_bit_size = + self.insert_binary(rhs_is_less_than_bit_size, BinaryOp::Mul { unchecked: true }, pow); + let pow_when_is_not_less_than_bit_size = self.insert_binary( + rhs_is_not_less_than_bit_size, + BinaryOp::Mul { unchecked: true }, + max, + ); + self.insert_binary( + pow_when_is_less_than_bit_size, + BinaryOp::Add { unchecked: true }, + pow_when_is_not_less_than_bit_size, + ) + } + /// Computes lhs^rhs via square&multiply, using the bits decomposition of rhs /// Pseudo-code of the computation: /// let mut r = 1; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs index 942fe67b5d5c..44a1a0b7acf2 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs @@ -10,7 +10,7 @@ //! before the [Instruction]. Continue inserting instructions until the next [Instruction::EnableSideEffectsIf] is encountered. use std::collections::HashSet; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use crate::ssa::{ ir::{ @@ -92,7 +92,7 @@ impl Context { let condition_is_one = function .dfg .get_numeric_constant(*condition) - .map_or(false, |condition| condition.is_one()); + .is_some_and(|condition| condition.is_one()); if condition_is_one { new_instructions.push(instruction_id); last_side_effects_enabled_instruction = None; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs index 8dde79a3c60d..94b0c1d0a851 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs @@ -1,6 +1,6 @@ use std::collections::hash_map::Entry; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use fxhash::FxHashMap as HashMap; use crate::ssa::ir::function::RuntimeType; @@ -8,6 +8,7 @@ use crate::ssa::ir::instruction::Hint; use crate::ssa::ir::types::NumericType; use crate::ssa::ir::value::ValueId; use crate::ssa::{ + Ssa, ir::{ dfg::DataFlowGraph, function::Function, @@ -16,7 +17,6 @@ use crate::ssa::{ value::Value, }, opt::flatten_cfg::value_merger::ValueMerger, - Ssa, }; impl Ssa { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs index 22fdf0a79872..3d812870c064 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs @@ -254,7 +254,9 @@ fn remove_block_parameters( let jump_args = match function.dfg[predecessor].unwrap_terminator_mut() { TerminatorInstruction::Jmp { arguments, .. } => std::mem::take(arguments), - TerminatorInstruction::JmpIf { .. } => unreachable!("If jmpif instructions are modified to support block arguments in the future, this match will need to be updated"), + TerminatorInstruction::JmpIf { .. } => unreachable!( + "If jmpif instructions are modified to support block arguments in the future, this match will need to be updated" + ), _ => unreachable!( "Predecessor was already validated to have only a single jmp destination" ), @@ -293,6 +295,7 @@ fn try_inline_into_predecessor( #[cfg(test)] mod test { use crate::ssa::{ + Ssa, function_builder::FunctionBuilder, ir::{ instruction::{BinaryOp, TerminatorInstruction}, @@ -300,7 +303,6 @@ mod test { types::Type, }, opt::assert_normalized_ssa_equals, - Ssa, }; use acvm::acir::AcirField; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs index 547a8a042c65..43afb9fa41a7 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -20,7 +20,7 @@ //! only used by Brillig bytecode. use std::collections::BTreeSet; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use im::HashSet; use crate::{ @@ -490,9 +490,19 @@ impl Loop { context.inline_instructions_from_block(); // Mutate the terminator if possible so that it points at the iteration block. match context.dfg()[fresh_block].unwrap_terminator() { - TerminatorInstruction::JmpIf { condition, then_destination, else_destination, call_stack } => { + TerminatorInstruction::JmpIf { + condition, + then_destination, + else_destination, + call_stack, + } => { let condition = *condition; - let next_blocks = context.handle_jmpif(condition, *then_destination, *else_destination, *call_stack); + let next_blocks = context.handle_jmpif( + condition, + *then_destination, + *else_destination, + *call_stack, + ); // If there is only 1 next block the jmpif evaluated to a single known block. // This is the expected case and lets us know if we should loop again or not. @@ -515,7 +525,9 @@ impl Loop { Err(context.inserter.function.dfg.get_value_call_stack(condition)) } } - other => unreachable!("Expected loop header to terminate in a JmpIf to the loop body, but found {other:?} instead"), + other => unreachable!( + "Expected loop header to terminate in a JmpIf to the loop body, but found {other:?} instead" + ), } } @@ -1021,9 +1033,9 @@ mod tests { use test_case::test_case; use crate::errors::RuntimeError; - use crate::ssa::{ir::value::ValueId, opt::assert_normalized_ssa_equals, Ssa}; + use crate::ssa::{Ssa, ir::value::ValueId, opt::assert_normalized_ssa_equals}; - use super::{is_new_size_ok, BoilerplateStats, Loops}; + use super::{BoilerplateStats, Loops, is_new_size_ok}; /// Tries to unroll all loops in each SSA function once, calling the `Function` directly, /// bypassing the iterative loop done by the SSA which does further optimisations. diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index fc9b1ae98bcd..d7ca5832f064 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -15,8 +15,8 @@ use crate::ssa::{ }; use super::{ - ast::AssertMessage, Identifier, ParsedBlock, ParsedFunction, ParsedGlobal, ParsedGlobalValue, - ParsedInstruction, ParsedSsa, ParsedTerminator, ParsedValue, RuntimeType, Ssa, SsaError, Type, + Identifier, ParsedBlock, ParsedFunction, ParsedGlobal, ParsedGlobalValue, ParsedInstruction, + ParsedSsa, ParsedTerminator, ParsedValue, RuntimeType, Ssa, SsaError, Type, ast::AssertMessage, }; impl ParsedSsa { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/lexer.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/lexer.rs index e22b6a661de0..e6e15d1559d3 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/lexer.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/lexer.rs @@ -171,7 +171,7 @@ impl<'a> Lexer<'a> { return Err(LexerError::InvalidIntegerLiteral { span: Span::inclusive(start, end), found: integer_str, - }) + }); } }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/mod.rs index 4c5f6334430d..9f4d38648e20 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/mod.rs @@ -5,11 +5,11 @@ use std::{ }; use super::{ + Ssa, ir::{ instruction::BinaryOp, types::{NumericType, Type}, }, - Ssa, }; use acvm::{AcirField, FieldElement}; @@ -675,11 +675,7 @@ impl<'a> Parser<'a> { } fn parse_value_or_error(&mut self) -> ParseResult { - if let Some(value) = self.parse_value()? { - Ok(value) - } else { - self.expected_value() - } + if let Some(value) = self.parse_value()? { Ok(value) } else { self.expected_value() } } fn parse_value(&mut self) -> ParseResult> { @@ -818,7 +814,7 @@ impl<'a> Parser<'a> { } fn eat_identifier(&mut self) -> ParseResult> { - let span = self.token.to_span(); + let span = self.token.span(); if let Some(name) = self.eat_ident()? { Ok(Some(Identifier::new(name, span))) } else { @@ -852,11 +848,7 @@ impl<'a> Parser<'a> { } fn eat_ident_or_error(&mut self) -> ParseResult { - if let Some(ident) = self.eat_ident()? { - Ok(ident) - } else { - self.expected_identifier() - } + if let Some(ident) = self.eat_ident()? { Ok(ident) } else { self.expected_identifier() } } fn eat_int(&mut self) -> ParseResult> { @@ -879,11 +871,7 @@ impl<'a> Parser<'a> { } fn eat_int_or_error(&mut self) -> ParseResult { - if let Some(int) = self.eat_int()? { - Ok(int) - } else { - self.expected_int() - } + if let Some(int) = self.eat_int()? { Ok(int) } else { self.expected_int() } } fn eat_int_type(&mut self) -> ParseResult> { @@ -933,11 +921,7 @@ impl<'a> Parser<'a> { } fn eat_or_error(&mut self, token: Token) -> ParseResult<()> { - if self.eat(token.clone())? { - Ok(()) - } else { - self.expected_token(token) - } + if self.eat(token.clone())? { Ok(()) } else { self.expected_token(token) } } fn at(&self, token: Token) -> bool { @@ -960,56 +944,53 @@ impl<'a> Parser<'a> { fn expected_instruction_or_terminator(&mut self) -> ParseResult { Err(ParserError::ExpectedInstructionOrTerminator { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } fn expected_string_or_data(&mut self) -> ParseResult { Err(ParserError::ExpectedStringOrData { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } fn expected_byte_string(&mut self) -> ParseResult { Err(ParserError::ExpectedByteString { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } fn expected_identifier(&mut self) -> ParseResult { Err(ParserError::ExpectedIdentifier { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } fn expected_int(&mut self) -> ParseResult { - Err(ParserError::ExpectedInt { - found: self.token.token().clone(), - span: self.token.to_span(), - }) + Err(ParserError::ExpectedInt { found: self.token.token().clone(), span: self.token.span() }) } fn expected_type(&mut self) -> ParseResult { Err(ParserError::ExpectedType { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } fn expected_value(&mut self) -> ParseResult { Err(ParserError::ExpectedValue { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } fn expected_global_value(&mut self) -> ParseResult { Err(ParserError::ExpectedGlobalValue { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } @@ -1017,7 +998,7 @@ impl<'a> Parser<'a> { Err(ParserError::ExpectedToken { token, found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } @@ -1025,7 +1006,7 @@ impl<'a> Parser<'a> { Err(ParserError::ExpectedOneOfTokens { tokens: tokens.to_vec(), found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 358c2e89a411..a865a31a060d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -1,7 +1,7 @@ #![cfg(test)] use crate::{ - ssa::{opt::assert_normalized_ssa_equals, Ssa}, + ssa::{Ssa, opt::assert_normalized_ssa_equals}, trim_leading_whitespace_from_lines, }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/token.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/token.rs index eb09209466d4..280a2ca95a7c 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -12,7 +12,7 @@ impl SpannedToken { SpannedToken(Spanned::from(span, token)) } - pub(crate) fn to_span(&self) -> Span { + pub(crate) fn span(&self) -> Span { self.0.span() } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index cbac4bd6d840..5db3ecb91b7f 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -1,12 +1,13 @@ use std::collections::BTreeMap; use std::sync::{Arc, Mutex, RwLock}; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use iter_extended::vecmap; use noirc_errors::Location; use noirc_frontend::ast::{BinaryOpKind, Signedness}; use noirc_frontend::monomorphization::ast::{self, GlobalId, InlineType, LocalId, Parameters}; use noirc_frontend::monomorphization::ast::{FuncId, Program}; +use noirc_frontend::signed_field::SignedField; use crate::errors::RuntimeError; use crate::ssa::function_builder::FunctionBuilder; @@ -19,8 +20,8 @@ use crate::ssa::ir::map::AtomicCounter; use crate::ssa::ir::types::{NumericType, Type}; use crate::ssa::ir::value::ValueId; -use super::value::{Tree, Value, Values}; use super::GlobalsGraph; +use super::value::{Tree, Value, Values}; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; /// The FunctionContext is the main context object for translating a @@ -289,32 +290,30 @@ impl<'a> FunctionContext<'a> { /// otherwise values like 2^128 can be assigned to a u8 without error or wrapping. pub(super) fn checked_numeric_constant( &mut self, - value: impl Into, - negative: bool, + value: SignedField, numeric_type: NumericType, ) -> Result { - let value = value.into(); - - if let Some(range) = numeric_type.value_is_outside_limits(value, negative) { + if let Some(range) = numeric_type.value_is_outside_limits(value) { let call_stack = self.builder.get_call_stack(); return Err(RuntimeError::IntegerOutOfBounds { - value: if negative { -value } else { value }, + value, typ: numeric_type, range, call_stack, }); } - let value = if negative { + let value = if value.is_negative { match numeric_type { - NumericType::NativeField => -value, + NumericType::NativeField => -value.field, NumericType::Signed { bit_size } | NumericType::Unsigned { bit_size } => { + assert!(bit_size < 128); let base = 1_u128 << bit_size; - FieldElement::from(base) - value + FieldElement::from(base) - value.field } } } else { - value + value.field }; Ok(self.builder.numeric_constant(value, numeric_type)) @@ -355,7 +354,8 @@ impl<'a> FunctionContext<'a> { /// Insert constraints ensuring that the operation does not overflow the bit size of the result /// - /// If the result is unsigned, overflow will be checked during acir-gen (cf. issue #4456), except for bit-shifts, because we will convert them to field multiplication + /// If the result is unsigned, overflow will be checked during acir-gen (cf. issue #4456), except for + /// bit-shifts, because we will convert them to field multiplication /// /// If the result is signed, we just prepare it for check_signed_overflow() by casting it to /// an unsigned value representing the signed integer. @@ -755,11 +755,7 @@ impl<'a> FunctionContext<'a> { Ok(match lvalue { ast::LValue::Ident(ident) => { let (reference, should_auto_deref) = self.ident_lvalue(ident); - if should_auto_deref { - LValue::Dereference { reference } - } else { - LValue::Ident - } + if should_auto_deref { LValue::Dereference { reference } } else { LValue::Ident } } ast::LValue::Index { array, index, location, .. } => { self.index_lvalue(array, index, location)?.2 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 91aa8b2914d2..a954ac3ab93c 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 @@ -130,7 +130,7 @@ pub(crate) fn generate_ssa(program: Program) -> Result { Ok(ssa) } -impl<'a> FunctionContext<'a> { +impl FunctionContext<'_> { /// Codegen a function's body and set its return value to that of its last parameter. /// For functions returning nothing, this will be an empty list. fn codegen_function_body(&mut self, body: &Expression) -> Result<(), RuntimeError> { @@ -233,10 +233,10 @@ impl<'a> FunctionContext<'a> { _ => unreachable!("ICE: unexpected slice literal type, got {}", array.typ), }) } - ast::Literal::Integer(value, negative, typ, location) => { + ast::Literal::Integer(value, typ, location) => { self.builder.set_location(*location); let typ = Self::convert_non_tuple_type(typ).unwrap_numeric(); - self.checked_numeric_constant(*value, *negative, typ).map(Into::into) + self.checked_numeric_constant(*value, typ).map(Into::into) } ast::Literal::Bool(value) => { // Don't need to call checked_numeric_constant here since `value` can only be true or false @@ -252,7 +252,7 @@ impl<'a> FunctionContext<'a> { let value = value.replace('{', "{{").replace('}', "}}"); string.push_str(&value); } - FmtStrFragment::Interpolation(value, _span) => { + FmtStrFragment::Interpolation(value, _) => { string.push('{'); string.push_str(value); string.push('}'); @@ -820,9 +820,7 @@ impl<'a> FunctionContext<'a> { typ: NumericType, ) -> Result { match constructor { - Constructor::Int(value) => { - self.checked_numeric_constant(value.field, value.is_negative, typ) - } + Constructor::Int(value) => self.checked_numeric_constant(*value, typ), other => Ok(self.builder.numeric_constant(other.variant_index(), typ)), } } diff --git a/noir/noir-repo/compiler/noirc_frontend/Cargo.toml b/noir/noir-repo/compiler/noirc_frontend/Cargo.toml index f9e36f7f3e0e..e5231565041c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_frontend/Cargo.toml @@ -33,7 +33,6 @@ strum.workspace = true strum_macros.workspace = true fxhash.workspace = true - [dev-dependencies] base64.workspace = true proptest.workspace = true diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/enumeration.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/enumeration.rs index 6789a200e6aa..5ac60be5fba5 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/enumeration.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/enumeration.rs @@ -4,7 +4,7 @@ use crate::ast::{Ident, UnresolvedGenerics, UnresolvedType}; use crate::token::SecondaryAttribute; use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::Location; use super::{Documented, ItemVisibility}; @@ -16,7 +16,7 @@ pub struct NoirEnumeration { pub visibility: ItemVisibility, pub generics: UnresolvedGenerics, pub variants: Vec>, - pub span: Span, + pub location: Location, } impl NoirEnumeration { 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 01705e0af881..398e52676950 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs @@ -7,16 +7,15 @@ use crate::ast::{ Ident, ItemVisibility, Path, Pattern, Statement, StatementKind, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, Visibility, }; -use crate::node_interner::{ - ExprId, InternedExpressionKind, InternedStatementKind, QuotedTypeId, TypeId, -}; +use crate::node_interner::{ExprId, InternedExpressionKind, InternedStatementKind, QuotedTypeId}; +use crate::signed_field::SignedField; use crate::token::{Attributes, FmtStrFragment, FunctionAttribute, Token, Tokens}; use crate::{Kind, Type}; -use acvm::{acir::AcirField, FieldElement}; +use acvm::FieldElement; use iter_extended::vecmap; -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Located, Location, Span}; -use super::{AsTraitPath, TypePath}; +use super::{AsTraitPath, TypePath, UnsafeExpression}; #[derive(Debug, PartialEq, Eq, Clone)] pub enum ExpressionKind { @@ -39,8 +38,8 @@ pub enum ExpressionKind { Parenthesized(Box), Quote(Tokens), Unquote(Box), - Comptime(BlockExpression, Span), - Unsafe(BlockExpression, Span), + Comptime(BlockExpression, Location), + Unsafe(UnsafeExpression), AsTraitPath(AsTraitPath), TypePath(TypePath), @@ -76,7 +75,7 @@ pub enum UnresolvedGeneric { /// splices existing types into a generic list. In this case we have /// to validate the type refers to a named generic and treat that /// as a ResolvedGeneric when this is resolved. - Resolved(QuotedTypeId, Span), + Resolved(QuotedTypeId, Location), } #[derive(Error, PartialEq, Eq, Debug, Clone)] @@ -87,14 +86,18 @@ pub struct UnsupportedNumericGenericType { } impl UnresolvedGeneric { - pub fn span(&self) -> Span { + pub fn location(&self) -> Location { match self { - UnresolvedGeneric::Variable(ident) => ident.0.span(), - UnresolvedGeneric::Numeric { ident, typ } => ident.0.span().merge(typ.span), - UnresolvedGeneric::Resolved(_, span) => *span, + UnresolvedGeneric::Variable(ident) => ident.0.location(), + UnresolvedGeneric::Numeric { ident, typ } => ident.location().merge(typ.location), + UnresolvedGeneric::Resolved(_, location) => *location, } } + pub fn span(&self) -> Span { + self.location().span + } + pub fn kind(&self) -> Result { match self { UnresolvedGeneric::Variable(_) => Ok(Kind::Normal), @@ -168,8 +171,8 @@ impl ExpressionKind { match (operator, &rhs) { ( UnaryOp::Minus, - Expression { kind: ExpressionKind::Literal(Literal::Integer(field, sign)), .. }, - ) => ExpressionKind::Literal(Literal::Integer(*field, !sign)), + Expression { kind: ExpressionKind::Literal(Literal::Integer(field)), .. }, + ) => ExpressionKind::Literal(Literal::Integer(-*field)), _ => ExpressionKind::Prefix(Box::new(PrefixExpression { operator, rhs })), } } @@ -197,7 +200,7 @@ impl ExpressionKind { } pub fn integer(contents: FieldElement) -> ExpressionKind { - ExpressionKind::Literal(Literal::Integer(contents, false)) + ExpressionKind::Literal(Literal::Integer(SignedField::positive(contents))) } pub fn boolean(contents: bool) -> ExpressionKind { @@ -219,18 +222,14 @@ impl ExpressionKind { pub fn constructor( (typ, fields): (UnresolvedType, Vec<(Ident, Expression)>), ) -> ExpressionKind { - ExpressionKind::Constructor(Box::new(ConstructorExpression { - typ, - fields, - struct_type: None, - })) + ExpressionKind::Constructor(Box::new(ConstructorExpression { typ, fields })) } } #[derive(Debug, Eq, Clone)] pub struct Expression { pub kind: ExpressionKind, - pub span: Span, + pub location: Location, } // This is important for tests. Two expressions are the same, if their Kind is the same @@ -242,23 +241,23 @@ impl PartialEq for Expression { } impl Expression { - pub fn new(kind: ExpressionKind, span: Span) -> Expression { - Expression { kind, span } + pub fn new(kind: ExpressionKind, location: Location) -> Expression { + Expression { kind, location } } - /// Returns the innermost span that gives this expression its type. - pub fn type_span(&self) -> Span { + /// Returns the innermost location that gives this expression its type. + pub fn type_location(&self) -> Location { match &self.kind { ExpressionKind::Block(block_expression) | ExpressionKind::Comptime(block_expression, _) - | ExpressionKind::Unsafe(block_expression, _) => { + | ExpressionKind::Unsafe(UnsafeExpression { block: block_expression, .. }) => { if let Some(statement) = block_expression.statements.last() { - statement.type_span() + statement.type_location() } else { - self.span + self.location } } - ExpressionKind::Parenthesized(expression) => expression.type_span(), + ExpressionKind::Parenthesized(expression) => expression.type_location(), ExpressionKind::Literal(..) | ExpressionKind::Prefix(..) | ExpressionKind::Index(..) @@ -281,12 +280,12 @@ impl Expression { | ExpressionKind::Resolved(..) | ExpressionKind::Interned(..) | ExpressionKind::InternedStatement(..) - | ExpressionKind::Error => self.span, + | ExpressionKind::Error => self.location, } } } -pub type BinaryOp = Spanned; +pub type BinaryOp = Located; #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Copy, Clone)] #[cfg_attr(test, derive(strum_macros::EnumIter))] @@ -411,7 +410,7 @@ pub enum Literal { Array(ArrayLiteral), Slice(ArrayLiteral), Bool(bool), - Integer(FieldElement, /*sign*/ bool), // false for positive integer and true for negative + Integer(SignedField), Str(String), RawStr(String, u8), FmtStr(Vec, u32 /* length */), @@ -478,7 +477,7 @@ pub struct FunctionDefinition { pub generics: UnresolvedGenerics, pub parameters: Vec, pub body: BlockExpression, - pub span: Span, + pub location: Location, pub where_clause: Vec, pub return_type: FunctionReturnType, pub return_visibility: Visibility, @@ -503,13 +502,13 @@ pub struct Param { pub visibility: Visibility, pub pattern: Pattern, pub typ: UnresolvedType, - pub span: Span, + pub location: Location, } #[derive(Debug, PartialEq, Eq, Clone)] pub enum FunctionReturnType { /// Returns type is not specified. - Default(Span), + Default(Location), /// Everything else. Ty(UnresolvedType), } @@ -541,11 +540,6 @@ pub struct MethodCallExpression { pub struct ConstructorExpression { pub typ: UnresolvedType, pub fields: Vec<(Ident, Expression)>, - - /// This may be filled out during macro expansion - /// so that we can skip re-resolving the type name since it - /// would be lost at that point. - pub struct_type: Option, } #[derive(Debug, PartialEq, Eq, Clone)] @@ -583,7 +577,7 @@ impl BlockExpression { pub struct ConstrainExpression { pub kind: ConstrainKind, pub arguments: Vec, - pub span: Span, + pub location: Location, } impl Display for ConstrainExpression { @@ -659,7 +653,7 @@ impl Display for ExpressionKind { Lambda(lambda) => lambda.fmt(f), Parenthesized(sub_expr) => write!(f, "({sub_expr})"), Comptime(block, _) => write!(f, "comptime {block}"), - Unsafe(block, _) => write!(f, "unsafe {block}"), + Unsafe(UnsafeExpression { block, .. }) => write!(f, "unsafe {block}"), Error => write!(f, "Error"), Resolved(_) => write!(f, "?Resolved"), Interned(_) => write!(f, "?Interned"), @@ -693,12 +687,8 @@ impl Display for Literal { write!(f, "&[{repeated_element}; {length}]") } Literal::Bool(boolean) => write!(f, "{}", if *boolean { "true" } else { "false" }), - Literal::Integer(integer, sign) => { - if *sign { - write!(f, "-{}", integer.to_u128()) - } else { - write!(f, "{}", integer.to_u128()) - } + Literal::Integer(signed_field) => { + write!(f, "{signed_field}") } Literal::Str(string) => write!(f, "\"{string}\""), Literal::RawStr(string, num_hashes) => { @@ -877,7 +867,7 @@ impl FunctionDefinition { visibility: Visibility::Private, pattern: Pattern::Identifier(ident.clone()), typ: unresolved_type.clone(), - span: ident.span().merge(unresolved_type.span), + location: ident.location().merge(unresolved_type.location), }) .collect(); @@ -890,7 +880,7 @@ impl FunctionDefinition { generics: generics.clone(), parameters: p, body, - span: name.span(), + location: name.location(), where_clause, return_type: return_type.clone(), return_visibility: Visibility::Private, @@ -898,13 +888,14 @@ impl FunctionDefinition { } pub fn signature(&self) -> String { - let parameters = vecmap(&self.parameters, |Param { visibility, pattern, typ, span: _ }| { - if *visibility == Visibility::Public { - format!("{pattern}: {visibility} {typ}") - } else { - format!("{pattern}: {typ}") - } - }); + let parameters = + vecmap(&self.parameters, |Param { visibility, pattern, typ, location: _ }| { + if *visibility == Visibility::Public { + format!("{pattern}: {visibility} {typ}") + } else { + format!("{pattern}: {typ}") + } + }); let where_clause = vecmap(&self.where_clause, ToString::to_string); let where_clause_str = if !where_clause.is_empty() { @@ -933,12 +924,19 @@ impl Display for FunctionDefinition { impl FunctionReturnType { pub fn get_type(&self) -> Cow { match self { - FunctionReturnType::Default(span) => { - Cow::Owned(UnresolvedType { typ: UnresolvedTypeData::Unit, span: *span }) + FunctionReturnType::Default(location) => { + Cow::Owned(UnresolvedType { typ: UnresolvedTypeData::Unit, location: *location }) } FunctionReturnType::Ty(typ) => Cow::Borrowed(typ), } } + + pub fn location(&self) -> Location { + match self { + FunctionReturnType::Default(location) => *location, + FunctionReturnType::Ty(typ) => typ.location, + } + } } impl Display for FunctionReturnType { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/function.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/function.rs index 8957564e0d63..f0e4ac812cae 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/function.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/function.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use noirc_errors::Span; +use noirc_errors::{Location, Span}; use crate::{ ast::{FunctionReturnType, Ident, Param, Visibility}, @@ -65,7 +65,9 @@ impl NoirFunction { pub fn return_type(&self) -> UnresolvedType { match &self.def.return_type { - FunctionReturnType::Default(span) => UnresolvedTypeData::Unit.with_span(*span), + FunctionReturnType::Default(location) => { + UnresolvedTypeData::Unit.with_location(*location) + } FunctionReturnType::Ty(ty) => ty.clone(), } } @@ -96,8 +98,11 @@ impl NoirFunction { pub fn number_of_statements(&self) -> usize { self.def.body.statements.len() } + pub fn location(&self) -> Location { + self.def.location + } pub fn span(&self) -> Span { - self.def.span + self.location().span } pub fn foreign(&self) -> Option<&FunctionDefinition> { 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 33c73eb2482e..8e74ce8877e9 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs @@ -14,6 +14,7 @@ mod traits; mod type_alias; mod visitor; +use noirc_errors::Location; pub use visitor::AttributeTarget; pub use visitor::Visitor; @@ -34,10 +35,10 @@ pub use traits::*; pub use type_alias::*; use crate::{ + BinaryTypeOperator, node_interner::{InternedUnresolvedTypeData, QuotedTypeId}, parser::{ParserError, ParserErrorReason}, token::IntType, - BinaryTypeOperator, }; use acvm::acir::AcirField; use iter_extended::vecmap; @@ -50,6 +51,7 @@ pub enum IntegerBitSize { Sixteen, ThirtyTwo, SixtyFour, + HundredTwentyEight, } impl IntegerBitSize { @@ -60,6 +62,7 @@ impl IntegerBitSize { IntegerBitSize::Sixteen => 16, IntegerBitSize::ThirtyTwo => 32, IntegerBitSize::SixtyFour => 64, + IntegerBitSize::HundredTwentyEight => 128, } } } @@ -79,6 +82,7 @@ impl From for u32 { Sixteen => 16, ThirtyTwo => 32, SixtyFour => 64, + HundredTwentyEight => 128, } } } @@ -96,6 +100,7 @@ impl TryFrom for IntegerBitSize { 16 => Ok(Sixteen), 32 => Ok(ThirtyTwo), 64 => Ok(SixtyFour), + 128 => Ok(HundredTwentyEight), _ => Err(InvalidIntegerBitSizeError(value)), } } @@ -165,7 +170,7 @@ pub enum UnresolvedTypeData { #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub struct UnresolvedType { pub typ: UnresolvedTypeData, - pub span: Span, + pub location: Location, } /// An argument to a generic type or trait. @@ -231,12 +236,12 @@ impl From> for GenericTypeArgs { #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum UnresolvedTypeExpression { Variable(Path), - Constant(FieldElement, Span), + Constant(FieldElement, Location), BinaryOperation( Box, BinaryTypeOperator, Box, - Span, + Location, ), AsTraitPath(Box), } @@ -352,7 +357,7 @@ impl UnresolvedType { } pub fn from_path(mut path: Path) -> Self { - let span = path.span; + let location = path.location; let last_segment = path.segments.last_mut().unwrap(); let generics = last_segment.generics.take(); let generic_type_args = if let Some(generics) = generics { @@ -365,7 +370,7 @@ impl UnresolvedType { GenericTypeArgs::default() }; let typ = UnresolvedTypeData::Named(path, generic_type_args, true); - UnresolvedType { typ, span } + UnresolvedType { typ, location } } pub(crate) fn contains_unspecified(&self) -> bool { @@ -380,7 +385,11 @@ impl UnresolvedTypeData { use {IntType::*, UnresolvedTypeData::Integer}; match token { Signed(num_bits) => { - Ok(Integer(Signedness::Signed, IntegerBitSize::try_from(num_bits)?)) + if num_bits == 128 { + Err(InvalidIntegerBitSizeError(128)) + } else { + Ok(Integer(Signedness::Signed, IntegerBitSize::try_from(num_bits)?)) + } } Unsigned(num_bits) => { Ok(Integer(Signedness::Unsigned, IntegerBitSize::try_from(num_bits)?)) @@ -388,8 +397,12 @@ impl UnresolvedTypeData { } } - pub fn with_span(&self, span: Span) -> UnresolvedType { - UnresolvedType { typ: self.clone(), span } + pub fn with_location(&self, location: Location) -> UnresolvedType { + UnresolvedType { typ: self.clone(), location } + } + + pub fn with_dummy_location(&self) -> UnresolvedType { + self.with_location(Location::dummy()) } fn contains_unspecified(&self) -> bool { @@ -455,37 +468,43 @@ impl UnresolvedTypeExpression { #[allow(clippy::result_large_err)] pub(crate) fn from_expr( expr: Expression, - span: Span, + location: Location, ) -> Result { Self::from_expr_helper(expr).map_err(|err_expr| { - ParserError::with_reason(ParserErrorReason::InvalidTypeExpression(err_expr), span) + ParserError::with_reason(ParserErrorReason::InvalidTypeExpression(err_expr), location) }) } - pub fn span(&self) -> Span { + pub fn location(&self) -> Location { match self { - UnresolvedTypeExpression::Variable(path) => path.span(), - UnresolvedTypeExpression::Constant(_, span) => *span, - UnresolvedTypeExpression::BinaryOperation(_, _, _, span) => *span, + UnresolvedTypeExpression::Variable(path) => path.location, + UnresolvedTypeExpression::Constant(_, location) => *location, + UnresolvedTypeExpression::BinaryOperation(_, _, _, location) => *location, UnresolvedTypeExpression::AsTraitPath(path) => { - path.trait_path.span.merge(path.impl_item.span()) + path.trait_path.location.merge(path.impl_item.location()) } } } + pub fn span(&self) -> Span { + self.location().span + } + fn from_expr_helper(expr: Expression) -> Result { match expr.kind { - ExpressionKind::Literal(Literal::Integer(int, _)) => match int.try_to_u32() { - Some(int) => Ok(UnresolvedTypeExpression::Constant(int.into(), expr.span)), + ExpressionKind::Literal(Literal::Integer(int)) => match int.try_to_unsigned::() { + Some(int) => Ok(UnresolvedTypeExpression::Constant(int.into(), expr.location)), None => Err(expr), }, ExpressionKind::Variable(path) => Ok(UnresolvedTypeExpression::Variable(path)), ExpressionKind::Prefix(prefix) if prefix.operator == UnaryOp::Minus => { - let lhs = - Box::new(UnresolvedTypeExpression::Constant(FieldElement::zero(), expr.span)); + let lhs = Box::new(UnresolvedTypeExpression::Constant( + FieldElement::zero(), + expr.location, + )); let rhs = Box::new(UnresolvedTypeExpression::from_expr_helper(prefix.rhs)?); let op = BinaryTypeOperator::Subtraction; - Ok(UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, expr.span)) + Ok(UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, expr.location)) } ExpressionKind::Infix(infix) if Self::operator_allowed(infix.operator.contents) => { let lhs = Box::new(UnresolvedTypeExpression::from_expr_helper(infix.lhs)?); @@ -511,7 +530,7 @@ impl UnresolvedTypeExpression { unreachable!("impossible via `operator_allowed` check") } }; - Ok(UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, expr.span)) + Ok(UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, expr.location)) } ExpressionKind::AsTraitPath(path) => { Ok(UnresolvedTypeExpression::AsTraitPath(Box::new(path))) 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 372f20f87800..78b29b8c1fdd 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs @@ -1,9 +1,9 @@ use std::fmt::Display; -use acvm::acir::AcirField; use acvm::FieldElement; +use acvm::acir::AcirField; use iter_extended::vecmap; -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Located, Location, Span}; use super::{ BinaryOpKind, BlockExpression, ConstructorExpression, Expression, ExpressionKind, @@ -11,14 +11,13 @@ use super::{ MethodCallExpression, UnresolvedType, }; use crate::ast::UnresolvedTypeData; -use crate::elaborator::types::SELF_TYPE_NAME; use crate::elaborator::Turbofish; -use crate::lexer::token::SpannedToken; +use crate::elaborator::types::SELF_TYPE_NAME; use crate::node_interner::{ InternedExpressionKind, InternedPattern, InternedStatementKind, NodeInterner, }; use crate::parser::{ParserError, ParserErrorReason}; -use crate::token::{SecondaryAttribute, Token}; +use crate::token::{LocatedToken, SecondaryAttribute, Token}; /// This is used when an identifier fails to parse in the parser. /// Instead of failing the parse, we can often recover using this @@ -33,7 +32,7 @@ pub const WILDCARD_TYPE: &str = "_"; #[derive(Debug, PartialEq, Eq, Clone)] pub struct Statement { pub kind: StatementKind, - pub span: Span, + pub location: Location, } /// Ast node for statements in noir. Statements are always within a block { } @@ -45,7 +44,7 @@ pub enum StatementKind { Expression(Expression), Assign(AssignStatement), For(ForLoopStatement), - Loop(Expression, Span /* loop keyword span */), + Loop(Expression, Location /* loop keyword location */), While(WhileStatement), Break, Continue, @@ -66,19 +65,19 @@ impl Statement { pub fn add_semicolon( mut self, semi: Option, - span: Span, + location: Location, last_statement_in_block: bool, emit_error: &mut dyn FnMut(ParserError), ) -> Self { - self.kind = self.kind.add_semicolon(semi, span, last_statement_in_block, emit_error); + self.kind = self.kind.add_semicolon(semi, location, last_statement_in_block, emit_error); self } - /// Returns the innermost span that gives this statement its type. - pub fn type_span(&self) -> Span { + /// Returns the innermost location that gives this statement its type. + pub fn type_location(&self) -> Location { match &self.kind { - StatementKind::Expression(expression) => expression.type_span(), - StatementKind::Comptime(statement) => statement.type_span(), + StatementKind::Expression(expression) => expression.type_location(), + StatementKind::Comptime(statement) => statement.type_location(), StatementKind::Let(..) | StatementKind::Assign(..) | StatementKind::For(..) @@ -88,7 +87,7 @@ impl Statement { | StatementKind::Continue | StatementKind::Semi(..) | StatementKind::Interned(..) - | StatementKind::Error => self.span, + | StatementKind::Error => self.location, } } } @@ -97,12 +96,12 @@ impl StatementKind { pub fn add_semicolon( self, semi: Option, - span: Span, + location: Location, last_statement_in_block: bool, emit_error: &mut dyn FnMut(ParserError), ) -> Self { let missing_semicolon = - ParserError::with_reason(ParserErrorReason::MissingSeparatingSemi, span); + ParserError::with_reason(ParserErrorReason::MissingSeparatingSemi, location); match self { StatementKind::Let(_) @@ -119,7 +118,7 @@ impl StatementKind { } StatementKind::Comptime(mut statement) => { *statement = - statement.add_semicolon(semi, span, last_statement_in_block, emit_error); + statement.add_semicolon(semi, location, last_statement_in_block, emit_error); StatementKind::Comptime(statement) } // A semicolon on a for loop, loop or while is optional and does nothing @@ -174,7 +173,7 @@ impl StatementKind { } #[derive(Eq, Debug, Clone, Default)] -pub struct Ident(pub Spanned); +pub struct Ident(pub Located); impl Ident { pub fn is_self_type_name(&self) -> bool { @@ -218,15 +217,15 @@ impl Display for Ident { } } -impl From> for Ident { - fn from(a: Spanned) -> Ident { +impl From> for Ident { + fn from(a: Located) -> Ident { Ident(a) } } impl From for Ident { fn from(a: String) -> Ident { - Spanned::from_position(Default::default(), Default::default(), a).into() + Located::from(Location::dummy(), a).into() } } impl From<&str> for Ident { @@ -235,37 +234,32 @@ impl From<&str> for Ident { } } -impl From for Ident { - fn from(st: SpannedToken) -> Ident { - let spanned_str = Spanned::from(st.to_span(), st.token().to_string()); - Ident(spanned_str) +impl From for Ident { + fn from(lt: LocatedToken) -> Ident { + let located_str = Located::from(lt.location(), lt.token().to_string()); + Ident(located_str) } } impl From for Expression { fn from(i: Ident) -> Expression { - Expression { - span: i.0.span(), - kind: ExpressionKind::Variable(Path { - span: i.span(), - segments: vec![PathSegment::from(i)], - kind: PathKind::Plain, - }), - } + let location = i.location(); + let kind = ExpressionKind::Variable(Path::plain(vec![PathSegment::from(i)], location)); + Expression { location, kind } } } impl Ident { - pub fn span(&self) -> Span { - self.0.span() + pub fn location(&self) -> Location { + self.0.location() } - pub fn from_token(token: Token, span: Span) -> Ident { - Ident::from(SpannedToken::new(token, span)) + pub fn span(&self) -> Span { + self.0.span() } - pub fn new(text: String, span: Span) -> Ident { - Ident(Spanned::from(span, text)) + pub fn new(text: String, location: Location) -> Ident { + Ident(Located::from(location, text)) } } @@ -302,7 +296,7 @@ pub enum PathKind { pub struct UseTree { pub prefix: Path, pub kind: UseTreeKind, - pub span: Span, + pub location: Location, } impl Display for UseTree { @@ -361,6 +355,12 @@ impl UseTree { } } +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct UnsafeExpression { + pub block: BlockExpression, + pub unsafe_keyword_location: Location, +} + /// A special kind of path in the form `::ident`. /// Note that this path must consist of exactly two segments. /// @@ -390,10 +390,16 @@ pub struct TypePath { pub struct Path { pub segments: Vec, pub kind: PathKind, - pub span: Span, + pub location: Location, + // The location of `kind` (this is the same as `location` for plain kinds) + pub kind_location: Location, } impl Path { + pub fn plain(segments: Vec, location: Location) -> Self { + Self { segments, location, kind: PathKind::Plain, kind_location: location } + } + pub fn pop(&mut self) -> PathSegment { self.segments.pop().unwrap() } @@ -404,17 +410,18 @@ impl Path { } /// Construct a PathKind::Plain from this single - pub fn from_single(name: String, span: Span) -> Path { - let segment = Ident::from(Spanned::from(span, name)); + pub fn from_single(name: String, location: Location) -> Path { + let segment = Ident::from(Located::from(location, name)); Path::from_ident(segment) } pub fn from_ident(name: Ident) -> Path { - Path { span: name.span(), segments: vec![PathSegment::from(name)], kind: PathKind::Plain } + let location = name.location(); + Path::plain(vec![PathSegment::from(name)], location) } pub fn span(&self) -> Span { - self.span + self.location.span } pub fn last_segment(&self) -> PathSegment { @@ -487,7 +494,7 @@ impl Path { pub struct PathSegment { pub ident: Ident, pub generics: Option>, - pub span: Span, + pub location: Location, } impl PathSegment { @@ -498,20 +505,29 @@ impl PathSegment { /// /// 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()) + if self.ident.location().file == self.location.file { + Span::from(self.ident.span().end()..self.location.span.end()) + } else { + self.location.span + } + } + + pub fn turbofish_location(&self) -> Location { + Location::new(self.turbofish_span(), self.location.file) } pub fn turbofish(&self) -> Option { - self.generics - .as_ref() - .map(|generics| Turbofish { span: self.turbofish_span(), generics: generics.clone() }) + self.generics.as_ref().map(|generics| Turbofish { + location: self.turbofish_location(), + generics: generics.clone(), + }) } } impl From for PathSegment { fn from(ident: Ident) -> PathSegment { - let span = ident.span(); - PathSegment { ident, generics: None, span } + let location = ident.location(); + PathSegment { ident, generics: None, location } } } @@ -550,35 +566,39 @@ pub struct AssignStatement { #[derive(Debug, PartialEq, Eq, Clone)] pub enum LValue { Ident(Ident), - MemberAccess { object: Box, field_name: Ident, span: Span }, - Index { array: Box, index: Expression, span: Span }, - Dereference(Box, Span), - Interned(InternedExpressionKind, Span), + MemberAccess { object: Box, field_name: Ident, location: Location }, + Index { array: Box, index: Expression, location: Location }, + Dereference(Box, Location), + Interned(InternedExpressionKind, Location), } #[derive(Debug, PartialEq, Eq, Clone)] pub enum Pattern { Identifier(Ident), - Mutable(Box, Span, /*is_synthesized*/ bool), - Tuple(Vec, Span), - Struct(Path, Vec<(Ident, Pattern)>, Span), - Interned(InternedPattern, Span), + Mutable(Box, Location, /*is_synthesized*/ bool), + Tuple(Vec, Location), + Struct(Path, Vec<(Ident, Pattern)>, Location), + Interned(InternedPattern, Location), } impl Pattern { pub fn is_synthesized(&self) -> bool { matches!(self, Pattern::Mutable(_, _, true)) } - - pub fn span(&self) -> Span { + pub fn location(&self) -> Location { match self { - Pattern::Identifier(ident) => ident.span(), - Pattern::Mutable(_, span, _) - | Pattern::Tuple(_, span) - | Pattern::Struct(_, _, span) - | Pattern::Interned(_, span) => *span, + Pattern::Identifier(ident) => ident.location(), + Pattern::Mutable(_, location, _) + | Pattern::Tuple(_, location) + | Pattern::Struct(_, _, location) + | Pattern::Interned(_, location) => *location, } } + + pub fn span(&self) -> Span { + self.location().span + } + pub fn name_ident(&self) -> &Ident { match self { Pattern::Identifier(name_ident) => name_ident, @@ -591,17 +611,17 @@ impl Pattern { match self { Pattern::Identifier(ident) => Some(Expression { kind: ExpressionKind::Variable(Path::from_ident(ident.clone())), - span: ident.span(), + location: ident.location(), }), Pattern::Mutable(_, _, _) => None, - Pattern::Tuple(patterns, span) => { + Pattern::Tuple(patterns, location) => { let mut expressions = Vec::new(); for pattern in patterns { expressions.push(pattern.try_as_expression(interner)?); } - Some(Expression { kind: ExpressionKind::Tuple(expressions), span: *span }) + Some(Expression { kind: ExpressionKind::Tuple(expressions), location: *location }) } - Pattern::Struct(path, patterns, span) => { + Pattern::Struct(path, patterns, location) => { let mut fields = Vec::new(); for (field, pattern) in patterns { let expression = pattern.try_as_expression(interner)?; @@ -611,9 +631,8 @@ impl Pattern { kind: ExpressionKind::Constructor(Box::new(ConstructorExpression { typ: UnresolvedType::from_path(path.clone()), fields, - struct_type: None, })), - span: *span, + location: *location, }) } Pattern::Interned(id, _) => interner.get_pattern(*id).try_as_expression(interner), @@ -625,13 +644,13 @@ impl LValue { pub fn as_expression(&self) -> Expression { let kind = match self { LValue::Ident(ident) => ExpressionKind::Variable(Path::from_ident(ident.clone())), - LValue::MemberAccess { object, field_name, span: _ } => { + LValue::MemberAccess { object, field_name, location: _ } => { ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { lhs: object.as_expression(), rhs: field_name.clone(), })) } - LValue::Index { array, index, span: _ } => { + LValue::Index { array, index, location: _ } => { ExpressionKind::Index(Box::new(IndexExpression { collection: array.as_expression(), index: index.clone(), @@ -645,52 +664,58 @@ impl LValue { } LValue::Interned(id, _) => ExpressionKind::Interned(*id), }; - let span = self.span(); - Expression::new(kind, span) + Expression::new(kind, self.location()) } pub fn from_expression(expr: Expression) -> Option { - LValue::from_expression_kind(expr.kind, expr.span) + LValue::from_expression_kind(expr.kind, expr.location) } - pub fn from_expression_kind(expr: ExpressionKind, span: Span) -> Option { + pub fn from_expression_kind(expr: ExpressionKind, location: Location) -> Option { match expr { ExpressionKind::Variable(path) => Some(LValue::Ident(path.as_ident().unwrap().clone())), ExpressionKind::MemberAccess(member_access) => Some(LValue::MemberAccess { object: Box::new(LValue::from_expression(member_access.lhs)?), field_name: member_access.rhs, - span, + location, }), ExpressionKind::Index(index) => Some(LValue::Index { array: Box::new(LValue::from_expression(index.collection)?), index: index.index, - span, + location, }), ExpressionKind::Prefix(prefix) => { if matches!( prefix.operator, crate::ast::UnaryOp::Dereference { implicitly_added: false } ) { - Some(LValue::Dereference(Box::new(LValue::from_expression(prefix.rhs)?), span)) + Some(LValue::Dereference( + Box::new(LValue::from_expression(prefix.rhs)?), + location, + )) } else { None } } ExpressionKind::Parenthesized(expr) => LValue::from_expression(*expr), - ExpressionKind::Interned(id) => Some(LValue::Interned(id, span)), + ExpressionKind::Interned(id) => Some(LValue::Interned(id, location)), _ => None, } } - pub fn span(&self) -> Span { + pub fn location(&self) -> Location { match self { - LValue::Ident(ident) => ident.span(), - LValue::MemberAccess { span, .. } - | LValue::Index { span, .. } - | LValue::Dereference(_, span) => *span, - LValue::Interned(_, span) => *span, + LValue::Ident(ident) => ident.location(), + LValue::MemberAccess { location, .. } + | LValue::Index { location, .. } + | LValue::Dereference(_, location) + | LValue::Interned(_, location) => *location, } } + + pub fn span(&self) -> Span { + self.location().span + } } #[derive(Debug, PartialEq, Eq, Clone)] @@ -707,13 +732,16 @@ impl ForBounds { /// Returns the `start` and `end` expressions. pub(crate) fn into_half_open(self) -> (Expression, Expression) { let end = if self.inclusive { - let end_span = self.end.span; + let end_location = self.end.location; let end = ExpressionKind::Infix(Box::new(InfixExpression { lhs: self.end, - operator: Spanned::from(end_span, BinaryOpKind::Add), - rhs: Expression::new(ExpressionKind::integer(FieldElement::from(1u32)), end_span), + operator: Located::from(end_location, BinaryOpKind::Add), + rhs: Expression::new( + ExpressionKind::integer(FieldElement::from(1u32)), + end_location, + ), })); - Expression::new(end, end_span) + Expression::new(end, end_location) } else { self.end }; @@ -758,7 +786,7 @@ impl ForRange { self, identifier: Ident, block: Expression, - for_loop_span: Span, + for_loop_location: Location, ) -> Statement { // Counter used to generate unique names when desugaring // code in the parser requires the creation of fresh variables. @@ -769,96 +797,91 @@ impl ForRange { unreachable!() } ForRange::Array(array) => { - let array_span = array.span; + let array_location = array.location; let start_range = ExpressionKind::integer(FieldElement::zero()); - let start_range = Expression::new(start_range, array_span); + let start_range = Expression::new(start_range, array_location); let next_unique_id = unique_name_counter; unique_name_counter += 1; let array_name = format!("$i{next_unique_id}"); - let array_span = array.span; - let array_ident = Ident::new(array_name, array_span); + let array_location = array.location; + let array_ident = Ident::new(array_name, array_location); // let fresh1 = array; let let_array = Statement { kind: StatementKind::new_let( Pattern::Identifier(array_ident.clone()), - UnresolvedTypeData::Unspecified.with_span(Default::default()), + UnresolvedTypeData::Unspecified.with_dummy_location(), array, vec![], ), - span: array_span, + location: array_location, }; // array.len() let segments = vec![PathSegment::from(array_ident)]; - let array_ident = ExpressionKind::Variable(Path { - segments, - kind: PathKind::Plain, - span: array_span, - }); + let array_ident = ExpressionKind::Variable(Path::plain(segments, array_location)); let end_range = ExpressionKind::MethodCall(Box::new(MethodCallExpression { - object: Expression::new(array_ident.clone(), array_span), - method_name: Ident::new("len".to_string(), array_span), + object: Expression::new(array_ident.clone(), array_location), + method_name: Ident::new("len".to_string(), array_location), generics: None, is_macro_call: false, arguments: vec![], })); - let end_range = Expression::new(end_range, array_span); + let end_range = Expression::new(end_range, array_location); let next_unique_id = unique_name_counter; let index_name = format!("$i{next_unique_id}"); - let fresh_identifier = Ident::new(index_name.clone(), array_span); + let fresh_identifier = Ident::new(index_name.clone(), array_location); // array[i] - 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 segments = vec![PathSegment::from(Ident::new(index_name, array_location))]; + let index_ident = ExpressionKind::Variable(Path::plain(segments, array_location)); let loop_element = ExpressionKind::Index(Box::new(IndexExpression { - collection: Expression::new(array_ident, array_span), - index: Expression::new(index_ident, array_span), + collection: Expression::new(array_ident, array_location), + index: Expression::new(index_ident, array_location), })); // let elem = array[i]; let let_elem = Statement { kind: StatementKind::new_let( Pattern::Identifier(identifier), - UnresolvedTypeData::Unspecified.with_span(Default::default()), - Expression::new(loop_element, array_span), + UnresolvedTypeData::Unspecified.with_dummy_location(), + Expression::new(loop_element, array_location), vec![], ), - span: array_span, + location: array_location, }; - let block_span = block.span; + let block_location = block.location; let new_block = BlockExpression { statements: vec![ let_elem, - Statement { kind: StatementKind::Expression(block), span: block_span }, + Statement { + kind: StatementKind::Expression(block), + location: block_location, + }, ], }; - let new_block = Expression::new(ExpressionKind::Block(new_block), block_span); + let new_block = Expression::new(ExpressionKind::Block(new_block), block_location); let for_loop = Statement { kind: StatementKind::For(ForLoopStatement { identifier: fresh_identifier, range: ForRange::range(start_range, end_range), block: new_block, - span: for_loop_span, + location: for_loop_location, }), - span: for_loop_span, + location: for_loop_location, }; let block = ExpressionKind::Block(BlockExpression { statements: vec![let_array, for_loop], }); Statement { - kind: StatementKind::Expression(Expression::new(block, for_loop_span)), - span: for_loop_span, + kind: StatementKind::Expression(Expression::new(block, for_loop_location)), + location: for_loop_location, } } } @@ -870,14 +893,14 @@ pub struct ForLoopStatement { pub identifier: Ident, pub range: ForRange, pub block: Expression, - pub span: Span, + pub location: Location, } #[derive(Debug, PartialEq, Eq, Clone)] pub struct WhileStatement { pub condition: Expression, pub body: Expression, - pub while_keyword_span: Span, + pub while_keyword_location: Location, } impl Display for StatementKind { @@ -921,10 +944,10 @@ impl Display for LValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { LValue::Ident(ident) => ident.fmt(f), - LValue::MemberAccess { object, field_name, span: _ } => { + LValue::MemberAccess { object, field_name, location: _ } => { write!(f, "{object}.{field_name}") } - LValue::Index { array, index, span: _ } => write!(f, "{array}[{index}]"), + LValue::Index { array, index, location: _ } => write!(f, "{array}[{index}]"), LValue::Dereference(lvalue, _span) => write!(f, "*{lvalue}"), LValue::Interned(_, _) => write!(f, "?Interned"), } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs index 8c8fe6e63872..7162a2d3ba68 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs @@ -4,7 +4,7 @@ use crate::ast::{Ident, UnresolvedGenerics, UnresolvedType}; use crate::token::SecondaryAttribute; use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::Location; use super::{Documented, ItemVisibility}; @@ -16,7 +16,7 @@ pub struct NoirStruct { pub visibility: ItemVisibility, pub generics: UnresolvedGenerics, pub fields: Vec>, - pub span: Span, + pub location: Location, } impl 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 e5040463d174..d7cc48ec7421 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/traits.rs @@ -1,7 +1,7 @@ use std::fmt::Display; use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::Location; use crate::ast::{ BlockExpression, Expression, FunctionReturnType, Ident, NoirFunction, Path, UnresolvedGenerics, @@ -20,7 +20,7 @@ pub struct NoirTrait { pub generics: UnresolvedGenerics, pub bounds: Vec, pub where_clause: Vec, - pub span: Span, + pub location: Location, pub items: Vec>, pub attributes: Vec, pub visibility: ItemVisibility, @@ -57,10 +57,10 @@ pub enum TraitItem { #[derive(Clone, Debug)] pub struct TypeImpl { pub object_type: UnresolvedType, - pub type_span: Span, + pub type_location: Location, pub generics: UnresolvedGenerics, pub where_clause: Vec, - pub methods: Vec<(Documented, Span)>, + pub methods: Vec<(Documented, Location)>, } /// Ast node for an implementation of a trait for a particular type @@ -104,7 +104,7 @@ pub struct TraitBound { #[derive(Clone, Debug)] pub struct TraitImplItem { pub kind: TraitImplItemKind, - pub span: Span, + pub location: Location, } #[derive(Clone, Debug)] @@ -197,11 +197,7 @@ impl Display for TraitItem { "{unconstrained}{visibility}{is_comptime}fn {name}<{generics}>({parameters}) -> {return_type} where {where_clause}" )?; - if let Some(body) = body { - write!(f, "{body}") - } else { - write!(f, ";") - } + if let Some(body) = body { write!(f, "{body}") } else { write!(f, ";") } } TraitItem::Constant { name, typ, default_value } => { write!(f, "let {name}: {typ}")?; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/type_alias.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/type_alias.rs index 5b26044c3289..532593fad0b0 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/type_alias.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/type_alias.rs @@ -1,6 +1,6 @@ use super::{Ident, ItemVisibility, UnresolvedGenerics, UnresolvedType}; use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::Location; use std::fmt::Display; /// Ast node for type aliases @@ -10,7 +10,7 @@ pub struct NoirTypeAlias { pub generics: UnresolvedGenerics, pub typ: UnresolvedType, pub visibility: ItemVisibility, - pub span: Span, + pub location: Location, } impl Display for NoirTypeAlias { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs index 7fe89489c3cf..b2142f266551 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs @@ -1,7 +1,7 @@ -use acvm::FieldElement; use noirc_errors::Span; use crate::{ + ParsedModule, QuotedType, ast::{ ArrayLiteral, AsTraitPath, AssignStatement, BlockExpression, CallExpression, CastExpression, ConstrainExpression, ConstructorExpression, Expression, ExpressionKind, @@ -16,15 +16,15 @@ use crate::{ InternedUnresolvedTypeData, QuotedTypeId, }, parser::{Item, ItemKind, ParsedSubModule}, + signed_field::SignedField, token::{FmtStrFragment, MetaAttribute, SecondaryAttribute, Tokens}, - ParsedModule, QuotedType, }; use super::{ ForBounds, FunctionReturnType, GenericTypeArgs, IntegerBitSize, ItemVisibility, MatchExpression, NoirEnumeration, Pattern, Signedness, TraitBound, TraitImplItemKind, TypePath, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, - UnresolvedTypeExpression, + UnresolvedTypeExpression, UnsafeExpression, }; #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -172,7 +172,7 @@ pub trait Visitor { fn visit_literal_bool(&mut self, _: bool, _: Span) {} - fn visit_literal_integer(&mut self, _value: FieldElement, _negative: bool, _: Span) {} + fn visit_literal_integer(&mut self, _value: SignedField, _: Span) {} fn visit_literal_str(&mut self, _: &str, _: Span) {} @@ -242,7 +242,7 @@ pub trait Visitor { true } - fn visit_unsafe(&mut self, _: &BlockExpression, _: Span) -> bool { + fn visit_unsafe_expression(&mut self, _: &UnsafeExpression, _: Span) -> bool { true } @@ -520,31 +520,33 @@ impl Item { } pub fn accept_children(&self, visitor: &mut impl Visitor) { + let span = self.location.span; + match &self.kind { ItemKind::Submodules(parsed_sub_module) => { - parsed_sub_module.accept(self.span, visitor); + parsed_sub_module.accept(span, visitor); } - ItemKind::Function(noir_function) => noir_function.accept(self.span, visitor), + ItemKind::Function(noir_function) => noir_function.accept(span, visitor), ItemKind::TraitImpl(noir_trait_impl) => { - noir_trait_impl.accept(self.span, visitor); + noir_trait_impl.accept(span, visitor); } - ItemKind::Impl(type_impl) => type_impl.accept(self.span, visitor), + ItemKind::Impl(type_impl) => type_impl.accept(span, visitor), ItemKind::Global(let_statement, _visibility) => { - if visitor.visit_global(let_statement, self.span) { + if visitor.visit_global(let_statement, span) { let_statement.accept(visitor); } } - ItemKind::Trait(noir_trait) => noir_trait.accept(self.span, visitor), + ItemKind::Trait(noir_trait) => noir_trait.accept(span, visitor), ItemKind::Import(use_tree, visibility) => { - if visitor.visit_import(use_tree, self.span, *visibility) { + if visitor.visit_import(use_tree, span, *visibility) { use_tree.accept(visitor); } } - ItemKind::TypeAlias(noir_type_alias) => noir_type_alias.accept(self.span, visitor), - ItemKind::Struct(noir_struct) => noir_struct.accept(self.span, visitor), - ItemKind::Enum(noir_enum) => noir_enum.accept(self.span, visitor), + ItemKind::TypeAlias(noir_type_alias) => noir_type_alias.accept(span, visitor), + ItemKind::Struct(noir_struct) => noir_struct.accept(span, visitor), + ItemKind::Enum(noir_enum) => noir_enum.accept(span, visitor), ItemKind::ModuleDecl(module_declaration) => { - module_declaration.accept(self.span, visitor); + module_declaration.accept(span, visitor); } ItemKind::InnerAttribute(attribute) => { attribute.accept(AttributeTarget::Module, visitor); @@ -620,7 +622,7 @@ impl TraitImplItem { } pub fn accept_children(&self, visitor: &mut impl Visitor) { - self.kind.accept(self.span, visitor); + self.kind.accept(self.location.span, visitor); } } @@ -663,8 +665,8 @@ impl TypeImpl { pub fn accept_children(&self, visitor: &mut impl Visitor) { self.object_type.accept(visitor); - for (method, span) in &self.methods { - method.item.accept(*span, visitor); + for (method, location) in &self.methods { + method.item.accept(location.span, visitor); } } } @@ -843,79 +845,78 @@ impl Expression { } pub fn accept_children(&self, visitor: &mut impl Visitor) { + let span = self.location.span; match &self.kind { - ExpressionKind::Literal(literal) => literal.accept(self.span, visitor), + ExpressionKind::Literal(literal) => literal.accept(span, visitor), ExpressionKind::Block(block_expression) => { - block_expression.accept(Some(self.span), visitor); + block_expression.accept(Some(span), visitor); } ExpressionKind::Prefix(prefix_expression) => { - prefix_expression.accept(self.span, visitor); + prefix_expression.accept(span, visitor); } ExpressionKind::Index(index_expression) => { - index_expression.accept(self.span, visitor); + index_expression.accept(span, visitor); } ExpressionKind::Call(call_expression) => { - call_expression.accept(self.span, visitor); + call_expression.accept(span, visitor); } ExpressionKind::MethodCall(method_call_expression) => { - method_call_expression.accept(self.span, visitor); + method_call_expression.accept(span, visitor); } ExpressionKind::Constrain(constrain) => { constrain.accept(visitor); } ExpressionKind::Constructor(constructor_expression) => { - constructor_expression.accept(self.span, visitor); + constructor_expression.accept(span, visitor); } ExpressionKind::MemberAccess(member_access_expression) => { - member_access_expression.accept(self.span, visitor); + member_access_expression.accept(span, visitor); } ExpressionKind::Cast(cast_expression) => { - cast_expression.accept(self.span, visitor); + cast_expression.accept(span, visitor); } ExpressionKind::Infix(infix_expression) => { - infix_expression.accept(self.span, visitor); + infix_expression.accept(span, visitor); } ExpressionKind::If(if_expression) => { - if_expression.accept(self.span, visitor); + if_expression.accept(span, visitor); } ExpressionKind::Match(match_expression) => { - match_expression.accept(self.span, visitor); + match_expression.accept(span, visitor); } ExpressionKind::Tuple(expressions) => { - if visitor.visit_tuple(expressions, self.span) { + if visitor.visit_tuple(expressions, span) { visit_expressions(expressions, visitor); } } - ExpressionKind::Lambda(lambda) => lambda.accept(self.span, visitor), + ExpressionKind::Lambda(lambda) => lambda.accept(span, visitor), ExpressionKind::Parenthesized(expression) => { - if visitor.visit_parenthesized(expression, self.span) { + if visitor.visit_parenthesized(expression, span) { expression.accept(visitor); } } ExpressionKind::Unquote(expression) => { - if visitor.visit_unquote(expression, self.span) { + if visitor.visit_unquote(expression, span) { expression.accept(visitor); } } ExpressionKind::Comptime(block_expression, _) => { - if visitor.visit_comptime_expression(block_expression, self.span) { + if visitor.visit_comptime_expression(block_expression, span) { block_expression.accept(None, visitor); } } - ExpressionKind::Unsafe(block_expression, _) => { - if visitor.visit_unsafe(block_expression, self.span) { - block_expression.accept(None, visitor); - } + ExpressionKind::Unsafe(unsafe_expression) => { + unsafe_expression.accept(span, visitor); } ExpressionKind::Variable(path) => { - if visitor.visit_variable(path, self.span) { + if visitor.visit_variable(path, span) { path.accept(visitor); } } ExpressionKind::AsTraitPath(as_trait_path) => { - as_trait_path.accept(self.span, visitor); + as_trait_path.accept(span, visitor); } - ExpressionKind::TypePath(path) => path.accept(self.span, visitor), + ExpressionKind::TypePath(path) => path.accept(span, visitor), ExpressionKind::Quote(tokens) => visitor.visit_quote(tokens), ExpressionKind::Resolved(expr_id) => visitor.visit_resolved_expression(*expr_id), ExpressionKind::Interned(id) => visitor.visit_interned_expression(*id), @@ -945,8 +946,8 @@ impl Literal { } } Literal::Bool(value) => visitor.visit_literal_bool(*value, span), - Literal::Integer(value, negative) => { - visitor.visit_literal_integer(*value, *negative, span); + Literal::Integer(value) => { + visitor.visit_literal_integer(*value, span); } Literal::Str(str) => visitor.visit_literal_str(str, span), Literal::RawStr(str, length) => visitor.visit_literal_raw_str(str, *length, span), @@ -1262,23 +1263,23 @@ impl LValue { pub fn accept_children(&self, visitor: &mut impl Visitor) { match self { LValue::Ident(ident) => visitor.visit_lvalue_ident(ident), - LValue::MemberAccess { object, field_name, span } => { - if visitor.visit_lvalue_member_access(object, field_name, *span) { + LValue::MemberAccess { object, field_name, location } => { + if visitor.visit_lvalue_member_access(object, field_name, location.span) { object.accept(visitor); } } - LValue::Index { array, index, span } => { - if visitor.visit_lvalue_index(array, index, *span) { + LValue::Index { array, index, location } => { + if visitor.visit_lvalue_index(array, index, location.span) { array.accept(visitor); index.accept(visitor); } } - LValue::Dereference(lvalue, span) => { - if visitor.visit_lvalue_dereference(lvalue, *span) { + LValue::Dereference(lvalue, location) => { + if visitor.visit_lvalue_dereference(lvalue, location.span) { lvalue.accept(visitor); } } - LValue::Interned(id, span) => visitor.visit_lvalue_interned(*id, *span), + LValue::Interned(id, location) => visitor.visit_lvalue_interned(*id, location.span), } } } @@ -1301,6 +1302,18 @@ impl ForRange { } } +impl UnsafeExpression { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_unsafe_expression(self, span) { + self.accept_children(span, visitor); + } + } + + pub fn accept_children(&self, span: Span, visitor: &mut impl Visitor) { + self.block.accept(Some(span), visitor); + } +} + impl AsTraitPath { pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { if visitor.visit_as_trait_path(self, span) { @@ -1339,73 +1352,84 @@ impl UnresolvedType { pub fn accept_children(&self, visitor: &mut impl Visitor) { match &self.typ { UnresolvedTypeData::Array(unresolved_type_expression, unresolved_type) => { - if visitor.visit_array_type(unresolved_type_expression, unresolved_type, self.span) - { + if visitor.visit_array_type( + unresolved_type_expression, + unresolved_type, + self.location.span, + ) { unresolved_type.accept(visitor); } } UnresolvedTypeData::Slice(unresolved_type) => { - if visitor.visit_slice_type(unresolved_type, self.span) { + if visitor.visit_slice_type(unresolved_type, self.location.span) { unresolved_type.accept(visitor); } } UnresolvedTypeData::Parenthesized(unresolved_type) => { - if visitor.visit_parenthesized_type(unresolved_type, self.span) { + if visitor.visit_parenthesized_type(unresolved_type, self.location.span) { unresolved_type.accept(visitor); } } UnresolvedTypeData::Named(path, generic_type_args, _) => { - if visitor.visit_named_type(path, generic_type_args, self.span) { + if visitor.visit_named_type(path, generic_type_args, self.location.span) { path.accept(visitor); generic_type_args.accept(visitor); } } UnresolvedTypeData::TraitAsType(path, generic_type_args) => { - if visitor.visit_trait_as_type(path, generic_type_args, self.span) { + if visitor.visit_trait_as_type(path, generic_type_args, self.location.span) { path.accept(visitor); generic_type_args.accept(visitor); } } UnresolvedTypeData::MutableReference(unresolved_type) => { - if visitor.visit_mutable_reference_type(unresolved_type, self.span) { + if visitor.visit_mutable_reference_type(unresolved_type, self.location.span) { unresolved_type.accept(visitor); } } UnresolvedTypeData::Tuple(unresolved_types) => { - if visitor.visit_tuple_type(unresolved_types, self.span) { + if visitor.visit_tuple_type(unresolved_types, self.location.span) { visit_unresolved_types(unresolved_types, visitor); } } UnresolvedTypeData::Function(args, ret, env, unconstrained) => { - if visitor.visit_function_type(args, ret, env, *unconstrained, self.span) { + if visitor.visit_function_type(args, ret, env, *unconstrained, self.location.span) { visit_unresolved_types(args, visitor); ret.accept(visitor); env.accept(visitor); } } UnresolvedTypeData::AsTraitPath(as_trait_path) => { - if visitor.visit_as_trait_path_type(as_trait_path, self.span) { - as_trait_path.accept(self.span, visitor); + if visitor.visit_as_trait_path_type(as_trait_path, self.location.span) { + as_trait_path.accept(self.location.span, visitor); } } - UnresolvedTypeData::Expression(expr) => visitor.visit_expression_type(expr, self.span), + UnresolvedTypeData::Expression(expr) => { + visitor.visit_expression_type(expr, self.location.span); + } UnresolvedTypeData::FormatString(expr, typ) => { - if visitor.visit_format_string_type(expr, typ, self.span) { + if visitor.visit_format_string_type(expr, typ, self.location.span) { typ.accept(visitor); } } - UnresolvedTypeData::String(expr) => visitor.visit_string_type(expr, self.span), - UnresolvedTypeData::Unspecified => visitor.visit_unspecified_type(self.span), - UnresolvedTypeData::Quoted(typ) => visitor.visit_quoted_type(typ, self.span), - UnresolvedTypeData::FieldElement => visitor.visit_field_element_type(self.span), - UnresolvedTypeData::Integer(signdness, size) => { - visitor.visit_integer_type(*signdness, *size, self.span); + UnresolvedTypeData::String(expr) => visitor.visit_string_type(expr, self.location.span), + UnresolvedTypeData::Unspecified => visitor.visit_unspecified_type(self.location.span), + UnresolvedTypeData::Quoted(typ) => visitor.visit_quoted_type(typ, self.location.span), + UnresolvedTypeData::FieldElement => { + visitor.visit_field_element_type(self.location.span); + } + UnresolvedTypeData::Integer(signedness, size) => { + visitor.visit_integer_type(*signedness, *size, self.location.span); + } + UnresolvedTypeData::Bool => visitor.visit_bool_type(self.location.span), + UnresolvedTypeData::Unit => visitor.visit_unit_type(self.location.span), + UnresolvedTypeData::Resolved(id) => { + visitor.visit_resolved_type(*id, self.location.span); + } + UnresolvedTypeData::Interned(id) => { + visitor.visit_interned_type(*id, self.location.span); } - UnresolvedTypeData::Bool => visitor.visit_bool_type(self.span), - UnresolvedTypeData::Unit => visitor.visit_unit_type(self.span), - UnresolvedTypeData::Resolved(id) => visitor.visit_resolved_type(*id, self.span), - UnresolvedTypeData::Interned(id) => visitor.visit_interned_type(*id, self.span), - UnresolvedTypeData::Error => visitor.visit_error_type(self.span), + UnresolvedTypeData::Error => visitor.visit_error_type(self.location.span), } } } @@ -1484,28 +1508,28 @@ impl Pattern { pub fn accept_children(&self, visitor: &mut impl Visitor) { match self { Pattern::Identifier(ident) => visitor.visit_identifier_pattern(ident), - Pattern::Mutable(pattern, span, is_synthesized) => { - if visitor.visit_mutable_pattern(pattern, *span, *is_synthesized) { + Pattern::Mutable(pattern, location, is_synthesized) => { + if visitor.visit_mutable_pattern(pattern, location.span, *is_synthesized) { pattern.accept(visitor); } } - Pattern::Tuple(patterns, span) => { - if visitor.visit_tuple_pattern(patterns, *span) { + Pattern::Tuple(patterns, location) => { + if visitor.visit_tuple_pattern(patterns, location.span) { for pattern in patterns { pattern.accept(visitor); } } } - Pattern::Struct(path, fields, span) => { - if visitor.visit_struct_pattern(path, fields, *span) { + Pattern::Struct(path, fields, location) => { + if visitor.visit_struct_pattern(path, fields, location.span) { path.accept(visitor); for (_, pattern) in fields { pattern.accept(visitor); } } } - Pattern::Interned(id, span) => { - visitor.visit_interned_pattern(id, *span); + Pattern::Interned(id, location) => { + visitor.visit_interned_pattern(id, location.span); } } } 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 d80302401e74..48bed1c41997 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs @@ -1,13 +1,15 @@ use crate::ast::PathSegment; use crate::parse_program; use crate::parser::ParsedModule; +use crate::signed_field::SignedField; use crate::{ ast, - ast::{Path, PathKind}, + ast::Path, parser::{Item, ItemKind}, }; +use fm::FileId; use noirc_errors::debug_info::{DebugFnId, DebugFunction}; -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Location, Span}; use std::collections::HashMap; use std::collections::VecDeque; use std::mem::take; @@ -56,7 +58,7 @@ impl Default for DebugInstrumenter { } impl DebugInstrumenter { - pub fn instrument_module(&mut self, module: &mut ParsedModule) { + pub fn instrument_module(&mut self, module: &mut ParsedModule, file: FileId) { module.items.iter_mut().for_each(|item| { if let Item { kind: ItemKind::Function(f), .. } = item { self.walk_fn(&mut f.def); @@ -64,7 +66,7 @@ impl DebugInstrumenter { }); // this part absolutely must happen after ast traversal above // so that oracle functions don't get wrapped, resulting in infinite recursion: - self.insert_state_set_oracle(module, 8); + self.insert_state_set_oracle(module, 8, file); } fn insert_var(&mut self, var_name: &str) -> Option { @@ -102,7 +104,7 @@ impl DebugInstrumenter { let func_args = func.parameters.iter().map(|param| pattern_to_string(¶m.pattern)).collect(); let fn_id = self.insert_function(func_name, func_args); - let enter_stmt = build_debug_call_stmt("enter", fn_id, func.span); + let enter_stmt = build_debug_call_stmt("enter", fn_id, func.location); self.scope.push(HashMap::default()); let set_fn_params: Vec<_> = func @@ -122,11 +124,11 @@ impl DebugInstrumenter { let func_body = &mut func.body.statements; let mut statements = take(func_body); - self.walk_scope(&mut statements, func.span); + self.walk_scope(&mut statements, func.location); // walk_scope ensures that the last statement is the return value of the function let last_stmt = statements.pop().expect("at least one statement after walk_scope"); - let exit_stmt = build_debug_call_stmt("exit", fn_id, last_stmt.span); + let exit_stmt = build_debug_call_stmt("exit", fn_id, last_stmt.location); // rebuild function body func_body.push(enter_stmt); @@ -138,7 +140,7 @@ impl DebugInstrumenter { // Modify a vector of statements in-place, adding instrumentation for sets and drops. // This function will consume a scope level. - fn walk_scope(&mut self, statements: &mut Vec, span: Span) { + fn walk_scope(&mut self, statements: &mut Vec, location: Location) { statements.iter_mut().for_each(|stmt| self.walk_statement(stmt)); // extract and save the return value from the scope if there is one @@ -148,12 +150,12 @@ impl DebugInstrumenter { Some(ast::Statement { kind: ast::StatementKind::Expression(ret_expr), .. }) => { let save_ret_expr = ast::Statement { kind: ast::StatementKind::new_let( - ast::Pattern::Identifier(ident("__debug_expr", ret_expr.span)), - ast::UnresolvedTypeData::Unspecified.with_span(Default::default()), + ast::Pattern::Identifier(ident("__debug_expr", ret_expr.location)), + ast::UnresolvedTypeData::Unspecified.with_dummy_location(), ret_expr.clone(), vec![], ), - span: ret_expr.span, + location: ret_expr.location, }; statements.push(save_ret_expr); true @@ -165,39 +167,44 @@ impl DebugInstrumenter { } }; - let span = Span::empty(span.end()); + let span = Span::empty(location.span.end()); + let location = Location::new(span, location.file); // drop scope variables let scope_vars = self.scope.pop().unwrap_or_default(); - let drop_vars_stmts = scope_vars.values().map(|var_id| build_drop_var_stmt(*var_id, span)); + let drop_vars_stmts = + scope_vars.values().map(|var_id| build_drop_var_stmt(*var_id, location)); statements.extend(drop_vars_stmts); // return the saved value in __debug_expr, or unit otherwise let last_stmt = if has_ret_expr { ast::Statement { kind: ast::StatementKind::Expression(ast::Expression { - kind: ast::ExpressionKind::Variable(ast::Path { - segments: vec![PathSegment::from(ident("__debug_expr", span))], - kind: PathKind::Plain, - span, - }), - span, + kind: ast::ExpressionKind::Variable(ast::Path::plain( + vec![PathSegment::from(ident("__debug_expr", location))], + location, + )), + location, }), - span, + location, } } else { ast::Statement { kind: ast::StatementKind::Expression(ast::Expression { kind: ast::ExpressionKind::Literal(ast::Literal::Unit), - span, + location, }), - span, + location, } }; statements.push(last_stmt); } - fn walk_let_statement(&mut self, let_stmt: &ast::LetStatement, span: &Span) -> ast::Statement { + fn walk_let_statement( + &mut self, + let_stmt: &ast::LetStatement, + location: Location, + ) -> ast::Statement { // rewrites let statements written like this: // let (((a,b,c),D { d }),e,f) = x; // @@ -221,7 +228,7 @@ impl DebugInstrumenter { if *is_mut { ast::Pattern::Mutable( Box::new(ast::Pattern::Identifier(id.clone())), - id.span(), + id.location(), true, ) } else { @@ -238,7 +245,7 @@ impl DebugInstrumenter { if id.0.contents == "_" { ast::Expression { kind: ast::ExpressionKind::Literal(ast::Literal::Unit), - span: id.span(), + location: id.location(), } } else { id_expr(id) @@ -247,7 +254,7 @@ impl DebugInstrumenter { .collect(); let mut block_stmts = - vec![ast::Statement { kind: ast::StatementKind::Let(let_stmt.clone()), span: *span }]; + vec![ast::Statement { kind: ast::StatementKind::Let(let_stmt.clone()), location }]; block_stmts.extend(vars.iter().filter_map(|(id, _)| { let var_id = self.insert_var(&id.0.contents)?; Some(build_assign_var_stmt(var_id, id_expr(id))) @@ -255,31 +262,31 @@ impl DebugInstrumenter { block_stmts.push(ast::Statement { kind: ast::StatementKind::Expression(ast::Expression { kind: ast::ExpressionKind::Tuple(vars_exprs), - span: let_stmt.pattern.span(), + location: let_stmt.pattern.location(), }), - span: let_stmt.pattern.span(), + location: let_stmt.pattern.location(), }); ast::Statement { kind: ast::StatementKind::new_let( - ast::Pattern::Tuple(vars_pattern, let_stmt.pattern.span()), - ast::UnresolvedTypeData::Unspecified.with_span(Default::default()), + ast::Pattern::Tuple(vars_pattern, let_stmt.pattern.location()), + ast::UnresolvedTypeData::Unspecified.with_dummy_location(), ast::Expression { kind: ast::ExpressionKind::Block(ast::BlockExpression { statements: block_stmts, }), - span: let_stmt.expression.span, + location: let_stmt.expression.location, }, vec![], ), - span: *span, + location, } } fn walk_assign_statement( &mut self, assign_stmt: &ast::AssignStatement, - span: &Span, + location: Location, ) -> ast::Statement { // X = Y becomes: // X = { @@ -293,26 +300,26 @@ impl DebugInstrumenter { // }; let let_kind = ast::StatementKind::new_let( - ast::Pattern::Identifier(ident("__debug_expr", assign_stmt.expression.span)), - ast::UnresolvedTypeData::Unspecified.with_span(Default::default()), + ast::Pattern::Identifier(ident("__debug_expr", assign_stmt.expression.location)), + ast::UnresolvedTypeData::Unspecified.with_dummy_location(), assign_stmt.expression.clone(), vec![], ); - let expression_span = assign_stmt.expression.span; + let expression_location = assign_stmt.expression.location; let new_assign_stmt = match &assign_stmt.lvalue { ast::LValue::Ident(id) => { let var_id = self .lookup_var(&id.0.contents) .unwrap_or_else(|| panic!("var lookup failed for var_name={}", &id.0.contents)); - build_assign_var_stmt(var_id, id_expr(&ident("__debug_expr", id.span()))) + build_assign_var_stmt(var_id, id_expr(&ident("__debug_expr", id.location()))) } - ast::LValue::Dereference(_lv, span) => { + ast::LValue::Dereference(_lv, location) => { // TODO: this is a dummy statement for now, but we should // somehow track the derefence and update the pointed to // variable ast::Statement { - kind: ast::StatementKind::Expression(uint_expr(0, *span)), - span: *span, + kind: ast::StatementKind::Expression(uint_expr(0, *location)), + location: *location, } } _ => { @@ -327,12 +334,12 @@ impl DebugInstrumenter { }); break; } - ast::LValue::MemberAccess { object, field_name, span } => { + ast::LValue::MemberAccess { object, field_name, location } => { cursor = object; let field_name_id = self.insert_field_name(&field_name.0.contents); - indexes.push(sint_expr(-(field_name_id.0 as i128), *span)); + indexes.push(sint_expr(-(field_name_id.0 as i128), *location)); } - ast::LValue::Index { index, array, span: _ } => { + ast::LValue::Index { index, array, location: _ } => { cursor = array; indexes.push(index.clone()); } @@ -347,13 +354,13 @@ impl DebugInstrumenter { build_assign_member_stmt( var_id, &indexes, - &id_expr(&ident("__debug_expr", expression_span)), + &id_expr(&ident("__debug_expr", expression_location)), ) } }; let ret_kind = - ast::StatementKind::Expression(id_expr(&ident("__debug_expr", expression_span))); + ast::StatementKind::Expression(id_expr(&ident("__debug_expr", expression_location))); ast::Statement { kind: ast::StatementKind::Assign(ast::AssignStatement { @@ -361,23 +368,23 @@ impl DebugInstrumenter { expression: ast::Expression { kind: ast::ExpressionKind::Block(ast::BlockExpression { statements: vec![ - ast::Statement { kind: let_kind, span: expression_span }, + ast::Statement { kind: let_kind, location: expression_location }, new_assign_stmt, - ast::Statement { kind: ret_kind, span: expression_span }, + ast::Statement { kind: ret_kind, location: expression_location }, ], }), - span: expression_span, + location: expression_location, }, }), - span: *span, + location, } } fn walk_expr(&mut self, expr: &mut ast::Expression) { match &mut expr.kind { - ast::ExpressionKind::Block(ast::BlockExpression { ref mut statements, .. }) => { + ast::ExpressionKind::Block(ast::BlockExpression { statements, .. }) => { self.scope.push(HashMap::default()); - self.walk_scope(statements, expr.span); + self.walk_scope(statements, expr.location); } ast::ExpressionKind::Prefix(prefix_expr) => { self.walk_expr(&mut prefix_expr.rhs); @@ -389,19 +396,19 @@ impl DebugInstrumenter { ast::ExpressionKind::Call(call_expr) => { // TODO: push a stack frame or something here? self.walk_expr(&mut call_expr.func); - call_expr.arguments.iter_mut().for_each(|ref mut expr| { + call_expr.arguments.iter_mut().for_each(|expr| { self.walk_expr(expr); }); } ast::ExpressionKind::MethodCall(mc_expr) => { // TODO: also push a stack frame here self.walk_expr(&mut mc_expr.object); - mc_expr.arguments.iter_mut().for_each(|ref mut expr| { + mc_expr.arguments.iter_mut().for_each(|expr| { self.walk_expr(expr); }); } ast::ExpressionKind::Constructor(c_expr) => { - c_expr.fields.iter_mut().for_each(|(_id, ref mut expr)| { + c_expr.fields.iter_mut().for_each(|(_id, expr)| { self.walk_expr(expr); }); } @@ -442,9 +449,10 @@ impl DebugInstrumenter { let var_id = self.insert_var(var_name); let set_and_drop_stmt = var_id.map(|var_id| { + let span = Span::empty(for_stmt.location.span.end()); ( build_assign_var_stmt(var_id, id_expr(&for_stmt.identifier)), - build_drop_var_stmt(var_id, Span::empty(for_stmt.span.end())), + build_drop_var_stmt(var_id, Location::new(span, for_stmt.location.file)), ) }); @@ -453,7 +461,7 @@ impl DebugInstrumenter { let mut statements = Vec::new(); let block_statement = ast::Statement { kind: ast::StatementKind::Semi(for_stmt.block.clone()), - span: for_stmt.block.span, + location: for_stmt.block.location, }; if let Some((set_stmt, drop_stmt)) = set_and_drop_stmt { @@ -466,17 +474,17 @@ impl DebugInstrumenter { for_stmt.block = ast::Expression { kind: ast::ExpressionKind::Block(ast::BlockExpression { statements }), - span: for_stmt.span, + location: for_stmt.location, }; } fn walk_statement(&mut self, stmt: &mut ast::Statement) { match &mut stmt.kind { ast::StatementKind::Let(let_stmt) => { - *stmt = self.walk_let_statement(let_stmt, &stmt.span); + *stmt = self.walk_let_statement(let_stmt, stmt.location); } ast::StatementKind::Assign(assign_stmt) => { - *stmt = self.walk_assign_statement(assign_stmt, &stmt.span); + *stmt = self.walk_assign_statement(assign_stmt, stmt.location); } ast::StatementKind::Expression(expr) => { self.walk_expr(expr); @@ -484,20 +492,21 @@ impl DebugInstrumenter { ast::StatementKind::Semi(expr) => { self.walk_expr(expr); } - ast::StatementKind::For(ref mut for_stmt) => { + ast::StatementKind::For(for_stmt) => { self.walk_for(for_stmt); } _ => {} // Constrain, Error } } - fn insert_state_set_oracle(&self, module: &mut ParsedModule, n: u32) { + fn insert_state_set_oracle(&self, module: &mut ParsedModule, n: u32, file: FileId) { let member_assigns = (1..=n) .map(|i| format!["__debug_member_assign_{i}"]) .collect::>() .join(",\n"); - let (program, errors) = parse_program(&format!( - r#" + let (program, errors) = parse_program( + &format!( + r#" use __debug::{{ __debug_var_assign, __debug_var_drop, @@ -506,7 +515,9 @@ impl DebugInstrumenter { __debug_dereference_assign, {member_assigns}, }};"# - )); + ), + file, + ); if !errors.is_empty() { panic!("errors parsing internal oracle definitions: {errors:?}") } @@ -619,36 +630,34 @@ pub fn build_debug_crate_file() -> String { } fn build_assign_var_stmt(var_id: SourceVarId, expr: ast::Expression) -> ast::Statement { - let span = expr.span; + let location = expr.location; let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression { func: Box::new(ast::Expression { - kind: ast::ExpressionKind::Variable(ast::Path { - segments: vec![PathSegment::from(ident("__debug_var_assign", span))], - kind: PathKind::Plain, - span, - }), - span, + kind: ast::ExpressionKind::Variable(ast::Path::plain( + vec![PathSegment::from(ident("__debug_var_assign", location))], + location, + )), + location, }), is_macro_call: false, - arguments: vec![uint_expr(var_id.0 as u128, span), expr], + arguments: vec![uint_expr(var_id.0 as u128, location), expr], })); - ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } + ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, location }), location } } -fn build_drop_var_stmt(var_id: SourceVarId, span: Span) -> ast::Statement { +fn build_drop_var_stmt(var_id: SourceVarId, location: Location) -> ast::Statement { let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression { func: Box::new(ast::Expression { - kind: ast::ExpressionKind::Variable(ast::Path { - segments: vec![PathSegment::from(ident("__debug_var_drop", span))], - kind: PathKind::Plain, - span, - }), - span, + kind: ast::ExpressionKind::Variable(ast::Path::plain( + vec![PathSegment::from(ident("__debug_var_drop", location))], + location, + )), + location, }), is_macro_call: false, - arguments: vec![uint_expr(var_id.0 as u128, span)], + arguments: vec![uint_expr(var_id.0 as u128, location)], })); - ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } + ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, location }), location } } fn build_assign_member_stmt( @@ -660,44 +669,39 @@ fn build_assign_member_stmt( if arity > MAX_MEMBER_ASSIGN_DEPTH { unreachable!("Assignment to member exceeds maximum depth for debugging"); } - let span = expr.span; + let location = expr.location; let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression { func: Box::new(ast::Expression { - kind: ast::ExpressionKind::Variable(ast::Path { - segments: vec![PathSegment::from(ident( - &format!["__debug_member_assign_{arity}"], - span, - ))], - kind: PathKind::Plain, - span, - }), - span, + kind: ast::ExpressionKind::Variable(ast::Path::plain( + vec![PathSegment::from(ident(&format!["__debug_member_assign_{arity}"], location))], + location, + )), + location, }), is_macro_call: false, arguments: [ - vec![uint_expr(var_id.0 as u128, span)], + vec![uint_expr(var_id.0 as u128, location)], vec![expr.clone()], indexes.iter().rev().cloned().collect(), ] .concat(), })); - ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } + ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, location }), location } } -fn build_debug_call_stmt(fname: &str, fn_id: DebugFnId, span: Span) -> ast::Statement { +fn build_debug_call_stmt(fname: &str, fn_id: DebugFnId, location: Location) -> ast::Statement { let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression { func: Box::new(ast::Expression { - kind: ast::ExpressionKind::Variable(ast::Path { - segments: vec![PathSegment::from(ident(&format!["__debug_fn_{fname}"], span))], - kind: PathKind::Plain, - span, - }), - span, + kind: ast::ExpressionKind::Variable(ast::Path::plain( + vec![PathSegment::from(ident(&format!["__debug_fn_{fname}"], location))], + location, + )), + location, }), is_macro_call: false, - arguments: vec![uint_expr(fn_id.0 as u128, span)], + arguments: vec![uint_expr(fn_id.0 as u128, location)], })); - ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } + ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, location }), location } } fn pattern_vars(pattern: &ast::Pattern) -> Vec<(ast::Ident, bool)> { @@ -750,31 +754,28 @@ fn pattern_to_string(pattern: &ast::Pattern) -> String { } } -fn ident(s: &str, span: Span) -> ast::Ident { - ast::Ident(Spanned::from(span, s.to_string())) +fn ident(s: &str, location: Location) -> ast::Ident { + ast::Ident::new(s.to_string(), location) } fn id_expr(id: &ast::Ident) -> ast::Expression { ast::Expression { - kind: ast::ExpressionKind::Variable(Path { - segments: vec![PathSegment::from(id.clone())], - kind: PathKind::Plain, - span: id.span(), - }), - span: id.span(), + kind: ast::ExpressionKind::Variable(Path::plain( + vec![PathSegment::from(id.clone())], + id.location(), + )), + location: id.location(), } } -fn uint_expr(x: u128, span: Span) -> ast::Expression { - ast::Expression { - kind: ast::ExpressionKind::Literal(ast::Literal::Integer(x.into(), false)), - span, - } +fn uint_expr(x: u128, location: Location) -> ast::Expression { + let value = SignedField::positive(x); + let kind = ast::ExpressionKind::Literal(ast::Literal::Integer(value)); + ast::Expression { kind, location } } -fn sint_expr(x: i128, span: Span) -> ast::Expression { - ast::Expression { - kind: ast::ExpressionKind::Literal(ast::Literal::Integer(x.abs().into(), x < 0)), - span, - } +fn sint_expr(x: i128, location: Location) -> ast::Expression { + let value = SignedField::from_signed(x); + let kind = ast::ExpressionKind::Literal(ast::Literal::Integer(value)); + ast::Expression { kind, location } } 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 b78787249cb7..7091f7b261cd 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -1,10 +1,10 @@ use std::{collections::BTreeMap, fmt::Display}; -use fm::FileId; use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use crate::{ + Type, TypeBindings, UnificationError, ast::{Documented, Expression, ExpressionKind}, hir::{ comptime::{Interpreter, InterpreterError, Value}, @@ -22,28 +22,23 @@ use crate::{ node_interner::{DefinitionKind, DependencyId, FuncId, NodeInterner, TraitId, TypeId}, parser::{Item, ItemKind}, token::{MetaAttribute, SecondaryAttribute}, - Type, TypeBindings, UnificationError, }; -use super::{Elaborator, FunctionContext, ResolverMeta}; +use super::{ElaborateReason, Elaborator, FunctionContext, ResolverMeta}; #[derive(Debug, Copy, Clone)] struct AttributeContext { - // The file where generated items should be added - file: FileId, // The module where generated items should be added module: LocalModuleId, - // The file where the attribute is located - attribute_file: FileId, // The module where the attribute is located attribute_module: LocalModuleId, } -type CollectedAttributes = Vec<(FuncId, Value, Vec, AttributeContext, Span)>; +type CollectedAttributes = Vec<(FuncId, Value, Vec, AttributeContext, Location)>; impl AttributeContext { - fn new(file: FileId, module: LocalModuleId) -> Self { - Self { file, module, attribute_file: file, attribute_module: module } + fn new(module: LocalModuleId) -> Self { + Self { module, attribute_module: module } } } @@ -62,7 +57,6 @@ impl<'context> Elaborator<'context> { 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()); } }) @@ -71,14 +65,12 @@ impl<'context> Elaborator<'context> { pub fn elaborate_item_from_comptime_in_module<'a, T>( &'a mut self, module: ModuleId, - file: FileId, f: impl FnOnce(&mut Elaborator<'a>) -> T, ) -> T { self.elaborate_item_from_comptime(f, |elaborator| { elaborator.current_item = None; elaborator.crate_id = module.krate; elaborator.local_module = module.local_id; - elaborator.file = file; }) } @@ -95,16 +87,15 @@ impl<'context> Elaborator<'context> { self.usage_tracker, self.crate_graph, self.crate_id, - self.debug_comptime_in_file, self.interpreter_call_stack.clone(), - self.pedantic_solving, + self.options, + self.elaborate_reasons.clone(), ); elaborator.function_context.push(FunctionContext::default()); elaborator.scopes.start_function(); elaborator.local_module = self.local_module; - elaborator.file = self.file; setup(&mut elaborator); @@ -167,7 +158,7 @@ impl<'context> Elaborator<'context> { attribute_context, attributes_to_run, ) { - this.errors.push(error); + this.push_err(error); } }); } @@ -180,12 +171,12 @@ impl<'context> Elaborator<'context> { item: Value, attribute_context: AttributeContext, attributes_to_run: &mut CollectedAttributes, - ) -> Result<(), (CompilationError, FileId)> { - self.file = attribute_context.attribute_file; + ) -> Result<(), CompilationError> { self.local_module = attribute_context.attribute_module; - let span = attribute.span; + let location = attribute.location; - let function = Expression { kind: ExpressionKind::Variable(attribute.name.clone()), span }; + let function = + Expression { kind: ExpressionKind::Variable(attribute.name.clone()), location }; let arguments = attribute.arguments.clone(); // Elaborate the function, rolling back any errors generated in case it is unknown @@ -197,22 +188,25 @@ impl<'context> Elaborator<'context> { let definition_id = match self.interner.expression(&function) { HirExpression::Ident(ident, _) => ident.id, _ => { - let error = - ResolverError::AttributeFunctionIsNotAPath { function: function_string, span }; - return Err((error.into(), self.file)); + let error = ResolverError::AttributeFunctionIsNotAPath { + function: function_string, + location, + }; + return Err(error.into()); } }; let Some(definition) = self.interner.try_definition(definition_id) else { - let error = ResolverError::AttributeFunctionNotInScope { name: function_string, span }; - return Err((error.into(), self.file)); + let error = + ResolverError::AttributeFunctionNotInScope { name: function_string, location }; + return Err(error.into()); }; let DefinitionKind::Function(function) = definition.kind else { - return Err((ResolverError::NonFunctionInAnnotation { span }.into(), self.file)); + return Err(ResolverError::NonFunctionInAnnotation { location }.into()); }; - attributes_to_run.push((function, item, arguments, attribute_context, span)); + attributes_to_run.push((function, item, arguments, attribute_context, location)); Ok(()) } @@ -224,8 +218,7 @@ impl<'context> Elaborator<'context> { item: Value, location: Location, generated_items: &mut CollectedItems, - ) -> Result<(), (CompilationError, FileId)> { - self.file = attribute_context.file; + ) -> Result<(), CompilationError> { self.local_module = attribute_context.module; let mut interpreter = self.setup_interpreter(); @@ -236,20 +229,19 @@ impl<'context> Elaborator<'context> { arguments, location, ) - .map_err(|error| error.into_compilation_error_pair())?; + .map_err(CompilationError::from)?; arguments.insert(0, (item, location)); let value = interpreter .call_function(function, arguments, TypeBindings::new(), location) - .map_err(|error| error.into_compilation_error_pair())?; + .map_err(CompilationError::from)?; self.debug_comptime(location, |interner| value.display(interner).to_string()); if value != Value::Unit { - let items = value - .into_top_level_items(location, self) - .map_err(|error| error.into_compilation_error_pair())?; + let items = + value.into_top_level_items(location, self).map_err(CompilationError::from)?; self.add_items(items, generated_items, location); } @@ -303,7 +295,7 @@ impl<'context> Elaborator<'context> { let mut varargs = im::Vector::new(); for (i, arg) in arguments.into_iter().enumerate() { - let arg_location = Location::new(arg.span, location.file); + let arg_location = arg.location; let param_type = parameters.get(i).or(varargs_elem_type).unwrap_or(&Type::Error); let mut push_arg = |arg| { @@ -350,9 +342,14 @@ impl<'context> Elaborator<'context> { generated_items: &mut CollectedItems, location: Location, ) { + let previous_errors = + self.push_elaborate_reason_and_take_errors(ElaborateReason::RunningAttribute, location); + for item in items { self.add_item(item, generated_items, location); } + + self.pop_elaborate_reason(previous_errors); } pub(crate) fn add_item( @@ -371,13 +368,12 @@ impl<'context> Elaborator<'context> { self.usage_tracker, &function, module_id, - self.file, item.doc_comments, &mut self.errors, ) { let functions = vec![(self.local_module, id, function)]; generated_items.functions.push(UnresolvedFunctions { - file_id: self.file, + file_id: location.file, functions, trait_id: None, self_type: None, @@ -390,12 +386,12 @@ impl<'context> Elaborator<'context> { self.interner, &mut trait_impl, self.crate_id, - self.file, + location.file, self.local_module, ); generated_items.trait_impls.push(UnresolvedTraitImpl { - file_id: self.file, + file_id: location.file, module_id: self.local_module, r#trait: trait_impl.r#trait, object_type: trait_impl.object_type, @@ -420,14 +416,14 @@ impl<'context> Elaborator<'context> { self.usage_tracker, Documented::new(global, item.doc_comments), visibility, - self.file, + location.file, self.local_module, self.crate_id, ); generated_items.globals.push(global); if let Some(error) = error { - self.errors.push(error); + self.push_err(error); } } ItemKind::Struct(struct_def) => { @@ -436,7 +432,6 @@ impl<'context> Elaborator<'context> { self.def_maps.get_mut(&self.crate_id).unwrap(), self.usage_tracker, Documented::new(struct_def, item.doc_comments), - self.file, self.local_module, self.crate_id, &mut self.errors, @@ -450,7 +445,7 @@ impl<'context> Elaborator<'context> { self.def_maps.get_mut(&self.crate_id).unwrap(), self.usage_tracker, Documented::new(enum_def, item.doc_comments), - self.file, + location.file, self.local_module, self.crate_id, &mut self.errors, @@ -464,7 +459,7 @@ impl<'context> Elaborator<'context> { self.interner, generated_items, r#impl, - self.file, + location.file, module, &mut self.errors, ); @@ -476,9 +471,10 @@ impl<'context> Elaborator<'context> { | ItemKind::TypeAlias(_) | ItemKind::Submodules(_) | ItemKind::InnerAttribute(_) => { + let location = item.location; let item = item.kind.to_string(); let error = InterpreterError::UnsupportedTopLevelItemUnquote { item, location }; - self.errors.push(error.into_compilation_error_pair()); + self.push_err(error); } } } @@ -488,7 +484,7 @@ impl<'context> Elaborator<'context> { Some(DependencyId::Function(function)) => Some(function), _ => None, }; - Interpreter::new(self, self.crate_id, current_function, self.pedantic_solving) + Interpreter::new(self, self.crate_id, current_function) } pub(super) fn debug_comptime T>( @@ -496,12 +492,11 @@ impl<'context> Elaborator<'context> { location: Location, mut expr_f: F, ) { - if Some(location.file) == self.debug_comptime_in_file { + if Some(location.file) == self.options.debug_comptime_in_file { let displayed_expr = expr_f(self.interner); - self.errors.push(( - InterpreterError::debug_evaluate_comptime(displayed_expr, location).into(), - location.file, - )); + let error: CompilationError = + InterpreterError::debug_evaluate_comptime(displayed_expr, location).into(); + self.push_err(error); } } @@ -520,7 +515,7 @@ impl<'context> Elaborator<'context> { for (trait_id, trait_) in traits { let attributes = &trait_.trait_def.attributes; let item = Value::TraitDefinition(*trait_id); - let context = AttributeContext::new(trait_.file_id, trait_.module_id); + let context = AttributeContext::new(trait_.module_id); self.collect_comptime_attributes_on_item( attributes, item, @@ -532,7 +527,7 @@ impl<'context> Elaborator<'context> { for (struct_id, struct_def) in types { let attributes = &struct_def.struct_def.attributes; let item = Value::StructDefinition(*struct_id); - let context = AttributeContext::new(struct_def.file_id, struct_def.module_id); + let context = AttributeContext::new(struct_def.module_id); self.collect_comptime_attributes_on_item( attributes, item, @@ -547,9 +542,7 @@ impl<'context> Elaborator<'context> { self.sort_attributes_by_run_order(&mut attributes_to_run); // run - for (attribute, item, args, context, span) in attributes_to_run { - let location = Location::new(span, context.attribute_file); - + for (attribute, item, args, context, location) in attributes_to_run { let mut generated_items = CollectedItems::default(); self.elaborate_in_comptime_context(|this| { if let Err(error) = this.run_attribute( @@ -560,12 +553,19 @@ impl<'context> Elaborator<'context> { location, &mut generated_items, ) { - this.errors.push(error); + this.push_err(error); } }); if !generated_items.is_empty() { + let previous_errors = self.push_elaborate_reason_and_take_errors( + ElaborateReason::RunningAttribute, + location, + ); + self.elaborate_items(generated_items); + + self.pop_elaborate_reason(previous_errors); } } } @@ -575,8 +575,8 @@ impl<'context> Elaborator<'context> { // Sort each attribute by (module, location in file) so that we can execute in // the order they were defined in, running attributes in child modules first. - attributes.sort_by_key(|(_, _, _, ctx, span)| { - (module_order[&ctx.attribute_module], span.start()) + attributes.sort_by_key(|(_, _, _, ctx, location)| { + (module_order[&ctx.attribute_module], location.span.start()) }); } @@ -592,9 +592,7 @@ impl<'context> Elaborator<'context> { let attribute = &module_attribute.attribute; let context = AttributeContext { - file: module_attribute.file_id, module: module_attribute.module_id, - attribute_file: module_attribute.attribute_file_id, attribute_module: module_attribute.attribute_module_id, }; @@ -611,7 +609,7 @@ impl<'context> Elaborator<'context> { self.self_type = function_set.self_type.clone(); for (local_module, function_id, function) in &function_set.functions { - let context = AttributeContext::new(function_set.file_id, *local_module); + let context = AttributeContext::new(*local_module); let attributes = function.secondary_attributes(); let item = Value::FunctionDefinition(*function_id); self.collect_comptime_attributes_on_item( @@ -653,4 +651,35 @@ impl<'context> Elaborator<'context> { _ => false, } } + + /// Pushes an ElaborateReason but also `std::mem::take`s the current errors and returns them. + pub(crate) fn push_elaborate_reason_and_take_errors( + &mut self, + reason: ElaborateReason, + location: Location, + ) -> Vec { + self.elaborate_reasons.push_back((reason, location)); + std::mem::take(&mut self.errors) + } + + /// Pops en ElaborateREason. Receives the errors that were returned by `push_elaborate_reason` + /// so they are restored, while also wrapping errors in the current Elaborator in a ComptimeError. + pub(crate) fn pop_elaborate_reason(&mut self, previous_errors: Vec) { + let new_errors = std::mem::take(&mut self.errors); + let new_errors = self.wrap_errors_in_macro_error(new_errors); + self.errors = previous_errors; + self.push_errors(new_errors); + self.elaborate_reasons.pop_back(); + } + + fn wrap_errors_in_macro_error(&self, errors: Vec) -> Vec { + vecmap(errors, |error| self.wrap_error_in_macro_error(error)) + } + + fn wrap_error_in_macro_error(&self, mut error: CompilationError) -> CompilationError { + for (reason, location) in self.elaborate_reasons.iter().rev() { + error = CompilationError::ComptimeError(reason.to_macro_error(error, *location)); + } + error + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/enums.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/enums.rs index 02d9bfae4946..90849a750d56 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/enums.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/enums.rs @@ -1,25 +1,28 @@ +use std::collections::BTreeMap; + use fxhash::FxHashMap as HashMap; use iter_extended::{try_vecmap, vecmap}; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use crate::{ + DataType, Kind, Shared, Type, ast::{ - EnumVariant, Expression, ExpressionKind, FunctionKind, Literal, NoirEnumeration, - StatementKind, UnresolvedType, Visibility, + ConstructorExpression, EnumVariant, Expression, ExpressionKind, FunctionKind, Ident, + Literal, NoirEnumeration, StatementKind, UnresolvedType, Visibility, }, elaborator::path_resolution::PathResolutionItem, hir::{comptime::Value, resolution::errors::ResolverError, type_check::TypeCheckError}, hir_def::{ expr::{ Case, Constructor, HirBlockExpression, HirEnumConstructorExpression, HirExpression, - HirIdent, HirMatch, SignedField, + HirIdent, HirMatch, }, function::{FuncMeta, FunctionBody, HirFunction, Parameters}, stmt::{HirLetStatement, HirPattern, HirStatement}, }, node_interner::{DefinitionId, DefinitionKind, ExprId, FunctionModifiers, GlobalValue, TypeId}, + signed_field::SignedField, token::Attributes, - DataType, Kind, Shared, Type, }; use super::Elaborator; @@ -75,13 +78,13 @@ impl Elaborator<'_> { self_type: &Type, ) { let name = &variant.name; - let location = Location::new(variant.name.span(), self.file); + let location = variant.name.location(); let global_id = self.interner.push_empty_global( name.clone(), type_id.local_module_id(), type_id.krate(), - self.file, + name.location().file, Vec::new(), false, false, @@ -127,7 +130,7 @@ impl Elaborator<'_> { ) { let name_string = variant.name.to_string(); let datatype_ref = datatype.borrow(); - let location = Location::new(variant.name.span(), self.file); + let location = variant.name.location(); let id = self.interner.push_empty_fn(); @@ -176,7 +179,7 @@ impl Elaborator<'_> { function_body: FunctionBody::Resolved, source_crate: self.crate_id, source_module: type_id.local_module_id(), - source_file: self.file, + source_file: variant.name.location().file, self_type: None, }; @@ -219,7 +222,7 @@ impl Elaborator<'_> { HirPattern::Identifier(ident) => { let id = self.interner.push_expr(HirExpression::Ident(ident.clone(), None)); self.interner.push_expr_type(id, typ.clone()); - self.interner.push_expr_location(id, location.span, location.file); + self.interner.push_expr_location(id, location); id } _ => unreachable!(), @@ -235,7 +238,7 @@ impl Elaborator<'_> { let enum_generics = self_type.borrow().generic_types(); let typ = Type::DataType(self_type.clone(), enum_generics); self.interner.push_expr_type(body, typ); - self.interner.push_expr_location(body, location.span, location.file); + self.interner.push_expr_location(body, location); body } @@ -270,17 +273,18 @@ impl Elaborator<'_> { let rows = vecmap(rules, |(pattern, branch)| { self.push_scope(); - let pattern = self.expression_to_pattern(pattern, &expected_pattern_type); + let pattern = + self.expression_to_pattern(pattern, &expected_pattern_type, &mut Vec::new()); let columns = vec![Column::new(variable_to_match, pattern)]; let guard = None; - let body_span = branch.type_span(); + let body_location = branch.type_location(); let (body, body_type) = self.elaborate_expression(branch); self.unify(&body_type, &result_type, || TypeCheckError::TypeMismatch { expected_typ: result_type.to_string(), expr_typ: body_type.to_string(), - expr_span: body_span, + expr_location: body_location, }); self.pop_scope(); @@ -290,21 +294,33 @@ impl Elaborator<'_> { } /// Convert an expression into a Pattern, defining any variables within. - fn expression_to_pattern(&mut self, expression: Expression, expected_type: &Type) -> Pattern { - let expr_span = expression.type_span(); + fn expression_to_pattern( + &mut self, + expression: Expression, + expected_type: &Type, + variables_defined: &mut Vec, + ) -> Pattern { + let expr_location = expression.type_location(); let unify_with_expected_type = |this: &mut Self, actual| { this.unify(actual, expected_type, || TypeCheckError::TypeMismatch { expected_typ: expected_type.to_string(), expr_typ: actual.to_string(), - expr_span, + expr_location, }); }; + // We want the actual expression's location here, not the innermost one from `type_location()` + let syntax_error = |this: &mut Self| { + let errors = ResolverError::InvalidSyntaxInPattern { location: expression.location }; + this.push_err(errors); + Pattern::Error + }; + match expression.kind { - ExpressionKind::Literal(Literal::Integer(value, negative)) => { + ExpressionKind::Literal(Literal::Integer(value)) => { let actual = self.interner.next_type_variable_with_kind(Kind::IntegerOrField); unify_with_expected_type(self, &actual); - Pattern::Int(SignedField::new(value, negative)) + Pattern::Int(value) } ExpressionKind::Literal(Literal::Bool(value)) => { unify_with_expected_type(self, &Type::Bool); @@ -318,39 +334,42 @@ impl Elaborator<'_> { // - Possible diagnostics improvement: warn if `a` is defined as a variable // when there is a matching enum variant with name `Foo::a` which can // be imported. The user likely intended to reference the enum variant. - let path_len = path.segments.len(); - let location = Location::new(path.span(), self.file); + let location = path.location; let last_ident = path.last_ident(); + // Setting this to `Some` allows us to shadow globals with the same name. + // We should avoid this if there is a `::` in the path since that means the + // user is trying to resolve to a non-local item. + let shadow_existing = path.is_ident().then_some(last_ident); + match self.resolve_path_or_error(path) { Ok(resolution) => self.path_resolution_to_constructor( resolution, + shadow_existing, Vec::new(), expected_type, - location.span, + location, + variables_defined, ), - Err(_) if path_len == 1 => { - // Define the variable - let kind = DefinitionKind::Local(None); - // TODO: `allow_shadowing` is false while I'm too lazy to add a check that we - // don't define the same name multiple times in one pattern. - let id = self.add_variable_decl(last_ident, false, false, true, kind).id; - self.interner.push_definition_type(id, expected_type.clone()); - Pattern::Binding(id) - } Err(error) => { - self.push_err(error); - // Default to defining a variable of the same name although this could - // cause further match warnings/errors (e.g. redundant cases). - let id = self.fresh_match_variable(expected_type.clone(), location); - Pattern::Binding(id) + if let Some(name) = shadow_existing { + self.define_pattern_variable(name, expected_type, variables_defined) + } else { + self.push_err(error); + Pattern::Error + } } } } - ExpressionKind::Call(call) => { - self.expression_to_constructor(*call.func, call.arguments, expected_type) + ExpressionKind::Call(call) => self.expression_to_constructor( + *call.func, + call.arguments, + expected_type, + variables_defined, + ), + ExpressionKind::Constructor(constructor) => { + self.constructor_to_pattern(*constructor, variables_defined) } - ExpressionKind::Constructor(_) => todo!("handle constructors"), ExpressionKind::Tuple(fields) => { let field_types = vecmap(0..fields.len(), |_| self.interner.next_type_variable()); let actual = Type::Tuple(field_types.clone()); @@ -358,23 +377,25 @@ impl Elaborator<'_> { let fields = vecmap(fields.into_iter().enumerate(), |(i, field)| { let expected = field_types.get(i).unwrap_or(&Type::Error); - self.expression_to_pattern(field, expected) + self.expression_to_pattern(field, expected, variables_defined) }); Pattern::Constructor(Constructor::Tuple(field_types.clone()), fields) } - ExpressionKind::Parenthesized(expr) => self.expression_to_pattern(*expr, expected_type), + ExpressionKind::Parenthesized(expr) => { + self.expression_to_pattern(*expr, expected_type, variables_defined) + } ExpressionKind::Interned(id) => { let kind = self.interner.get_expression_kind(id); - let expr = Expression::new(kind.clone(), expression.span); - self.expression_to_pattern(expr, expected_type) + let expr = Expression::new(kind.clone(), expression.location); + self.expression_to_pattern(expr, expected_type, variables_defined) } ExpressionKind::InternedStatement(id) => { if let StatementKind::Expression(expr) = self.interner.get_statement_kind(id) { - self.expression_to_pattern(expr.clone(), expected_type) + self.expression_to_pattern(expr.clone(), expected_type, variables_defined) } else { - panic!("Invalid expr kind {expression}") + syntax_error(self) } } @@ -393,14 +414,85 @@ impl Elaborator<'_> { | ExpressionKind::Quote(_) | ExpressionKind::Unquote(_) | ExpressionKind::Comptime(_, _) - | ExpressionKind::Unsafe(_, _) + | ExpressionKind::Unsafe(_) | ExpressionKind::AsTraitPath(_) | ExpressionKind::TypePath(_) | ExpressionKind::Resolved(_) - | ExpressionKind::Error => { - panic!("Invalid expr kind {expression}") + | ExpressionKind::Error => syntax_error(self), + } + } + + fn define_pattern_variable( + &mut self, + name: Ident, + expected_type: &Type, + variables_defined: &mut Vec, + ) -> Pattern { + // Define the variable + let kind = DefinitionKind::Local(None); + + if let Some(existing) = variables_defined.iter().find(|elem| *elem == &name) { + // Allow redefinition of `_` only, to ignore variables + if name.0.contents != "_" { + self.push_err(ResolverError::VariableAlreadyDefinedInPattern { + existing: existing.clone(), + new_location: name.location(), + }); } + } else { + variables_defined.push(name.clone()); } + + let id = self.add_variable_decl(name, false, true, true, kind).id; + self.interner.push_definition_type(id, expected_type.clone()); + Pattern::Binding(id) + } + + fn constructor_to_pattern( + &mut self, + constructor: ConstructorExpression, + variables_defined: &mut Vec, + ) -> Pattern { + let location = constructor.typ.location; + let typ = self.resolve_type(constructor.typ); + + let Some((struct_name, mut expected_field_types)) = + self.struct_name_and_field_types(&typ, location) + else { + return Pattern::Error; + }; + + let mut fields = BTreeMap::default(); + for (field_name, field) in constructor.fields { + let Some(field_index) = + expected_field_types.iter().position(|(name, _)| *name == field_name.0.contents) + else { + let error = if fields.contains_key(&field_name.0.contents) { + ResolverError::DuplicateField { field: field_name } + } else { + let struct_definition = struct_name.clone(); + ResolverError::NoSuchField { field: field_name, struct_definition } + }; + self.push_err(error); + continue; + }; + + let (field_name, expected_field_type) = expected_field_types.swap_remove(field_index); + let pattern = + self.expression_to_pattern(field, &expected_field_type, variables_defined); + fields.insert(field_name, pattern); + } + + if !expected_field_types.is_empty() { + let struct_definition = struct_name; + let missing_fields = vecmap(expected_field_types, |(name, _)| name); + let error = + ResolverError::MissingFields { location, missing_fields, struct_definition }; + self.push_err(error); + } + + let args = vecmap(fields, |(_name, field)| field); + Pattern::Constructor(Constructor::Variant(typ, 0), args) } fn expression_to_constructor( @@ -408,16 +500,28 @@ impl Elaborator<'_> { name: Expression, args: Vec, expected_type: &Type, + variables_defined: &mut Vec, ) -> Pattern { + let syntax_error = |this: &mut Self| { + this.push_err(ResolverError::InvalidSyntaxInPattern { location: name.location }); + Pattern::Error + }; + match name.kind { ExpressionKind::Variable(path) => { - let span = path.span(); - let location = Location::new(span, self.file); + let location = path.location; match self.resolve_path_or_error(path) { - Ok(resolution) => { - self.path_resolution_to_constructor(resolution, args, expected_type, span) - } + // Use None for `name` here - we don't want to define a variable if this + // resolves to an existing item. + Ok(resolution) => self.path_resolution_to_constructor( + resolution, + None, + args, + expected_type, + location, + variables_defined, + ), Err(error) => { self.push_err(error); let id = self.fresh_match_variable(expected_type.clone(), location); @@ -426,38 +530,63 @@ impl Elaborator<'_> { } } ExpressionKind::Parenthesized(expr) => { - self.expression_to_constructor(*expr, args, expected_type) + self.expression_to_constructor(*expr, args, expected_type, variables_defined) } ExpressionKind::Interned(id) => { let kind = self.interner.get_expression_kind(id); - let expr = Expression::new(kind.clone(), name.span); - self.expression_to_constructor(expr, args, expected_type) + let expr = Expression::new(kind.clone(), name.location); + self.expression_to_constructor(expr, args, expected_type, variables_defined) } ExpressionKind::InternedStatement(id) => { if let StatementKind::Expression(expr) = self.interner.get_statement_kind(id) { - self.expression_to_constructor(expr.clone(), args, expected_type) + self.expression_to_constructor( + expr.clone(), + args, + expected_type, + variables_defined, + ) } else { - panic!("Invalid expr kind {name}") + syntax_error(self) } } - other => todo!("invalid constructor `{other}`"), + _ => syntax_error(self), } } + /// Convert a PathResolutionItem - usually an enum variant or global - to a Constructor. + /// If `name` is `Some`, we'll define a Pattern::Binding instead of erroring if the + /// item doesn't resolve to a variant or global. This would shadow an existing + /// value such as a free function. Generally this is desired unless the variable was + /// a path with multiple components such as `foo::bar` which should always be treated as + /// a path to an existing item. fn path_resolution_to_constructor( &mut self, - name: PathResolutionItem, + resolution: PathResolutionItem, + name: Option, args: Vec, expected_type: &Type, - span: Span, + location: Location, + variables_defined: &mut Vec, ) -> Pattern { - let (actual_type, expected_arg_types, variant_index) = match name { + let (actual_type, expected_arg_types, variant_index) = match &resolution { PathResolutionItem::Global(id) => { // variant constant - let global = self.interner.get_global(id); - let variant_index = match global.value { - GlobalValue::Resolved(Value::Enum(tag, ..)) => tag, - _ => todo!("Value is not an enum constant"), + self.elaborate_global_if_unresolved(id); + let global = self.interner.get_global(*id); + let variant_index = match &global.value { + GlobalValue::Resolved(Value::Enum(tag, ..)) => *tag, + // This may be a global constant. Treat it like a normal constant + GlobalValue::Resolved(value) => { + let value = value.clone(); + return self.global_constant_to_integer_constructor( + value, + expected_type, + location, + ); + } + // We tried to resolve this value above so there must have been an error + // in doing so. Avoid reporting an additional error. + _ => return Pattern::Error, }; let global_type = self.interner.definition_type(global.definition_id); @@ -466,8 +595,12 @@ impl Elaborator<'_> { } PathResolutionItem::Method(_type_id, _type_turbofish, func_id) => { // TODO(#7430): Take type_turbofish into account when instantiating the function's type - let meta = self.interner.function_meta(&func_id); - let Some(variant_index) = meta.enum_variant_index else { todo!("not a variant") }; + let meta = self.interner.function_meta(func_id); + let Some(variant_index) = meta.enum_variant_index else { + let item = resolution.description(); + self.push_err(ResolverError::UnexpectedItemInPattern { location, item }); + return Pattern::Error; + }; let (actual_type, expected_arg_types) = match meta.typ.instantiate(self.interner).0 { @@ -477,18 +610,22 @@ impl Elaborator<'_> { (actual_type, expected_arg_types, variant_index) } - PathResolutionItem::Module(_) => todo!("path_resolution_to_constructor {name:?}"), - PathResolutionItem::Type(_) => todo!("path_resolution_to_constructor {name:?}"), - PathResolutionItem::TypeAlias(_) => todo!("path_resolution_to_constructor {name:?}"), - PathResolutionItem::Trait(_) => todo!("path_resolution_to_constructor {name:?}"), - PathResolutionItem::ModuleFunction(_) => { - todo!("path_resolution_to_constructor {name:?}") - } - PathResolutionItem::TypeAliasFunction(_, _, _) => { - todo!("path_resolution_to_constructor {name:?}") - } - PathResolutionItem::TraitFunction(_, _, _) => { - todo!("path_resolution_to_constructor {name:?}") + PathResolutionItem::Module(_) + | PathResolutionItem::Type(_) + | PathResolutionItem::TypeAlias(_) + | PathResolutionItem::Trait(_) + | PathResolutionItem::ModuleFunction(_) + | PathResolutionItem::TypeAliasFunction(_, _, _) + | PathResolutionItem::TraitFunction(_, _, _) => { + // This variable refers to an existing item + if let Some(name) = name { + // If name is set, shadow the existing item + return self.define_pattern_variable(name, expected_type, variables_defined); + } else { + let item = resolution.description(); + self.push_err(ResolverError::UnexpectedItemInPattern { location, item }); + return Pattern::Error; + } } }; @@ -497,21 +634,89 @@ impl Elaborator<'_> { self.unify(&actual_type, expected_type, || TypeCheckError::TypeMismatch { expected_typ: expected_type.to_string(), expr_typ: actual_type.to_string(), - expr_span: span, + expr_location: location, }); if args.len() != expected_arg_types.len() { - // error expected N args, found M? + let expected = expected_arg_types.len(); + let found = args.len(); + self.push_err(TypeCheckError::ArityMisMatch { expected, found, location }); + return Pattern::Error; } let args = args.into_iter().zip(expected_arg_types); let args = vecmap(args, |(arg, expected_arg_type)| { - self.expression_to_pattern(arg, &expected_arg_type) + self.expression_to_pattern(arg, &expected_arg_type, variables_defined) }); let constructor = Constructor::Variant(actual_type, variant_index); Pattern::Constructor(constructor, args) } + fn global_constant_to_integer_constructor( + &mut self, + constant: Value, + expected_type: &Type, + location: Location, + ) -> Pattern { + let actual_type = constant.get_type(); + self.unify(&actual_type, expected_type, || TypeCheckError::TypeMismatch { + expected_typ: expected_type.to_string(), + expr_typ: actual_type.to_string(), + expr_location: location, + }); + + // Convert a signed integer type like i32 to SignedField + macro_rules! signed_to_signed_field { + ($value:expr) => {{ + let negative = $value < 0; + // Widen the value so that SignedType::MIN does not wrap to 0 when negated below + let mut widened = $value as i128; + if negative { + widened = -widened; + } + SignedField::new(widened.into(), negative) + }}; + } + + let value = match constant { + Value::Bool(value) => SignedField::positive(value), + Value::Field(value) => SignedField::positive(value), + Value::I8(value) => signed_to_signed_field!(value), + Value::I16(value) => signed_to_signed_field!(value), + Value::I32(value) => signed_to_signed_field!(value), + Value::I64(value) => signed_to_signed_field!(value), + Value::U1(value) => SignedField::positive(value), + Value::U8(value) => SignedField::positive(value as u128), + Value::U16(value) => SignedField::positive(value as u128), + Value::U32(value) => SignedField::positive(value), + Value::U64(value) => SignedField::positive(value), + Value::U128(value) => SignedField::positive(value), + Value::Zeroed(_) => SignedField::positive(0u32), + _ => { + self.push_err(ResolverError::NonIntegerGlobalUsedInPattern { location }); + return Pattern::Error; + } + }; + + Pattern::Int(value) + } + + fn struct_name_and_field_types( + &mut self, + typ: &Type, + location: Location, + ) -> Option<(Ident, Vec<(String, Type)>)> { + if let Type::DataType(typ, generics) = typ.follow_bindings_shallow().as_ref() { + if let Some(fields) = typ.borrow().get_fields(generics) { + return Some((typ.borrow().name.clone(), fields)); + } + } + + let error = ResolverError::NonStructUsedInConstructor { typ: typ.to_string(), location }; + self.push_err(error); + None + } + /// Compiles the rows of a match expression, outputting a decision tree for the match. /// /// This is an adaptation of https://github.com/yorickpeterse/pattern-matching-in-rust/tree/main/jacobs2021 @@ -532,7 +737,7 @@ impl Elaborator<'_> { self.push_tests_against_bare_variables(&mut rows); // If the first row is a match-all we match it and the remaining rows are ignored. - if rows.first().map_or(false, |row| row.columns.is_empty()) { + if rows.first().is_some_and(|row| row.columns.is_empty()) { let row = rows.remove(0); return Ok(match row.guard { @@ -557,8 +762,6 @@ impl Elaborator<'_> { Ok(HirMatch::Switch(branch_var, cases, Some(fallback))) } - Type::Array(_, _) => todo!(), - Type::Slice(_) => todo!(), Type::Bool => { let cases = vec![ (Constructor::False, Vec::new(), Vec::new()), @@ -609,12 +812,16 @@ impl Elaborator<'_> { } else { drop(def); let typ = Type::DataType(type_def, generics); - todo!("Cannot match on type {typ}") + Err(ResolverError::TypeUnsupportedInMatch { typ, location }) } } - typ @ (Type::Alias(_, _) - | Type::TypeVariable(_) + // We could match on these types in the future + typ @ (Type::Array(_, _) + | Type::Slice(_) | Type::String(_) + // But we'll never be able to match on these + | Type::Alias(_, _) + | Type::TypeVariable(_) | Type::FmtString(_, _) | Type::TraitAsType(_, _, _) | Type::NamedGeneric(_, _) @@ -625,7 +832,9 @@ impl Elaborator<'_> { | Type::Constant(_, _) | Type::Quoted(_) | Type::InfixExpr(_, _, _, _) - | Type::Error) => todo!("Cannot match on type {typ:?}"), + | Type::Error) => { + Err(ResolverError::TypeUnsupportedInMatch { typ, location }) + }, } } @@ -663,10 +872,9 @@ impl Elaborator<'_> { let (key, cons) = match col.pattern { Pattern::Int(val) => ((val, val), Constructor::Int(val)), Pattern::Range(start, stop) => ((start, stop), Constructor::Range(start, stop)), - pattern => { - eprintln!("Unexpected pattern for integer type: {pattern:?}"); - continue; - } + // Any other pattern shouldn't have an integer type and we expect a type + // check error to already have been issued. + _ => continue, }; if let Some(index) = tested.get(&key) { @@ -721,6 +929,7 @@ impl Elaborator<'_> { /// /// Types with infinite constructors (e.g. int and string) are handled /// separately; they don't need most of this work anyway. + #[allow(clippy::type_complexity)] fn compile_constructor_cases( &mut self, rows: Vec, @@ -845,7 +1054,7 @@ impl Elaborator<'_> { let rhs = HirExpression::Ident(HirIdent::non_trait_method(rhs, location), None); let rhs = self.interner.push_expr(rhs); self.interner.push_expr_type(rhs, rhs_type); - self.interner.push_expr_location(rhs, location.span, location.file); + self.interner.push_expr_location(rhs, location); let let_ = HirStatement::Let(HirLetStatement { pattern: HirPattern::Identifier(variable), @@ -860,13 +1069,13 @@ impl Elaborator<'_> { let let_ = self.interner.push_stmt(let_); let body = self.interner.push_stmt(HirStatement::Expression(body)); - self.interner.push_stmt_location(let_, location.span, location.file); - self.interner.push_stmt_location(body, location.span, location.file); + self.interner.push_stmt_location(let_, location); + self.interner.push_stmt_location(body, location); let block = HirExpression::Block(HirBlockExpression { statements: vec![let_, body] }); let block = self.interner.push_expr(block); self.interner.push_expr_type(block, body_type); - self.interner.push_expr_location(block, location.span, location.file); + self.interner.push_expr_location(block, location); block } } @@ -890,6 +1099,11 @@ enum Pattern { /// 1 <= n < 20. #[allow(unused)] Range(SignedField, SignedField), + + /// An error occurred while translating this pattern. This Pattern kind always translates + /// to a Fail branch in the decision tree, although the compiler is expected to halt + /// with errors before execution. + Error, } #[derive(Clone)] 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 3b25f85a25c2..17f256715183 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -1,29 +1,32 @@ use acvm::{AcirField, FieldElement}; use iter_extended::vecmap; -use noirc_errors::{Location, Span, Spanned}; +use noirc_errors::{Located, Location}; use rustc_hash::FxHashSet as HashSet; use crate::{ + DataType, Kind, QuotedType, Shared, Type, ast::{ - ArrayLiteral, BinaryOpKind, BlockExpression, CallExpression, CastExpression, + ArrayLiteral, AsTraitPath, BinaryOpKind, BlockExpression, CallExpression, CastExpression, ConstrainExpression, ConstrainKind, ConstructorExpression, Expression, ExpressionKind, Ident, IfExpression, IndexExpression, InfixExpression, ItemVisibility, Lambda, Literal, MatchExpression, MemberAccessExpression, MethodCallExpression, Path, PathSegment, - PrefixExpression, StatementKind, UnaryOp, UnresolvedTypeData, UnresolvedTypeExpression, + PrefixExpression, StatementKind, TraitBound, UnaryOp, UnresolvedTraitConstraint, + UnresolvedTypeData, UnresolvedTypeExpression, UnsafeExpression, }, hir::{ comptime::{self, InterpreterError}, + def_collector::dc_crate::CompilationError, resolution::{ errors::ResolverError, import::PathResolutionError, visibility::method_call_is_visible, }, - type_check::{generics::TraitGenerics, TypeCheckError}, + type_check::{TypeCheckError, generics::TraitGenerics}, }, hir_def::{ expr::{ HirArrayLiteral, HirBinaryOp, HirBlockExpression, HirCallExpression, HirCastExpression, HirConstrainExpression, HirConstructorExpression, HirExpression, HirIdent, HirIfExpression, HirIndexExpression, HirInfixExpression, HirLambda, HirLiteral, - HirMemberAccess, HirMethodCallExpression, HirPrefixExpression, + HirMemberAccess, HirMethodCallExpression, HirPrefixExpression, ImplKind, TraitMethod, }, stmt::{HirLetStatement, HirPattern, HirStatement}, traits::{ResolvedTraitBound, TraitConstraint}, @@ -32,12 +35,11 @@ use crate::{ DefinitionId, DefinitionKind, ExprId, FuncId, InternedStatementKind, StmtId, TraitMethodId, }, token::{FmtStrFragment, Tokens}, - DataType, Kind, QuotedType, Shared, Type, }; use super::{Elaborator, LambdaContext, UnsafeBlockStatus}; -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { pub(crate) fn elaborate_expression(&mut self, expr: Expression) -> (ExprId, Type) { self.elaborate_expression_with_target_type(expr, None) } @@ -48,58 +50,57 @@ impl<'context> Elaborator<'context> { target_type: Option<&Type>, ) -> (ExprId, Type) { let (hir_expr, typ) = match expr.kind { - ExpressionKind::Literal(literal) => self.elaborate_literal(literal, expr.span), + ExpressionKind::Literal(literal) => self.elaborate_literal(literal, expr.location), ExpressionKind::Block(block) => self.elaborate_block(block, target_type), - ExpressionKind::Prefix(prefix) => return self.elaborate_prefix(*prefix, expr.span), + ExpressionKind::Prefix(prefix) => return self.elaborate_prefix(*prefix, expr.location), 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), + ExpressionKind::Call(call) => self.elaborate_call(*call, expr.location), + ExpressionKind::MethodCall(call) => self.elaborate_method_call(*call, expr.location), ExpressionKind::Constrain(constrain) => self.elaborate_constrain(constrain), ExpressionKind::Constructor(constructor) => self.elaborate_constructor(*constructor), ExpressionKind::MemberAccess(access) => { - return self.elaborate_member_access(*access, expr.span) + return self.elaborate_member_access(*access, expr.location); } - ExpressionKind::Cast(cast) => self.elaborate_cast(*cast, expr.span), - ExpressionKind::Infix(infix) => return self.elaborate_infix(*infix, expr.span), + ExpressionKind::Cast(cast) => self.elaborate_cast(*cast, expr.location), + ExpressionKind::Infix(infix) => return self.elaborate_infix(*infix, expr.location), ExpressionKind::If(if_) => self.elaborate_if(*if_, target_type), - ExpressionKind::Match(match_) => self.elaborate_match(*match_, expr.span), + ExpressionKind::Match(match_) => self.elaborate_match(*match_, expr.location), ExpressionKind::Variable(variable) => return self.elaborate_variable(variable), ExpressionKind::Tuple(tuple) => self.elaborate_tuple(tuple, target_type), ExpressionKind::Lambda(lambda) => { self.elaborate_lambda_with_target_type(*lambda, target_type) } ExpressionKind::Parenthesized(expr) => { - return self.elaborate_expression_with_target_type(*expr, target_type) + return self.elaborate_expression_with_target_type(*expr, target_type); } - ExpressionKind::Quote(quote) => self.elaborate_quote(quote, expr.span), + ExpressionKind::Quote(quote) => self.elaborate_quote(quote, expr.location), ExpressionKind::Comptime(comptime, _) => { - return self.elaborate_comptime_block(comptime, expr.span, target_type) + return self.elaborate_comptime_block(comptime, expr.location, target_type); } - ExpressionKind::Unsafe(block_expression, span) => { - self.elaborate_unsafe_block(block_expression, span, target_type) + ExpressionKind::Unsafe(unsafe_expression) => { + self.elaborate_unsafe_block(unsafe_expression, target_type) } ExpressionKind::Resolved(id) => return (id, self.interner.id_type(id)), ExpressionKind::Interned(id) => { let expr_kind = self.interner.get_expression_kind(id); - let expr = Expression::new(expr_kind.clone(), expr.span); + let expr = Expression::new(expr_kind.clone(), expr.location); return self.elaborate_expression(expr); } ExpressionKind::InternedStatement(id) => { - return self.elaborate_interned_statement_as_expr(id, expr.span); + return self.elaborate_interned_statement_as_expr(id, expr.location); } ExpressionKind::Error => (HirExpression::Error, Type::Error), ExpressionKind::Unquote(_) => { - self.push_err(ResolverError::UnquoteUsedOutsideQuote { span: expr.span }); + self.push_err(ResolverError::UnquoteUsedOutsideQuote { location: expr.location }); (HirExpression::Error, Type::Error) } - ExpressionKind::AsTraitPath(_) => { - self.push_err(ResolverError::AsTraitPathNotYetImplemented { span: expr.span }); - (HirExpression::Error, Type::Error) + ExpressionKind::AsTraitPath(path) => { + return self.elaborate_as_trait_path(path); } ExpressionKind::TypePath(path) => return self.elaborate_type_path(path), }; let id = self.interner.push_expr(hir_expr); - self.interner.push_expr_location(id, expr.span, self.file); + self.interner.push_expr_location(id, expr.location); self.interner.push_expr_type(id, typ.clone()); (id, typ) } @@ -107,21 +108,24 @@ impl<'context> Elaborator<'context> { fn elaborate_interned_statement_as_expr( &mut self, id: InternedStatementKind, - span: Span, + location: Location, ) -> (ExprId, Type) { match self.interner.get_statement_kind(id) { StatementKind::Expression(expr) | StatementKind::Semi(expr) => { self.elaborate_expression(expr.clone()) } - StatementKind::Interned(id) => self.elaborate_interned_statement_as_expr(*id, span), + StatementKind::Interned(id) => self.elaborate_interned_statement_as_expr(*id, location), StatementKind::Error => { - let expr = Expression::new(ExpressionKind::Error, span); + let expr = Expression::new(ExpressionKind::Error, location); self.elaborate_expression(expr) } other => { let statement = other.to_string(); - self.push_err(ResolverError::InvalidInternedStatementInExpr { statement, span }); - let expr = Expression::new(ExpressionKind::Error, span); + self.push_err(ResolverError::InvalidInternedStatementInExpr { + statement, + location, + }); + let expr = Expression::new(ExpressionKind::Error, location); self.elaborate_expression(expr) } } @@ -154,11 +158,11 @@ impl<'context> Elaborator<'context> { if let HirStatement::Semi(expr) = self.interner.statement(&id) { let inner_expr_type = self.interner.id_type(expr); - let span = self.interner.expr_span(&expr); + let location = self.interner.expr_location(&expr); self.unify(&inner_expr_type, &Type::Unit, || TypeCheckError::UnusedResultError { expr_type: inner_expr_type.clone(), - expr_span: span, + expr_location: location, }); } @@ -173,8 +177,7 @@ impl<'context> Elaborator<'context> { fn elaborate_unsafe_block( &mut self, - block: BlockExpression, - span: Span, + unsafe_expression: UnsafeExpression, target_type: Option<&Type>, ) -> (HirExpression, Type) { // Before entering the block we cache the old value of `in_unsafe_block` so it can be restored. @@ -182,18 +185,21 @@ impl<'context> Elaborator<'context> { let is_nested_unsafe_block = !matches!(old_in_unsafe_block, UnsafeBlockStatus::NotInUnsafeBlock); if is_nested_unsafe_block { - let span = Span::from(span.start()..span.start() + 6); // Only highlight the `unsafe` keyword - self.push_err(TypeCheckError::NestedUnsafeBlock { span }); + self.push_err(TypeCheckError::NestedUnsafeBlock { + location: unsafe_expression.unsafe_keyword_location, + }); } self.unsafe_block_status = UnsafeBlockStatus::InUnsafeBlockWithoutUnconstrainedCalls; - let (hir_block_expression, typ) = self.elaborate_block_expression(block, target_type); + let (hir_block_expression, typ) = + self.elaborate_block_expression(unsafe_expression.block, target_type); if let UnsafeBlockStatus::InUnsafeBlockWithoutUnconstrainedCalls = self.unsafe_block_status { - let span = Span::from(span.start()..span.start() + 6); // Only highlight the `unsafe` keyword - self.push_err(TypeCheckError::UnnecessaryUnsafeBlock { span }); + self.push_err(TypeCheckError::UnnecessaryUnsafeBlock { + location: unsafe_expression.unsafe_keyword_location, + }); } // Finally, we restore the original value of `self.in_unsafe_block`, @@ -206,14 +212,13 @@ impl<'context> Elaborator<'context> { (HirExpression::Unsafe(hir_block_expression), typ) } - fn elaborate_literal(&mut self, literal: Literal, span: Span) -> (HirExpression, Type) { + fn elaborate_literal(&mut self, literal: Literal, location: Location) -> (HirExpression, Type) { use HirExpression::Literal as Lit; match literal { Literal::Unit => (Lit(HirLiteral::Unit), Type::Unit), Literal::Bool(b) => (Lit(HirLiteral::Bool(b)), Type::Bool), - Literal::Integer(integer, sign) => { - let int = HirLiteral::Integer(integer, sign); - (Lit(int), self.polymorphic_integer_or_field()) + Literal::Integer(integer) => { + (Lit(HirLiteral::Integer(integer)), self.polymorphic_integer_or_field()) } Literal::Str(str) | Literal::RawStr(str, _) => { let len = Type::Constant(str.len().into(), Kind::u32()); @@ -221,10 +226,10 @@ impl<'context> Elaborator<'context> { } Literal::FmtStr(fragments, length) => self.elaborate_fmt_string(fragments, length), Literal::Array(array_literal) => { - self.elaborate_array_literal(array_literal, span, true) + self.elaborate_array_literal(array_literal, location, true) } Literal::Slice(array_literal) => { - self.elaborate_array_literal(array_literal, span, false) + self.elaborate_array_literal(array_literal, location, false) } } } @@ -232,24 +237,24 @@ impl<'context> Elaborator<'context> { fn elaborate_array_literal( &mut self, array_literal: ArrayLiteral, - span: Span, + location: Location, is_array: bool, ) -> (HirExpression, Type) { let (expr, elem_type, length) = match array_literal { ArrayLiteral::Standard(elements) => { let first_elem_type = self.interner.next_type_variable(); - let first_span = elements.first().map(|elem| elem.span).unwrap_or(span); + let first_location = elements.first().map(|elem| elem.location).unwrap_or(location); let elements = vecmap(elements.into_iter().enumerate(), |(i, elem)| { - let span = elem.span; + let location = elem.location; let (elem_id, elem_type) = self.elaborate_expression(elem); self.unify(&elem_type, &first_elem_type, || { TypeCheckError::NonHomogeneousArray { - first_span, + first_location, first_type: first_elem_type.to_string(), first_index: 0, - second_span: span, + second_location: location, second_type: elem_type.to_string(), second_index: i, } @@ -262,14 +267,15 @@ impl<'context> Elaborator<'context> { (HirArrayLiteral::Standard(elements), first_elem_type, length) } ArrayLiteral::Repeated { repeated_element, length } => { - let span = length.span; - let length = - UnresolvedTypeExpression::from_expr(*length, span).unwrap_or_else(|error| { + let location = length.location; + let length = UnresolvedTypeExpression::from_expr(*length, location).unwrap_or_else( + |error| { self.push_err(ResolverError::ParserError(Box::new(error))); - UnresolvedTypeExpression::Constant(FieldElement::zero(), span) - }); + UnresolvedTypeExpression::Constant(FieldElement::zero(), location) + }, + ); - let length = self.convert_expression_type(length, &Kind::u32(), span); + let length = self.convert_expression_type(length, &Kind::u32(), location); let (repeated_element, elem_type) = self.elaborate_expression(*repeated_element); let length_clone = length.clone(); @@ -295,7 +301,7 @@ impl<'context> Elaborator<'context> { let mut capture_types = Vec::new(); for fragment in &fragments { - if let FmtStrFragment::Interpolation(ident_name, string_span) = fragment { + if let FmtStrFragment::Interpolation(ident_name, location) = fragment { let scope_tree = self.scopes.current_scope_tree(); let variable = scope_tree.find(ident_name); @@ -303,23 +309,20 @@ impl<'context> Elaborator<'context> { old_value.num_times_used += 1; old_value.ident.clone() } else if let Ok((definition_id, _)) = - self.lookup_global(Path::from_single(ident_name.to_string(), *string_span)) + self.lookup_global(Path::from_single(ident_name.to_string(), *location)) { - HirIdent::non_trait_method( - definition_id, - Location::new(*string_span, self.file), - ) + HirIdent::non_trait_method(definition_id, *location) } else { self.push_err(ResolverError::VariableNotDeclared { name: ident_name.to_owned(), - span: *string_span, + location: *location, }); continue; }; let hir_expr = HirExpression::Ident(hir_ident.clone(), None); let expr_id = self.interner.push_expr(hir_expr); - self.interner.push_expr_location(expr_id, *string_span, self.file); + self.interner.push_expr_location(expr_id, *location); let typ = self.type_check_variable(hir_ident, expr_id, None); self.interner.push_expr_type(expr_id, typ.clone()); capture_types.push(typ); @@ -332,8 +335,8 @@ impl<'context> Elaborator<'context> { (HirExpression::Literal(HirLiteral::FmtStr(fragments, fmt_str_idents, length)), typ) } - fn elaborate_prefix(&mut self, prefix: PrefixExpression, span: Span) -> (ExprId, Type) { - let rhs_span = prefix.rhs.span; + fn elaborate_prefix(&mut self, prefix: PrefixExpression, location: Location) -> (ExprId, Type) { + let rhs_location = prefix.rhs.location; let (rhs, rhs_type) = self.elaborate_expression(prefix.rhs); let trait_id = self.interner.get_prefix_operator_trait_method(&prefix.operator); @@ -341,55 +344,81 @@ impl<'context> Elaborator<'context> { let operator = prefix.operator; if let UnaryOp::MutableReference = operator { - self.check_can_mutate(rhs, rhs_span); + self.check_can_mutate(rhs, rhs_location); } let expr = HirExpression::Prefix(HirPrefixExpression { operator, rhs, trait_method_id: trait_id }); let expr_id = self.interner.push_expr(expr); - self.interner.push_expr_location(expr_id, span, self.file); + self.interner.push_expr_location(expr_id, location); - let result = self.prefix_operand_type_rules(&operator, &rhs_type, span); - let typ = self.handle_operand_type_rules_result(result, &rhs_type, trait_id, expr_id, span); + let result = self.prefix_operand_type_rules(&operator, &rhs_type, location); + let typ = + self.handle_operand_type_rules_result(result, &rhs_type, trait_id, expr_id, location); self.interner.push_expr_type(expr_id, typ.clone()); (expr_id, typ) } - fn check_can_mutate(&mut self, expr_id: ExprId, span: Span) { + pub(super) fn check_can_mutate(&mut self, expr_id: ExprId, location: Location) { let expr = self.interner.expression(&expr_id); match expr { HirExpression::Ident(hir_ident, _) => { if let Some(definition) = self.interner.try_definition(hir_ident.id) { + let name = definition.name.clone(); if !definition.mutable { self.push_err(TypeCheckError::CannotMutateImmutableVariable { - name: definition.name.clone(), - span, + name, + location, }); + } else { + self.check_can_mutate_lambda_capture(hir_ident.id, name, location); } } } + HirExpression::Index(_) => { + self.push_err(TypeCheckError::MutableReferenceToArrayElement { location }); + } HirExpression::MemberAccess(member_access) => { - self.check_can_mutate(member_access.lhs, span); + self.check_can_mutate(member_access.lhs, location); } _ => (), } } + // We must check whether the mutable variable we are attempting to mutate + // comes from a lambda capture. All captures are immutable so we want to error + // if the user attempts to mutate a captured variable inside of a lambda without mutable references. + pub(super) fn check_can_mutate_lambda_capture( + &mut self, + id: DefinitionId, + name: String, + location: Location, + ) { + if let Some(lambda_context) = self.lambda_stack.last() { + let typ = self.interner.definition_type(id); + if !typ.is_mutable_ref() && lambda_context.captures.iter().any(|var| var.ident.id == id) + { + self.push_err(TypeCheckError::MutableCaptureWithoutRef { name, location }); + } + } + } + fn elaborate_index(&mut self, index_expr: IndexExpression) -> (HirExpression, Type) { - let span = index_expr.index.span; + let location = index_expr.index.location; + let (index, index_type) = self.elaborate_expression(index_expr.index); let expected = self.polymorphic_integer_or_field(); self.unify(&index_type, &expected, || TypeCheckError::TypeMismatch { expected_typ: "an integer".to_owned(), expr_typ: index_type.to_string(), - expr_span: span, + expr_location: location, }); // When writing `a[i]`, if `a : &mut ...` then automatically dereference `a` as many // times as needed to get the underlying array. - let lhs_span = index_expr.collection.span; + let lhs_location = index_expr.collection.location; let (lhs, lhs_type) = self.elaborate_expression(index_expr.collection); let (collection, lhs_type) = self.insert_auto_dereferences(lhs, lhs_type); @@ -400,14 +429,16 @@ impl<'context> Elaborator<'context> { Type::Slice(base_type) => *base_type, Type::Error => Type::Error, Type::TypeVariable(_) => { - self.push_err(TypeCheckError::TypeAnnotationsNeededForIndex { span: lhs_span }); + self.push_err(TypeCheckError::TypeAnnotationsNeededForIndex { + location: lhs_location, + }); Type::Error } typ => { self.push_err(TypeCheckError::TypeMismatch { expected_typ: "Array".to_owned(), expr_typ: typ.to_string(), - expr_span: lhs_span, + expr_location: lhs_location, }); Type::Error } @@ -417,7 +448,11 @@ impl<'context> Elaborator<'context> { (expr, typ) } - fn elaborate_call(&mut self, call: CallExpression, span: Span) -> (HirExpression, Type) { + fn elaborate_call( + &mut self, + call: CallExpression, + location: Location, + ) -> (HirExpression, Type) { let (func, func_type) = self.elaborate_expression(*call.func); let func_type = func_type.follow_bindings(); let func_arg_types = @@ -425,7 +460,7 @@ impl<'context> Elaborator<'context> { let mut arguments = Vec::with_capacity(call.arguments.len()); let args = vecmap(call.arguments.into_iter().enumerate(), |(arg_index, arg)| { - let span = arg.span; + let location = arg.location; let expected_type = func_arg_types.and_then(|args| args.get(arg_index)); let (arg, typ) = if call.is_macro_call { @@ -443,7 +478,7 @@ impl<'context> Elaborator<'context> { } arguments.push(arg); - (typ, arg, span) + (typ, arg, location) }); // Avoid cloning arguments unless this is a macro call @@ -452,10 +487,9 @@ impl<'context> Elaborator<'context> { comptime_args = arguments.clone(); } - let location = Location::new(span, self.file); let is_macro_call = call.is_macro_call; let hir_call = HirCallExpression { func, arguments, location, is_macro_call }; - let mut typ = self.type_check_call(&hir_call, func_type, args, span); + let mut typ = self.type_check_call(&hir_call, func_type, args, location); if is_macro_call { if self.in_comptime_context() { @@ -463,7 +497,7 @@ impl<'context> Elaborator<'context> { } else { return self .call_macro(func, comptime_args, location, typ) - .unwrap_or_else(|| (HirExpression::Error, Type::Error)); + .unwrap_or((HirExpression::Error, Type::Error)); } } @@ -473,15 +507,16 @@ impl<'context> Elaborator<'context> { fn elaborate_method_call( &mut self, method_call: MethodCallExpression, - span: Span, + location: Location, ) -> (HirExpression, Type) { - let object_span = method_call.object.span; + let object_location = method_call.object.location; let (mut object, mut object_type) = self.elaborate_expression(method_call.object); object_type = object_type.follow_bindings(); - let method_name_span = method_call.method_name.span(); + let method_name_location = method_call.method_name.location(); let method_name = method_call.method_name.0.contents.as_str(); - match self.lookup_method(&object_type, method_name, span, true) { + let check_self_param = true; + match self.lookup_method(&object_type, method_name, location, check_self_param) { Some(method_ref) => { // Automatically add `&mut` if the method expects a mutable reference and // the object is not already one. @@ -497,13 +532,16 @@ impl<'context> Elaborator<'context> { &mut object, ); - self.resolve_function_turbofish_generics(&func_id, method_call.generics, span) + self.resolve_function_turbofish_generics( + &func_id, + method_call.generics, + location, + ) } else { None }; - let call_span = Span::from(object_span.start()..method_name_span.end()); - let location = Location::new(call_span, self.file); + let location = object_location.merge(method_name_location); let (function_id, function_name) = method_ref.clone().into_function_id_and_name( object_type.clone(), @@ -533,10 +571,10 @@ impl<'context> Elaborator<'context> { let mut function_args = Vec::with_capacity(method_call.arguments.len() + 1); let mut arguments = Vec::with_capacity(method_call.arguments.len()); - function_args.push((object_type.clone(), object, object_span)); + function_args.push((object_type.clone(), object, object_location)); for (arg_index, arg) in method_call.arguments.into_iter().enumerate() { - let span = arg.span; + let location = arg.location; let expected_type = func_arg_types.and_then(|args| args.get(arg_index + 1)); let (arg, typ) = self.elaborate_expression_with_type(arg, expected_type); @@ -547,7 +585,7 @@ impl<'context> Elaborator<'context> { } arguments.push(arg); - function_args.push((typ, arg, span)); + function_args.push((typ, arg, location)); } let method = method_call.method_name; @@ -564,12 +602,12 @@ impl<'context> Elaborator<'context> { let function_call = method_call.into_function_call(function_id, is_macro_call, location); - self.interner - .add_function_reference(func_id, Location::new(method_name_span, self.file)); + self.interner.add_function_reference(func_id, method_name_location); // Type check the new call now that it has been changed from a method call // to a function call. This way we avoid duplicating code. - let mut typ = self.type_check_call(&function_call, func_type, function_args, span); + let mut typ = + self.type_check_call(&function_call, func_type, function_args, location); if is_macro_call { if self.in_comptime_context() { typ = self.interner.next_type_variable(); @@ -577,7 +615,7 @@ impl<'context> Elaborator<'context> { let args = function_call.arguments.clone(); return self .call_macro(function_call.func, args, location, typ) - .unwrap_or_else(|| (HirExpression::Error, Type::Error)); + .unwrap_or((HirExpression::Error, Type::Error)); } } (HirExpression::Call(function_call), typ) @@ -590,7 +628,7 @@ impl<'context> Elaborator<'context> { &mut self, mut expr: ConstrainExpression, ) -> (HirExpression, Type) { - let span = expr.span; + let location = expr.location; let min_args_count = expr.kind.required_arguments_count(); let max_args_count = min_args_count + 1; let actual_args_count = expr.arguments.len(); @@ -599,14 +637,14 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::AssertionParameterCountMismatch { kind: expr.kind, found: actual_args_count, - span, + location, }); // Given that we already produced an error, let's make this an `assert(true)` so // we don't get further errors. let message = None; let kind = ExpressionKind::Literal(crate::ast::Literal::Bool(true)); - let expr = Expression { kind, span }; + let expr = Expression { kind, location }; (message, expr) } else { let message = @@ -616,17 +654,17 @@ impl<'context> Elaborator<'context> { ConstrainKind::AssertEq => { let rhs = expr.arguments.pop().unwrap(); let lhs = expr.arguments.pop().unwrap(); - let span = Span::from(lhs.span.start()..rhs.span.end()); - let operator = Spanned::from(span, BinaryOpKind::Equal); + let location = lhs.location.merge(rhs.location); + let operator = Located::from(location, BinaryOpKind::Equal); let kind = ExpressionKind::Infix(Box::new(InfixExpression { lhs, operator, rhs })); - Expression { kind, span } + Expression { kind, location } } }; (message, expr) }; - let expr_span = expr.span; + let expr_location = expr.location; let (expr_id, expr_type) = self.elaborate_expression(expr); // Must type check the assertion message expression so that we instantiate bindings @@ -635,10 +673,10 @@ impl<'context> Elaborator<'context> { self.unify(&expr_type, &Type::Bool, || TypeCheckError::TypeMismatch { expr_typ: expr_type.to_string(), expected_typ: Type::Bool.to_string(), - expr_span, + expr_location, }); - (HirExpression::Constrain(HirConstrainExpression(expr_id, self.file, msg)), Type::Unit) + (HirExpression::Constrain(HirConstrainExpression(expr_id, location.file, msg)), Type::Unit) } /// Elaborates an expression knowing that it has to match a given type. @@ -651,12 +689,12 @@ impl<'context> Elaborator<'context> { return self.elaborate_expression(arg); }; - let span = arg.span; + let location = arg.location; let type_hint = if let Some(Type::Function(func_args, _, _, _)) = typ { Some(func_args) } else { None }; let (hir_expr, typ) = self.elaborate_lambda_with_parameter_type_hints(*lambda, type_hint); let id = self.interner.push_expr(hir_expr); - self.interner.push_expr_location(id, span, self.file); + self.interner.push_expr_location(id, location); self.interner.push_expr_type(id, typ.clone()); (id, typ) } @@ -679,7 +717,7 @@ impl<'context> Elaborator<'context> { &mut self, constructor: ConstructorExpression, ) -> (HirExpression, Type) { - let span = constructor.typ.span; + let location = constructor.typ.location; // A constructor type can either be a Path or an interned UnresolvedType. // We represent both as UnresolvedType (with Path being a Named UnresolvedType) @@ -692,10 +730,18 @@ impl<'context> Elaborator<'context> { // If this type is already resolved we can skip the rest of this function // which just resolves the type, and go straight to resolving the fields. let resolved = self.interner.get_quoted_type(id).clone(); - return self.elaborate_constructor_with_type(resolved, constructor.fields, span, None); + return self.elaborate_constructor_with_type( + resolved, + constructor.fields, + location, + None, + ); } let UnresolvedTypeData::Named(mut path, generics, _) = typ else { - self.push_err(ResolverError::NonStructUsedInConstructor { typ: typ.to_string(), span }); + self.push_err(ResolverError::NonStructUsedInConstructor { + typ: typ.to_string(), + location, + }); return (HirExpression::Error, Type::Error); }; @@ -706,25 +752,18 @@ impl<'context> Elaborator<'context> { let last_segment = path.last_segment(); - let typ = if let Some(struct_id) = constructor.struct_type { - let typ = self.interner.get_type(struct_id); - let generics = typ.borrow().instantiate(self.interner); - Type::DataType(typ, generics) - } else { - match self.lookup_type_or_error(path) { - Some(typ) => typ, - None => return (HirExpression::Error, Type::Error), - } + let Some(typ) = self.lookup_type_or_error(path) else { + return (HirExpression::Error, Type::Error); }; - self.elaborate_constructor_with_type(typ, constructor.fields, span, Some(last_segment)) + self.elaborate_constructor_with_type(typ, constructor.fields, location, Some(last_segment)) } fn elaborate_constructor_with_type( &mut self, typ: Type, fields: Vec<(Ident, Expression)>, - span: Span, + location: Location, last_segment: Option, ) -> (HirExpression, Type) { let typ = typ.follow_bindings_shallow(); @@ -735,7 +774,7 @@ impl<'context> Elaborator<'context> { typ => { self.push_err(ResolverError::NonStructUsedInConstructor { typ: typ.to_string(), - span, + location, }); return (HirExpression::Error, Type::Error); } @@ -745,18 +784,18 @@ impl<'context> Elaborator<'context> { // `last_segment` is optional if this constructor was resolved from a quoted type let mut generics = generics.clone(); let mut is_self_type = false; - let mut constructor_type_span = span; + let mut constructor_type_location = location; if let Some(last_segment) = last_segment { - let turbofish_span = last_segment.turbofish_span(); + let turbofish_location = last_segment.turbofish_location(); is_self_type = last_segment.ident.is_self_type_name(); - constructor_type_span = last_segment.ident.span(); + constructor_type_location = last_segment.ident.location(); generics = self.resolve_struct_turbofish_generics( &r#type.borrow(), generics, last_segment.generics, - turbofish_span, + turbofish_location, ); } @@ -767,8 +806,12 @@ impl<'context> Elaborator<'context> { .get_fields_with_visibility(&generics) .expect("This type should already be validated to be a struct"); - let fields = - self.resolve_constructor_expr_fields(struct_type.clone(), field_types, fields, span); + let fields = self.resolve_constructor_expr_fields( + struct_type.clone(), + field_types, + fields, + location, + ); let expr = HirExpression::Constructor(HirConstructorExpression { fields, r#type: struct_type.clone(), @@ -776,8 +819,7 @@ impl<'context> Elaborator<'context> { }); let struct_id = struct_type.borrow().id; - let reference_location = Location::new(constructor_type_span, self.file); - self.interner.add_type_reference(struct_id, reference_location, is_self_type); + self.interner.add_type_reference(struct_id, constructor_type_location, is_self_type); (expr, Type::DataType(struct_type, generics)) } @@ -796,7 +838,7 @@ impl<'context> Elaborator<'context> { struct_type: Shared, field_types: Vec<(String, ItemVisibility, Type)>, fields: Vec<(Ident, Expression)>, - span: Span, + location: Location, ) -> Vec<(Ident, ExprId)> { let mut ret = Vec::with_capacity(fields.len()); let mut seen_fields = HashSet::default(); @@ -815,20 +857,24 @@ impl<'context> Elaborator<'context> { let expected_type = expected_field_with_index.map(|(_, (_, _, typ))| typ).unwrap_or(&Type::Error); - let field_span = field.span; + let field_location = field.location; let (resolved, field_type) = self.elaborate_expression(field); if unseen_fields.contains(&field_name) { unseen_fields.remove(&field_name); seen_fields.insert(field_name.clone()); - self.unify_with_coercions(&field_type, expected_type, resolved, field_span, || { - TypeCheckError::TypeMismatch { + self.unify_with_coercions( + &field_type, + expected_type, + resolved, + field_location, + || TypeCheckError::TypeMismatch { expected_typ: expected_type.to_string(), expr_typ: field_type.to_string(), - expr_span: field_span, - } - }); + expr_location: field_location, + }, + ); } else if seen_fields.contains(&field_name) { // duplicate field self.push_err(ResolverError::DuplicateField { field: field_name.clone() }); @@ -842,20 +888,16 @@ impl<'context> Elaborator<'context> { if let Some((index, visibility)) = expected_index_and_visibility { let struct_type = struct_type.borrow(); - let field_span = field_name.span(); + let field_location = field_name.location(); let field_name = &field_name.0.contents; self.check_struct_field_visibility( &struct_type, field_name, *visibility, - field_span, + field_location, ); - self.interner.add_struct_member_reference( - struct_type.id, - index, - Location::new(field_span, self.file), - ); + self.interner.add_struct_member_reference(struct_type.id, index, field_location); } ret.push((field_name, resolved)); @@ -863,7 +905,7 @@ impl<'context> Elaborator<'context> { if !unseen_fields.is_empty() { self.push_err(ResolverError::MissingFields { - span, + location, missing_fields: unseen_fields.into_iter().map(|field| field.to_string()).collect(), struct_definition: struct_type.borrow().name.clone(), }); @@ -875,39 +917,44 @@ impl<'context> Elaborator<'context> { fn elaborate_member_access( &mut self, access: MemberAccessExpression, - span: Span, + location: Location, ) -> (ExprId, Type) { let (lhs, lhs_type) = self.elaborate_expression(access.lhs); let rhs = access.rhs; - let rhs_span = rhs.span(); + let rhs_location = rhs.location(); // `is_offset` is only used when lhs is a reference and we want to return a reference to rhs let access = HirMemberAccess { lhs, rhs, is_offset: false }; - let expr_id = self.intern_expr(HirExpression::MemberAccess(access.clone()), span); - let typ = self.type_check_member_access(access, expr_id, lhs_type, rhs_span); + let expr_id = self.intern_expr(HirExpression::MemberAccess(access.clone()), location); + let typ = self.type_check_member_access(access, expr_id, lhs_type, rhs_location); self.interner.push_expr_type(expr_id, typ.clone()); (expr_id, typ) } - pub fn intern_expr(&mut self, expr: HirExpression, span: Span) -> ExprId { + pub fn intern_expr(&mut self, expr: HirExpression, location: Location) -> ExprId { let id = self.interner.push_expr(expr); - self.interner.push_expr_location(id, span, self.file); + self.interner.push_expr_location(id, location); id } - fn elaborate_cast(&mut self, cast: CastExpression, span: Span) -> (HirExpression, Type) { + fn elaborate_cast( + &mut self, + cast: CastExpression, + location: Location, + ) -> (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, &lhs_type, &r#type, span); + let result = self.check_cast(&lhs, &lhs_type, &r#type, location); let expr = HirExpression::Cast(HirCastExpression { lhs, r#type }); (expr, result) } - fn elaborate_infix(&mut self, infix: InfixExpression, span: Span) -> (ExprId, Type) { + fn elaborate_infix(&mut self, infix: InfixExpression, location: Location) -> (ExprId, Type) { let (lhs, lhs_type) = self.elaborate_expression(infix.lhs); let (rhs, rhs_type) = self.elaborate_expression(infix.rhs); let trait_id = self.interner.get_operator_trait_method(infix.operator.contents); - let operator = HirBinaryOp::new(infix.operator, self.file); + let file = infix.operator.location().file; + let operator = HirBinaryOp::new(infix.operator, file); let expr = HirExpression::Infix(HirInfixExpression { lhs, operator, @@ -916,11 +963,16 @@ impl<'context> Elaborator<'context> { }); let expr_id = self.interner.push_expr(expr); - self.interner.push_expr_location(expr_id, span, self.file); + self.interner.push_expr_location(expr_id, location); - let result = self.infix_operand_type_rules(&lhs_type, &operator, &rhs_type, span); - let typ = - self.handle_operand_type_rules_result(result, &lhs_type, Some(trait_id), expr_id, span); + let result = self.infix_operand_type_rules(&lhs_type, &operator, &rhs_type, location); + let typ = self.handle_operand_type_rules_result( + result, + &lhs_type, + Some(trait_id), + expr_id, + location, + ); self.interner.push_expr_type(expr_id, typ.clone()); (expr_id, typ) @@ -932,7 +984,7 @@ impl<'context> Elaborator<'context> { operand_type: &Type, trait_id: Option, expr_id: ExprId, - span: Span, + location: Location, ) -> Type { match result { Ok((typ, use_impl)) => { @@ -948,14 +1000,14 @@ impl<'context> Elaborator<'context> { trait_bound: ResolvedTraitBound { trait_id: trait_id.trait_id, trait_generics: TraitGenerics::default(), - span, + location, }, }; self.push_trait_constraint( constraint, expr_id, true, // this constraint should lead to choosing a trait impl ); - self.type_check_operator_method(expr_id, trait_id, operand_type, span); + self.type_check_operator_method(expr_id, trait_id, operand_type, location); } typ } @@ -971,8 +1023,8 @@ impl<'context> Elaborator<'context> { if_expr: IfExpression, target_type: Option<&Type>, ) -> (HirExpression, Type) { - let expr_span = if_expr.condition.type_span(); - let consequence_span = if_expr.consequence.type_span(); + let expr_location = if_expr.condition.type_location(); + let consequence_location = if_expr.consequence.type_location(); let (condition, cond_type) = self.elaborate_expression(if_expr.condition); let (consequence, mut ret_type) = self.elaborate_expression_with_target_type(if_expr.consequence, target_type); @@ -980,23 +1032,24 @@ impl<'context> Elaborator<'context> { self.unify(&cond_type, &Type::Bool, || TypeCheckError::TypeMismatch { expected_typ: Type::Bool.to_string(), expr_typ: cond_type.to_string(), - expr_span, + expr_location, }); - let (alternative, else_type, error_span) = if let Some(alternative) = if_expr.alternative { - let alternative_span = alternative.type_span(); - let (else_, else_type) = - self.elaborate_expression_with_target_type(alternative, target_type); - (Some(else_), else_type, alternative_span) - } else { - (None, Type::Unit, consequence_span) - }; + let (alternative, else_type, error_location) = + if let Some(alternative) = if_expr.alternative { + let alternative_location = alternative.type_location(); + let (else_, else_type) = + self.elaborate_expression_with_target_type(alternative, target_type); + (Some(else_), else_type, alternative_location) + } else { + (None, Type::Unit, consequence_location) + }; self.unify(&ret_type, &else_type, || { let err = TypeCheckError::TypeMismatch { expected_typ: ret_type.to_string(), expr_typ: else_type.to_string(), - expr_span: error_span, + expr_location: error_location, }; let context = if ret_type == Type::Unit { @@ -1021,8 +1074,10 @@ impl<'context> Elaborator<'context> { fn elaborate_match( &mut self, match_expr: MatchExpression, - span: Span, + location: Location, ) -> (HirExpression, Type) { + self.use_unstable_feature(super::UnstableFeature::Enums, location); + let (expression, typ) = self.elaborate_expression(match_expr.expression); let (let_, variable) = self.wrap_in_let(expression, typ); @@ -1030,10 +1085,10 @@ impl<'context> Elaborator<'context> { let tree = HirExpression::Match(self.elaborate_match_rows(rows)); let tree = self.interner.push_expr(tree); self.interner.push_expr_type(tree, result_type.clone()); - self.interner.push_expr_location(tree, span, self.file); + self.interner.push_expr_location(tree, location); let tree = self.interner.push_stmt(HirStatement::Expression(tree)); - self.interner.push_stmt_location(tree, span, self.file); + self.interner.push_stmt_location(tree, location); let block = HirExpression::Block(HirBlockExpression { statements: vec![let_, tree] }); (block, result_type) @@ -1049,7 +1104,7 @@ impl<'context> Elaborator<'context> { let pattern = HirPattern::Identifier(HirIdent::non_trait_method(variable, location)); let let_ = HirStatement::Let(HirLetStatement::basic(pattern, typ, expr_id)); let let_ = self.interner.push_stmt(let_); - self.interner.push_stmt_location(let_, location.span, location.file); + self.interner.push_stmt_location(let_, location); (let_, variable) } @@ -1121,7 +1176,7 @@ impl<'context> Elaborator<'context> { }); let return_type = self.resolve_inferred_type(lambda.return_type); - let body_span = lambda.body.span; + let body_location = lambda.body.location; let (body, body_type) = self.elaborate_expression(lambda.body); let lambda_context = self.lambda_stack.pop().unwrap(); @@ -1130,7 +1185,7 @@ impl<'context> Elaborator<'context> { self.unify(&body_type, &return_type, || TypeCheckError::TypeMismatch { expected_typ: return_type.to_string(), expr_typ: body_type.to_string(), - expr_span: body_span, + expr_location: body_location, }); let captured_vars = vecmap(&lambda_context.captures, |capture| { @@ -1145,13 +1200,13 @@ impl<'context> Elaborator<'context> { (expr, Type::Function(arg_types, Box::new(body_type), Box::new(env_type), false)) } - fn elaborate_quote(&mut self, mut tokens: Tokens, span: Span) -> (HirExpression, Type) { + fn elaborate_quote(&mut self, mut tokens: Tokens, location: Location) -> (HirExpression, Type) { tokens = self.find_unquoted_exprs_tokens(tokens); if self.in_comptime_context() { (HirExpression::Quote(tokens), Type::Quoted(QuotedType::Quoted)) } else { - self.push_err(ResolverError::QuoteInRuntimeCode { span }); + self.push_err(ResolverError::QuoteInRuntimeCode { location }); (HirExpression::Error, Type::Quoted(QuotedType::Quoted)) } } @@ -1159,7 +1214,7 @@ impl<'context> Elaborator<'context> { fn elaborate_comptime_block( &mut self, block: BlockExpression, - span: Span, + location: Location, target_type: Option<&Type>, ) -> (ExprId, Type) { let (block, _typ) = self.elaborate_in_comptime_context(|this| { @@ -1168,11 +1223,11 @@ impl<'context> Elaborator<'context> { let mut interpreter = self.setup_interpreter(); let value = interpreter.evaluate_block(block); - let (id, typ) = self.inline_comptime_value(value, span); + let (id, typ) = self.inline_comptime_value(value, location); let location = self.interner.id_location(id); self.debug_comptime(location, |interner| { - interner.expression(&id).to_display_ast(interner, location.span).kind + interner.expression(&id).to_display_ast(interner, location).kind }); (id, typ) @@ -1181,12 +1236,13 @@ impl<'context> Elaborator<'context> { pub fn inline_comptime_value( &mut self, value: Result, - span: Span, + location: Location, ) -> (ExprId, Type) { let make_error = |this: &mut Self, error: InterpreterError| { - this.errors.push(error.into_compilation_error_pair()); + let error: CompilationError = error.into(); + this.push_err(error); let error = this.interner.push_expr(HirExpression::Error); - this.interner.push_expr_location(error, span, this.file); + this.interner.push_expr_location(error, location); (error, Type::Error) }; @@ -1195,7 +1251,6 @@ impl<'context> Elaborator<'context> { Err(error) => return make_error(self, error), }; - let location = Location::new(span, self.file); match value.into_expression(self, location) { Ok(new_expr) => { // At this point the Expression was already elaborated and we got a Value. @@ -1225,17 +1280,17 @@ impl<'context> Elaborator<'context> { if meta.is_comptime { Ok(Some(function)) } else { - Err(ResolverError::MacroIsNotComptime { span: location.span }) + Err(ResolverError::MacroIsNotComptime { location }) } } else { - Err(ResolverError::InvalidSyntaxInMacroCall { span: location.span }) + Err(ResolverError::InvalidSyntaxInMacroCall { location }) } } else { // Assume a name resolution error has already been issued Ok(None) } } - _ => Err(ResolverError::InvalidSyntaxInMacroCall { span: location.span }), + _ => Err(ResolverError::InvalidSyntaxInMacroCall { location }), } } @@ -1249,7 +1304,7 @@ impl<'context> Elaborator<'context> { return_type: Type, ) -> Option<(HirExpression, Type)> { self.unify(&return_type, &Type::Quoted(QuotedType::Quoted), || { - TypeCheckError::MacroReturningNonExpr { typ: return_type.clone(), span: location.span } + TypeCheckError::MacroReturningNonExpr { typ: return_type.clone(), location } }); let function = match self.try_get_comptime_function(func, location) { @@ -1260,7 +1315,6 @@ impl<'context> Elaborator<'context> { } }; - let file = self.file; let mut interpreter = self.setup_interpreter(); let mut comptime_args = Vec::new(); let mut errors = Vec::new(); @@ -1271,7 +1325,7 @@ impl<'context> Elaborator<'context> { let location = interpreter.elaborator.interner.expr_location(&argument); comptime_args.push((arg, location)); } - Err(error) => errors.push((error.into(), file)), + Err(error) => errors.push(error.into()), } } @@ -1283,7 +1337,58 @@ impl<'context> Elaborator<'context> { return None; } - let (expr_id, typ) = self.inline_comptime_value(result, location.span); + let (expr_id, typ) = self.inline_comptime_value(result, location); Some((self.interner.expression(&expr_id), typ)) } + + fn elaborate_as_trait_path(&mut self, path: AsTraitPath) -> (ExprId, Type) { + let location = path.typ.location.merge(path.trait_path.location); + + let constraint = UnresolvedTraitConstraint { + typ: path.typ, + trait_bound: TraitBound { + trait_path: path.trait_path, + trait_id: None, + trait_generics: path.trait_generics, + }, + }; + + let typ = self.resolve_type(constraint.typ.clone()); + let Some(trait_bound) = self.resolve_trait_bound(&constraint.trait_bound) else { + // resolve_trait_bound only returns None if it has already issued an error, so don't + // issue another here. + let error = self.interner.push_expr_full(HirExpression::Error, location, Type::Error); + return (error, Type::Error); + }; + + let constraint = TraitConstraint { typ, trait_bound }; + + let the_trait = self.interner.get_trait(constraint.trait_bound.trait_id); + let Some(method) = the_trait.find_method(&path.impl_item.0.contents) else { + let trait_name = the_trait.name.to_string(); + let method_name = path.impl_item.to_string(); + let location = path.impl_item.location(); + self.push_err(ResolverError::NoSuchMethodInTrait { trait_name, method_name, location }); + let error = self.interner.push_expr_full(HirExpression::Error, location, Type::Error); + return (error, Type::Error); + }; + + let trait_method = + TraitMethod { method_id: method, constraint: constraint.clone(), assumed: true }; + + let definition_id = self.interner.trait_method_id(trait_method.method_id); + + let ident = HirIdent { + location: path.impl_item.location(), + id: definition_id, + impl_kind: ImplKind::TraitMethod(trait_method), + }; + + let id = self.interner.push_expr(HirExpression::Ident(ident.clone(), None)); + self.interner.push_expr_location(id, location); + + let typ = self.type_check_variable(ident, id, None); + self.interner.push_expr_type(id, typ.clone()); + (id, typ) + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/lints.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/lints.rs index 3a5844ab21d2..4ce797c6e077 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/lints.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/lints.rs @@ -1,4 +1,5 @@ use crate::{ + Type, ast::{Ident, NoirFunction, Signedness, UnaryOp, Visibility}, graph::CrateId, hir::{ @@ -13,10 +14,9 @@ use crate::{ node_interner::{ DefinitionId, DefinitionKind, ExprId, FuncId, FunctionModifiers, NodeInterner, }, - Type, }; -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Located, Location}; pub(super) fn deprecated_function(interner: &NodeInterner, expr: ExprId) -> Option { let HirExpression::Ident(HirIdent { location, id, impl_kind: _ }, _) = @@ -34,7 +34,7 @@ pub(super) fn deprecated_function(interner: &NodeInterner, expr: ExprId) -> Opti attributes.get_deprecated_note().map(|note| TypeCheckError::CallDeprecated { name: interner.definition_name(id).to_string(), note, - span: location.span, + location, }) } @@ -67,7 +67,7 @@ pub(super) fn low_level_function_outside_stdlib( crate_id: CrateId, ) -> Option { let is_low_level_function = - modifiers.attributes.function().map_or(false, |func| func.is_low_level()); + modifiers.attributes.function().is_some_and(|func| func.is_low_level()); if !crate_id.is_stdlib() && is_low_level_function { let ident = func_meta_name_ident(func, modifiers); Some(ResolverError::LowLevelFunctionOutsideOfStdlib { ident }) @@ -81,7 +81,7 @@ pub(super) fn oracle_not_marked_unconstrained( func: &FuncMeta, modifiers: &FunctionModifiers, ) -> Option { - let is_oracle_function = modifiers.attributes.function().map_or(false, |func| func.is_oracle()); + let is_oracle_function = modifiers.attributes.function().is_some_and(|func| func.is_oracle()); if is_oracle_function && !modifiers.is_unconstrained { let ident = func_meta_name_ident(func, modifiers); Some(ResolverError::OracleMarkedAsConstrained { ident }) @@ -97,16 +97,16 @@ pub(super) fn oracle_called_from_constrained_function( interner: &NodeInterner, called_func: &FuncId, calling_from_constrained_runtime: bool, - span: Span, + location: Location, ) -> Option { if !calling_from_constrained_runtime { return None; } let function_attributes = interner.function_attributes(called_func); - let is_oracle_call = function_attributes.function().map_or(false, |func| func.is_oracle()); + let is_oracle_call = function_attributes.function().is_some_and(|func| func.is_oracle()); if is_oracle_call { - Some(ResolverError::UnconstrainedOracleReturnToConstrained { span }) + Some(ResolverError::UnconstrainedOracleReturnToConstrained { location }) } else { None } @@ -127,13 +127,13 @@ pub(super) fn missing_pub(func: &FuncMeta, modifiers: &FunctionModifiers) -> Opt /// Check that we are not passing a mutable reference from a constrained runtime to an unconstrained runtime. pub(super) fn unconstrained_function_args( - function_args: &[(Type, ExprId, Span)], + function_args: &[(Type, ExprId, Location)], ) -> Vec { function_args .iter() - .filter_map(|(typ, _, span)| { + .filter_map(|(typ, _, location)| { if !typ.is_valid_for_unconstrained_boundary() { - Some(TypeCheckError::ConstrainedReferenceToUnconstrained { span: *span }) + Some(TypeCheckError::ConstrainedReferenceToUnconstrained { location: *location }) } else { None } @@ -144,12 +144,12 @@ pub(super) fn unconstrained_function_args( /// Check that we are not passing a slice from an unconstrained runtime to a constrained runtime. pub(super) fn unconstrained_function_return( return_type: &Type, - span: Span, + location: Location, ) -> Option { if return_type.contains_slice() { - Some(TypeCheckError::UnconstrainedSliceReturnToConstrained { span }) + Some(TypeCheckError::UnconstrainedSliceReturnToConstrained { location }) } else if !return_type.is_valid_for_unconstrained_boundary() { - Some(TypeCheckError::UnconstrainedReferenceToConstrained { span }) + Some(TypeCheckError::UnconstrainedReferenceToConstrained { location }) } else { None } @@ -197,20 +197,20 @@ pub(crate) fn overflowing_int( annotated_type: &Type, ) -> Vec { let expr = interner.expression(rhs_expr); - let span = interner.expr_span(rhs_expr); + let location = interner.expr_location(rhs_expr); let mut errors = Vec::with_capacity(2); match expr { - HirExpression::Literal(HirLiteral::Integer(value, negative)) => match annotated_type { - Type::Integer(Signedness::Unsigned, bit_count) => { - let bit_count: u32 = (*bit_count).into(); - let max = 2u128.pow(bit_count) - 1; - if value > max.into() || negative { + HirExpression::Literal(HirLiteral::Integer(value)) => match annotated_type { + Type::Integer(Signedness::Unsigned, bit_size) => { + let bit_size: u32 = (*bit_size).into(); + let max = if bit_size == 128 { u128::MAX } else { 2u128.pow(bit_size) - 1 }; + if value.field > max.into() || value.is_negative { errors.push(TypeCheckError::OverflowingAssignment { - expr: if negative { -value } else { value }, + expr: value, ty: annotated_type.clone(), range: format!("0..={}", max), - span, + location, }); } } @@ -218,12 +218,14 @@ pub(crate) fn overflowing_int( let bit_count: u32 = (*bit_count).into(); let min = 2u128.pow(bit_count - 1); let max = 2u128.pow(bit_count - 1) - 1; - if (negative && value > min.into()) || (!negative && value > max.into()) { + if (value.is_negative && value.field > min.into()) + || (!value.is_negative && value.field > max.into()) + { errors.push(TypeCheckError::OverflowingAssignment { - expr: if negative { -value } else { value }, + expr: value, ty: annotated_type.clone(), range: format!("-{}..={}", min, max), - span, + location, }); } } @@ -234,7 +236,7 @@ pub(crate) fn overflowing_int( if expr.operator == UnaryOp::Minus && annotated_type.is_unsigned() { errors.push(TypeCheckError::InvalidUnaryOp { kind: annotated_type.to_string(), - span, + location, }); } } @@ -249,7 +251,7 @@ pub(crate) fn overflowing_int( } fn func_meta_name_ident(func: &FuncMeta, modifiers: &FunctionModifiers) -> Ident { - Ident(Spanned::from(func.name.location.span, modifiers.name.clone())) + Ident(Located::from(func.name.location, modifiers.name.clone())) } /// Check that a recursive function *can* return without endlessly calling itself. @@ -257,13 +259,13 @@ pub(crate) fn unbounded_recursion<'a>( interner: &'a NodeInterner, func_id: FuncId, func_name: impl FnOnce() -> &'a str, - func_span: Span, + func_location: Location, body_id: ExprId, ) -> Option { if !can_return_without_recursing(interner, func_id, body_id) { Some(ResolverError::UnconditionalRecursion { name: func_name().to_string(), - span: func_span, + location: func_location, }) } else { None @@ -297,11 +299,7 @@ fn can_return_without_recursing(interner: &NodeInterner, func_id: FuncId, expr_i return true; } let definition = interner.definition(ident.id); - if let DefinitionKind::Function(id) = definition.kind { - func_id != id - } else { - true - } + if let DefinitionKind::Function(id) = definition.kind { func_id != id } else { true } } HirExpression::Block(b) => check_block(b), HirExpression::Prefix(e) => check(e.rhs), @@ -344,7 +342,7 @@ fn can_return_without_recursing_match( HirMatch::Guard { cond: _, body, otherwise } => check(*body) && check_match(otherwise), HirMatch::Switch(_, cases, otherwise) => { cases.iter().all(|case| check_match(&case.body)) - && otherwise.as_ref().map_or(true, |case| check_match(case)) + && otherwise.as_ref().is_none_or(|case| check_match(case)) } } } 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 d007bee0d8d3..27726744fd2d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs @@ -4,6 +4,15 @@ use std::{ }; use crate::{ + DataType, StructField, TypeBindings, + ast::{ItemVisibility, UnresolvedType}, + graph::CrateGraph, + hir_def::traits::ResolvedTraitBound, + node_interner::GlobalValue, + usage_tracker::UsageTracker, +}; +use crate::{ + EnumVariant, Shared, Type, TypeVariable, ast::{ BlockExpression, FunctionKind, GenericTypeArgs, Ident, NoirFunction, NoirStruct, Param, Path, Pattern, TraitBound, UnresolvedGeneric, UnresolvedGenerics, @@ -11,19 +20,20 @@ use crate::{ }, graph::CrateId, hir::{ + Context, + comptime::ComptimeError, def_collector::{ dc_crate::{ - filter_literal_globals, CollectedItems, CompilationError, ImplMap, UnresolvedEnum, - UnresolvedFunctions, UnresolvedGlobal, UnresolvedStruct, UnresolvedTraitImpl, - UnresolvedTypeAlias, + CollectedItems, CompilationError, ImplMap, UnresolvedEnum, UnresolvedFunctions, + UnresolvedGlobal, UnresolvedStruct, UnresolvedTraitImpl, UnresolvedTypeAlias, + filter_literal_globals, }, errors::DefCollectorErrorKind, }, - def_map::{DefMaps, LocalModuleId, ModuleData, ModuleId, MAIN_FUNCTION}, + def_map::{DefMaps, LocalModuleId, MAIN_FUNCTION, ModuleData, ModuleId}, resolution::errors::ResolverError, scope::ScopeForest as GenericScopeForest, - type_check::{generics::TraitGenerics, TypeCheckError}, - Context, + type_check::{TypeCheckError, generics::TraitGenerics}, }, hir_def::{ expr::{HirCapturedVar, HirIdent}, @@ -35,22 +45,15 @@ use crate::{ DefinitionKind, DependencyId, ExprId, FuncId, FunctionModifiers, GlobalId, NodeInterner, ReferenceId, TraitId, TraitImplId, TypeAliasId, TypeId, }, + parser::{ParserError, ParserErrorReason}, token::SecondaryAttribute, - EnumVariant, Shared, Type, TypeVariable, -}; -use crate::{ - ast::{ItemVisibility, UnresolvedType}, - graph::CrateGraph, - hir_def::traits::ResolvedTraitBound, - node_interner::GlobalValue, - usage_tracker::UsageTracker, - DataType, StructField, TypeBindings, }; mod comptime; mod enums; mod expressions; mod lints; +mod options; mod path_resolution; mod patterns; mod scope; @@ -60,9 +63,10 @@ mod traits; pub mod types; mod unquote; -use fm::FileId; use iter_extended::vecmap; -use noirc_errors::{Location, Span, Spanned}; +use noirc_errors::{Located, Location}; +pub(crate) use options::ElaboratorOptions; +pub use options::{FrontendOptions, UnstableFeature}; pub use path_resolution::Turbofish; use path_resolution::{PathResolution, PathResolutionItem}; use types::bind_ordered_generics; @@ -104,15 +108,13 @@ pub struct Loop { pub struct Elaborator<'context> { scopes: ScopeForest, - pub(crate) errors: Vec<(CompilationError, FileId)>, + pub(crate) errors: Vec, pub(crate) interner: &'context mut NodeInterner, pub(crate) def_maps: &'context mut DefMaps, pub(crate) usage_tracker: &'context mut UsageTracker, pub(crate) crate_graph: &'context CrateGraph, - pub(crate) file: FileId, - unsafe_block_status: UnsafeBlockStatus, current_loop: Option, @@ -178,9 +180,6 @@ pub struct Elaborator<'context> { crate_id: CrateId, - /// The scope of --debug-comptime, or None if unset - debug_comptime_in_file: Option, - /// These are the globals that have yet to be elaborated. /// This map is used to lazily evaluate these globals if they're encountered before /// they are elaborated (e.g. in a function's type or another global's RHS). @@ -194,8 +193,34 @@ pub struct Elaborator<'context> { /// that comptime value and any visibility errors were already reported. silence_field_visibility_errors: usize, - /// Use pedantic ACVM solving - pedantic_solving: bool, + /// Options from the nargo cli + options: ElaboratorOptions<'context>, + + /// Sometimes items are elaborated because a function attribute ran and generated items. + /// The Elaborator keeps track of these reasons so that when an error is produced it will + /// be wrapped in another error that will include this reason. + pub(crate) elaborate_reasons: im::Vector<(ElaborateReason, Location)>, +} + +#[derive(Copy, Clone)] +pub enum ElaborateReason { + /// A function attribute generated an item that's being elaborated. + RunningAttribute, + /// Evaluating `Module::add_item` + AddingItemToModule, +} + +impl ElaborateReason { + fn to_macro_error(self, error: CompilationError, location: Location) -> ComptimeError { + match self { + ElaborateReason::RunningAttribute => { + ComptimeError::ErrorRunningAttribute { error: Box::new(error), location } + } + ElaborateReason::AddingItemToModule => { + ComptimeError::ErrorAddingItemToModule { error: Box::new(error), location } + } + } + } } #[derive(Default)] @@ -224,9 +249,9 @@ impl<'context> Elaborator<'context> { usage_tracker: &'context mut UsageTracker, crate_graph: &'context CrateGraph, crate_id: CrateId, - debug_comptime_in_file: Option, interpreter_call_stack: im::Vector, - pedantic_solving: bool, + options: ElaboratorOptions<'context>, + elaborate_reasons: im::Vector<(ElaborateReason, Location)>, ) -> Self { Self { scopes: ScopeForest::default(), @@ -235,7 +260,6 @@ impl<'context> Elaborator<'context> { def_maps, usage_tracker, crate_graph, - file: FileId::dummy(), unsafe_block_status: UnsafeBlockStatus::NotInUnsafeBlock, current_loop: None, generics: Vec::new(), @@ -248,21 +272,20 @@ impl<'context> Elaborator<'context> { trait_bounds: Vec::new(), function_context: vec![FunctionContext::default()], current_trait_impl: None, - debug_comptime_in_file, unresolved_globals: BTreeMap::new(), current_trait: None, interpreter_call_stack, in_comptime_context: false, silence_field_visibility_errors: 0, - pedantic_solving, + options, + elaborate_reasons, } } pub fn from_context( context: &'context mut Context, crate_id: CrateId, - debug_comptime_in_file: Option, - pedantic_solving: bool, + options: ElaboratorOptions<'context>, ) -> Self { Self::new( &mut context.def_interner, @@ -270,9 +293,9 @@ impl<'context> Elaborator<'context> { &mut context.usage_tracker, &context.crate_graph, crate_id, - debug_comptime_in_file, im::Vector::new(), - pedantic_solving, + options, + im::Vector::new(), ) } @@ -280,28 +303,18 @@ impl<'context> Elaborator<'context> { context: &'context mut Context, crate_id: CrateId, items: CollectedItems, - debug_comptime_in_file: Option, - pedantic_solving: bool, - ) -> Vec<(CompilationError, FileId)> { - Self::elaborate_and_return_self( - context, - crate_id, - items, - debug_comptime_in_file, - pedantic_solving, - ) - .errors + options: ElaboratorOptions<'context>, + ) -> Vec { + Self::elaborate_and_return_self(context, crate_id, items, options).errors } pub fn elaborate_and_return_self( context: &'context mut Context, crate_id: CrateId, items: CollectedItems, - debug_comptime_in_file: Option, - pedantic_solving: bool, + options: ElaboratorOptions<'context>, ) -> Self { - let mut this = - Self::from_context(context, crate_id, debug_comptime_in_file, pedantic_solving); + let mut this = Self::from_context(context, crate_id, options); this.elaborate_items(items); this.check_and_pop_function_context(); this @@ -382,7 +395,12 @@ impl<'context> Elaborator<'context> { self.elaborate_trait_impl(trait_impl); } - self.errors.extend(self.interner.check_for_dependency_cycles()); + self.push_errors(self.interner.check_for_dependency_cycles()); + } + + /// True if we should use pedantic ACVM solving + pub fn pedantic_solving(&self) -> bool { + self.options.pedantic_solving } /// Runs `f` and if it modifies `self.generics`, `self.generics` is truncated @@ -409,7 +427,7 @@ impl<'context> Elaborator<'context> { if let Kind::Numeric(typ) = &generic.kind() { let definition = DefinitionKind::NumericGeneric(generic.type_var.clone(), typ.clone()); - let ident = Ident::new(generic.name.to_string(), generic.span); + let ident = Ident::new(generic.name.to_string(), generic.location); let hir_ident = self.add_variable_decl( ident, false, // mutable false, // allow_shadowing @@ -428,8 +446,8 @@ impl<'context> Elaborator<'context> { let func_meta = func_meta.expect("FuncMetas should be declared before a function is elaborated"); - let (kind, body, body_span) = match func_meta.take_body() { - FunctionBody::Unresolved(kind, body, span) => (kind, body, span), + let (kind, body, body_location) = match func_meta.take_body() { + FunctionBody::Unresolved(kind, body, location) => (kind, body, location), FunctionBody::Resolved => return, // Do not error for the still-resolving case. If there is a dependency cycle, // the dependency cycle check will find it later on. @@ -444,7 +462,6 @@ impl<'context> Elaborator<'context> { ); 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; @@ -460,8 +477,8 @@ impl<'context> Elaborator<'context> { // Check arg and return-value visibility of standalone functions. if self.should_check_function_visibility(&func_meta, &modifiers) { - let name = Ident(Spanned::from( - func_meta.name.location.span, + let name = Ident(Located::from( + func_meta.name.location, self.interner.definition_name(func_meta.name.id).to_string(), )); for (_, typ, _) in func_meta.parameters.iter() { @@ -469,14 +486,14 @@ impl<'context> Elaborator<'context> { &name, modifiers.visibility, typ, - name.span(), + name.location(), ); } self.check_type_is_not_more_private_then_item( &name, modifiers.visibility, func_meta.return_type(), - name.span(), + name.location(), ); } @@ -493,7 +510,7 @@ impl<'context> Elaborator<'context> { self.add_existing_variable_to_scope(name, parameter.clone(), warn_if_unused); } - self.add_trait_constraints_to_scope(&func_meta.trait_constraints, func_meta.location.span); + self.add_trait_constraints_to_scope(&func_meta.trait_constraints, func_meta.location); let (hir_func, body_type) = match kind { FunctionKind::Builtin @@ -503,7 +520,7 @@ impl<'context> Elaborator<'context> { FunctionKind::Normal => { let return_type = func_meta.return_type(); let (block, body_type) = self.elaborate_block(body, Some(return_type)); - let expr_id = self.intern_expr(block, body_span); + let expr_id = self.intern_expr(block, body_location); self.interner.push_expr_type(expr_id, body_type.clone()); (HirFunction::unchecked_from_expr(expr_id), body_type) } @@ -536,7 +553,7 @@ impl<'context> Elaborator<'context> { elaborator.interner, id, || elaborator.interner.definition_name(func_meta.name.id), - func_meta.name.location.span, + func_meta.name.location, hir_func.as_expr(), ) .map(Into::into) @@ -569,7 +586,7 @@ impl<'context> Elaborator<'context> { } for (mut constraint, expr_id, select_impl) in context.trait_constraints { - let span = self.interner.expr_span(&expr_id); + let location = self.interner.expr_location(&expr_id); if matches!(&constraint.typ, Type::MutableReference(_)) { let (_, dereferenced_typ) = @@ -584,7 +601,7 @@ impl<'context> Elaborator<'context> { &constraint.trait_bound.trait_generics.named, expr_id, select_impl, - span, + location, ); } } @@ -635,9 +652,9 @@ impl<'context> Elaborator<'context> { } }; - let span = generic.span(); + let location = generic.location(); let name_owned = name.as_ref().clone(); - let resolved_generic = ResolvedGeneric { name, type_var, span }; + let resolved_generic = ResolvedGeneric { name, type_var, location }; // Check for name collisions of this generic // Checking `is_error` here prevents DuplicateDefinition errors when @@ -647,8 +664,8 @@ impl<'context> Elaborator<'context> { if let Some(generic) = self.find_generic(&name_owned) { self.push_err(ResolverError::DuplicateDefinition { name: name_owned, - first_span: generic.span, - second_span: span, + first_location: generic.location, + second_location: location, }); } else { self.generics.push(resolved_generic.clone()); @@ -675,11 +692,11 @@ impl<'context> Elaborator<'context> { } // An already-resolved generic is only possible if it is the result of a // previous macro call being inserted into a generics list. - UnresolvedGeneric::Resolved(id, span) => { + UnresolvedGeneric::Resolved(id, location) => { match self.interner.get_quoted_type(*id).follow_bindings() { Type::NamedGeneric(type_variable, name) => Ok((type_variable.clone(), name)), other => Err(ResolverError::MacroResultInGenericsListNotAGeneric { - span: *span, + location: *location, typ: other.clone(), }), } @@ -715,8 +732,13 @@ impl<'context> Elaborator<'context> { } } - fn push_err(&mut self, error: impl Into) { - self.errors.push((error.into(), self.file)); + pub(crate) fn push_err(&mut self, error: impl Into) { + let error: CompilationError = error.into(); + self.errors.push(error); + } + + pub(crate) fn push_errors(&mut self, errors: impl IntoIterator) { + self.errors.extend(errors); } fn run_lint(&mut self, lint: impl Fn(&Elaborator) -> Option) { @@ -728,11 +750,7 @@ impl<'context> Elaborator<'context> { pub fn resolve_module_by_path(&mut self, path: Path) -> Option { match self.resolve_path(path.clone()) { Ok(PathResolution { item: PathResolutionItem::Module(module_id), errors }) => { - if errors.is_empty() { - Some(module_id) - } else { - None - } + if errors.is_empty() { Some(module_id) } else { None } } _ => None, } @@ -831,16 +849,16 @@ impl<'context> Elaborator<'context> { let kind = associated_type.type_var.kind(); let type_var = TypeVariable::unbound(new_generic_id, kind); - let span = bound.trait_path.span; + let location = bound.trait_path.location; let name = format!("<{object} as {trait_name}>::{}", associated_type.name); let name = Rc::new(name); let typ = Type::NamedGeneric(type_var.clone(), name.clone()); let typ = self.interner.push_quoted_type(typ); - let typ = UnresolvedTypeData::Resolved(typ).with_span(span); - let ident = Ident::new(associated_type.name.as_ref().clone(), span); + let typ = UnresolvedTypeData::Resolved(typ).with_location(location); + let ident = Ident::new(associated_type.name.as_ref().clone(), location); bound.trait_generics.named_args.push((ident, typ)); - added_generics.push(ResolvedGeneric { name, span, type_var }); + added_generics.push(ResolvedGeneric { name, location, type_var }); } } } @@ -860,7 +878,7 @@ impl<'context> Elaborator<'context> { let trait_bound = self.resolve_trait_bound(&constraint.trait_bound)?; self.add_trait_bound_to_scope( - constraint.trait_bound.trait_path.span, + constraint.trait_bound.trait_path.location, &typ, &trait_bound, trait_bound.trait_id, @@ -872,12 +890,13 @@ impl<'context> Elaborator<'context> { pub fn resolve_trait_bound(&mut self, bound: &TraitBound) -> Option { let the_trait = self.lookup_trait_or_error(bound.trait_path.clone())?; let trait_id = the_trait.id; - let span = bound.trait_path.span; + let location = bound.trait_path.location; - let (ordered, named) = self.resolve_type_args(bound.trait_generics.clone(), trait_id, span); + let (ordered, named) = + self.resolve_type_args(bound.trait_generics.clone(), trait_id, location); let trait_generics = TraitGenerics { ordered, named }; - Some(ResolvedTraitBound { trait_id, trait_generics, span }) + Some(ResolvedTraitBound { trait_id, trait_generics, location }) } /// Extract metadata from a NoirFunction @@ -901,7 +920,7 @@ impl<'context> Elaborator<'context> { self.scopes.start_function(); self.current_item = Some(DependencyId::Function(func_id)); - let location = Location::new(func.name_ident().span(), self.file); + let location = func.name_ident().location(); let id = self.interner.function_definition_id(func_id); let name_ident = HirIdent::non_trait_method(id, location); @@ -926,12 +945,12 @@ impl<'context> Elaborator<'context> { let mut parameter_types = Vec::new(); let mut parameter_idents = Vec::new(); - for Param { visibility, pattern, typ, span: _ } in func.parameters().iter().cloned() { + for Param { visibility, pattern, typ, location: _ } in func.parameters().iter().cloned() { self.run_lint(|_| { lints::unnecessary_pub_argument(func, visibility, is_pub_allowed).map(Into::into) }); - let type_span = typ.span; + let type_location = typ.location; let typ = match typ.typ { UnresolvedTypeData::TraitAsType(path, args) => { self.desugar_impl_trait_arg(path, args, &mut generics, &mut trait_constraints) @@ -944,7 +963,7 @@ impl<'context> Elaborator<'context> { &typ, is_entry_point, has_inline_attribute, - type_span, + type_location, ); if is_entry_point { @@ -1016,9 +1035,9 @@ impl<'context> Elaborator<'context> { has_inline_attribute, source_crate: self.crate_id, source_module: self.local_module, - function_body: FunctionBody::Unresolved(func.kind, body, func.def.span), + function_body: FunctionBody::Unresolved(func.kind, body, func.def.location), self_type: self.self_type.clone(), - source_file: self.file, + source_file: location.file, }; self.interner.push_fn_meta(meta, func_id); @@ -1105,12 +1124,12 @@ impl<'context> Elaborator<'context> { typ: &Type, is_entry_point: bool, has_inline_attribute: bool, - span: Span, + location: Location, ) { if (is_entry_point && !typ.is_valid_for_program_input()) || (has_inline_attribute && !typ.is_valid_non_inlined_function_input()) { - self.push_err(TypeCheckError::InvalidTypeForEntryPoint { span }); + self.push_err(TypeCheckError::InvalidTypeForEntryPoint { location }); } } @@ -1143,10 +1162,14 @@ impl<'context> Elaborator<'context> { } } - fn add_trait_constraints_to_scope(&mut self, constraints: &[TraitConstraint], span: Span) { + fn add_trait_constraints_to_scope( + &mut self, + constraints: &[TraitConstraint], + location: Location, + ) { for constraint in constraints { self.add_trait_bound_to_scope( - span, + location, &constraint.typ, &constraint.trait_bound, constraint.trait_bound.trait_id, @@ -1156,11 +1179,11 @@ impl<'context> Elaborator<'context> { // Also assume `self` implements the current trait if we are inside a trait definition if let Some(trait_id) = self.current_trait { let the_trait = self.interner.get_trait(trait_id); - let constraint = the_trait.as_constraint(the_trait.name.span()); + let constraint = the_trait.as_constraint(the_trait.name.location()); let self_type = self.self_type.clone().expect("Expected a self type if there's a current trait"); self.add_trait_bound_to_scope( - span, + location, &self_type, &constraint.trait_bound, constraint.trait_bound.trait_id, @@ -1182,7 +1205,7 @@ impl<'context> Elaborator<'context> { fn add_trait_bound_to_scope( &mut self, - span: Span, + location: Location, object: &Type, trait_bound: &ResolvedTraitBound, starting_trait_id: TraitId, @@ -1194,7 +1217,11 @@ impl<'context> Elaborator<'context> { if let Some(the_trait) = self.interner.try_get_trait(trait_id) { let trait_name = the_trait.name.to_string(); let typ = object.clone(); - self.push_err(TypeCheckError::UnneededTraitConstraint { trait_name, typ, span }); + self.push_err(TypeCheckError::UnneededTraitConstraint { + trait_name, + typ, + location, + }); } } @@ -1210,20 +1237,23 @@ impl<'context> Elaborator<'context> { let parent_trait_bound = self.instantiate_parent_trait_bound(trait_bound, &parent_trait_bound); - self.add_trait_bound_to_scope(span, object, &parent_trait_bound, starting_trait_id); + self.add_trait_bound_to_scope( + location, + object, + &parent_trait_bound, + starting_trait_id, + ); } } } - fn elaborate_impls(&mut self, impls: Vec<(UnresolvedGenerics, Span, UnresolvedFunctions)>) { + fn elaborate_impls(&mut self, impls: Vec<(UnresolvedGenerics, Location, UnresolvedFunctions)>) { for (_, _, functions) in impls { - self.file = functions.file_id; self.recover_generics(|this| this.elaborate_functions(functions)); } } fn elaborate_trait_impl(&mut self, trait_impl: UnresolvedTraitImpl) { - self.file = trait_impl.file_id; self.local_module = trait_impl.module_id; self.generics = trait_impl.resolved_generics.clone(); @@ -1234,10 +1264,14 @@ impl<'context> Elaborator<'context> { self.check_parent_traits_are_implemented(&trait_impl); self.remove_trait_impl_assumed_trait_implementations(trait_impl.impl_id); - for (module, function, _) in &trait_impl.methods.functions { + for (module, function, noir_function) in &trait_impl.methods.functions { self.local_module = *module; - let errors = check_trait_impl_method_matches_declaration(self.interner, *function); - self.errors.extend(errors.into_iter().map(|error| (error.into(), self.file))); + let errors = check_trait_impl_method_matches_declaration( + self.interner, + *function, + noir_function, + ); + self.push_errors(errors.into_iter().map(|error| error.into())); } self.elaborate_functions(trait_impl.methods); @@ -1293,7 +1327,6 @@ impl<'context> Elaborator<'context> { } let impl_trait = the_trait.name.to_string(); - let the_trait_file = the_trait.location.file; let mut bindings = TypeBindings::new(); bind_ordered_generics( @@ -1329,8 +1362,8 @@ impl<'context> Elaborator<'context> { impl_trait: impl_trait.clone(), missing_trait, type_missing_trait: trait_constraint_type.to_string(), - span: trait_impl.object_type.span, - missing_trait_location: Location::new(trait_bound.span, the_trait_file), + location: trait_impl.object_type.location, + missing_trait_location: trait_bound.location, }); } } @@ -1354,7 +1387,6 @@ impl<'context> Elaborator<'context> { } let impl_trait = the_trait.name.to_string(); - let the_trait_file = the_trait.location.file; let mut bindings = TypeBindings::new(); bind_ordered_generics( @@ -1396,8 +1428,8 @@ impl<'context> Elaborator<'context> { impl_trait: impl_trait.clone(), missing_trait, type_missing_trait: trait_impl.object_type.to_string(), - span: trait_impl.object_type.span, - missing_trait_location: Location::new(parent_trait_bound.span, the_trait_file), + location: trait_impl.object_type.location, + missing_trait_location: parent_trait_bound.location, }); } } @@ -1406,22 +1438,20 @@ impl<'context> Elaborator<'context> { fn collect_impls( &mut self, module: LocalModuleId, - impls: &mut [(UnresolvedGenerics, Span, UnresolvedFunctions)], + impls: &mut [(UnresolvedGenerics, Location, UnresolvedFunctions)], ) { self.local_module = module; - for (generics, span, unresolved) in impls { - self.file = unresolved.file_id; + for (generics, location, unresolved) in impls { let old_generic_count = self.generics.len(); self.add_generics(generics); - self.declare_methods_on_struct(None, unresolved, *span); + self.declare_methods_on_struct(None, unresolved, *location); self.generics.truncate(old_generic_count); } } fn collect_trait_impl(&mut self, trait_impl: &mut UnresolvedTraitImpl) { self.local_module = trait_impl.module_id; - self.file = trait_impl.file_id; self.current_trait_impl = trait_impl.impl_id; let self_type = trait_impl.methods.self_type.clone(); @@ -1429,11 +1459,12 @@ impl<'context> Elaborator<'context> { self_type.expect("Expected struct type to be set before collect_trait_impl"); self.self_type = Some(self_type.clone()); - let self_type_span = trait_impl.object_type.span; + let self_type_location = trait_impl.object_type.location; if matches!(self_type, Type::MutableReference(_)) { - let span = self_type_span; - self.push_err(DefCollectorErrorKind::MutableReferenceInTraitImpl { span }); + self.push_err(DefCollectorErrorKind::MutableReferenceInTraitImpl { + location: self_type_location, + }); } if let Some(trait_id) = trait_impl.trait_id { @@ -1444,8 +1475,8 @@ impl<'context> Elaborator<'context> { self.collect_trait_impl_methods(trait_id, trait_impl, &where_clause); - let span = trait_impl.object_type.span; - self.declare_methods_on_struct(Some(trait_id), &mut trait_impl.methods, span); + let location = trait_impl.object_type.location; + self.declare_methods_on_struct(Some(trait_id), &mut trait_impl.methods, location); let trait_visibility = self.interner.get_trait(trait_id).visibility; @@ -1468,12 +1499,12 @@ impl<'context> Elaborator<'context> { } else { typ.to_string() }; - Ident::new(name, trait_impl.r#trait.span) + Ident::new(name, trait_impl.r#trait.location) } _ => { // We don't error in this case because an error will be produced later on when // solving the trait impl trait type - Ident::new(trait_impl.r#trait.to_string(), trait_impl.r#trait.span) + Ident::new(trait_impl.r#trait.to_string(), trait_impl.r#trait.location) } }; @@ -1489,7 +1520,7 @@ impl<'context> Elaborator<'context> { let generics = vecmap(&self.generics, |generic| generic.type_var.clone()); - if let Err((prev_span, prev_file)) = self.interner.add_trait_implementation( + if let Err(prev_location) = self.interner.add_trait_implementation( self_type.clone(), trait_id, trait_impl.impl_id.expect("impl_id should be set in define_function_metas"), @@ -1498,14 +1529,9 @@ impl<'context> Elaborator<'context> { ) { self.push_err(DefCollectorErrorKind::OverlappingImpl { typ: self_type.clone(), - span: self_type_span, + location: self_type_location, + prev_location, }); - - // The 'previous impl defined here' note must be a separate error currently - // since it may be in a different file and all errors have the same file id. - self.file = prev_file; - self.push_err(DefCollectorErrorKind::OverlappingImplNote { span: prev_span }); - self.file = trait_impl.file_id; } } @@ -1529,7 +1555,7 @@ impl<'context> Elaborator<'context> { &mut self, trait_id: Option, functions: &mut UnresolvedFunctions, - span: Span, + location: Location, ) { let self_type = functions.self_type.as_ref(); let self_type = @@ -1543,7 +1569,7 @@ impl<'context> Elaborator<'context> { // `impl`s are only allowed on types defined within the current crate if trait_id.is_none() && struct_ref.id.krate() != self.crate_id { let type_name = struct_ref.name.to_string(); - self.push_err(DefCollectorErrorKind::ForeignImpl { span, type_name }); + self.push_err(DefCollectorErrorKind::ForeignImpl { location, type_name }); return; } @@ -1589,7 +1615,7 @@ impl<'context> Elaborator<'context> { self.declare_methods(self_type, &function_ids); } } else { - self.push_err(DefCollectorErrorKind::NonStructTypeInImpl { span }); + self.push_err(DefCollectorErrorKind::NonStructTypeInImpl { location }); } } } @@ -1601,10 +1627,12 @@ impl<'context> Elaborator<'context> { if let Some(first_fn) = self.interner.add_method(self_type, method_name.clone(), *method_id, None) { + let first_location = self.interner.function_ident(&first_fn).location(); + let second_location = self.interner.function_ident(method_id).location(); let error = ResolverError::DuplicateDefinition { name: method_name, - first_span: self.interner.function_ident(&first_fn).span(), - second_span: self.interner.function_ident(method_id).span(), + first_location, + second_location, }; self.push_err(error); } @@ -1612,19 +1640,18 @@ impl<'context> Elaborator<'context> { } fn define_type_alias(&mut self, alias_id: TypeAliasId, alias: UnresolvedTypeAlias) { - self.file = alias.file_id; self.local_module = alias.module_id; let name = &alias.type_alias_def.name; let visibility = alias.type_alias_def.visibility; - let span = alias.type_alias_def.typ.span; + let location = alias.type_alias_def.typ.location; let generics = self.add_generics(&alias.type_alias_def.generics); self.current_item = Some(DependencyId::Alias(alias_id)); let typ = self.resolve_type(alias.type_alias_def.typ); if visibility != ItemVisibility::Private { - self.check_type_is_not_more_private_then_item(name, visibility, &typ, span); + self.check_type_is_not_more_private_then_item(name, visibility, &typ, location); } self.interner.set_type_alias(alias_id, typ, generics); @@ -1671,7 +1698,7 @@ impl<'context> Elaborator<'context> { name: &Ident, visibility: ItemVisibility, typ: &Type, - span: Span, + location: Location, ) { match typ { Type::DataType(struct_type, generics) => { @@ -1687,19 +1714,21 @@ impl<'context> Elaborator<'context> { self.push_err(ResolverError::TypeIsMorePrivateThenItem { typ: struct_type.name.to_string(), item: name.to_string(), - span, + location, }); } } } for generic in generics { - self.check_type_is_not_more_private_then_item(name, visibility, generic, span); + self.check_type_is_not_more_private_then_item( + name, visibility, generic, location, + ); } } Type::Tuple(types) => { for typ in types { - self.check_type_is_not_more_private_then_item(name, visibility, typ, span); + self.check_type_is_not_more_private_then_item(name, visibility, typ, location); } } Type::Alias(alias_type, generics) => { @@ -1707,26 +1736,31 @@ impl<'context> Elaborator<'context> { name, visibility, &alias_type.borrow().get_type(generics), - span, + location, ); } Type::CheckedCast { from, to } => { - self.check_type_is_not_more_private_then_item(name, visibility, from, span); - self.check_type_is_not_more_private_then_item(name, visibility, to, span); + self.check_type_is_not_more_private_then_item(name, visibility, from, location); + self.check_type_is_not_more_private_then_item(name, visibility, to, location); } Type::Function(args, return_type, env, _) => { for arg in args { - self.check_type_is_not_more_private_then_item(name, visibility, arg, span); + self.check_type_is_not_more_private_then_item(name, visibility, arg, location); } - self.check_type_is_not_more_private_then_item(name, visibility, return_type, span); - self.check_type_is_not_more_private_then_item(name, visibility, env, span); + self.check_type_is_not_more_private_then_item( + name, + visibility, + return_type, + location, + ); + self.check_type_is_not_more_private_then_item(name, visibility, env, location); } Type::MutableReference(typ) | Type::Array(_, typ) | Type::Slice(typ) => { - self.check_type_is_not_more_private_then_item(name, visibility, typ, span); + self.check_type_is_not_more_private_then_item(name, visibility, typ, location); } Type::InfixExpr(left, _op, right, _) => { - self.check_type_is_not_more_private_then_item(name, visibility, left, span); - self.check_type_is_not_more_private_then_item(name, visibility, right, span); + self.check_type_is_not_more_private_then_item(name, visibility, left, location); + self.check_type_is_not_more_private_then_item(name, visibility, right, location); } Type::FieldElement | Type::Integer(..) @@ -1752,7 +1786,6 @@ impl<'context> Elaborator<'context> { // Resolve each field in each struct. // Each struct should already be present in the NodeInterner after def collection. for (type_id, typ) in structs { - self.file = typ.file_id; self.local_module = typ.module_id; let fields = self.resolve_struct_fields(&typ.struct_def, *type_id); @@ -1766,22 +1799,22 @@ impl<'context> Elaborator<'context> { // Check that the a public struct doesn't have a private type as a public field. if typ.struct_def.visibility != ItemVisibility::Private { for field in &fields { - let ident = Ident(Spanned::from( - field.name.span(), + let ident = Ident::from(Located::from( + field.name.location(), format!("{}::{}", typ.struct_def.name, field.name), )); self.check_type_is_not_more_private_then_item( &ident, field.visibility, &field.typ, - field.name.span(), + field.name.location(), ); } } if self.interner.is_in_lsp_mode() { for (field_index, field) in fields.iter().enumerate() { - let location = Location::new(field.name.span(), self.file); + let location = field.name.location(); let reference_id = ReferenceId::StructMember(*type_id, field_index); self.interner.add_definition_location(reference_id, location, None); } @@ -1805,8 +1838,7 @@ impl<'context> Elaborator<'context> { for (_, field_type) in fields.iter() { if field_type.is_nested_slice() { let location = struct_type.borrow().location; - self.file = location.file; - self.push_err(ResolverError::NestedSlices { span: location.span }); + self.push_err(ResolverError::NestedSlices { location }); } } } @@ -1841,20 +1873,22 @@ impl<'context> Elaborator<'context> { fn collect_enum_definitions(&mut self, enums: &BTreeMap) { for (type_id, typ) in enums { - self.file = typ.file_id; self.local_module = typ.module_id; self.generics.clear(); let datatype = self.interner.get_type(*type_id); - let generics = datatype.borrow().generic_types(); - self.add_existing_generics(&typ.enum_def.generics, &datatype.borrow().generics); + let datatype_ref = datatype.borrow(); + let generics = datatype_ref.generic_types(); + self.add_existing_generics(&typ.enum_def.generics, &datatype_ref.generics); + + self.use_unstable_feature(UnstableFeature::Enums, datatype_ref.name.location()); + drop(datatype_ref); let self_type = Type::DataType(datatype.clone(), generics); let self_type_id = self.interner.push_quoted_type(self_type.clone()); - let unresolved = UnresolvedType { - typ: UnresolvedTypeData::Resolved(self_type_id), - span: typ.enum_def.span, - }; + let location = typ.enum_def.location; + let unresolved = + UnresolvedType { typ: UnresolvedTypeData::Resolved(self_type_id), location }; datatype.borrow_mut().init_variants(); let module_id = ModuleId { krate: self.crate_id, local_id: typ.module_id }; @@ -1881,7 +1915,7 @@ impl<'context> Elaborator<'context> { ); let reference_id = ReferenceId::EnumVariant(*type_id, i); - let location = Location::new(variant.item.name.span(), self.file); + let location = variant.item.name.location(); self.interner.add_definition_location(reference_id, location, Some(module_id)); } } @@ -1889,7 +1923,6 @@ impl<'context> Elaborator<'context> { fn elaborate_global(&mut self, global: UnresolvedGlobal) { let old_module = std::mem::replace(&mut self.local_module, global.module_id); - let old_file = std::mem::replace(&mut self.file, global.file_id); let old_item = self.current_item.take(); let global_id = global.global_id; @@ -1902,16 +1935,16 @@ impl<'context> Elaborator<'context> { None }; - let span = let_stmt.pattern.span(); + let location = let_stmt.pattern.location(); if !self.in_contract() && let_stmt.attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) { - self.push_err(ResolverError::AbiAttributeOutsideContract { span }); + self.push_err(ResolverError::AbiAttributeOutsideContract { location }); } if !let_stmt.comptime && matches!(let_stmt.pattern, Pattern::Mutable(..)) { - self.push_err(ResolverError::MutableGlobal { span }); + self.push_err(ResolverError::MutableGlobal { location }); } let (let_statement, _typ) = self @@ -1923,7 +1956,6 @@ impl<'context> Elaborator<'context> { self.elaborate_comptime_global(global_id); if let Some(name) = name { - let location = Location::new(span, self.file); self.interner.register_global( global_id, name, @@ -1934,7 +1966,6 @@ impl<'context> Elaborator<'context> { } self.local_module = old_module; - self.file = old_file; self.current_item = old_item; } @@ -1950,7 +1981,8 @@ impl<'context> Elaborator<'context> { let mut interpreter = self.setup_interpreter(); if let Err(error) = interpreter.evaluate_let(let_statement) { - self.errors.push(error.into_compilation_error_pair()); + let error: CompilationError = error.into(); + self.push_err(error); } else { let value = interpreter .lookup_id(definition_id, location) @@ -1986,7 +2018,6 @@ impl<'context> Elaborator<'context> { self.local_module = *local_module; for (generics, _, function_set) in function_sets { - self.file = function_set.file_id; self.add_generics(generics); let self_type = self.resolve_type(self_type.clone()); function_set.self_type = Some(self_type.clone()); @@ -1998,20 +2029,19 @@ impl<'context> Elaborator<'context> { } for trait_impl in trait_impls { - self.file = trait_impl.file_id; self.local_module = trait_impl.module_id; - let (trait_id, mut trait_generics, path_span) = match &trait_impl.r#trait.typ { + let (trait_id, mut trait_generics, path_location) = match &trait_impl.r#trait.typ { UnresolvedTypeData::Named(trait_path, trait_generics, _) => { let trait_id = self.resolve_trait_by_path(trait_path.clone()); - (trait_id, trait_generics.clone(), trait_path.span) + (trait_id, trait_generics.clone(), trait_path.location) } UnresolvedTypeData::Resolved(quoted_type_id) => { let typ = self.interner.get_quoted_type(*quoted_type_id); - let span = trait_impl.r#trait.span; + let location = trait_impl.r#trait.location; let Type::TraitAsType(trait_id, _, trait_generics) = typ else { let found = typ.to_string(); - self.push_err(ResolverError::ExpectedTrait { span, found }); + self.push_err(ResolverError::ExpectedTrait { location, found }); continue; }; @@ -2023,24 +2053,24 @@ impl<'context> Elaborator<'context> { ordered_args: vecmap(&trait_generics.ordered, |typ| { let quoted_type_id = self.interner.push_quoted_type(typ.clone()); let typ = UnresolvedTypeData::Resolved(quoted_type_id); - UnresolvedType { typ, span } + UnresolvedType { typ, location } }), named_args: vecmap(&trait_generics.named, |named_type| { let quoted_type_id = self.interner.push_quoted_type(named_type.typ.clone()); let typ = UnresolvedTypeData::Resolved(quoted_type_id); - (named_type.name.clone(), UnresolvedType { typ, span }) + (named_type.name.clone(), UnresolvedType { typ, location }) }), kinds: Vec::new(), }; - (Some(trait_id), trait_generics, span) + (Some(trait_id), trait_generics, location) } _ => { - let span = trait_impl.r#trait.span; + let location = trait_impl.r#trait.location; let found = trait_impl.r#trait.typ.to_string(); - self.push_err(ResolverError::ExpectedTrait { span, found }); - continue; + self.push_err(ResolverError::ExpectedTrait { location, found }); + (None, GenericTypeArgs::default(), location) } }; @@ -2076,7 +2106,7 @@ impl<'context> Elaborator<'context> { .trait_id .map(|trait_id| { // Check for missing generics & associated types for the trait being implemented - self.resolve_trait_args_from_trait_impl(trait_generics, trait_id, path_span) + self.resolve_trait_args_from_trait_impl(trait_generics, trait_id, path_location) }) .unwrap_or_default(); @@ -2096,22 +2126,19 @@ impl<'context> Elaborator<'context> { self.generics.clear(); if let Some(trait_id) = trait_id { - let (span, is_self_type_name) = match &trait_impl.r#trait.typ { + let (location, is_self_type_name) = match &trait_impl.r#trait.typ { UnresolvedTypeData::Named(trait_path, _, _) => { let trait_name = trait_path.last_ident(); - (trait_name.span(), trait_name.is_self_type_name()) + (trait_name.location(), trait_name.is_self_type_name()) } - _ => (trait_impl.r#trait.span, false), + _ => (trait_impl.r#trait.location, false), }; - let location = Location::new(span, trait_impl.file_id); self.interner.add_trait_reference(trait_id, location, is_self_type_name); } } } fn define_function_metas_for_functions(&mut self, function_set: &mut UnresolvedFunctions) { - self.file = function_set.file_id; - for (local_module, id, func) in &mut function_set.functions { self.local_module = *local_module; self.recover_generics(|this| { @@ -2124,11 +2151,20 @@ impl<'context> Elaborator<'context> { /// Defaults to `true` if the current function is unknown. fn in_constrained_function(&self) -> bool { !self.in_comptime_context() - && self.current_item.map_or(true, |id| match id { + && self.current_item.is_none_or(|id| match id { DependencyId::Function(id) => { !self.interner.function_modifiers(&id).is_unconstrained } _ => true, }) } + + /// Register a use of the given unstable feature. Errors if the feature has not + /// been explicitly enabled in this package. + pub fn use_unstable_feature(&mut self, feature: UnstableFeature, location: Location) { + if !self.options.enabled_unstable_features.contains(&feature) { + let reason = ParserErrorReason::ExperimentalFeature(feature); + self.push_err(ParserError::with_reason(reason, location)); + } + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/options.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/options.rs new file mode 100644 index 000000000000..58bb5e73a618 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/options.rs @@ -0,0 +1,62 @@ +use std::str::FromStr; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum UnstableFeature { + Enums, + ArrayOwnership, +} + +impl std::fmt::Display for UnstableFeature { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Enums => write!(f, "enums"), + Self::ArrayOwnership => write!(f, "array-ownership"), + } + } +} + +impl FromStr for UnstableFeature { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "enums" => Ok(Self::Enums), + "array-ownership" => Ok(Self::ArrayOwnership), + other => Err(format!("Unknown unstable feature '{other}'")), + } + } +} + +/// Generic options struct meant to resolve to ElaboratorOptions below when +/// we can resolve a file path to a file id later. This generic struct is used +/// so that FrontendOptions doesn't need to duplicate fields and methods with ElaboratorOptions. +#[derive(Copy, Clone)] +pub struct GenericOptions<'a, T> { + /// The scope of --debug-comptime, or None if unset + pub debug_comptime_in_file: Option, + + /// Use pedantic ACVM solving + pub pedantic_solving: bool, + + /// Unstable compiler features that were explicitly enabled. Any unstable features + /// that are not in this list result in an error when used. + pub enabled_unstable_features: &'a [UnstableFeature], +} + +/// Options from nargo_cli that need to be passed down to the elaborator +pub(crate) type ElaboratorOptions<'a> = GenericOptions<'a, fm::FileId>; + +/// This is the unresolved version of `ElaboratorOptions` +/// CLI options that need to be passed to the compiler frontend (the elaborator). +pub type FrontendOptions<'a> = GenericOptions<'a, &'a str>; + +impl GenericOptions<'_, T> { + /// A sane default of frontend options for running tests + pub fn test_default() -> GenericOptions<'static, T> { + GenericOptions { + debug_comptime_in_file: None, + pedantic_solving: true, + enabled_unstable_features: &[UnstableFeature::Enums], + } + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/path_resolution.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/path_resolution.rs index 67a99da70eb8..c0dfa90d36c9 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/path_resolution.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/path_resolution.rs @@ -1,9 +1,9 @@ use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use crate::ast::{Ident, Path, PathKind, UnresolvedType}; use crate::hir::def_map::{ModuleData, ModuleDefId, ModuleId, PerNs}; -use crate::hir::resolution::import::{resolve_path_kind, PathResolutionError}; +use crate::hir::resolution::import::{PathResolutionError, resolve_path_kind}; use crate::hir::resolution::errors::ResolverError; use crate::hir::resolution::visibility::item_in_module_is_visible; @@ -12,8 +12,8 @@ use crate::locations::ReferencesTracker; use crate::node_interner::{FuncId, GlobalId, TraitId, TypeAliasId, TypeId}; use crate::{Shared, Type, TypeAlias}; -use super::types::SELF_TYPE_NAME; use super::Elaborator; +use super::types::SELF_TYPE_NAME; #[derive(Debug)] pub(crate) struct PathResolution { @@ -73,7 +73,7 @@ impl PathResolutionItem { #[derive(Debug, Clone)] pub struct Turbofish { pub generics: Vec, - pub span: Span, + pub location: Location, } /// Any item that can appear before the last segment in a path. @@ -102,7 +102,7 @@ enum MethodLookupResult { FoundMultipleTraitMethods(Vec), } -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { pub(super) fn resolve_path_or_error( &mut self, path: Path, @@ -149,7 +149,7 @@ impl<'context> Elaborator<'context> { importing_module: ModuleId, ) -> PathResolutionResult { let references_tracker = if self.interner.is_in_lsp_mode() { - Some(ReferencesTracker::new(self.interner, self.file)) + Some(ReferencesTracker::new(self.interner)) } else { None }; @@ -204,7 +204,7 @@ impl<'context> Elaborator<'context> { Some((typ, visibility, _)) => (typ, visibility), }; - let location = Location::new(last_segment.span, self.file); + let location = last_segment.location; self.interner.add_module_def_id_reference( typ, location, @@ -218,7 +218,7 @@ impl<'context> Elaborator<'context> { if last_segment_generics.is_some() { errors.push(PathResolutionError::TurbofishNotAllowedOnItem { item: format!("module `{last_ident}`"), - span: last_segment.turbofish_span(), + location: last_segment.turbofish_location(), }); } @@ -324,7 +324,7 @@ impl<'context> Elaborator<'context> { let name = path.last_ident(); let is_self_type = name.is_self_type_name(); - let location = Location::new(name.span(), self.file); + let location = name.location(); self.interner.add_module_def_id_reference(module_def_id, location, is_self_type); let item = merge_intermediate_path_resolution_item_with_module_def_id( 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 b01c4e0d768b..6ad8e1ec04c9 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -1,11 +1,12 @@ use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use rustc_hash::FxHashSet as HashSet; use crate::{ + DataType, Kind, Shared, Type, TypeAlias, TypeBindings, ast::{ - Expression, ExpressionKind, Ident, ItemVisibility, Path, Pattern, TypePath, UnresolvedType, - ERROR_IDENT, + ERROR_IDENT, Expression, ExpressionKind, Ident, ItemVisibility, Path, Pattern, TypePath, + UnresolvedType, }, hir::{ def_collector::dc_crate::CompilationError, @@ -17,12 +18,11 @@ use crate::{ stmt::HirPattern, }, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, TraitImplKind}, - DataType, Kind, Shared, Type, TypeAlias, TypeBindings, }; -use super::{path_resolution::PathResolutionItem, Elaborator, ResolverMeta}; +use super::{Elaborator, ResolverMeta, path_resolution::PathResolutionItem}; -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { pub(super) fn elaborate_pattern( &mut self, pattern: Pattern, @@ -66,7 +66,7 @@ impl<'context> Elaborator<'context> { pattern: Pattern, expected_type: Type, definition: DefinitionKind, - mutable: Option, + mutable: Option, new_definitions: &mut Vec, warn_if_unused: bool, ) -> HirPattern { @@ -81,7 +81,7 @@ impl<'context> Elaborator<'context> { let ident = if let DefinitionKind::Global(global_id) = definition { // Globals don't need to be added to scope, they're already in the def_maps let id = self.interner.get_global(global_id).definition_id; - let location = Location::new(name.span(), self.file); + let location = name.location(); HirIdent::non_trait_method(id, location) } else { self.add_variable_decl( @@ -96,23 +96,25 @@ impl<'context> Elaborator<'context> { new_definitions.push(ident.clone()); HirPattern::Identifier(ident) } - Pattern::Mutable(pattern, span, _) => { + Pattern::Mutable(pattern, location, _) => { if let Some(first_mut) = mutable { - self.push_err(ResolverError::UnnecessaryMut { first_mut, second_mut: span }); + self.push_err(ResolverError::UnnecessaryMut { + first_mut, + second_mut: location, + }); } let pattern = self.elaborate_pattern_mut( *pattern, expected_type, definition, - Some(span), + Some(location), new_definitions, warn_if_unused, ); - let location = Location::new(span, self.file); HirPattern::Mutable(Box::new(pattern), location) } - Pattern::Tuple(fields, span) => { + Pattern::Tuple(fields, location) => { let field_types = match expected_type.follow_bindings() { Type::Tuple(fields) => fields, Type::Error => Vec::new(), @@ -123,7 +125,7 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::TypeMismatchWithSource { expected: expected_type, actual: tuple, - span, + location, source: Source::Assignment, }); Vec::new() @@ -141,13 +143,12 @@ impl<'context> Elaborator<'context> { warn_if_unused, ) }); - let location = Location::new(span, self.file); HirPattern::Tuple(fields, location) } - Pattern::Struct(name, fields, span) => self.elaborate_struct_pattern( + Pattern::Struct(name, fields, location) => self.elaborate_struct_pattern( name, fields, - span, + location, expected_type, definition, mutable, @@ -172,14 +173,14 @@ impl<'context> Elaborator<'context> { &mut self, name: Path, fields: Vec<(Ident, Pattern)>, - span: Span, + location: Location, expected_type: Type, definition: DefinitionKind, - mutable: Option, + mutable: Option, new_definitions: &mut Vec, ) -> HirPattern { let last_segment = name.last_segment(); - let name_span = last_segment.ident.span(); + let name_location = last_segment.ident.location(); let is_self_type = last_segment.ident.is_self_type_name(); let error_identifier = |this: &mut Self| { @@ -200,27 +201,26 @@ impl<'context> Elaborator<'context> { None => return error_identifier(self), Some(typ) => { let typ = typ.to_string(); - self.push_err(ResolverError::NonStructUsedInConstructor { typ, span }); + self.push_err(ResolverError::NonStructUsedInConstructor { typ, location }); return error_identifier(self); } }; - let turbofish_span = last_segment.turbofish_span(); + let turbofish_location = last_segment.turbofish_location(); let generics = self.resolve_struct_turbofish_generics( &struct_type.borrow(), generics, last_segment.generics, - turbofish_span, + turbofish_location, ); let actual_type = Type::DataType(struct_type.clone(), generics); - let location = Location::new(span, self.file); self.unify(&actual_type, &expected_type, || TypeCheckError::TypeMismatchWithSource { expected: expected_type.clone(), actual: actual_type.clone(), - span: location.span, + location, source: Source::Assignment, }); @@ -228,7 +228,7 @@ impl<'context> Elaborator<'context> { let fields = self.resolve_constructor_pattern_fields( typ, fields, - span, + location, expected_type.clone(), definition, mutable, @@ -237,11 +237,10 @@ impl<'context> Elaborator<'context> { let struct_id = struct_type.borrow().id; - let reference_location = Location::new(name_span, self.file); - self.interner.add_type_reference(struct_id, reference_location, is_self_type); + self.interner.add_type_reference(struct_id, name_location, is_self_type); for (field_index, field) in fields.iter().enumerate() { - let reference_location = Location::new(field.0.span(), self.file); + let reference_location = field.0.location(); self.interner.add_struct_member_reference(struct_id, field_index, reference_location); } @@ -256,10 +255,10 @@ impl<'context> Elaborator<'context> { &mut self, struct_type: Shared, fields: Vec<(Ident, Pattern)>, - span: Span, + location: Location, expected_type: Type, definition: DefinitionKind, - mutable: Option, + mutable: Option, new_definitions: &mut Vec, ) -> Vec<(Ident, HirPattern)> { let mut ret = Vec::with_capacity(fields.len()); @@ -290,7 +289,7 @@ impl<'context> Elaborator<'context> { &struct_type.borrow(), &field.0.contents, visibility, - field.span(), + field.location(), ); } else if seen_fields.contains(&field) { // duplicate field @@ -308,7 +307,7 @@ impl<'context> Elaborator<'context> { if !unseen_fields.is_empty() { self.push_err(ResolverError::MissingFields { - span, + location, missing_fields: unseen_fields.into_iter().map(|field| field.to_string()).collect(), struct_definition: struct_type.borrow().name.clone(), }); @@ -329,7 +328,7 @@ impl<'context> Elaborator<'context> { return self.add_global_variable_decl(name, global_id); } - let location = Location::new(name.span(), self.file); + let location = name.location(); let name = name.0.contents; let comptime = self.in_comptime_context(); let id = @@ -346,8 +345,8 @@ impl<'context> Elaborator<'context> { if let Some(old_value) = old_value { self.push_err(ResolverError::DuplicateDefinition { name, - first_span: old_value.ident.location.span, - second_span: location.span, + first_location: old_value.ident.location, + second_location: location, }); } } @@ -362,14 +361,18 @@ impl<'context> Elaborator<'context> { ident: HirIdent, warn_if_unused: bool, ) { - let second_span = ident.location.span; + let second_location = ident.location; let resolver_meta = ResolverMeta { num_times_used: 0, ident, warn_if_unused }; let old_value = self.scopes.get_mut_scope().add_key_value(name.clone(), resolver_meta); if let Some(old_value) = old_value { - let first_span = old_value.ident.location.span; - self.push_err(ResolverError::DuplicateDefinition { name, first_span, second_span }); + let first_location = old_value.ident.location; + self.push_err(ResolverError::DuplicateDefinition { + name, + first_location, + second_location, + }); } } @@ -383,9 +386,9 @@ impl<'context> Elaborator<'context> { let old_global_value = scope.add_key_value(name.0.contents.clone(), resolver_meta); if let Some(old_global_value) = old_global_value { self.push_err(ResolverError::DuplicateDefinition { - second_span: name.span(), + first_location: old_global_value.ident.location, + second_location: name.location(), name: name.0.contents, - first_span: old_global_value.ident.location.span, }); } ident @@ -402,7 +405,7 @@ impl<'context> Elaborator<'context> { let scope_tree = self.scopes.current_scope_tree(); let variable = scope_tree.find(&name.0.contents); - let location = Location::new(name.span(), self.file); + let location = name.location(); if let Some((variable_found, scope)) = variable { variable_found.num_times_used += 1; let id = variable_found.ident.id; @@ -410,7 +413,7 @@ impl<'context> Elaborator<'context> { } else { Err(ResolverError::VariableNotDeclared { name: name.0.contents.clone(), - span: name.0.span(), + location: name.0.location(), }) } } @@ -420,7 +423,7 @@ impl<'context> Elaborator<'context> { &mut self, func_id: &FuncId, unresolved_turbofish: Option>, - span: Span, + location: Location, ) -> Option> { let direct_generic_kinds = vecmap(&self.interner.function_meta(func_id).direct_generics, |generic| generic.kind()); @@ -430,7 +433,7 @@ impl<'context> Elaborator<'context> { let type_check_err = TypeCheckError::IncorrectTurbofishGenericCount { expected_count: direct_generic_kinds.len(), actual_count: unresolved_turbofish.len(), - span, + location, }; self.push_err(type_check_err); } @@ -444,7 +447,7 @@ impl<'context> Elaborator<'context> { struct_type: &DataType, generics: Vec, unresolved_turbofish: Option>, - span: Span, + location: Location, ) -> Vec { let kinds = vecmap(&struct_type.generics, |generic| generic.kind()); self.resolve_item_turbofish_generics( @@ -453,7 +456,7 @@ impl<'context> Elaborator<'context> { kinds, generics, unresolved_turbofish, - span, + location, ) } @@ -463,7 +466,7 @@ impl<'context> Elaborator<'context> { trait_generic_kinds: Vec, generics: Vec, unresolved_turbofish: Option>, - span: Span, + location: Location, ) -> Vec { self.resolve_item_turbofish_generics( "trait", @@ -471,7 +474,7 @@ impl<'context> Elaborator<'context> { trait_generic_kinds, generics, unresolved_turbofish, - span, + location, ) } @@ -480,7 +483,7 @@ impl<'context> Elaborator<'context> { type_alias: &TypeAlias, generics: Vec, unresolved_turbofish: Option>, - span: Span, + location: Location, ) -> Vec { let kinds = vecmap(&type_alias.generics, |generic| generic.kind()); self.resolve_item_turbofish_generics( @@ -489,7 +492,7 @@ impl<'context> Elaborator<'context> { kinds, generics, unresolved_turbofish, - span, + location, ) } @@ -500,7 +503,7 @@ impl<'context> Elaborator<'context> { item_generic_kinds: Vec, generics: Vec, unresolved_turbofish: Option>, - span: Span, + location: Location, ) -> Vec { let Some(turbofish_generics) = unresolved_turbofish else { return generics; @@ -511,7 +514,7 @@ impl<'context> Elaborator<'context> { item: format!("{item_kind} {item_name}"), expected: generics.len(), found: turbofish_generics.len(), - span, + location, }); return generics; } @@ -533,7 +536,7 @@ impl<'context> Elaborator<'context> { pub(super) fn elaborate_variable(&mut self, variable: Path) -> (ExprId, Type) { let unresolved_turbofish = variable.segments.last().unwrap().generics.clone(); - let span = variable.span; + let location = variable.location; let (expr, item) = self.resolve_variable(variable); let definition_id = expr.id; @@ -547,7 +550,7 @@ impl<'context> Elaborator<'context> { // Resolve any generics if we the variable we have resolved is a function // and if the turbofish operator was used. let generics = if let Some(DefinitionKind::Function(func_id)) = &definition_kind { - self.resolve_function_turbofish_generics(func_id, unresolved_turbofish, span) + self.resolve_function_turbofish_generics(func_id, unresolved_turbofish, location) } else { None }; @@ -567,7 +570,7 @@ impl<'context> Elaborator<'context> { let id = self.interner.push_expr(HirExpression::Ident(expr.clone(), generics.clone())); - self.interner.push_expr_location(id, span, self.file); + self.interner.push_expr_location(id, location); let typ = self.type_check_variable_with_bindings(expr, id, generics, bindings); self.interner.push_expr_type(id, typ.clone()); @@ -589,7 +592,7 @@ impl<'context> Elaborator<'context> { &struct_type, struct_generics, Some(generics.generics), - generics.span, + generics.location, ) } PathResolutionItem::TypeAliasFunction(type_alias_id, generics, _func_id) => { @@ -605,7 +608,7 @@ impl<'context> Elaborator<'context> { &type_alias, alias_generics, Some(generics.generics), - generics.span, + generics.location, ) } else { alias_generics @@ -628,7 +631,7 @@ impl<'context> Elaborator<'context> { kinds, trait_generics, Some(generics.generics), - generics.span, + generics.location, ) } _ => Vec::new(), @@ -643,7 +646,7 @@ impl<'context> Elaborator<'context> { ( HirIdent { - location: Location::new(path.span, self.file), + location: path.location, id: self.interner.trait_method_id(trait_path_resolution.method.method_id), impl_kind: ImplKind::TraitMethod(trait_path_resolution.method), }, @@ -654,7 +657,7 @@ impl<'context> Elaborator<'context> { // Otherwise, then it is referring to an Identifier // This lookup allows support of such statements: let x = foo::bar::SOME_GLOBAL + 10; // If the expression is a singular indent, we search the resolver's current scope as normal. - let span = path.span(); + let location = path.location; let ((hir_ident, var_scope_index), item) = self.get_ident_from_path(path); if hir_ident.id != DefinitionId::dummy_id() { @@ -689,8 +692,7 @@ impl<'context> Elaborator<'context> { // only local variables can be captured by closures. self.resolve_local_variable(hir_ident.clone(), var_scope_index); - let reference_location = Location::new(span, self.file); - self.interner.add_local_reference(hir_ident.id, reference_location); + self.interner.add_local_reference(hir_ident.id, location); } } } @@ -742,14 +744,13 @@ impl<'context> Elaborator<'context> { _ => 0, }); - let span = self.interner.expr_span(&expr_id); let location = self.interner.expr_location(&expr_id); // This instantiates a trait's generics as well which need to be set // when the constraint below is later solved for when the function is // finished. How to link the two? let (typ, bindings) = - self.instantiate(t, bindings, generics, function_generic_count, span, location); + self.instantiate(t, bindings, generics, function_generic_count, location); // Push any trait constraints required by this definition to the context // to be checked later when the type of this variable is further constrained. @@ -796,7 +797,6 @@ impl<'context> Elaborator<'context> { bindings: TypeBindings, turbofish_generics: Option>, function_generic_count: usize, - span: Span, location: Location, ) -> (Type, TypeBindings) { match turbofish_generics { @@ -805,9 +805,9 @@ impl<'context> Elaborator<'context> { let type_check_err = TypeCheckError::IncorrectTurbofishGenericCount { expected_count: function_generic_count, actual_count: turbofish_generics.len(), - span, + location, }; - self.errors.push((CompilationError::TypeError(type_check_err), location.file)); + self.push_err(CompilationError::TypeError(type_check_err)); typ.instantiate_with_bindings(bindings, self.interner) } else { // Fetch the count of any implicit generics on the function, such as @@ -827,20 +827,20 @@ impl<'context> Elaborator<'context> { &mut self, path: Path, ) -> ((HirIdent, usize), Option) { - let location = Location::new(path.last_ident().span(), self.file); + let location = Location::new(path.last_ident().span(), path.location.file); let error = match path.as_ident().map(|ident| self.use_variable(ident)) { Some(Ok(found)) => return (found, None), // Try to look it up as a global, but still issue the first error if we fail Some(Err(error)) => match self.lookup_global(path) { Ok((id, item)) => { - return ((HirIdent::non_trait_method(id, location), 0), Some(item)) + return ((HirIdent::non_trait_method(id, location), 0), Some(item)); } Err(_) => error, }, None => match self.lookup_global(path) { Ok((id, item)) => { - return ((HirIdent::non_trait_method(id, location), 0), Some(item)) + return ((HirIdent::non_trait_method(id, location), 0), Some(item)); } Err(error) => error, }, @@ -851,11 +851,14 @@ impl<'context> Elaborator<'context> { } pub(super) fn elaborate_type_path(&mut self, path: TypePath) -> (ExprId, Type) { - let span = path.item.span(); + let location = path.item.location(); let typ = self.resolve_type(path.typ); + let check_self_param = false; - let Some(method) = self.lookup_method(&typ, &path.item.0.contents, span, false) else { - let error = Expression::new(ExpressionKind::Error, span); + let Some(method) = + self.lookup_method(&typ, &path.item.0.contents, location, check_self_param) + else { + let error = Expression::new(ExpressionKind::Error, location); return self.elaborate_expression(error); }; @@ -864,16 +867,15 @@ impl<'context> Elaborator<'context> { .expect("Expected trait function to be a DefinitionKind::Function"); let generics = - path.turbofish.map(|turbofish| self.resolve_type_args(turbofish, func_id, span).0); + path.turbofish.map(|turbofish| self.resolve_type_args(turbofish, func_id, location).0); - let location = Location::new(span, self.file); let id = self.interner.function_definition_id(func_id); let impl_kind = match method { HirMethodReference::FuncId(_) => ImplKind::NotATraitMethod, HirMethodReference::TraitMethodId(method_id, generics, _) => { let mut constraint = - self.interner.get_trait(method_id.trait_id).as_constraint(span); + self.interner.get_trait(method_id.trait_id).as_constraint(location); constraint.trait_bound.trait_generics = generics; ImplKind::TraitMethod(TraitMethod { method_id, constraint, assumed: false }) } @@ -881,7 +883,7 @@ impl<'context> Elaborator<'context> { let ident = HirIdent { location, id, impl_kind }; let id = self.interner.push_expr(HirExpression::Ident(ident.clone(), generics.clone())); - self.interner.push_expr_location(id, location.span, location.file); + self.interner.push_expr_location(id, location); let typ = self.type_check_variable(ident, id, generics); self.interner.push_expr_type(id, typ.clone()); 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 327ae02b2046..9955d874927e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs @@ -1,17 +1,17 @@ -use noirc_errors::Spanned; +use noirc_errors::Located; -use crate::ast::{Ident, Path, ERROR_IDENT}; +use crate::ast::{ERROR_IDENT, Ident, Path}; use crate::hir::def_map::{LocalModuleId, ModuleId}; use crate::hir::scope::{Scope as GenericScope, ScopeTree as GenericScopeTree}; use crate::{ + DataType, Shared, hir::resolution::errors::ResolverError, hir_def::{ expr::{HirCapturedVar, HirIdent}, traits::Trait, }, node_interner::{DefinitionId, TraitId, TypeId}, - DataType, Shared, }; use crate::{Type, TypeAlias}; @@ -22,7 +22,7 @@ use super::{Elaborator, ResolverMeta}; type Scope = GenericScope; type ScopeTree = GenericScopeTree; -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { pub fn module_id(&self) -> ModuleId { assert_ne!(self.local_module, LocalModuleId::dummy_id(), "local_module is unset"); ModuleId { krate: self.crate_id, local_id: self.local_module } @@ -83,7 +83,7 @@ impl<'context> Elaborator<'context> { &mut self, path: Path, ) -> Result<(DefinitionId, PathResolutionItem), ResolverError> { - let span = path.span(); + let location = path.location; let item = self.resolve_path_or_error(path)?; if let Some(function) = item.function_id() { @@ -97,7 +97,7 @@ impl<'context> Elaborator<'context> { let expected = "global variable"; let got = "local variable"; - Err(ResolverError::Expected { span, expected, got }) + Err(ResolverError::Expected { location, expected, got }) } pub fn push_scope(&mut self) { @@ -121,7 +121,7 @@ impl<'context> Elaborator<'context> { if let Some(definition_info) = self.interner.try_definition(unused_var.id) { let name = &definition_info.name; if name != ERROR_IDENT && !definition_info.is_global() { - let ident = Ident(Spanned::from(unused_var.location.span, name.to_owned())); + let ident = Ident(Located::from(unused_var.location, name.to_owned())); self.push_err(ResolverError::UnusedVariable { ident }); } } @@ -138,7 +138,7 @@ impl<'context> Elaborator<'context> { /// Lookup a given trait by name/path. pub fn lookup_trait_or_error(&mut self, path: Path) -> Option<&mut Trait> { - let span = path.span(); + let location = path.location; match self.resolve_path_or_error(path) { Ok(item) => { if let PathResolutionItem::Trait(trait_id) = item { @@ -147,7 +147,7 @@ impl<'context> Elaborator<'context> { self.push_err(ResolverError::Expected { expected: "trait", got: item.description(), - span, + location, }); None } @@ -161,7 +161,7 @@ impl<'context> Elaborator<'context> { /// Lookup a given struct type by name. pub fn lookup_datatype_or_error(&mut self, path: Path) -> Option> { - let span = path.span(); + let location = path.location; match self.resolve_path_or_error(path) { Ok(item) => { if let PathResolutionItem::Type(struct_id) = item { @@ -170,7 +170,7 @@ impl<'context> Elaborator<'context> { self.push_err(ResolverError::Expected { expected: "type", got: item.description(), - span, + location, }); None } @@ -186,13 +186,13 @@ impl<'context> Elaborator<'context> { /// This will also instantiate any struct types found. pub(super) fn lookup_type_or_error(&mut self, path: Path) -> Option { let ident = path.as_ident(); - if ident.map_or(false, |i| i == SELF_TYPE_NAME) { + if ident.is_some_and(|i| i == SELF_TYPE_NAME) { if let Some(typ) = &self.self_type { return Some(typ.clone()); } } - let span = path.span; + let location = path.location; match self.resolve_path_or_error(path) { Ok(PathResolutionItem::Type(struct_id)) => { let struct_type = self.get_type(struct_id); @@ -208,7 +208,7 @@ impl<'context> Elaborator<'context> { self.push_err(ResolverError::Expected { expected: "type", got: other.description(), - span, + location, }); None } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs index 3379db4aa668..f00b2a87b1ed 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs @@ -1,6 +1,7 @@ -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use crate::{ + DataType, Type, ast::{ AssignStatement, Expression, ForLoopStatement, ForRange, Ident, ItemVisibility, LValue, LetStatement, Path, Statement, StatementKind, WhileStatement, @@ -17,12 +18,11 @@ use crate::{ stmt::{HirAssignStatement, HirForStatement, HirLValue, HirLetStatement, HirStatement}, }, node_interner::{DefinitionId, DefinitionKind, GlobalId, StmtId}, - DataType, Type, }; -use super::{lints, Elaborator, Loop}; +use super::{Elaborator, Loop, lints}; -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { fn elaborate_statement_value(&mut self, statement: Statement) -> (HirStatement, Type) { self.elaborate_statement_value_with_target_type(statement, None) } @@ -36,10 +36,10 @@ impl<'context> Elaborator<'context> { StatementKind::Let(let_stmt) => self.elaborate_local_let(let_stmt), StatementKind::Assign(assign) => self.elaborate_assign(assign), StatementKind::For(for_stmt) => self.elaborate_for(for_stmt), - StatementKind::Loop(block, span) => self.elaborate_loop(block, span), + StatementKind::Loop(block, location) => self.elaborate_loop(block, location), StatementKind::While(while_) => self.elaborate_while(while_), - StatementKind::Break => self.elaborate_jump(true, statement.span), - StatementKind::Continue => self.elaborate_jump(false, statement.span), + StatementKind::Break => self.elaborate_jump(true, statement.location), + StatementKind::Continue => self.elaborate_jump(false, statement.location), StatementKind::Comptime(statement) => self.elaborate_comptime_statement(*statement), StatementKind::Expression(expr) => { let (expr, typ) = self.elaborate_expression_with_target_type(expr, target_type); @@ -51,7 +51,7 @@ impl<'context> Elaborator<'context> { } StatementKind::Interned(id) => { let kind = self.interner.get_statement_kind(id); - let statement = Statement { kind: kind.clone(), span: statement.span }; + let statement = Statement { kind: kind.clone(), location: statement.location }; self.elaborate_statement_value_with_target_type(statement, target_type) } StatementKind::Error => (HirStatement::Error, Type::Error), @@ -67,11 +67,11 @@ impl<'context> Elaborator<'context> { statement: Statement, target_type: Option<&Type>, ) -> (StmtId, Type) { - let span = statement.span; + let location = statement.location; let (hir_statement, typ) = self.elaborate_statement_value_with_target_type(statement, target_type); let id = self.interner.push_stmt(hir_statement); - self.interner.push_stmt_location(id, span, self.file); + self.interner.push_stmt_location(id, location); (id, typ) } @@ -91,15 +91,19 @@ impl<'context> Elaborator<'context> { let type_contains_unspecified = let_stmt.r#type.contains_unspecified(); let annotated_type = self.resolve_inferred_type(let_stmt.r#type); - let expr_span = let_stmt.expression.span; + let pattern_location = let_stmt.pattern.location(); + let expr_location = let_stmt.expression.location; let (expression, expr_type) = self.elaborate_expression_with_target_type(let_stmt.expression, Some(&annotated_type)); // Require the top-level of a global's type to be fully-specified if type_contains_unspecified && global_id.is_some() { - let span = expr_span; let expected_type = annotated_type.clone(); - let error = ResolverError::UnspecifiedGlobalType { span, expected_type }; + let error = ResolverError::UnspecifiedGlobalType { + pattern_location, + expr_location, + expected_type, + }; self.push_err(error); } @@ -110,11 +114,11 @@ impl<'context> Elaborator<'context> { // Now check if LHS is the same type as the RHS // Importantly, we do not coerce any types implicitly - self.unify_with_coercions(&expr_type, &annotated_type, expression, expr_span, || { + self.unify_with_coercions(&expr_type, &annotated_type, expression, expr_location, || { TypeCheckError::TypeMismatch { expected_typ: annotated_type.to_string(), expr_typ: expr_type.to_string(), - expr_span, + expr_location, } }); @@ -146,20 +150,23 @@ impl<'context> Elaborator<'context> { } pub(super) fn elaborate_assign(&mut self, assign: AssignStatement) -> (HirStatement, Type) { - let expr_span = assign.expression.span; + let expr_location = assign.expression.location; let (expression, expr_type) = self.elaborate_expression(assign.expression); let (lvalue, lvalue_type, mutable) = self.elaborate_lvalue(assign.lvalue); if !mutable { - let (name, span) = self.get_lvalue_name_and_span(&lvalue); - self.push_err(TypeCheckError::VariableMustBeMutable { name, span }); + let (_, name, location) = self.get_lvalue_error_info(&lvalue); + self.push_err(TypeCheckError::VariableMustBeMutable { name, location }); + } else { + let (id, name, location) = self.get_lvalue_error_info(&lvalue); + self.check_can_mutate_lambda_capture(id, name, location); } - self.unify_with_coercions(&expr_type, &lvalue_type, expression, expr_span, || { + self.unify_with_coercions(&expr_type, &lvalue_type, expression, expr_location, || { TypeCheckError::TypeMismatchWithSource { actual: expr_type.clone(), expected: lvalue_type.clone(), - span: expr_span, + location: expr_location, source: Source::Assignment, } }); @@ -173,14 +180,14 @@ impl<'context> Elaborator<'context> { ForRange::Range(bounds) => bounds.into_half_open(), ForRange::Array(_) => { let for_stmt = - for_loop.range.into_for(for_loop.identifier, for_loop.block, for_loop.span); + for_loop.range.into_for(for_loop.identifier, for_loop.block, for_loop.location); return self.elaborate_statement_value(for_stmt); } }; - let start_span = start.span; - let end_span = end.span; + let start_location = start.location; + let end_location = end.location; let (start_range, start_range_type) = self.elaborate_expression(start); let (end_range, end_range_type) = self.elaborate_expression(end); @@ -202,11 +209,10 @@ impl<'context> Elaborator<'context> { ); // Check that start range and end range have the same types - let range_span = start_span.merge(end_span); self.unify(&start_range_type, &end_range_type, || TypeCheckError::TypeMismatch { expected_typ: start_range_type.to_string(), expr_typ: end_range_type.to_string(), - expr_span: range_span, + expr_location: end_location, }); let expected_type = self.polymorphic_integer(); @@ -214,18 +220,18 @@ impl<'context> Elaborator<'context> { self.unify(&start_range_type, &expected_type, || TypeCheckError::TypeCannotBeUsed { typ: start_range_type.clone(), place: "for loop", - span: range_span, + location: start_location, }); self.interner.push_definition_type(identifier.id, start_range_type); - let block_span = block.type_span(); + let block_location = block.type_location(); let (block, block_type) = self.elaborate_expression(block); self.unify(&block_type, &Type::Unit, || TypeCheckError::TypeMismatch { expected_typ: Type::Unit.to_string(), expr_typ: block_type.to_string(), - expr_span: block_span, + expr_location: block_location, }); self.pop_scope(); @@ -240,24 +246,24 @@ impl<'context> Elaborator<'context> { pub(super) fn elaborate_loop( &mut self, block: Expression, - span: noirc_errors::Span, + location: Location, ) -> (HirStatement, Type) { let in_constrained_function = self.in_constrained_function(); if in_constrained_function { - self.push_err(ResolverError::LoopInConstrainedFn { span }); + self.push_err(ResolverError::LoopInConstrainedFn { location }); } let old_loop = std::mem::take(&mut self.current_loop); self.current_loop = Some(Loop { is_for: false, has_break: false }); self.push_scope(); - let block_span = block.type_span(); + let block_location = block.type_location(); let (block, block_type) = self.elaborate_expression(block); self.unify(&block_type, &Type::Unit, || TypeCheckError::TypeMismatch { expected_typ: Type::Unit.to_string(), expr_typ: block_type.to_string(), - expr_span: block_span, + expr_location: block_location, }); self.pop_scope(); @@ -265,7 +271,7 @@ impl<'context> Elaborator<'context> { let last_loop = std::mem::replace(&mut self.current_loop, old_loop).expect("Expected a loop"); if !last_loop.has_break { - self.push_err(ResolverError::LoopWithoutBreak { span }); + self.push_err(ResolverError::LoopWithoutBreak { location }); } let statement = HirStatement::Loop(block); @@ -276,29 +282,31 @@ impl<'context> Elaborator<'context> { pub(super) fn elaborate_while(&mut self, while_: WhileStatement) -> (HirStatement, Type) { let in_constrained_function = self.in_constrained_function(); if in_constrained_function { - self.push_err(ResolverError::WhileInConstrainedFn { span: while_.while_keyword_span }); + self.push_err(ResolverError::WhileInConstrainedFn { + location: while_.while_keyword_location, + }); } let old_loop = std::mem::take(&mut self.current_loop); self.current_loop = Some(Loop { is_for: false, has_break: false }); self.push_scope(); - let condition_span = while_.condition.type_span(); + let location = while_.condition.type_location(); let (condition, cond_type) = self.elaborate_expression(while_.condition); self.unify(&cond_type, &Type::Bool, || TypeCheckError::TypeMismatch { expected_typ: Type::Bool.to_string(), expr_typ: cond_type.to_string(), - expr_span: condition_span, + expr_location: location, }); - let block_span = while_.body.type_span(); + let block_location = while_.body.type_location(); let (block, block_type) = self.elaborate_expression(while_.body); self.unify(&block_type, &Type::Unit, || TypeCheckError::TypeMismatch { expected_typ: Type::Unit.to_string(), expr_typ: block_type.to_string(), - expr_span: block_span, + expr_location: block_location, }); self.pop_scope(); @@ -310,11 +318,11 @@ impl<'context> Elaborator<'context> { (statement, Type::Unit) } - fn elaborate_jump(&mut self, is_break: bool, span: noirc_errors::Span) -> (HirStatement, Type) { + fn elaborate_jump(&mut self, is_break: bool, location: Location) -> (HirStatement, Type) { let in_constrained_function = self.in_constrained_function(); if in_constrained_function { - self.push_err(ResolverError::JumpInConstrainedFn { is_break, span }); + self.push_err(ResolverError::JumpInConstrainedFn { is_break, location }); } if let Some(current_loop) = &mut self.current_loop { @@ -322,27 +330,27 @@ impl<'context> Elaborator<'context> { current_loop.has_break = true; } } else { - self.push_err(ResolverError::JumpOutsideLoop { is_break, span }); + self.push_err(ResolverError::JumpOutsideLoop { is_break, location }); } let expr = if is_break { HirStatement::Break } else { HirStatement::Continue }; (expr, self.interner.next_type_variable()) } - fn get_lvalue_name_and_span(&self, lvalue: &HirLValue) -> (String, Span) { + fn get_lvalue_error_info(&self, lvalue: &HirLValue) -> (DefinitionId, String, Location) { match lvalue { HirLValue::Ident(name, _) => { - let span = name.location.span; + let location = name.location; if let Some(definition) = self.interner.try_definition(name.id) { - (definition.name.clone(), span) + (name.id, definition.name.clone(), location) } else { - ("(undeclared variable)".into(), span) + (DefinitionId::dummy_id(), "(undeclared variable)".into(), location) } } - HirLValue::MemberAccess { object, .. } => self.get_lvalue_name_and_span(object), - HirLValue::Index { array, .. } => self.get_lvalue_name_and_span(array), - HirLValue::Dereference { lvalue, .. } => self.get_lvalue_name_and_span(lvalue), + HirLValue::MemberAccess { object, .. } => self.get_lvalue_error_info(object), + HirLValue::Index { array, .. } => self.get_lvalue_error_info(array), + HirLValue::Dereference { lvalue, .. } => self.get_lvalue_error_info(lvalue), } } @@ -350,8 +358,8 @@ impl<'context> Elaborator<'context> { match lvalue { LValue::Ident(ident) => { let mut mutable = true; - let span = ident.span(); - let path = Path::from_single(ident.0.contents, span); + let location = ident.location(); + let path = Path::from_single(ident.0.contents, location); let ((ident, scope_index), _) = self.get_ident_from_path(path); self.resolve_local_variable(ident.clone(), scope_index); @@ -365,7 +373,7 @@ impl<'context> Elaborator<'context> { if definition.comptime && !self.in_comptime_context() { self.push_err(ResolverError::MutatingComptimeInNonComptimeContext { name: definition.name.clone(), - span: ident.location.span, + location: ident.location, }); } } @@ -374,19 +382,17 @@ impl<'context> Elaborator<'context> { typ.follow_bindings() }; - let reference_location = Location::new(span, self.file); - self.interner.add_local_reference(ident.id, reference_location); + self.interner.add_local_reference(ident.id, location); (HirLValue::Ident(ident.clone(), typ.clone()), typ, mutable) } - LValue::MemberAccess { object, field_name, span } => { + LValue::MemberAccess { object, field_name, location } => { let (object, lhs_type, mut mutable) = self.elaborate_lvalue(*object); let mut object = Box::new(object); let field_name = field_name.clone(); let object_ref = &mut object; let mutable_ref = &mut mutable; - let location = Location::new(span, self.file); let dereference_lhs = move |_: &mut Self, _, element_type| { // We must create a temporary value first to move out of object_ref before @@ -403,7 +409,12 @@ impl<'context> Elaborator<'context> { let name = &field_name.0.contents; let (object_type, field_index) = self - .check_field_access(&lhs_type, name, field_name.span(), Some(dereference_lhs)) + .check_field_access( + &lhs_type, + name, + field_name.location(), + Some(dereference_lhs), + ) .unwrap_or((Type::Error, 0)); let field_index = Some(field_index); @@ -412,16 +423,15 @@ impl<'context> Elaborator<'context> { HirLValue::MemberAccess { object, field_name, field_index, typ, location }; (lvalue, object_type, mutable) } - LValue::Index { array, index, span } => { - let expr_span = index.span; + LValue::Index { array, index, location } => { + let expr_location = index.location; let (index, index_type) = self.elaborate_expression(index); - let location = Location::new(span, self.file); let expected = self.polymorphic_integer_or_field(); self.unify(&index_type, &expected, || TypeCheckError::TypeMismatch { expected_typ: "an integer".to_owned(), expr_typ: index_type.to_string(), - expr_span, + expr_location, }); let (mut lvalue, mut lvalue_type, mut mutable) = self.elaborate_lvalue(*array); @@ -442,19 +452,22 @@ impl<'context> Elaborator<'context> { Type::Slice(elem_type) => *elem_type, Type::Error => Type::Error, Type::String(_) => { - let (_lvalue_name, lvalue_span) = self.get_lvalue_name_and_span(&lvalue); - self.push_err(TypeCheckError::StringIndexAssign { span: lvalue_span }); + let (_id, _lvalue_name, lvalue_location) = + self.get_lvalue_error_info(&lvalue); + self.push_err(TypeCheckError::StringIndexAssign { + location: lvalue_location, + }); Type::Error } Type::TypeVariable(_) => { - self.push_err(TypeCheckError::TypeAnnotationsNeededForIndex { span }); + self.push_err(TypeCheckError::TypeAnnotationsNeededForIndex { location }); Type::Error } other => { self.push_err(TypeCheckError::TypeMismatch { expected_typ: "array".to_string(), expr_typ: other.to_string(), - expr_span: span, + expr_location: location, }); Type::Error } @@ -464,10 +477,9 @@ impl<'context> Elaborator<'context> { let array_type = typ.clone(); (HirLValue::Index { array, index, typ, location }, array_type, mutable) } - LValue::Dereference(lvalue, span) => { + LValue::Dereference(lvalue, location) => { let (lvalue, reference_type, _) = self.elaborate_lvalue(*lvalue); let lvalue = Box::new(lvalue); - let location = Location::new(span, self.file); let element_type = Type::type_variable(self.interner.next_type_variable_id()); let expected_type = Type::MutableReference(Box::new(element_type.clone())); @@ -475,7 +487,7 @@ impl<'context> Elaborator<'context> { self.unify(&reference_type, &expected_type, || TypeCheckError::TypeMismatch { expected_typ: expected_type.to_string(), expr_typ: reference_type.to_string(), - expr_span: span, + expr_location: location, }); // Dereferences are always mutable since we already type checked against a &mut T @@ -483,8 +495,8 @@ impl<'context> Elaborator<'context> { let lvalue = HirLValue::Dereference { lvalue, element_type, location }; (lvalue, typ, true) } - LValue::Interned(id, span) => { - let lvalue = self.interner.get_lvalue(id, span).clone(); + LValue::Interned(id, location) => { + let lvalue = self.interner.get_lvalue(id, location).clone(); self.elaborate_lvalue(lvalue) } } @@ -495,7 +507,7 @@ impl<'context> Elaborator<'context> { &mut self, lhs_type: &Type, field_name: &str, - span: Span, + location: Location, dereference_lhs: Option, ) -> Option<(Type, usize)> { let lhs_type = lhs_type.follow_bindings(); @@ -504,10 +516,9 @@ impl<'context> Elaborator<'context> { Type::DataType(s, args) => { let s = s.borrow(); if let Some((field, visibility, index)) = s.get_field(field_name, args) { - let reference_location = Location::new(span, self.file); - self.interner.add_struct_member_reference(s.id, index, reference_location); + self.interner.add_struct_member_reference(s.id, index, location); - self.check_struct_field_visibility(&s, field_name, visibility, span); + self.check_struct_field_visibility(&s, field_name, visibility, location); return Some((field, index)); } @@ -522,7 +533,7 @@ impl<'context> Elaborator<'context> { index, lhs_type, length, - span, + location, }); return None; } @@ -536,12 +547,12 @@ impl<'context> Elaborator<'context> { return self.check_field_access( element, field_name, - span, + location, Some(dereference_lhs), ); } else { let (element, index) = - self.check_field_access(element, field_name, span, dereference_lhs)?; + self.check_field_access(element, field_name, location, dereference_lhs)?; return Some((Type::MutableReference(Box::new(element)), index)); } } @@ -551,12 +562,12 @@ impl<'context> Elaborator<'context> { // If we get here the type has no field named 'access.rhs'. // Now we specialize the error message based on whether we know the object type in question yet. if let Type::TypeVariable(..) = &lhs_type { - self.push_err(TypeCheckError::TypeAnnotationsNeededForFieldAccess { span }); + self.push_err(TypeCheckError::TypeAnnotationsNeededForFieldAccess { location }); } else if lhs_type != Type::Error { self.push_err(TypeCheckError::AccessUnknownMember { lhs_type, field_name: field_name.to_string(), - span, + location, }); } @@ -568,7 +579,7 @@ impl<'context> Elaborator<'context> { struct_type: &DataType, field_name: &str, visibility: ItemVisibility, - span: Span, + location: Location, ) { if self.silence_field_visibility_errors > 0 { return; @@ -576,18 +587,18 @@ impl<'context> Elaborator<'context> { if !struct_member_is_visible(struct_type.id, visibility, self.module_id(), self.def_maps) { self.push_err(ResolverError::PathResolutionError(PathResolutionError::Private( - Ident::new(field_name.to_string(), span), + Ident::new(field_name.to_string(), location), ))); } } fn elaborate_comptime_statement(&mut self, statement: Statement) -> (HirStatement, Type) { - let span = statement.span; + let location = statement.location; let (hir_statement, _typ) = self.elaborate_in_comptime_context(|this| this.elaborate_statement(statement)); let mut interpreter = self.setup_interpreter(); let value = interpreter.evaluate_statement(hir_statement); - let (expr, typ) = self.inline_comptime_value(value, span); + let (expr, typ) = self.inline_comptime_value(value, location); let location = self.interner.id_location(hir_statement); self.debug_comptime(location, |interner| expr.to_display_ast(interner).kind); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/trait_impls.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/trait_impls.rs index aa27ac29fa67..63befb67b093 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/trait_impls.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/trait_impls.rs @@ -1,23 +1,25 @@ use crate::{ + ResolvedGeneric, ast::{Ident, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, graph::CrateId, - hir::def_collector::{dc_crate::UnresolvedTraitImpl, errors::DefCollectorErrorKind}, + hir::def_collector::{ + dc_crate::{CompilationError, UnresolvedTraitImpl}, + errors::DefCollectorErrorKind, + }, node_interner::TraitImplId, - ResolvedGeneric, }; use crate::{ + Type, hir::def_collector::errors::DuplicateType, hir_def::traits::{TraitConstraint, TraitFunction}, node_interner::{FuncId, TraitId}, - Type, }; -use noirc_errors::Location; use rustc_hash::FxHashSet as HashSet; use super::Elaborator; -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { pub(super) fn collect_trait_impl_methods( &mut self, trait_id: TraitId, @@ -25,7 +27,6 @@ impl<'context> Elaborator<'context> { trait_impl_where_clause: &[TraitConstraint], ) { self.local_module = trait_impl.module_id; - self.file = trait_impl.file_id; let impl_id = trait_impl.impl_id.expect("impl_id should be set in define_function_metas"); @@ -59,7 +60,7 @@ impl<'context> Elaborator<'context> { let func_id = self.interner.push_empty_fn(); let module = self.module_id(); - let location = Location::new(default_impl.def.span, trait_impl.file_id); + let location = default_impl.def.location; self.interner.push_function(func_id, &default_impl.def, module, location); self.define_function_meta(&mut default_impl_clone, func_id, None); func_ids_in_trait.insert(func_id); @@ -72,7 +73,7 @@ impl<'context> Elaborator<'context> { self.push_err(DefCollectorErrorKind::TraitMissingMethod { trait_name: self.interner.get_trait(trait_id).name.clone(), method_name: method.name.clone(), - trait_impl_span: trait_impl.object_type.span, + trait_impl_location: trait_impl.object_type.location, }); } } else { @@ -105,14 +106,17 @@ impl<'context> Elaborator<'context> { let the_trait = self.interner.get_trait_mut(trait_id); the_trait.set_methods(methods); + let trait_name = the_trait.name.clone(); + // Emit MethodNotInTrait error for methods in the impl block that // don't have a corresponding method signature defined in the trait for (_, func_id, func) in &trait_impl.methods.functions { if !func_ids_in_trait.contains(func_id) { - let trait_name = the_trait.name.clone(); + let trait_name = trait_name.clone(); let impl_method = func.name_ident().clone(); let error = DefCollectorErrorKind::MethodNotInTrait { trait_name, impl_method }; - self.errors.push((error.into(), self.file)); + let error: CompilationError = error.into(); + self.push_err(error); } } @@ -200,9 +204,9 @@ impl<'context> Elaborator<'context> { constraint_typ: override_trait_constraint.typ, constraint_name: the_trait.name.0.contents.clone(), constraint_generics: override_trait_constraint.trait_bound.trait_generics, - constraint_span: override_trait_constraint.trait_bound.span, + constraint_location: override_trait_constraint.trait_bound.location, trait_method_name: method.name.0.contents.clone(), - trait_method_span: method.location.span, + trait_method_location: method.location, }); } } @@ -214,7 +218,6 @@ impl<'context> Elaborator<'context> { trait_impl: &UnresolvedTraitImpl, ) { self.local_module = trait_impl.module_id; - self.file = trait_impl.file_id; let object_crate = match &trait_impl.resolved_object_type { Some(Type::DataType(struct_type, _)) => struct_type.borrow().id.krate(), @@ -224,7 +227,7 @@ impl<'context> Elaborator<'context> { let the_trait = self.interner.get_trait(trait_id); if self.crate_id != the_trait.crate_id && self.crate_id != object_crate { self.push_err(DefCollectorErrorKind::TraitImplOrphaned { - span: trait_impl.object_type.span, + location: trait_impl.object_type.location, }); } } @@ -235,12 +238,12 @@ impl<'context> Elaborator<'context> { ) -> Vec<(Ident, UnresolvedType)> { let mut associated_types = Vec::new(); for (name, _, expr) in trait_impl.associated_constants.drain(..) { - let span = expr.span; - let typ = match UnresolvedTypeExpression::from_expr(expr, span) { - Ok(expr) => UnresolvedTypeData::Expression(expr).with_span(span), + let location = expr.location; + let typ = match UnresolvedTypeExpression::from_expr(expr, location) { + Ok(expr) => UnresolvedTypeData::Expression(expr).with_location(location), Err(error) => { self.push_err(error); - UnresolvedTypeData::Error.with_span(span) + UnresolvedTypeData::Error.with_location(location) } }; associated_types.push((name, typ)); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs index bbc1214de9ed..a931dde93de5 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs @@ -1,9 +1,10 @@ use std::{collections::BTreeMap, rc::Rc}; use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use crate::{ + ResolvedGeneric, Type, TypeBindings, ast::{ BlockExpression, FunctionDefinition, FunctionKind, FunctionReturnType, Ident, ItemVisibility, NoirFunction, TraitItem, UnresolvedGeneric, UnresolvedGenerics, @@ -15,12 +16,11 @@ use crate::{ traits::{ResolvedTraitBound, TraitFunction}, }, node_interner::{DependencyId, FuncId, NodeInterner, ReferenceId, TraitId}, - ResolvedGeneric, Type, TypeBindings, }; use super::Elaborator; -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { pub fn collect_traits(&mut self, traits: &mut BTreeMap) { for (trait_id, unresolved_trait) in traits { self.local_module = unresolved_trait.module_id; @@ -97,7 +97,6 @@ impl<'context> Elaborator<'context> { unresolved_trait: &UnresolvedTrait, ) -> Vec { self.local_module = unresolved_trait.module_id; - self.file = self.def_maps[&self.crate_id].file_id(unresolved_trait.module_id); let mut functions = vec![]; @@ -117,15 +116,15 @@ impl<'context> Elaborator<'context> { self.recover_generics(|this| { let the_trait = this.interner.get_trait(trait_id); let self_typevar = the_trait.self_type_typevar.clone(); - let name_span = the_trait.name.span(); + let name_location = the_trait.name.location(); this.add_existing_generic( &UnresolvedGeneric::Variable(Ident::from("Self")), - name_span, + name_location, &ResolvedGeneric { name: Rc::new("Self".to_owned()), type_var: self_typevar, - span: name_span, + location: name_location, }, ); @@ -274,6 +273,7 @@ impl<'context> Elaborator<'context> { pub(crate) fn check_trait_impl_method_matches_declaration( interner: &mut NodeInterner, function: FuncId, + noir_function: &NoirFunction, ) -> Vec { let meta = interner.function_meta(&function); let modifiers = interner.function_modifiers(&function); @@ -297,9 +297,9 @@ pub(crate) fn check_trait_impl_method_matches_declaration( if trait_info.generics.len() != impl_.trait_generics.len() { let expected = trait_info.generics.len(); let found = impl_.trait_generics.len(); - let span = impl_.ident.span(); + let location = impl_.ident.location(); let item = trait_info.name.to_string(); - errors.push(TypeCheckError::GenericCountMismatch { item, expected, found, span }); + errors.push(TypeCheckError::GenericCountMismatch { item, expected, found, location }); } // Substitute each generic on the trait with the corresponding generic on the impl @@ -319,17 +319,17 @@ pub(crate) fn check_trait_impl_method_matches_declaration( if modifiers.is_unconstrained != trait_fn_modifiers.is_unconstrained { let expected = trait_fn_modifiers.is_unconstrained; - let span = meta.name.location.span; + let location = meta.name.location; let item = method_name.to_string(); - errors.push(TypeCheckError::UnconstrainedMismatch { item, expected, span }); + errors.push(TypeCheckError::UnconstrainedMismatch { item, expected, location }); } if trait_fn_meta.direct_generics.len() != meta.direct_generics.len() { let expected = trait_fn_meta.direct_generics.len(); let found = meta.direct_generics.len(); - let span = meta.name.location.span; + let location = meta.name.location; let item = method_name.to_string(); - errors.push(TypeCheckError::GenericCountMismatch { item, expected, found, span }); + errors.push(TypeCheckError::GenericCountMismatch { item, expected, found, location }); } // Substitute each generic on the trait function with the corresponding generic on the impl function @@ -350,7 +350,9 @@ pub(crate) fn check_trait_impl_method_matches_declaration( definition_type, method_name, &meta.parameters, - meta.name.location.span, + &meta.return_type, + noir_function, + meta.name.location, &trait_info.name.0.contents, &mut errors, ); @@ -359,12 +361,15 @@ pub(crate) fn check_trait_impl_method_matches_declaration( errors } +#[allow(clippy::too_many_arguments)] fn check_function_type_matches_expected_type( expected: &Type, actual: &Type, method_name: &str, actual_parameters: &Parameters, - span: Span, + actual_return_type: &FunctionReturnType, + noir_function: &NoirFunction, + location: Location, trait_name: &str, errors: &mut Vec, ) { @@ -382,11 +387,16 @@ fn check_function_type_matches_expected_type( if params_a.len() == params_b.len() { for (i, (a, b)) in params_a.iter().zip(params_b.iter()).enumerate() { if a.try_unify(b, &mut bindings).is_err() { + let parameter_location = noir_function.def.parameters.get(i); + let parameter_location = parameter_location.map(|param| param.typ.location); + let parameter_location = + parameter_location.unwrap_or_else(|| actual_parameters.0[i].0.location()); + errors.push(TypeCheckError::TraitMethodParameterTypeMismatch { method_name: method_name.to_string(), expected_typ: a.to_string(), actual_typ: b.to_string(), - parameter_span: actual_parameters.0[i].0.span(), + parameter_location, parameter_index: i + 1, }); } @@ -396,7 +406,7 @@ fn check_function_type_matches_expected_type( errors.push(TypeCheckError::TypeMismatch { expected_typ: ret_a.to_string(), expr_typ: ret_b.to_string(), - expr_span: span, + expr_location: actual_return_type.location(), }); } } else { @@ -405,7 +415,7 @@ fn check_function_type_matches_expected_type( expected_num_parameters: params_a.len(), trait_name: trait_name.to_string(), method_name: method_name.to_string(), - span, + location, }); } } @@ -416,6 +426,10 @@ fn check_function_type_matches_expected_type( if !bindings.is_empty() { let expected_typ = expected.to_string(); let expr_typ = actual.to_string(); - errors.push(TypeCheckError::TypeMismatch { expected_typ, expr_typ, expr_span: span }); + errors.push(TypeCheckError::TypeMismatch { + expected_typ, + expr_typ, + expr_location: location, + }); } } 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 53d0860ebf12..fb6431610269 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs @@ -2,10 +2,11 @@ use std::{borrow::Cow, rc::Rc}; use im::HashSet; use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use rustc_hash::FxHashMap as HashMap; use crate::{ + Generics, Kind, ResolvedGeneric, Type, TypeBinding, TypeBindings, UnificationError, ast::{ AsTraitPath, BinaryOpKind, GenericTypeArgs, Ident, IntegerBitSize, Path, PathKind, Signedness, UnaryOp, UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, @@ -13,11 +14,11 @@ use crate::{ }, hir::{ def_collector::dc_crate::CompilationError, - def_map::{fully_qualified_module_path, ModuleDefId}, + def_map::{ModuleDefId, fully_qualified_module_path}, resolution::{errors::ResolverError, import::PathResolutionError}, type_check::{ - generics::{Generic, TraitGenerics}, NoMatchingImplFoundError, Source, TypeCheckError, + generics::{Generic, TraitGenerics}, }, }, hir_def::{ @@ -30,14 +31,14 @@ use crate::{ traits::{NamedType, ResolvedTraitBound, Trait, TraitConstraint}, }, node_interner::{ - DependencyId, ExprId, FuncId, GlobalValue, ImplSearchErrorKind, NodeInterner, TraitId, - TraitImplKind, TraitMethodId, + DependencyId, ExprId, FuncId, GlobalValue, ImplSearchErrorKind, TraitId, TraitImplKind, + TraitMethodId, }, + signed_field::SignedField, token::SecondaryAttribute, - Generics, Kind, ResolvedGeneric, Type, TypeBinding, TypeBindings, UnificationError, }; -use super::{lints, path_resolution::PathResolutionItem, Elaborator, UnsafeBlockStatus}; +use super::{Elaborator, UnsafeBlockStatus, lints, path_resolution::PathResolutionItem}; pub const SELF_TYPE_NAME: &str = "Self"; @@ -47,13 +48,13 @@ pub(super) struct TraitPathResolution { pub(super) errors: Vec, } -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { /// Translates an UnresolvedType to a Type with a `TypeKind::Normal` pub(crate) fn resolve_type(&mut self, typ: UnresolvedType) -> Type { - let span = typ.span; + let location = typ.location; let resolved_type = self.resolve_type_inner(typ, &Kind::Normal); if resolved_type.is_nested_slice() { - self.push_err(ResolverError::NestedSlices { span }); + self.push_err(ResolverError::NestedSlices { location }); } resolved_type } @@ -63,11 +64,11 @@ impl<'context> Elaborator<'context> { pub fn resolve_type_inner(&mut self, typ: UnresolvedType, kind: &Kind) -> Type { use crate::ast::UnresolvedTypeData::*; - let span = typ.span; - let (named_path_span, is_self_type_name, is_synthetic) = + let location = typ.location; + let (named_path_location, is_self_type_name, is_synthetic) = if let Named(ref named_path, _, synthetic) = typ.typ { ( - Some(named_path.last_ident().span()), + Some(named_path.last_ident().location()), named_path.last_ident().is_self_type_name(), synthetic, ) @@ -79,38 +80,38 @@ impl<'context> Elaborator<'context> { FieldElement => Type::FieldElement, Array(size, elem) => { let elem = Box::new(self.resolve_type_inner(*elem, kind)); - let size = self.convert_expression_type(size, &Kind::u32(), span); + let size = self.convert_expression_type(size, &Kind::u32(), location); Type::Array(Box::new(size), elem) } Slice(elem) => { let elem = Box::new(self.resolve_type_inner(*elem, kind)); Type::Slice(elem) } - Expression(expr) => self.convert_expression_type(expr, kind, span), + Expression(expr) => self.convert_expression_type(expr, kind, location), Integer(sign, bits) => Type::Integer(sign, bits), Bool => Type::Bool, String(size) => { - let resolved_size = self.convert_expression_type(size, &Kind::u32(), span); + let resolved_size = self.convert_expression_type(size, &Kind::u32(), location); Type::String(Box::new(resolved_size)) } FormatString(size, fields) => { - let resolved_size = self.convert_expression_type(size, &Kind::u32(), span); + let resolved_size = self.convert_expression_type(size, &Kind::u32(), location); let fields = self.resolve_type_inner(*fields, kind); Type::FmtString(Box::new(resolved_size), Box::new(fields)) } Quoted(quoted) => { let in_function = matches!(self.current_item, Some(DependencyId::Function(_))); if in_function && !self.in_comptime_context() { - let span = typ.span; + let location = typ.location; let typ = quoted.to_string(); - self.push_err(ResolverError::ComptimeTypeInRuntimeCode { span, typ }); + self.push_err(ResolverError::ComptimeTypeInRuntimeCode { location, typ }); } Type::Quoted(quoted) } Unit => Type::Unit, Unspecified => { - let span = typ.span; - self.push_err(TypeCheckError::UnspecifiedType { span }); + let location = typ.location; + self.push_err(TypeCheckError::UnspecifiedType { location }); Type::Error } Error => Type::Error, @@ -123,7 +124,7 @@ impl<'context> Elaborator<'context> { Function(args, ret, env, unconstrained) => { let args = vecmap(args, |arg| self.resolve_type_inner(arg, kind)); let ret = Box::new(self.resolve_type_inner(*ret, kind)); - let env_span = env.span; + let env_location = env.location; let env = Box::new(self.resolve_type_inner(*env, kind)); @@ -134,7 +135,7 @@ impl<'context> Elaborator<'context> { _ => { self.push_err(ResolverError::InvalidClosureEnvironment { typ: *env, - span: env_span, + location: env_location, }); Type::Error } @@ -148,11 +149,11 @@ impl<'context> Elaborator<'context> { AsTraitPath(path) => self.resolve_as_trait_path(*path), Interned(id) => { let typ = self.interner.get_unresolved_type_data(id).clone(); - return self.resolve_type_inner(UnresolvedType { typ, span }, kind); + return self.resolve_type_inner(UnresolvedType { typ, location }, kind); } }; - let location = Location::new(named_path_span.unwrap_or(typ.span), self.file); + let location = named_path_location.unwrap_or(typ.location); match resolved_type { Type::DataType(ref data_type, _) => { // Record the location of the type reference @@ -173,11 +174,11 @@ impl<'context> Elaborator<'context> { if !kind.unifies(&resolved_type.kind()) { let expected_typ_err = CompilationError::TypeError(TypeCheckError::TypeKindMismatch { - expected_kind: kind.to_string(), - expr_kind: resolved_type.kind().to_string(), - expr_span: span, + expected_kind: kind.clone(), + expr_kind: resolved_type.kind(), + expr_location: location, }); - self.errors.push((expected_typ_err, self.file)); + self.push_err(expected_typ_err); return Type::Error; } @@ -223,7 +224,9 @@ impl<'context> Elaborator<'context> { if name == SELF_TYPE_NAME { if let Some(self_type) = self.self_type.clone() { if !args.is_empty() { - self.push_err(ResolverError::GenericsOnSelfType { span: path.span() }); + self.push_err(ResolverError::GenericsOnSelfType { + location: path.location, + }); } return self_type; } @@ -232,16 +235,16 @@ impl<'context> Elaborator<'context> { } } else if let Some(typ) = self.lookup_associated_type_on_self(&path) { if !args.is_empty() { - self.push_err(ResolverError::GenericsOnAssociatedType { span: path.span() }); + self.push_err(ResolverError::GenericsOnAssociatedType { location: path.location }); } return typ; } - let span = path.span(); + let location = path.location; if let Some(type_alias) = self.lookup_type_alias(path.clone()) { let id = type_alias.borrow().id; - let (args, _) = self.resolve_type_args(args, id, path.span()); + let (args, _) = self.resolve_type_args(args, id, location); if let Some(item) = self.current_item { self.interner.add_type_alias_dependency(item, id); @@ -249,7 +252,7 @@ impl<'context> Elaborator<'context> { // Collecting Type Alias references [Location]s to be used by LSP in order // to resolve the definition of the type alias - self.interner.add_type_alias_ref(id, Location::new(span, self.file)); + self.interner.add_type_alias_ref(id, location); // Because there is no ordering to when type aliases (and other globals) are resolved, // it is possible for one to refer to an Error type and issue no error if it is set @@ -263,7 +266,7 @@ impl<'context> Elaborator<'context> { Some(data_type) => { if self.resolving_ids.contains(&data_type.borrow().id) { self.push_err(ResolverError::SelfReferentialType { - span: data_type.borrow().name.span(), + location: data_type.borrow().name.location(), }); return Type::Error; @@ -277,11 +280,11 @@ impl<'context> Elaborator<'context> { .any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) { self.push_err(ResolverError::AbiAttributeOutsideContract { - span: data_type.borrow().name.span(), + location: data_type.borrow().name.location(), }); } - let (args, _) = self.resolve_type_args(args, data_type.borrow(), span); + let (args, _) = self.resolve_type_args(args, data_type.borrow(), location); if let Some(current_item) = self.current_item { let dependency_id = data_type.borrow().id; @@ -297,11 +300,11 @@ impl<'context> Elaborator<'context> { fn resolve_trait_as_type(&mut self, path: Path, args: GenericTypeArgs) -> Type { // Fetch information needed from the trait as the closure for resolving all the `args` // requires exclusive access to `self` - let span = path.span; + let location = path.location; let trait_as_type_info = self.lookup_trait_or_error(path).map(|t| t.id); if let Some(id) = trait_as_type_info { - let (ordered, named) = self.resolve_type_args(args, id, span); + let (ordered, named) = self.resolve_type_args(args, id, location); let name = self.interner.get_trait(id).name.to_string(); let generics = TraitGenerics { ordered, named }; Type::TraitAsType(id, Rc::new(name), generics) @@ -316,25 +319,25 @@ impl<'context> Elaborator<'context> { &mut self, args: GenericTypeArgs, item: TraitId, - span: Span, + location: Location, ) -> (Vec, Vec) { - self.resolve_type_args_inner(args, item, span, false) + self.resolve_type_args_inner(args, item, location, false) } pub(super) fn resolve_type_args( &mut self, args: GenericTypeArgs, item: impl Generic, - span: Span, + location: Location, ) -> (Vec, Vec) { - self.resolve_type_args_inner(args, item, span, true) + self.resolve_type_args_inner(args, item, location, true) } pub(super) fn resolve_type_args_inner( &mut self, mut args: GenericTypeArgs, item: impl Generic, - span: Span, + location: Location, allow_implicit_named_args: bool, ) -> (Vec, Vec) { let expected_kinds = item.generics(self.interner); @@ -344,9 +347,9 @@ impl<'context> Elaborator<'context> { item: item.item_name(self.interner), expected: expected_kinds.len(), found: args.ordered_args.len(), - span, + location, }); - let error_type = UnresolvedTypeData::Error.with_span(span); + let error_type = UnresolvedTypeData::Error.with_location(location); args.ordered_args.resize(expected_kinds.len(), error_type); } @@ -360,12 +363,12 @@ impl<'context> Elaborator<'context> { associated = self.resolve_associated_type_args( args.named_args, item, - span, + location, allow_implicit_named_args, ); } else if !args.named_args.is_empty() { let item_kind = item.item_kind(); - self.push_err(ResolverError::NamedTypeArgs { span, item_kind }); + self.push_err(ResolverError::NamedTypeArgs { location, item_kind }); } (ordered, associated) @@ -375,7 +378,7 @@ impl<'context> Elaborator<'context> { &mut self, args: Vec<(Ident, UnresolvedType)>, item: impl Generic, - span: Span, + location: Location, allow_implicit_named_args: bool, ) -> Vec { let mut seen_args = HashMap::default(); @@ -389,8 +392,8 @@ impl<'context> Elaborator<'context> { required_args.iter().position(|item| item.name.as_ref() == &name.0.contents); let Some(index) = index else { - if let Some(prev_span) = seen_args.get(&name.0.contents).copied() { - self.push_err(TypeCheckError::DuplicateNamedTypeArg { name, prev_span }); + if let Some(prev_location) = seen_args.get(&name.0.contents).copied() { + self.push_err(TypeCheckError::DuplicateNamedTypeArg { name, prev_location }); } else { let item = item.item_name(self.interner); self.push_err(TypeCheckError::NoSuchNamedTypeArg { name, item }); @@ -400,7 +403,7 @@ impl<'context> Elaborator<'context> { // Remove the argument from the required list so we remember that we already have it let expected = required_args.remove(index); - seen_args.insert(name.0.contents.clone(), name.span()); + seen_args.insert(name.0.contents.clone(), name.location()); let typ = self.resolve_type_inner(typ, &expected.kind()); resolved.push(NamedType { name, typ }); @@ -412,12 +415,12 @@ impl<'context> Elaborator<'context> { let name = generic.name.clone(); if allow_implicit_named_args { - let name = Ident::new(name.as_ref().clone(), span); + let name = Ident::new(name.as_ref().clone(), location); let typ = self.interner.next_type_variable(); resolved.push(NamedType { name, typ }); } else { let item = item.item_name(self.interner); - self.push_err(TypeCheckError::MissingNamedTypeArg { item, span, name }); + self.push_err(TypeCheckError::MissingNamedTypeArg { item, location, name }); } } @@ -442,7 +445,7 @@ impl<'context> Elaborator<'context> { self.interner.add_global_dependency(current_item, id); } - let reference_location = Location::new(path.span(), self.file); + let reference_location = path.location; self.interner.add_global_reference(id, reference_location); let kind = self .interner @@ -461,26 +464,33 @@ impl<'context> Elaborator<'context> { }; let rhs = stmt.expression; - let span = self.interner.expr_span(&rhs); + let location = self.interner.expr_location(&rhs); let GlobalValue::Resolved(global_value) = &self.interner.get_global(id).value else { - self.push_err(ResolverError::UnevaluatedGlobalType { span }); + self.push_err(ResolverError::UnevaluatedGlobalType { location }); return None; }; let Some(global_value) = global_value.to_field_element() else { let global_value = global_value.clone(); if global_value.is_integral() { - self.push_err(ResolverError::NegativeGlobalType { span, global_value }); + self.push_err(ResolverError::NegativeGlobalType { location, global_value }); } else { - self.push_err(ResolverError::NonIntegralGlobalType { span, global_value }); + self.push_err(ResolverError::NonIntegralGlobalType { + location, + global_value, + }); } return None; }; - let Ok(global_value) = kind.ensure_value_fits(global_value, span) else { - self.push_err(ResolverError::GlobalLargerThanKind { span, global_value, kind }); + let Ok(global_value) = kind.ensure_value_fits(global_value, location) else { + self.push_err(ResolverError::GlobalLargerThanKind { + location, + global_value, + kind, + }); return None; }; @@ -494,42 +504,38 @@ impl<'context> Elaborator<'context> { &mut self, length: UnresolvedTypeExpression, expected_kind: &Kind, - span: Span, + location: Location, ) -> Type { match length { UnresolvedTypeExpression::Variable(path) => { let typ = self.resolve_named_type(path, GenericTypeArgs::default()); - self.check_kind(typ, expected_kind, span) + self.check_kind(typ, expected_kind, location) } UnresolvedTypeExpression::Constant(int, _span) => { Type::Constant(int, expected_kind.clone()) } - UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, span) => { - let (lhs_span, rhs_span) = (lhs.span(), rhs.span()); - let lhs = self.convert_expression_type(*lhs, expected_kind, lhs_span); - let rhs = self.convert_expression_type(*rhs, expected_kind, rhs_span); + UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, location) => { + let (lhs_location, rhs_location) = (lhs.location(), rhs.location()); + let lhs = self.convert_expression_type(*lhs, expected_kind, lhs_location); + let rhs = self.convert_expression_type(*rhs, expected_kind, rhs_location); match (lhs, rhs) { (Type::Constant(lhs, lhs_kind), Type::Constant(rhs, rhs_kind)) => { if !lhs_kind.unifies(&rhs_kind) { self.push_err(TypeCheckError::TypeKindMismatch { - expected_kind: lhs_kind.to_string(), - expr_kind: rhs_kind.to_string(), - expr_span: span, + expected_kind: lhs_kind, + expr_kind: rhs_kind, + expr_location: location, }); return Type::Error; } - match op.function(lhs, rhs, &lhs_kind, span) { + match op.function(lhs, rhs, &lhs_kind, location) { Ok(result) => Type::Constant(result, lhs_kind), Err(err) => { let err = Box::new(err); - self.push_err(ResolverError::BinaryOpError { - lhs, - op, - rhs, - err, - span, - }); + let error = + ResolverError::BinaryOpError { lhs, op, rhs, err, location }; + self.push_err(error); Type::Error } } @@ -543,17 +549,17 @@ impl<'context> Elaborator<'context> { } UnresolvedTypeExpression::AsTraitPath(path) => { let typ = self.resolve_as_trait_path(*path); - self.check_kind(typ, expected_kind, span) + self.check_kind(typ, expected_kind, location) } } } - fn check_kind(&mut self, typ: Type, expected_kind: &Kind, span: Span) -> Type { + fn check_kind(&mut self, typ: Type, expected_kind: &Kind, location: Location) -> Type { if !typ.kind().unifies(expected_kind) { self.push_err(TypeCheckError::TypeKindMismatch { - expected_kind: expected_kind.to_string(), - expr_kind: typ.kind().to_string(), - expr_span: span, + expected_kind: expected_kind.clone(), + expr_kind: typ.kind(), + expr_location: location, }); return Type::Error; } @@ -561,19 +567,20 @@ impl<'context> Elaborator<'context> { } fn resolve_as_trait_path(&mut self, path: AsTraitPath) -> Type { - let span = path.trait_path.span; + let location = path.trait_path.location; let Some(trait_id) = self.resolve_trait_by_path(path.trait_path.clone()) else { // Error should already be pushed in the None case return Type::Error; }; - let (ordered, named) = self.resolve_type_args(path.trait_generics.clone(), trait_id, span); + let (ordered, named) = + self.resolve_type_args(path.trait_generics.clone(), trait_id, location); let object_type = self.resolve_type(path.typ.clone()); match self.interner.lookup_trait_implementation(&object_type, trait_id, &ordered, &named) { Ok(impl_kind) => self.get_associated_type_from_trait_impl(path, impl_kind), Err(constraints) => { - self.push_trait_constraint_error(&object_type, constraints, span); + self.push_trait_constraint_error(&object_type, constraints, location); Type::Error } } @@ -622,7 +629,7 @@ impl<'context> Elaborator<'context> { if name == SELF_TYPE_NAME { let the_trait = self.interner.get_trait(trait_id); let method = the_trait.find_method(method.0.contents.as_str())?; - let constraint = the_trait.as_constraint(path.span); + let constraint = the_trait.as_constraint(path.location); return Some(TraitPathResolution { method: TraitMethod { method_id: method, constraint, assumed: true }, item: None, @@ -643,7 +650,7 @@ impl<'context> Elaborator<'context> { let meta = self.interner.try_function_meta(&func_id)?; let the_trait = self.interner.get_trait(meta.trait_id?); let method = the_trait.find_method(path.last_name())?; - let constraint = the_trait.as_constraint(path.span); + let constraint = the_trait.as_constraint(path.location); Some(TraitPathResolution { method: TraitMethod { method_id: method, constraint, assumed: false }, item: Some(path_resolution.item), @@ -691,7 +698,7 @@ impl<'context> Elaborator<'context> { } let mut path = path.clone(); - let span = path.span(); + let location = path.location; let last_segment = path.pop(); let before_last_segment = path.last_segment(); @@ -716,7 +723,7 @@ impl<'context> Elaborator<'context> { } let (hir_method_reference, error) = - self.get_trait_method_in_scope(&trait_methods, method_name, last_segment.span); + self.get_trait_method_in_scope(&trait_methods, method_name, last_segment.location); let hir_method_reference = hir_method_reference?; let func_id = hir_method_reference.func_id(self.interner)?; let HirMethodReference::TraitMethodId(trait_method_id, _, _) = hir_method_reference else { @@ -725,7 +732,7 @@ impl<'context> Elaborator<'context> { let trait_id = trait_method_id.trait_id; let trait_ = self.interner.get_trait(trait_id); - let mut constraint = trait_.as_constraint(span); + let mut constraint = trait_.as_constraint(location); constraint.typ = typ; let method = TraitMethod { method_id: trait_method_id, constraint, assumed: false }; @@ -759,7 +766,8 @@ impl<'context> Elaborator<'context> { make_error: impl FnOnce() -> TypeCheckError, ) { if let Err(UnificationError) = actual.unify(expected) { - self.errors.push((make_error().into(), self.file)); + let error: CompilationError = make_error().into(); + self.push_err(error); } } @@ -770,12 +778,12 @@ impl<'context> Elaborator<'context> { &mut self, actual: &Type, expected: &Type, - file: fm::FileId, make_error: impl FnOnce() -> TypeCheckError, ) { let mut bindings = TypeBindings::new(); if actual.try_unify(expected, &mut bindings).is_err() { - self.errors.push((make_error().into(), file)); + let error: CompilationError = make_error().into(); + self.push_err(error); } } @@ -785,19 +793,19 @@ impl<'context> Elaborator<'context> { actual: &Type, expected: &Type, expression: ExprId, - span: Span, + location: Location, make_error: impl FnOnce() -> TypeCheckError, ) { let mut errors = Vec::new(); actual.unify_with_coercions( expected, expression, - span, + location, self.interner, &mut errors, make_error, ); - self.errors.extend(errors.into_iter().map(|error| (error.into(), self.file))); + self.push_errors(errors.into_iter().map(|error| error.into())); } /// Return a fresh integer or field type variable and log it @@ -847,7 +855,7 @@ impl<'context> Elaborator<'context> { trait_method_id: None, })); self.interner.push_expr_type(object, element.as_ref().clone()); - self.interner.push_expr_location(object, location.span, location.file); + self.interner.push_expr_location(object, location); // Recursively dereference to allow for converting &mut &mut T to T self.insert_auto_dereferences(object, *element) @@ -885,24 +893,24 @@ impl<'context> Elaborator<'context> { &mut self, fn_params: &[Type], fn_ret: &Type, - callsite_args: &[(Type, ExprId, Span)], - span: Span, + callsite_args: &[(Type, ExprId, Location)], + location: Location, ) -> Type { if fn_params.len() != callsite_args.len() { self.push_err(TypeCheckError::ParameterCountMismatch { expected: fn_params.len(), found: callsite_args.len(), - span, + location, }); return Type::Error; } - for (param, (arg, arg_expr_id, arg_span)) in fn_params.iter().zip(callsite_args) { - self.unify_with_coercions(arg, param, *arg_expr_id, *arg_span, || { + for (param, (arg, arg_expr_id, arg_location)) in fn_params.iter().zip(callsite_args) { + self.unify_with_coercions(arg, param, *arg_expr_id, *arg_location, || { TypeCheckError::TypeMismatch { expected_typ: param.to_string(), expr_typ: arg.to_string(), - expr_span: *arg_span, + expr_location: *arg_location, } }); } @@ -913,15 +921,15 @@ impl<'context> Elaborator<'context> { pub(super) fn bind_function_type( &mut self, function: Type, - args: Vec<(Type, ExprId, Span)>, - span: Span, + args: Vec<(Type, ExprId, Location)>, + location: Location, ) -> Type { // Could do a single unification for the entire function type, but matching beforehand // lets us issue a more precise error on the individual argument that fails to type check. match function { Type::TypeVariable(binding) if binding.kind() == Kind::Normal => { if let TypeBinding::Bound(typ) = &*binding.borrow() { - return self.bind_function_type(typ.clone(), args, span); + return self.bind_function_type(typ.clone(), args, location); } let ret = self.interner.next_type_variable(); @@ -931,7 +939,7 @@ impl<'context> Elaborator<'context> { Type::Function(args, Box::new(ret.clone()), Box::new(env_type), false); let expected_kind = expected.kind(); - if let Err(error) = binding.try_bind(expected, &expected_kind, span) { + if let Err(error) = binding.try_bind(expected, &expected_kind, location) { self.push_err(error); } ret @@ -939,11 +947,11 @@ impl<'context> Elaborator<'context> { // The closure env is ignored on purpose: call arguments never place // constraints on closure environments. Type::Function(parameters, ret, _env, _unconstrained) => { - self.bind_function_type_impl(¶meters, &ret, &args, span) + self.bind_function_type_impl(¶meters, &ret, &args, location) } Type::Error => Type::Error, found => { - self.push_err(TypeCheckError::ExpectedFunction { found, span }); + self.push_err(TypeCheckError::ExpectedFunction { found, location }); Type::Error } } @@ -954,12 +962,13 @@ impl<'context> Elaborator<'context> { from_expr_id: &ExprId, from: &Type, to: &Type, - span: Span, + location: Location, ) -> Type { let from_follow_bindings = from.follow_bindings(); + use HirExpression::Literal; let from_value_opt = match self.interner.expression(from_expr_id) { - HirExpression::Literal(HirLiteral::Integer(int, false)) => Some(int), + Literal(HirLiteral::Integer(SignedField { field, is_negative: false })) => Some(field), // TODO(https://github.com/noir-lang/noir/issues/6247): // handle negative literals @@ -976,7 +985,7 @@ impl<'context> Elaborator<'context> { let expected = self.polymorphic_integer_or_field(); self.unify(from, &expected, || TypeCheckError::InvalidCast { from: from.clone(), - span, + location, reason: "casting from a non-integral type is unsupported".into(), }); true @@ -984,7 +993,7 @@ impl<'context> Elaborator<'context> { Type::Error => return Type::Error, from => { let reason = "casting from this type is unsupported".into(); - self.push_err(TypeCheckError::InvalidCast { from, span, reason }); + self.push_err(TypeCheckError::InvalidCast { from, location, reason }); return Type::Error; } }; @@ -999,9 +1008,11 @@ impl<'context> Elaborator<'context> { if from_is_polymorphic && from_value > to_maximum_size { let from = from.clone(); let to = to.clone(); - let reason = format!("casting untyped value ({from_value}) to a type with a maximum size ({to_maximum_size}) that's smaller than it"); + let reason = format!( + "casting untyped value ({from_value}) to a type with a maximum size ({to_maximum_size}) that's smaller than it" + ); // we warn that the 'to' type is too small for the value - self.push_err(TypeCheckError::DownsizingCast { from, to, span, reason }); + self.push_err(TypeCheckError::DownsizingCast { from, to, location, reason }); } } @@ -1011,7 +1022,7 @@ impl<'context> Elaborator<'context> { Type::Bool => Type::Bool, Type::Error => Type::Error, _ => { - self.push_err(TypeCheckError::UnsupportedCast { span }); + self.push_err(TypeCheckError::UnsupportedCast { location }); Type::Error } } @@ -1026,7 +1037,7 @@ impl<'context> Elaborator<'context> { lhs_type: &Type, rhs_type: &Type, op: &HirBinaryOp, - span: Span, + location: Location, ) -> Result<(Type, bool), TypeCheckError> { use Type::*; @@ -1035,17 +1046,17 @@ impl<'context> Elaborator<'context> { (Error, _) | (_, Error) => Ok((Bool, false)), (Alias(alias, args), other) | (other, Alias(alias, args)) => { let alias = alias.borrow().get_type(args); - self.comparator_operand_type_rules(&alias, other, op, span) + self.comparator_operand_type_rules(&alias, other, op, location) } // Matches on TypeVariable must be first to follow any type // bindings. (TypeVariable(var), other) | (other, TypeVariable(var)) => { - if let TypeBinding::Bound(ref binding) = &*var.borrow() { - return self.comparator_operand_type_rules(other, binding, op, span); + if let TypeBinding::Bound(binding) = &*var.borrow() { + return self.comparator_operand_type_rules(other, binding, op, location); } - let use_impl = self.bind_type_variables_for_infix(lhs_type, op, rhs_type, span); + let use_impl = self.bind_type_variables_for_infix(lhs_type, op, rhs_type, location); Ok((Bool, use_impl)) } (Integer(sign_x, bit_width_x), Integer(sign_y, bit_width_y)) => { @@ -1053,14 +1064,14 @@ impl<'context> Elaborator<'context> { return Err(TypeCheckError::IntegerSignedness { sign_x: *sign_x, sign_y: *sign_y, - span, + location, }); } if bit_width_x != bit_width_y { return Err(TypeCheckError::IntegerBitWidth { bit_width_x: *bit_width_x, bit_width_y: *bit_width_y, - span, + location, }); } Ok((Bool, false)) @@ -1069,7 +1080,7 @@ impl<'context> Elaborator<'context> { if op.kind.is_valid_for_field_type() { Ok((Bool, false)) } else { - Err(TypeCheckError::FieldComparison { span }) + Err(TypeCheckError::FieldComparison { location }) } } @@ -1080,7 +1091,7 @@ impl<'context> Elaborator<'context> { self.unify(lhs, rhs, || TypeCheckError::TypeMismatchWithSource { expected: lhs.clone(), actual: rhs.clone(), - span: op.location.span, + location: op.location, source: Source::Binary, }); Ok((Bool, true)) @@ -1096,13 +1107,13 @@ impl<'context> Elaborator<'context> { lhs_type: &Type, op: &HirBinaryOp, rhs_type: &Type, - span: Span, + location: Location, ) -> bool { self.unify(lhs_type, rhs_type, || TypeCheckError::TypeMismatchWithSource { expected: lhs_type.clone(), actual: rhs_type.clone(), source: Source::Binary, - span, + location, }); let use_impl = !lhs_type.is_numeric_value(); @@ -1117,9 +1128,9 @@ impl<'context> Elaborator<'context> { use crate::ast::BinaryOpKind::*; use TypeCheckError::*; self.unify(lhs_type, &target, || match op.kind { - Less | LessEqual | Greater | GreaterEqual => FieldComparison { span }, - And | Or | Xor | ShiftRight | ShiftLeft => FieldBitwiseOp { span }, - Modulo => FieldModulo { span }, + Less | LessEqual | Greater | GreaterEqual => FieldComparison { location }, + And | Or | Xor | ShiftRight | ShiftLeft => FieldBitwiseOp { location }, + Modulo => FieldModulo { location }, other => unreachable!("Operator {other:?} should be valid for Field"), }); } @@ -1136,10 +1147,10 @@ impl<'context> Elaborator<'context> { lhs_type: &Type, op: &HirBinaryOp, rhs_type: &Type, - span: Span, + location: Location, ) -> Result<(Type, bool), TypeCheckError> { if op.kind.is_comparator() { - return self.comparator_operand_type_rules(lhs_type, rhs_type, op, span); + return self.comparator_operand_type_rules(lhs_type, rhs_type, op, location); } use Type::*; @@ -1148,7 +1159,7 @@ impl<'context> Elaborator<'context> { (Error, _) | (_, Error) => Ok((Error, false)), (Alias(alias, args), other) | (other, Alias(alias, args)) => { let alias = alias.borrow().get_type(args); - self.infix_operand_type_rules(&alias, op, other, span) + self.infix_operand_type_rules(&alias, op, other, location) } // Matches on TypeVariable must be first so that we follow any type @@ -1158,26 +1169,26 @@ impl<'context> Elaborator<'context> { self.unify( rhs_type, &Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight), - || TypeCheckError::InvalidShiftSize { span }, + || TypeCheckError::InvalidShiftSize { location }, ); let use_impl = if lhs_type.is_numeric_value() { let integer_type = self.polymorphic_integer(); - self.bind_type_variables_for_infix(lhs_type, op, &integer_type, span) + self.bind_type_variables_for_infix(lhs_type, op, &integer_type, location) } else { true }; return Ok((lhs_type.clone(), use_impl)); } - if let TypeBinding::Bound(ref binding) = &*int.borrow() { - return self.infix_operand_type_rules(binding, op, other, span); + if let TypeBinding::Bound(binding) = &*int.borrow() { + return self.infix_operand_type_rules(binding, op, other, location); } - let use_impl = self.bind_type_variables_for_infix(lhs_type, op, rhs_type, span); + let use_impl = self.bind_type_variables_for_infix(lhs_type, op, rhs_type, location); Ok((other.clone(), use_impl)) } (Integer(sign_x, bit_width_x), Integer(sign_y, bit_width_y)) => { if op.kind == BinaryOpKind::ShiftLeft || op.kind == BinaryOpKind::ShiftRight { if *sign_y != Signedness::Unsigned || *bit_width_y != IntegerBitSize::Eight { - return Err(TypeCheckError::InvalidShiftSize { span }); + return Err(TypeCheckError::InvalidShiftSize { location }); } return Ok((Integer(*sign_x, *bit_width_x), false)); } @@ -1185,14 +1196,14 @@ impl<'context> Elaborator<'context> { return Err(TypeCheckError::IntegerSignedness { sign_x: *sign_x, sign_y: *sign_y, - span, + location, }); } if bit_width_x != bit_width_y { return Err(TypeCheckError::IntegerBitWidth { bit_width_x: *bit_width_x, bit_width_y: *bit_width_y, - span, + location, }); } Ok((Integer(*sign_x, *bit_width_x), false)) @@ -1201,9 +1212,9 @@ impl<'context> Elaborator<'context> { (FieldElement, FieldElement) => { if !op.kind.is_valid_for_field_type() { if op.kind == BinaryOpKind::Modulo { - return Err(TypeCheckError::FieldModulo { span }); + return Err(TypeCheckError::FieldModulo { location }); } else { - return Err(TypeCheckError::FieldBitwiseOp { span }); + return Err(TypeCheckError::FieldBitwiseOp { location }); } } Ok((FieldElement, false)) @@ -1216,12 +1227,12 @@ impl<'context> Elaborator<'context> { if rhs == &Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight) { return Ok((lhs.clone(), true)); } - return Err(TypeCheckError::InvalidShiftSize { span }); + return Err(TypeCheckError::InvalidShiftSize { location }); } self.unify(lhs, rhs, || TypeCheckError::TypeMismatchWithSource { expected: lhs.clone(), actual: rhs.clone(), - span: op.location.span, + location: op.location, source: Source::Binary, }); Ok((lhs.clone(), true)) @@ -1237,7 +1248,7 @@ impl<'context> Elaborator<'context> { &mut self, op: &UnaryOp, rhs_type: &Type, - span: Span, + location: Location, ) -> Result<(Type, bool), TypeCheckError> { use Type::*; @@ -1248,14 +1259,14 @@ impl<'context> Elaborator<'context> { Error => Ok((Error, false)), Alias(alias, args) => { let alias = alias.borrow().get_type(args); - self.prefix_operand_type_rules(op, &alias, span) + self.prefix_operand_type_rules(op, &alias, location) } // Matches on TypeVariable must be first so that we follow any type // bindings. TypeVariable(int) => { - if let TypeBinding::Bound(ref binding) = &*int.borrow() { - return self.prefix_operand_type_rules(op, binding, span); + if let TypeBinding::Bound(binding) = &*int.borrow() { + return self.prefix_operand_type_rules(op, binding, location); } // The `!` prefix operator is not valid for Field, so if this is a numeric @@ -1263,7 +1274,10 @@ impl<'context> Elaborator<'context> { if matches!(op, crate::ast::UnaryOp::Not) && rhs_type.is_numeric_value() { let integer_type = Type::polymorphic_integer(self.interner); self.unify(rhs_type, &integer_type, || { - TypeCheckError::InvalidUnaryOp { kind: rhs_type.to_string(), span } + TypeCheckError::InvalidUnaryOp { + kind: rhs_type.to_string(), + location, + } }); } @@ -1273,7 +1287,7 @@ impl<'context> Elaborator<'context> { if *op == UnaryOp::Minus && *sign_x == Signedness::Unsigned { return Err(TypeCheckError::InvalidUnaryOp { kind: rhs_type.to_string(), - span, + location, }); } Ok((Integer(*sign_x, *bit_width_x), false)) @@ -1281,7 +1295,7 @@ impl<'context> Elaborator<'context> { // The result of a Field is always a witness FieldElement => { if *op == UnaryOp::Not { - return Err(TypeCheckError::FieldNot { span }); + return Err(TypeCheckError::FieldNot { location }); } Ok((FieldElement, false)) } @@ -1300,7 +1314,7 @@ impl<'context> Elaborator<'context> { self.unify(rhs_type, &expected, || TypeCheckError::TypeMismatch { expr_typ: rhs_type.to_string(), expected_typ: expected.to_string(), - expr_span: span, + expr_location: location, }); Ok((element_type, false)) } @@ -1318,7 +1332,7 @@ impl<'context> Elaborator<'context> { expr_id: ExprId, trait_method_id: TraitMethodId, object_type: &Type, - span: Span, + location: Location, ) { let the_trait = self.interner.get_trait(trait_method_id.trait_id); @@ -1333,7 +1347,7 @@ impl<'context> Elaborator<'context> { self.unify(object_type, expected_object_type, || TypeCheckError::TypeMismatch { expected_typ: expected_object_type.to_string(), expr_typ: object_type.to_string(), - expr_span: span, + expr_location: location, }); } other => { @@ -1369,7 +1383,7 @@ impl<'context> Elaborator<'context> { mut access: HirMemberAccess, expr_id: ExprId, lhs_type: Type, - span: Span, + location: Location, ) -> Type { let access_lhs = &mut access.lhs; @@ -1384,13 +1398,15 @@ impl<'context> Elaborator<'context> { this.interner.push_expr_type(*access_lhs, element); let old_location = this.interner.id_location(old_lhs); - this.interner.push_expr_location(*access_lhs, span, old_location.file); + let location = Location::new(location.span, old_location.file); + this.interner.push_expr_location(*access_lhs, location); }; // If this access is just a field offset, we want to avoid dereferencing let dereference_lhs = (!access.is_offset).then_some(dereference_lhs); - match self.check_field_access(&lhs_type, &access.rhs.0.contents, span, dereference_lhs) { + match self.check_field_access(&lhs_type, &access.rhs.0.contents, location, dereference_lhs) + { Some((element_type, index)) => { self.interner.set_field_index(expr_id, index); // We must update `access` in case we added any dereferences to it @@ -1405,8 +1421,8 @@ impl<'context> Elaborator<'context> { &mut self, object_type: &Type, method_name: &str, - span: Span, - has_self_arg: bool, + location: Location, + check_self_param: bool, ) -> Option { match object_type.follow_bindings() { // TODO: We should allow method calls on `impl Trait`s eventually. @@ -1415,17 +1431,17 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::UnresolvedMethodCall { method_name: method_name.to_string(), object_type: object_type.clone(), - span, + location, }); None } Type::NamedGeneric(_, _) => { - self.lookup_method_in_trait_constraints(object_type, method_name, span) + self.lookup_method_in_trait_constraints(object_type, method_name, location) } // Mutable references to another type should resolve to methods of their element type. // This may be a data type or a primitive type. Type::MutableReference(element) => { - self.lookup_method(&element, method_name, span, has_self_arg) + self.lookup_method(&element, method_name, location, check_self_param) } // If we fail to resolve the object to a data type, we have no way of type @@ -1434,11 +1450,16 @@ impl<'context> Elaborator<'context> { // The type variable must be unbound at this point since follow_bindings was called Type::TypeVariable(var) if var.kind() == Kind::Normal => { - self.push_err(TypeCheckError::TypeAnnotationsNeededForMethodCall { span }); + self.push_err(TypeCheckError::TypeAnnotationsNeededForMethodCall { location }); None } - other => self.lookup_type_or_primitive_method(&other, method_name, span, has_self_arg), + other => self.lookup_type_or_primitive_method( + &other, + method_name, + location, + check_self_param, + ), } } @@ -1446,31 +1467,31 @@ impl<'context> Elaborator<'context> { &mut self, object_type: &Type, method_name: &str, - span: Span, - has_self_arg: bool, + location: Location, + check_self_param: bool, ) -> Option { // First search in the type methods. If there is one, that's the one. if let Some(method_id) = - self.interner.lookup_direct_method(object_type, method_name, has_self_arg) + self.interner.lookup_direct_method(object_type, method_name, check_self_param) { return Some(HirMethodReference::FuncId(method_id)); } // Next lookup all matching trait methods. let trait_methods = - self.interner.lookup_trait_methods(object_type, method_name, has_self_arg); + self.interner.lookup_trait_methods(object_type, method_name, check_self_param); // If there's at least one matching trait method we need to see if only one is in scope. if !trait_methods.is_empty() { - return self.return_trait_method_in_scope(&trait_methods, method_name, span); + return self.return_trait_method_in_scope(&trait_methods, method_name, location); } // If we couldn't find any trait methods, search in // impls for all types `T`, e.g. `impl Foo for T` let generic_methods = - self.interner.lookup_generic_methods(object_type, method_name, has_self_arg); + self.interner.lookup_generic_methods(object_type, method_name, check_self_param); if !generic_methods.is_empty() { - return self.return_trait_method_in_scope(&generic_methods, method_name, span); + return self.return_trait_method_in_scope(&generic_methods, method_name, location); } if let Type::DataType(datatype, _) = object_type { @@ -1487,13 +1508,13 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::CannotInvokeStructFieldFunctionType { method_name: method_name.to_string(), object_type: object_type.clone(), - span, + location, }); } else { self.push_err(TypeCheckError::UnresolvedMethodCall { method_name: method_name.to_string(), object_type: object_type.clone(), - span, + location, }); } None @@ -1501,7 +1522,7 @@ impl<'context> Elaborator<'context> { // 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) + self.lookup_method_in_trait_constraints(object_type, method_name, location) } } @@ -1511,9 +1532,9 @@ impl<'context> Elaborator<'context> { &mut self, trait_methods: &[(FuncId, TraitId)], method_name: &str, - span: Span, + location: Location, ) -> Option { - let (method, error) = self.get_trait_method_in_scope(trait_methods, method_name, span); + let (method, error) = self.get_trait_method_in_scope(trait_methods, method_name, location); if let Some(error) = error { self.push_err(error); } @@ -1524,7 +1545,7 @@ impl<'context> Elaborator<'context> { &mut self, trait_methods: &[(FuncId, TraitId)], method_name: &str, - span: Span, + location: Location, ) -> (Option, Option) { let module_id = self.module_id(); let module_data = self.get_module(module_id); @@ -1560,9 +1581,9 @@ impl<'context> Elaborator<'context> { let trait_ = self.interner.get_trait(trait_id); let trait_name = self.fully_qualified_trait_path(trait_); let method = - self.trait_hir_method_reference(trait_id, trait_methods, method_name, span); + self.trait_hir_method_reference(trait_id, trait_methods, method_name, location); let error = PathResolutionError::TraitMethodNotInScope { - ident: Ident::new(method_name.into(), span), + ident: Ident::new(method_name.into(), location), trait_name, }; return (Some(method), Some(error)); @@ -1573,7 +1594,7 @@ impl<'context> Elaborator<'context> { }); let method = None; let error = PathResolutionError::UnresolvedWithPossibleTraitsToImport { - ident: Ident::new(method_name.into(), span), + ident: Ident::new(method_name.into(), location), traits, }; return (method, Some(error)); @@ -1587,14 +1608,15 @@ impl<'context> Elaborator<'context> { }); let method = None; let error = PathResolutionError::MultipleTraitsInScope { - ident: Ident::new(method_name.into(), span), + ident: Ident::new(method_name.into(), location), traits, }; return (method, Some(error)); } let trait_id = traits_in_scope[0].0; - let method = self.trait_hir_method_reference(trait_id, trait_methods, method_name, span); + let method = + self.trait_hir_method_reference(trait_id, trait_methods, method_name, location); let error = None; (Some(method), error) } @@ -1604,7 +1626,7 @@ impl<'context> Elaborator<'context> { trait_id: TraitId, trait_methods: &[(FuncId, TraitId)], method_name: &str, - span: Span, + location: Location, ) -> HirMethodReference { // If we find a single trait impl method, return it so we don't have to later determine the impl if trait_methods.len() == 1 { @@ -1614,7 +1636,7 @@ impl<'context> Elaborator<'context> { // Return a TraitMethodId with unbound generics. These will later be bound by the type-checker. let trait_ = self.interner.get_trait(trait_id); - let generics = trait_.get_trait_generics(span); + let generics = trait_.get_trait_generics(location); let trait_method_id = trait_.find_method(method_name).unwrap(); HirMethodReference::TraitMethodId(trait_method_id, generics, false) } @@ -1623,7 +1645,7 @@ impl<'context> Elaborator<'context> { &mut self, object_type: &Type, method_name: &str, - span: Span, + location: Location, ) -> Option { let func_id = match self.current_item { Some(DependencyId::Function(id)) => id, @@ -1635,7 +1657,7 @@ impl<'context> Elaborator<'context> { if let Some(trait_id) = func_meta.trait_id { if Some(object_type) == self.self_type.as_ref() { let the_trait = self.interner.get_trait(trait_id); - let constraint = the_trait.as_constraint(the_trait.name.span()); + let constraint = the_trait.as_constraint(the_trait.name.location()); if let Some(HirMethodReference::TraitMethodId(method_id, generics, _)) = self .lookup_method_in_trait( the_trait, @@ -1670,7 +1692,7 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::UnresolvedMethodCall { method_name: method_name.to_string(), object_type: object_type.clone(), - span, + location, }); None @@ -1719,8 +1741,8 @@ impl<'context> Elaborator<'context> { &mut self, call: &HirCallExpression, func_type: Type, - args: Vec<(Type, ExprId, Span)>, - span: Span, + args: Vec<(Type, ExprId, Location)>, + location: Location, ) -> Type { self.run_lint(|elaborator| { lints::deprecated_function(elaborator.interner, call.func).map(Into::into) @@ -1741,7 +1763,7 @@ impl<'context> Elaborator<'context> { if crossing_runtime_boundary { match self.unsafe_block_status { UnsafeBlockStatus::NotInUnsafeBlock => { - self.push_err(TypeCheckError::Unsafe { span }); + self.push_err(TypeCheckError::Unsafe { location }); } UnsafeBlockStatus::InUnsafeBlockWithoutUnconstrainedCalls => { self.unsafe_block_status = UnsafeBlockStatus::InUnsafeBlockWithConstrainedCalls; @@ -1755,7 +1777,7 @@ impl<'context> Elaborator<'context> { elaborator.interner, &called_func_id, is_current_func_constrained, - span, + location, ) .map(Into::into) }); @@ -1767,11 +1789,11 @@ impl<'context> Elaborator<'context> { } } - let return_type = self.bind_function_type(func_type, args, span); + let return_type = self.bind_function_type(func_type, args, location); if crossing_runtime_boundary { self.run_lint(|_| { - lints::unconstrained_function_return(&return_type, span).map(Into::into) + lints::unconstrained_function_return(&return_type, location).map(Into::into) }); } @@ -1817,9 +1839,8 @@ impl<'context> Elaborator<'context> { if matches!(expected_object_type.follow_bindings(), Type::MutableReference(_)) { if !matches!(actual_type, Type::MutableReference(_)) { - if let Err(error) = verify_mutable_reference(self.interner, *object) { - self.push_err(TypeCheckError::ResolverError(error)); - } + let location = self.interner.id_location(*object); + self.check_can_mutate(*object, location); let new_type = Type::MutableReference(Box::new(actual_type)); *object_type = new_type.clone(); @@ -1830,8 +1851,6 @@ impl<'context> Elaborator<'context> { // If that didn't work, then wrap the whole expression in an `&mut` *object = new_object.unwrap_or_else(|| { - let location = self.interner.id_location(*object); - let new_object = self.interner.push_expr(HirExpression::Prefix(HirPrefixExpression { operator: UnaryOp::MutableReference, @@ -1839,7 +1858,7 @@ impl<'context> Elaborator<'context> { trait_method_id: None, })); self.interner.push_expr_type(new_object, new_type); - self.interner.push_expr_location(new_object, location.span, location.file); + self.interner.push_expr_location(new_object, location); new_object }); } @@ -1854,10 +1873,10 @@ impl<'context> Elaborator<'context> { } pub fn type_check_function_body(&mut self, body_type: Type, meta: &FuncMeta, body_id: ExprId) { - let (expr_span, empty_function) = self.function_info(body_id); + let (expr_location, empty_function) = self.function_info(body_id); let declared_return_type = meta.return_type(); - let func_span = self.interner.expr_span(&body_id); // XXX: We could be more specific and return the span of the last stmt, however stmts do not have spans yet + let func_location = self.interner.expr_location(&body_id); // XXX: We could be more specific and return the span of the last stmt, however stmts do not have spans yet if let Type::TraitAsType(trait_id, _, generics) = declared_return_type { if self .interner @@ -1872,46 +1891,52 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::TypeMismatchWithSource { expected: declared_return_type.clone(), actual: body_type, - span: func_span, - source: Source::Return(meta.return_type.clone(), expr_span), + location: func_location, + source: Source::Return(meta.return_type.clone(), expr_location), }); } } else { - self.unify_with_coercions(&body_type, declared_return_type, body_id, func_span, || { - let mut error = TypeCheckError::TypeMismatchWithSource { - expected: declared_return_type.clone(), - actual: body_type.clone(), - span: func_span, - source: Source::Return(meta.return_type.clone(), expr_span), - }; + self.unify_with_coercions( + &body_type, + declared_return_type, + body_id, + func_location, + || { + let mut error = TypeCheckError::TypeMismatchWithSource { + expected: declared_return_type.clone(), + actual: body_type.clone(), + location: func_location, + source: Source::Return(meta.return_type.clone(), expr_location), + }; - if empty_function { - error = error.add_context( + if empty_function { + error = error.add_context( "implicitly returns `()` as its body has no tail or `return` expression", ); - } - error - }); + } + error + }, + ); } } - fn function_info(&self, function_body_id: ExprId) -> (noirc_errors::Span, bool) { - let (expr_span, empty_function) = + fn function_info(&self, function_body_id: ExprId) -> (noirc_errors::Location, bool) { + let (expr_location, empty_function) = if let HirExpression::Block(block) = self.interner.expression(&function_body_id) { let last_stmt = block.statements().last(); - let mut span = self.interner.expr_span(&function_body_id); + let mut location = self.interner.expr_location(&function_body_id); if let Some(last_stmt) = last_stmt { if let HirStatement::Expression(expr) = self.interner.statement(last_stmt) { - span = self.interner.expr_span(&expr); + location = self.interner.expr_location(&expr); } } - (span, last_stmt.is_none()) + (location, last_stmt.is_none()) } else { - (self.interner.expr_span(&function_body_id), false) + (self.interner.expr_location(&function_body_id), false) }; - (expr_span, empty_function) + (expr_location, empty_function) } #[allow(clippy::too_many_arguments)] @@ -1923,7 +1948,7 @@ impl<'context> Elaborator<'context> { associated_types: &[NamedType], function_ident_id: ExprId, select_impl: bool, - span: Span, + location: Location, ) { match self.interner.lookup_trait_implementation( object_type, @@ -1936,7 +1961,7 @@ impl<'context> Elaborator<'context> { self.interner.select_impl_for_expression(function_ident_id, impl_kind); } } - Err(error) => self.push_trait_constraint_error(object_type, error, span), + Err(error) => self.push_trait_constraint_error(object_type, error, location), } } @@ -1944,25 +1969,24 @@ impl<'context> Elaborator<'context> { &mut self, object_type: &Type, error: ImplSearchErrorKind, - span: Span, + location: Location, ) { match error { ImplSearchErrorKind::TypeAnnotationsNeededOnObjectType => { - self.push_err(TypeCheckError::TypeAnnotationsNeededForMethodCall { span }); + self.push_err(TypeCheckError::TypeAnnotationsNeededForMethodCall { location }); } ImplSearchErrorKind::Nested(constraints) => { - if let Some(error) = NoMatchingImplFoundError::new(self.interner, constraints, span) + if let Some(error) = + NoMatchingImplFoundError::new(self.interner, constraints, location) { self.push_err(TypeCheckError::NoMatchingImplFound(error)); } } ImplSearchErrorKind::MultipleMatching(candidates) => { let object_type = object_type.clone(); - self.push_err(TypeCheckError::MultipleMatchingImpls { - object_type, - span, - candidates, - }); + let err = + TypeCheckError::MultipleMatchingImpls { object_type, location, candidates }; + self.push_err(err); } } } @@ -1975,14 +1999,14 @@ impl<'context> Elaborator<'context> { assert_eq!(unresolved_generics.len(), generics.len()); for (unresolved_generic, generic) in unresolved_generics.iter().zip(generics) { - self.add_existing_generic(unresolved_generic, unresolved_generic.span(), generic); + self.add_existing_generic(unresolved_generic, unresolved_generic.location(), generic); } } pub fn add_existing_generic( &mut self, unresolved_generic: &UnresolvedGeneric, - span: Span, + location: Location, resolved_generic: &ResolvedGeneric, ) { let name = &unresolved_generic.ident().0.contents; @@ -1990,8 +2014,8 @@ impl<'context> Elaborator<'context> { if let Some(generic) = self.find_generic(name) { self.push_err(ResolverError::DuplicateDefinition { name: name.clone(), - first_span: generic.span, - second_span: span, + first_location: generic.location, + second_location: location, }); } else { self.generics.push(resolved_generic.clone()); @@ -2109,29 +2133,3 @@ fn bind_generic(param: &ResolvedGeneric, arg: &Type, bindings: &mut TypeBindings bindings.insert(param.type_var.id(), (param.type_var.clone(), param.kind(), arg.clone())); } } - -/// Gives an error if a user tries to create a mutable reference -/// to an immutable variable. -fn verify_mutable_reference(interner: &NodeInterner, rhs: ExprId) -> Result<(), ResolverError> { - match interner.expression(&rhs) { - HirExpression::MemberAccess(member_access) => { - verify_mutable_reference(interner, member_access.lhs) - } - HirExpression::Index(_) => { - let span = interner.expr_span(&rhs); - Err(ResolverError::MutableReferenceToArrayElement { span }) - } - HirExpression::Ident(ident, _) => { - if let Some(definition) = interner.try_definition(ident.id) { - if !definition.mutable { - return Err(ResolverError::MutableReferenceToImmutableVariable { - span: interner.expr_span(&rhs), - variable: definition.name.clone(), - }); - } - } - Ok(()) - } - _ => Ok(()), - } -} 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 982ad3d2e1f8..56f9a694c9bf 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/unquote.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/unquote.rs @@ -1,11 +1,11 @@ use crate::{ ast::Path, - token::{SpannedToken, Token, Tokens}, + token::{LocatedToken, Token, Tokens}, }; use super::Elaborator; -impl<'a> Elaborator<'a> { +impl Elaborator<'_> { /// Go through the given tokens looking for a '$' token followed by a variable to unquote. /// Each time these two tokens are found, they are replaced by a new UnquoteMarker token /// containing the ExprId of the resolved variable to unquote. @@ -20,17 +20,18 @@ impl<'a> Elaborator<'a> { if is_unquote { if let Some(next) = tokens.next() { - let span = next.to_span(); + let location = next.location(); match next.into_token() { Token::Ident(name) => { // Don't want the leading `$` anymore new_tokens.pop(); - let path = Path::from_single(name, span); + let path = Path::from_single(name, location); let (expr_id, _) = self.elaborate_variable(path); - new_tokens.push(SpannedToken::new(Token::UnquoteMarker(expr_id), span)); + new_tokens + .push(LocatedToken::new(Token::UnquoteMarker(expr_id), location)); } - other_next => new_tokens.push(SpannedToken::new(other_next, span)), + other_next => new_tokens.push(LocatedToken::new(other_next, location)), } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/graph/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/graph/mod.rs index c007d6792bd1..0ce744aded5a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/graph/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/graph/mod.rs @@ -87,7 +87,7 @@ impl FromStr for CrateName { #[cfg(test)] mod crate_name { - use super::{CrateName, CHARACTER_BLACK_LIST}; + use super::{CHARACTER_BLACK_LIST, CrateName}; #[test] fn it_rejects_empty_string() { @@ -379,7 +379,7 @@ mod tests { use super::{CrateGraph, FileId}; fn dummy_file_ids(n: usize) -> Vec { - use fm::{FileMap, FILE_EXTENSION}; + use fm::{FILE_EXTENSION, FileMap}; let mut fm = FileMap::default(); let mut vec_ids = Vec::with_capacity(n); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/display.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/display.rs index aa2f7d99fab9..c0283d9701b4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/display.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/display.rs @@ -1,30 +1,30 @@ -use std::{fmt::Display, rc::Rc}; +use std::fmt::Display; use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ + Type, ast::{ ArrayLiteral, AsTraitPath, AssignStatement, BlockExpression, CallExpression, CastExpression, ConstrainExpression, ConstructorExpression, Expression, ExpressionKind, ForBounds, ForLoopStatement, ForRange, GenericTypeArgs, IfExpression, IndexExpression, InfixExpression, LValue, Lambda, LetStatement, Literal, MatchExpression, MemberAccessExpression, MethodCallExpression, Pattern, PrefixExpression, Statement, - StatementKind, UnresolvedType, UnresolvedTypeData, WhileStatement, + StatementKind, UnresolvedType, UnresolvedTypeData, UnsafeExpression, WhileStatement, }, hir_def::traits::TraitConstraint, node_interner::{InternedStatementKind, NodeInterner}, - token::{Keyword, Token}, - Type, + token::{Keyword, LocatedToken, Token}, }; use super::{ - value::{ExprValue, TypedExpr}, Value, + value::{ExprValue, TypedExpr}, }; pub(super) fn display_quoted( - tokens: &[Token], + tokens: &[LocatedToken], indent: usize, interner: &NodeInterner, f: &mut std::fmt::Formatter<'_>, @@ -44,16 +44,16 @@ pub(super) fn display_quoted( } struct TokensPrettyPrinter<'tokens, 'interner> { - tokens: &'tokens [Token], + tokens: &'tokens [LocatedToken], interner: &'interner NodeInterner, indent: usize, } -impl<'tokens, 'interner> Display for TokensPrettyPrinter<'tokens, 'interner> { +impl Display for TokensPrettyPrinter<'_, '_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut token_printer = TokenPrettyPrinter::new(self.interner, self.indent); for token in self.tokens { - token_printer.print(token, f)?; + token_printer.print(token.token(), f)?; } // If the printer refrained from printing a token right away, this will make it do it @@ -63,9 +63,8 @@ impl<'tokens, 'interner> Display for TokensPrettyPrinter<'tokens, 'interner> { } } -pub(super) fn tokens_to_string(tokens: Rc>, interner: &NodeInterner) -> String { - let tokens: Vec = tokens.iter().cloned().collect(); - TokensPrettyPrinter { tokens: &tokens, interner, indent: 0 }.to_string() +pub(super) fn tokens_to_string(tokens: &[LocatedToken], interner: &NodeInterner) -> String { + TokensPrettyPrinter { tokens, interner, indent: 0 }.to_string() } /// Tries to print tokens in a way that it'll be easier for the user to understand a @@ -180,7 +179,7 @@ impl<'interner> TokenPrettyPrinter<'interner> { self.print_value(&value, f) } Token::InternedLValue(id) => { - let value = Value::lvalue(LValue::Interned(*id, Span::default())); + let value = Value::lvalue(LValue::Interned(*id, Location::dummy())); self.print_value(&value, f) } Token::InternedUnresolvedTypeData(id) => { @@ -188,7 +187,7 @@ impl<'interner> TokenPrettyPrinter<'interner> { self.print_value(&value, f) } Token::InternedPattern(id) => { - let value = Value::pattern(Pattern::Interned(*id, Span::default())); + let value = Value::pattern(Pattern::Interned(*id, Location::dummy())); self.print_value(&value, f) } Token::UnquoteMarker(id) => { @@ -229,8 +228,7 @@ impl<'interner> TokenPrettyPrinter<'interner> { if last_was_alphanumeric { write!(f, " ")?; } - let tokens = vecmap(&tokens.0, |spanned_token| spanned_token.clone().into_token()); - display_quoted(&tokens, self.indent, self.interner, f) + display_quoted(&tokens.0, self.indent, self.interner, f) } Token::Colon => { write!(f, "{token} ") @@ -329,7 +327,7 @@ pub struct ValuePrinter<'value, 'interner> { interner: &'interner NodeInterner, } -impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { +impl Display for ValuePrinter<'_, '_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self.value { Value::Unit => write!(f, "()"), @@ -347,6 +345,7 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { Value::U16(value) => write!(f, "{value}"), Value::U32(value) => write!(f, "{value}"), Value::U64(value) => write!(f, "{value}"), + Value::U128(value) => write!(f, "{value}"), Value::String(value) => write!(f, "{value}"), Value::CtString(value) => write!(f, "{value}"), Value::FormatString(value, _) => write!(f, "{value}"), @@ -466,12 +465,12 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { }, Value::TypedExpr(TypedExpr::ExprId(id)) => { let hir_expr = self.interner.expression(id); - let expr = hir_expr.to_display_ast(self.interner, Span::default()); + let expr = hir_expr.to_display_ast(self.interner, Location::dummy()); write!(f, "{}", expr.kind) } Value::TypedExpr(TypedExpr::StmtId(id)) => { let hir_statement = self.interner.statement(id); - let stmt = hir_statement.to_display_ast(self.interner, Span::default()); + let stmt = hir_statement.to_display_ast(self.interner, Location::dummy()); write!(f, "{}", stmt.kind) } Value::UnresolvedType(typ) => { @@ -495,7 +494,7 @@ pub struct TokenPrinter<'token, 'interner> { interner: &'interner NodeInterner, } -impl<'token, 'interner> Display for TokenPrinter<'token, 'interner> { +impl Display for TokenPrinter<'_, '_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self.token { Token::QuotedType(id) => { @@ -510,7 +509,7 @@ impl<'token, 'interner> Display for TokenPrinter<'token, 'interner> { value.display(self.interner).fmt(f) } Token::InternedLValue(id) => { - let value = Value::lvalue(LValue::Interned(*id, Span::default())); + let value = Value::lvalue(LValue::Interned(*id, Location::dummy())); value.display(self.interner).fmt(f) } Token::InternedUnresolvedTypeData(id) => { @@ -518,7 +517,7 @@ impl<'token, 'interner> Display for TokenPrinter<'token, 'interner> { value.display(self.interner).fmt(f) } Token::InternedPattern(id) => { - let value = Value::pattern(Pattern::Interned(*id, Span::default())); + let value = Value::pattern(Pattern::Interned(*id, Location::dummy())); value.display(self.interner).fmt(f) } Token::UnquoteMarker(id) => { @@ -540,7 +539,10 @@ fn display_trait_constraint(interner: &NodeInterner, trait_constraint: &TraitCon // Returns a new Expression where all Interned and Resolved expressions have been turned into non-interned ExpressionKind. fn remove_interned_in_expression(interner: &NodeInterner, expr: Expression) -> Expression { - Expression { kind: remove_interned_in_expression_kind(interner, expr.kind), span: expr.span } + Expression { + kind: remove_interned_in_expression_kind(interner, expr.kind), + location: expr.location, + } } // Returns a new ExpressionKind where all Interned and Resolved expressions have been turned into non-interned ExpressionKind. @@ -643,10 +645,13 @@ fn remove_interned_in_expression_kind( vecmap(block.statements, |stmt| remove_interned_in_statement(interner, stmt)); ExpressionKind::Comptime(BlockExpression { statements }, span) } - ExpressionKind::Unsafe(block, span) => { + ExpressionKind::Unsafe(UnsafeExpression { block, unsafe_keyword_location }) => { let statements = vecmap(block.statements, |stmt| remove_interned_in_statement(interner, stmt)); - ExpressionKind::Unsafe(BlockExpression { statements }, span) + ExpressionKind::Unsafe(UnsafeExpression { + block: BlockExpression { statements }, + unsafe_keyword_location, + }) } ExpressionKind::AsTraitPath(mut path) => { path.typ = remove_interned_in_unresolved_type(interner, path.typ); @@ -663,7 +668,7 @@ fn remove_interned_in_expression_kind( } ExpressionKind::Resolved(id) => { let expr = interner.expression(&id); - expr.to_display_ast(interner, Span::default()).kind + expr.to_display_ast(interner, Location::dummy()).kind } ExpressionKind::Interned(id) => { let expr = interner.get_expression_kind(id).clone(); @@ -695,7 +700,7 @@ fn remove_interned_in_literal(interner: &NodeInterner, literal: Literal) -> Lite Literal::Array(remove_interned_in_array_literal(interner, array_literal)) } Literal::Bool(_) - | Literal::Integer(_, _) + | Literal::Integer(_) | Literal::Str(_) | Literal::RawStr(_, _) | Literal::FmtStr(_, _) @@ -724,7 +729,7 @@ fn remove_interned_in_array_literal( fn remove_interned_in_statement(interner: &NodeInterner, statement: Statement) -> Statement { Statement { kind: remove_interned_in_statement_kind(interner, statement.kind), - span: statement.span, + location: statement.location, } } @@ -769,7 +774,7 @@ fn remove_interned_in_statement_kind( StatementKind::While(while_) => StatementKind::While(WhileStatement { condition: remove_interned_in_expression(interner, while_.condition), body: remove_interned_in_expression(interner, while_.body), - while_keyword_span: while_.while_keyword_span, + while_keyword_location: while_.while_keyword_location, }), StatementKind::Comptime(statement) => { StatementKind::Comptime(Box::new(remove_interned_in_statement(interner, *statement))) @@ -789,15 +794,15 @@ fn remove_interned_in_statement_kind( fn remove_interned_in_lvalue(interner: &NodeInterner, lvalue: LValue) -> LValue { match lvalue { LValue::Ident(_) => lvalue, - LValue::MemberAccess { object, field_name, span } => LValue::MemberAccess { + LValue::MemberAccess { object, field_name, location: span } => LValue::MemberAccess { object: Box::new(remove_interned_in_lvalue(interner, *object)), field_name, - span, + location: span, }, - LValue::Index { array, index, span } => LValue::Index { + LValue::Index { array, index, location: span } => LValue::Index { array: Box::new(remove_interned_in_lvalue(interner, *array)), index: remove_interned_in_expression(interner, index), - span, + location: span, }, LValue::Dereference(lvalue, span) => { LValue::Dereference(Box::new(remove_interned_in_lvalue(interner, *lvalue)), span) @@ -815,7 +820,7 @@ fn remove_interned_in_unresolved_type( ) -> UnresolvedType { UnresolvedType { typ: remove_interned_in_unresolved_type_data(interner, typ.typ), - span: typ.span, + location: typ.location, } } 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 64297a240621..27440069c023 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,16 +2,16 @@ use std::fmt::Display; use std::rc::Rc; use crate::{ + Type, ast::TraitBound, hir::{ def_collector::dc_crate::CompilationError, type_check::{NoMatchingImplFoundError, TypeCheckError}, }, parser::ParserError, - Type, + signed_field::SignedField, }; -use acvm::{acir::AcirField, BlackBoxResolutionError, FieldElement}; -use fm::FileId; +use acvm::BlackBoxResolutionError; use noirc_errors::{CustomDiagnostic, Location}; /// The possible errors that can halt the interpreter. @@ -35,7 +35,7 @@ pub enum InterpreterError { location: Location, }, IntegerOutOfRangeForType { - value: FieldElement, + value: SignedField, typ: Type, location: Location, }, @@ -157,7 +157,7 @@ pub enum InterpreterError { error: Box, tokens: String, rule: &'static str, - file: FileId, + location: Location, }, UnsupportedTopLevelItemUnquote { item: String, @@ -172,7 +172,6 @@ pub enum InterpreterError { }, NoMatchingImplFound { error: NoMatchingImplFoundError, - file: FileId, }, ImplMethodTypeMismatch { expected: Type, @@ -274,12 +273,7 @@ impl From for CompilationError { } impl InterpreterError { - pub fn into_compilation_error_pair(self) -> (CompilationError, fm::FileId) { - let location = self.get_location(); - (CompilationError::InterpreterError(self), location.file) - } - - pub fn get_location(&self) -> Location { + pub fn location(&self) -> Location { match self { InterpreterError::ArgumentCountMismatch { location, .. } | InterpreterError::TypeMismatch { location, .. } @@ -339,12 +333,8 @@ impl InterpreterError { | InterpreterError::GlobalsDependencyCycle { location } | InterpreterError::LoopHaltedForUiResponsiveness { location } => *location, - InterpreterError::FailedToParseMacro { error, file, .. } => { - Location::new(error.span(), *file) - } - InterpreterError::NoMatchingImplFound { error, file } => { - Location::new(error.span, *file) - } + InterpreterError::FailedToParseMacro { error, .. } => error.location(), + InterpreterError::NoMatchingImplFound { error } => error.location, InterpreterError::Break | InterpreterError::Continue => { panic!("Tried to get the location of Break/Continue error!") } @@ -360,7 +350,7 @@ impl InterpreterError { let diagnostic = CustomDiagnostic::simple_info( "`comptime` expression ran:".to_string(), format!("After evaluation: {}", formatted_result), - location.span, + location, ); InterpreterError::DebugEvaluateComptime { diagnostic, location } } @@ -379,66 +369,62 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let few_many = if actual < expected { "few" } else { "many" }; let secondary = format!("Too {few_many} arguments"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::TypeMismatch { expected, actual, location } => { let msg = format!("Expected `{expected}` but a value of type `{actual}` was given"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NonComptimeVarReferenced { name, location } => { let msg = format!("Non-comptime variable `{name}` referenced in comptime code"); let secondary = "Non-comptime variables can't be used in comptime code".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::VariableNotInScope { location } => { let msg = "Variable not in scope".to_string(); let secondary = "Could not find variable".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::IntegerOutOfRangeForType { value, typ, location } => { - let int = match value.try_into_u128() { - Some(int) => int.to_string(), - None => value.to_string(), - }; - let msg = format!("{int} is outside the range of the {typ} type"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + let msg = format!("{value} is outside the range of the {typ} type"); + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::ErrorNodeEncountered { location } => { let msg = "Internal Compiler Error: Error node encountered".to_string(); let secondary = "This is a bug, please report this if found!".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonFunctionCalled { typ, location } => { let msg = "Only functions may be called".to_string(); let secondary = format!("Expression has type {typ}"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonBoolUsedInIf { typ, location } => { let msg = format!("Expected a `bool` but found `{typ}`"); let secondary = "If conditions must be a boolean value".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonBoolUsedInWhile { typ, location } => { let msg = format!("Expected a `bool` but found `{typ}`"); let secondary = "While conditions must be a boolean value".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonBoolUsedInConstrain { typ, location } => { let msg = format!("Expected a `bool` but found `{typ}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::FailingConstraint { message, location, call_stack } => { let (primary, secondary) = match message { Some(msg) => (msg.clone(), "Assertion failed".into()), None => ("Assertion failed".into(), String::new()), }; - let diagnostic = CustomDiagnostic::simple_error(primary, secondary, location.span); + let diagnostic = CustomDiagnostic::simple_error(primary, secondary, *location); diagnostic.with_call_stack(call_stack.into_iter().copied().collect()) } InterpreterError::NoMethodFound { name, typ, location } => { let msg = format!("No method named `{name}` found for type `{typ}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NonIntegerUsedInLoop { typ, location } => { let msg = format!("Non-integer type `{typ}` used in for loop"); @@ -447,97 +433,97 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { } else { String::new() }; - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonPointerDereferenced { typ, location } => { let msg = format!("Only references may be dereferenced, but found `{typ}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NonTupleOrStructInMemberAccess { typ, location } => { let msg = format!("The type `{typ}` has no fields to access"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NonArrayIndexed { typ, location } => { let msg = format!("Expected an array or slice but found a(n) {typ}"); let secondary = "Only arrays or slices may be indexed".into(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonIntegerUsedAsIndex { typ, location } => { let msg = format!("Expected an integer but found a(n) {typ}"); let secondary = "Only integers may be indexed. Note that this excludes `field`s".into(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonIntegerIntegerLiteral { typ, location } => { let msg = format!("This integer literal somehow has the type `{typ}`"); let secondary = "This is likely a bug".into(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonIntegerArrayLength { typ, err, location } => { let msg = format!("Non-integer array length: `{typ}`"); let secondary = if let Some(err) = err { - format!("Array lengths must be integers, but evaluating `{typ}` resulted in `{err}`") + format!( + "Array lengths must be integers, but evaluating `{typ}` resulted in `{err}`" + ) } else { "Array lengths must be integers".to_string() }; - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonNumericCasted { typ, location } => { let msg = "Only numeric types may be casted".into(); let secondary = format!("`{typ}` is non-numeric"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::IndexOutOfBounds { index, length, location } => { let msg = format!("{index} is out of bounds for the array of length {length}"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::ExpectedStructToHaveField { typ, field_name, location } => { let msg = format!("The type `{typ}` has no field named `{field_name}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::TypeUnsupported { typ, location } => { let msg = format!("The type `{typ}` is currently unsupported in comptime expressions"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::InvalidValueForUnary { typ, operator, location } => { let msg = format!("`{typ}` cannot be used with unary {operator}"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::InvalidValuesForBinary { lhs, rhs, operator, location } => { let msg = format!("No implementation for `{lhs}` {operator} `{rhs}`",); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::CastToNonNumericType { typ, location } => { let msg = format!("Cannot cast to non-numeric type `{typ}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::QuoteInRuntimeCode { location } => { let msg = "`quote` may only be used in comptime code".into(); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NonStructInConstructor { typ, location } => { let msg = format!("`{typ}` is not a struct type"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NonEnumInConstructor { typ, location } => { let msg = format!("`{typ}` is not an enum type"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::CannotInlineMacro { value, typ, location } => { let msg = format!("Cannot inline values of type `{typ}` into this position"); let secondary = format!("Cannot inline value `{value}`"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::UnquoteFoundDuringEvaluation { location } => { let msg = "Unquote found during comptime evaluation".into(); let secondary = "This is a bug".into(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::DebugEvaluateComptime { diagnostic, .. } => diagnostic.clone(), - InterpreterError::FailedToParseMacro { error, tokens, rule, file: _ } => { - let message = format!("Failed to parse macro's token stream into {rule}"); - + InterpreterError::FailedToParseMacro { error, tokens, rule, location } => { // If it's less than 48 chars, the error message fits in a single line (less than 80 chars total) let token_stream = if tokens.len() <= 48 && !tokens.contains('\n') { format!("The resulting token stream was: {tokens}") @@ -549,12 +535,13 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let push_the_problem_on_the_library_author = "To avoid this error in the future, try adding input validation to your macro. Erroring out early with an `assert` can be a good way to provide a user-friendly error message".into(); - let mut diagnostic = CustomDiagnostic::from(error.as_ref()); - // Swap the parser's primary note to become the secondary note so that it is - // more clear this error originates from failing to parse a macro. - let secondary = std::mem::take(&mut diagnostic.message); - diagnostic.add_secondary(secondary, error.span()); - diagnostic.message = message; + let mut diagnostic = CustomDiagnostic::from(&**error); + + // Given more prominence to where the parser error happened, but still show that it's + // because of a failure to parse a macro's token stream, and where that happens. + let message = format!("Failed to parse macro's token stream into {rule}"); + diagnostic.add_secondary(message, *location); + diagnostic.add_note(token_stream); diagnostic.add_note(push_the_problem_on_the_library_author); diagnostic @@ -563,7 +550,7 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let msg = "Unsupported statement type to unquote".into(); let secondary = "Only functions, structs, globals, and impls can be unquoted here".into(); - let mut error = CustomDiagnostic::simple_error(msg, secondary, location.span); + let mut error = CustomDiagnostic::simple_error(msg, secondary, *location); error.add_note(format!("Unquoted item was:\n{item}")); error } @@ -571,63 +558,63 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let msg = format!("Comptime dependency cycle while resolving `{function}`"); let secondary = "This function uses comptime code internally which calls into itself".into(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::Unimplemented { item, location } => { let msg = format!("{item} is currently unimplemented"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::InvalidInComptimeContext { item, location, explanation } => { let msg = format!("{item} is invalid in comptime context"); - CustomDiagnostic::simple_error(msg, explanation.clone(), location.span) + CustomDiagnostic::simple_error(msg, explanation.clone(), *location) } InterpreterError::BreakNotInLoop { location } => { let msg = "There is no loop to break out of!".into(); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::ContinueNotInLoop { location } => { let msg = "There is no loop to continue!".into(); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NoImpl { location } => { let msg = "No impl found due to prior type error".into(); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::ImplMethodTypeMismatch { expected, actual, location } => { let msg = format!( "Impl method type {actual} does not unify with trait method type {expected}" ); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::BlackBoxError(error, location) => { - CustomDiagnostic::simple_error(error.to_string(), String::new(), location.span) + CustomDiagnostic::simple_error(error.to_string(), String::new(), *location) } InterpreterError::FailedToResolveTraitBound { trait_bound, location } => { let msg = format!("Failed to resolve trait bound `{trait_bound}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NoMatchingImplFound { error, .. } => error.into(), InterpreterError::Break => unreachable!("Uncaught InterpreterError::Break"), InterpreterError::Continue => unreachable!("Uncaught InterpreterError::Continue"), InterpreterError::TraitDefinitionMustBeAPath { location } => { let msg = "Trait definition arguments must be a variable or path".to_string(); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::FailedToResolveTraitDefinition { location } => { let msg = "Failed to resolve to a trait definition".to_string(); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::FunctionAlreadyResolved { location } => { let msg = "Function already resolved".to_string(); let secondary = "The function was previously called at compile-time or is in another crate" .to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::MultipleMatchingImpls { object_type, candidates, location } => { let message = format!("Multiple trait impls match the object type `{object_type}`"); let secondary = "Ambiguous impl".to_string(); - let mut error = CustomDiagnostic::simple_error(message, secondary, location.span); + let mut error = CustomDiagnostic::simple_error(message, secondary, *location); for (i, candidate) in candidates.iter().enumerate() { error.add_note(format!("Candidate {}: `{candidate}`", i + 1)); } @@ -637,7 +624,7 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let mut error = CustomDiagnostic::simple_error( "Object type is unknown in method call".to_string(), "Type must be known by this point to know which method to call".to_string(), - location.span, + *location, ); let message = "Try adding a type annotation for the object type before this method call"; @@ -649,19 +636,19 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { "Quoted value in index {index} of this slice is not a valid field name" ); let secondary = format!("`{value}` is not a valid field name for `set_fields`"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::InvalidAttribute { attribute, location } => { let msg = format!("`{attribute}` is not a valid attribute"); let secondary = "Note that this method expects attribute contents, without the leading `#[` or trailing `]`".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::GenericNameShouldBeAnIdent { name, location } => { let msg = "Generic name needs to be a valid identifier (one word beginning with a letter)" .to_string(); let secondary = format!("`{name}` is not a valid identifier"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::DuplicateGeneric { name, @@ -671,47 +658,76 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { } => { let msg = format!("`{struct_name}` already has a generic named `{name}`"); let secondary = format!("`{name}` added here a second time"); - let mut error = - CustomDiagnostic::simple_error(msg, secondary, duplicate_location.span); + let mut error = CustomDiagnostic::simple_error(msg, secondary, *duplicate_location); let existing_msg = format!("`{name}` was previously defined here"); - error.add_secondary_with_file( - existing_msg, - existing_location.span, - existing_location.file, - ); + error.add_secondary(existing_msg, *existing_location); error } InterpreterError::CannotResolveExpression { location, expression } => { let msg = format!("Cannot resolve expression `{expression}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::CannotSetFunctionBody { location, expression } => { let msg = format!("`{expression}` is not a valid function body"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::UnknownArrayLength { length, err, location } => { let msg = format!("Could not determine array length `{length}`"); let secondary = format!("Evaluating the length failed with: `{err}`"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::CannotInterpretFormatStringWithErrors { location } => { let msg = "Cannot interpret format string with errors".to_string(); let secondary = "Some of the variables to interpolate could not be evaluated".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::GlobalsDependencyCycle { location } => { let msg = "This global recursively depends on itself".to_string(); let secondary = String::new(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::LoopHaltedForUiResponsiveness { location } => { let msg = "This loop took too much time to execute so it was halted for UI responsiveness" .to_string(); let secondary = "This error doesn't happen in normal executions of `nargo`".to_string(); - CustomDiagnostic::simple_warning(msg, secondary, location.span) + CustomDiagnostic::simple_warning(msg, secondary, *location) + } + } + } +} + +/// Comptime errors always wrap another error to show it together with a +/// comptime call or macro "something" that eventually led to that error. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ComptimeError { + ErrorRunningAttribute { error: Box, location: Location }, + ErrorAddingItemToModule { error: Box, location: Location }, +} + +impl ComptimeError { + pub fn location(&self) -> Location { + match self { + ComptimeError::ErrorRunningAttribute { location, .. } + | ComptimeError::ErrorAddingItemToModule { location, .. } => *location, + } + } +} + +impl<'a> From<&'a ComptimeError> for CustomDiagnostic { + fn from(error: &'a ComptimeError) -> Self { + match error { + ComptimeError::ErrorRunningAttribute { error, location } => { + let mut diagnostic = CustomDiagnostic::from(&**error); + diagnostic.add_secondary("While running this function attribute".into(), *location); + diagnostic + } + ComptimeError::ErrorAddingItemToModule { error, location } => { + let mut diagnostic = CustomDiagnostic::from(&**error); + diagnostic.add_secondary("While interpreting `Module::add_item`".into(), *location); + diagnostic } } } 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 05a0435b4503..dcc938faf2ac 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 @@ -1,12 +1,13 @@ +use fm::FileId; use iter_extended::vecmap; -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Located, Location, Span}; use crate::ast::{ ArrayLiteral, AssignStatement, BlockExpression, CallExpression, CastExpression, ConstrainKind, ConstructorExpression, ExpressionKind, ForLoopStatement, ForRange, GenericTypeArgs, Ident, IfExpression, IndexExpression, InfixExpression, LValue, Lambda, Literal, MatchExpression, - MemberAccessExpression, MethodCallExpression, Path, PathKind, PathSegment, Pattern, - PrefixExpression, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, WhileStatement, + MemberAccessExpression, MethodCallExpression, Path, PathSegment, Pattern, PrefixExpression, + UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, UnsafeExpression, WhileStatement, }; use crate::ast::{ConstrainExpression, Expression, Statement, StatementKind}; use crate::hir_def::expr::{ @@ -15,6 +16,7 @@ use crate::hir_def::expr::{ use crate::hir_def::stmt::{HirLValue, HirPattern, HirStatement}; use crate::hir_def::types::{Type, TypeBinding}; use crate::node_interner::{DefinitionId, ExprId, NodeInterner, StmtId}; +use crate::signed_field::SignedField; // TODO: // - Full path for idents & types @@ -24,7 +26,7 @@ use crate::node_interner::{DefinitionId, ExprId, NodeInterner, StmtId}; // - Type::TypeVariable has no equivalent in the Ast impl HirStatement { - pub fn to_display_ast(&self, interner: &NodeInterner, span: Span) -> Statement { + pub fn to_display_ast(&self, interner: &NodeInterner, location: Location) -> Statement { let kind = match self { HirStatement::Let(let_stmt) => { let pattern = let_stmt.pattern.to_display_ast(interner); @@ -43,13 +45,15 @@ impl HirStatement { for_stmt.end_range.to_display_ast(interner), ), block: for_stmt.block.to_display_ast(interner), - span, + location, }), - HirStatement::Loop(block) => StatementKind::Loop(block.to_display_ast(interner), span), + HirStatement::Loop(block) => { + StatementKind::Loop(block.to_display_ast(interner), location) + } HirStatement::While(condition, block) => StatementKind::While(WhileStatement { condition: condition.to_display_ast(interner), body: block.to_display_ast(interner), - while_keyword_span: span, + while_keyword_location: location, }), HirStatement::Break => StatementKind::Break, HirStatement::Continue => StatementKind::Continue, @@ -63,7 +67,7 @@ impl HirStatement { } }; - Statement { kind, span } + Statement { kind, location } } } @@ -71,32 +75,32 @@ impl StmtId { /// Convert to AST for display (some details lost) pub fn to_display_ast(self, interner: &NodeInterner) -> Statement { let statement = interner.statement(&self); - let span = interner.statement_span(self); + let location = interner.statement_location(self); - statement.to_display_ast(interner, span) + statement.to_display_ast(interner, location) } } impl HirExpression { /// Convert to AST for display (some details lost) - pub fn to_display_ast(&self, interner: &NodeInterner, span: Span) -> Expression { + pub fn to_display_ast(&self, interner: &NodeInterner, location: Location) -> Expression { let kind = match self { HirExpression::Ident(ident, generics) => { - ident.to_display_expr(interner, generics, span) + ident.to_display_expr(interner, generics, location) } HirExpression::Literal(HirLiteral::Array(array)) => { - let array = array.to_display_ast(interner, span); + let array = array.to_display_ast(interner, location); ExpressionKind::Literal(Literal::Array(array)) } HirExpression::Literal(HirLiteral::Slice(array)) => { - let array = array.to_display_ast(interner, span); + let array = array.to_display_ast(interner, location); ExpressionKind::Literal(Literal::Slice(array)) } HirExpression::Literal(HirLiteral::Bool(value)) => { ExpressionKind::Literal(Literal::Bool(*value)) } - HirExpression::Literal(HirLiteral::Integer(value, sign)) => { - ExpressionKind::Literal(Literal::Integer(*value, *sign)) + HirExpression::Literal(HirLiteral::Integer(value)) => { + ExpressionKind::Literal(Literal::Integer(*value)) } HirExpression::Literal(HirLiteral::Str(string)) => { ExpressionKind::Literal(Literal::Str(string.clone())) @@ -113,7 +117,7 @@ impl HirExpression { })), HirExpression::Infix(infix) => ExpressionKind::Infix(Box::new(InfixExpression { lhs: infix.lhs.to_display_ast(interner), - operator: Spanned::from(infix.operator.location.span, infix.operator.kind), + operator: Located::from(infix.operator.location, infix.operator.kind), rhs: infix.rhs.to_display_ast(interner), })), HirExpression::Index(index) => ExpressionKind::Index(Box::new(IndexExpression { @@ -122,16 +126,14 @@ impl HirExpression { })), HirExpression::Constructor(constructor) => { let type_name = constructor.r#type.borrow().name.to_string(); - let type_name = Path::from_single(type_name, span); + let type_name = Path::from_single(type_name, location); let fields = vecmap(constructor.fields.clone(), |(name, expr): (Ident, ExprId)| { (name, expr.to_display_ast(interner)) }); - let struct_type = None; ExpressionKind::Constructor(Box::new(ConstructorExpression { typ: UnresolvedType::from_path(type_name), fields, - struct_type, })) } HirExpression::MemberAccess(access) => { @@ -170,7 +172,7 @@ impl HirExpression { ExpressionKind::Constrain(ConstrainExpression { kind: ConstrainKind::Assert, arguments, - span, + location, }) } HirExpression::Cast(cast) => { @@ -183,7 +185,7 @@ impl HirExpression { consequence: if_expr.consequence.to_display_ast(interner), alternative: if_expr.alternative.map(|expr| expr.to_display_ast(interner)), })), - HirExpression::Match(match_expr) => match_expr.to_display_ast(interner, span), + HirExpression::Match(match_expr) => match_expr.to_display_ast(interner, location), HirExpression::Tuple(fields) => { ExpressionKind::Tuple(vecmap(fields, |field| field.to_display_ast(interner))) } @@ -197,11 +199,12 @@ impl HirExpression { } HirExpression::Error => ExpressionKind::Error, HirExpression::Comptime(block) => { - ExpressionKind::Comptime(block.to_display_ast(interner), span) - } - HirExpression::Unsafe(block) => { - ExpressionKind::Unsafe(block.to_display_ast(interner), span) + ExpressionKind::Comptime(block.to_display_ast(interner), location) } + HirExpression::Unsafe(block) => ExpressionKind::Unsafe(UnsafeExpression { + block: block.to_display_ast(interner), + unsafe_keyword_location: location, + }), HirExpression::Quote(block) => ExpressionKind::Quote(block.clone()), // A macro was evaluated here: return the quoted result @@ -211,22 +214,23 @@ impl HirExpression { HirExpression::EnumConstructor(constructor) => { let typ = constructor.r#type.borrow(); let variant = &typ.variant_at(constructor.variant_index); - let segment1 = PathSegment { ident: typ.name.clone(), span, generics: None }; - let segment2 = PathSegment { ident: variant.name.clone(), span, generics: None }; - let path = Path { segments: vec![segment1, segment2], kind: PathKind::Plain, span }; - let func = Box::new(Expression::new(ExpressionKind::Variable(path), span)); + let segment1 = PathSegment { ident: typ.name.clone(), location, generics: None }; + let segment2 = + PathSegment { ident: variant.name.clone(), location, generics: None }; + let path = Path::plain(vec![segment1, segment2], location); + let func = Box::new(Expression::new(ExpressionKind::Variable(path), location)); let arguments = vecmap(&constructor.arguments, |arg| arg.to_display_ast(interner)); let call = CallExpression { func, arguments, is_macro_call: false }; ExpressionKind::Call(Box::new(call)) } }; - Expression::new(kind, span) + Expression::new(kind, location) } } impl HirMatch { - fn to_display_ast(&self, interner: &NodeInterner, span: Span) -> ExpressionKind { + fn to_display_ast(&self, interner: &NodeInterner, location: Location) -> ExpressionKind { match self { HirMatch::Success(expr) => expr.to_display_ast(interner).kind, HirMatch::Failure => ExpressionKind::Error, @@ -234,28 +238,29 @@ impl HirMatch { let condition = cond.to_display_ast(interner); let consequence = body.to_display_ast(interner); let alternative = - Some(Expression::new(otherwise.to_display_ast(interner, span), span)); + Some(Expression::new(otherwise.to_display_ast(interner, location), location)); ExpressionKind::If(Box::new(IfExpression { condition, consequence, alternative })) } HirMatch::Switch(variable, cases, default) => { let location = interner.definition(*variable).location; let ident = HirIdent::non_trait_method(*variable, location); - let expression = ident.to_display_expr(interner, &None, location.span); - let expression = Expression::new(expression, location.span); + let expression = ident.to_display_expr(interner, &None, location); + let expression = Expression::new(expression, location); let mut rules = vecmap(cases, |case| { let args = vecmap(&case.arguments, |arg| arg.to_display_ast(interner)); let constructor = case.constructor.to_display_ast(args); - let constructor = Expression::new(constructor, span); - let branch = case.body.to_display_ast(interner, span); - (constructor, Expression::new(branch, span)) + let constructor = Expression::new(constructor, location); + let branch = case.body.to_display_ast(interner, location); + (constructor, Expression::new(branch, location)) }); if let Some(case) = default { - let kind = ExpressionKind::Variable(Path::from_single("_".to_string(), span)); - let pattern = Expression::new(kind, span); - let branch = Expression::new(case.to_display_ast(interner, span), span); + let kind = + ExpressionKind::Variable(Path::from_single("_".to_string(), location)); + let pattern = Expression::new(kind, location); + let branch = Expression::new(case.to_display_ast(interner, location), location); rules.push((pattern, branch)); } @@ -268,12 +273,9 @@ impl HirMatch { impl DefinitionId { fn to_display_ast(self, interner: &NodeInterner) -> Expression { let location = interner.definition(self).location; - let kind = HirIdent::non_trait_method(self, location).to_display_expr( - interner, - &None, - location.span, - ); - Expression::new(kind, location.span) + let kind = + HirIdent::non_trait_method(self, location).to_display_expr(interner, &None, location); + Expression::new(kind, location) } } @@ -283,9 +285,7 @@ impl Constructor { Constructor::True => ExpressionKind::Literal(Literal::Bool(true)), Constructor::False => ExpressionKind::Literal(Literal::Bool(false)), Constructor::Unit => ExpressionKind::Literal(Literal::Unit), - Constructor::Int(value) => { - ExpressionKind::Literal(Literal::Integer(value.field, value.is_negative)) - } + Constructor::Int(value) => ExpressionKind::Literal(Literal::Integer(*value)), Constructor::Tuple(_) => ExpressionKind::Tuple(arguments), Constructor::Variant(typ, index) => { let typ = typ.follow_bindings_shallow(); @@ -301,9 +301,9 @@ impl Constructor { return ExpressionKind::Error; }; - let span = name.span(); + let location = name.location(); let name = ExpressionKind::Variable(Path::from_ident(name)); - let func = Box::new(Expression::new(name, span)); + let func = Box::new(Expression::new(name, location)); let is_macro_call = false; ExpressionKind::Call(Box::new(CallExpression { func, arguments, is_macro_call })) } @@ -319,8 +319,8 @@ impl ExprId { pub fn to_display_ast(self, interner: &NodeInterner) -> Expression { let expression = interner.expression(&self); // TODO: empty 0 span - let span = interner.try_expr_span(&self).unwrap_or_else(|| Span::empty(0)); - expression.to_display_ast(interner, span) + let location = interner.try_id_location(self).unwrap_or_else(Location::dummy); + expression.to_display_ast(interner, location) } } @@ -331,11 +331,11 @@ impl HirPattern { HirPattern::Identifier(ident) => Pattern::Identifier(ident.to_display_ast(interner)), HirPattern::Mutable(pattern, location) => { let pattern = Box::new(pattern.to_display_ast(interner)); - Pattern::Mutable(pattern, location.span, false) + Pattern::Mutable(pattern, *location, false) } HirPattern::Tuple(patterns, location) => { let patterns = vecmap(patterns, |pattern| pattern.to_display_ast(interner)); - Pattern::Tuple(patterns, location.span) + Pattern::Tuple(patterns, *location) } HirPattern::Struct(typ, patterns, location) => { let patterns = vecmap(patterns, |(name, pattern)| { @@ -352,8 +352,8 @@ impl HirPattern { other => other.to_string(), }; // The name span is lost here - let path = Path::from_single(name, location.span); - Pattern::Struct(path, patterns, location.span) + let path = Path::from_single(name, *location); + Pattern::Struct(path, patterns, *location) } } } @@ -363,14 +363,14 @@ impl HirIdent { /// Convert to AST for display (some details lost) fn to_display_ast(&self, interner: &NodeInterner) -> Ident { let name = interner.definition_name(self.id).to_owned(); - Ident(Spanned::from(self.location.span, name)) + Ident(Located::from(self.location, name)) } fn to_display_expr( &self, interner: &NodeInterner, generics: &Option>, - span: Span, + location: Location, ) -> ExpressionKind { let ident = self.to_display_ast(interner); let segment = PathSegment { @@ -378,10 +378,10 @@ impl HirIdent { generics: generics .as_ref() .map(|option| option.iter().map(|generic| generic.to_display_ast()).collect()), - span, + location, }; - let path = Path { segments: vec![segment], kind: crate::ast::PathKind::Plain, span }; + let path = Path::plain(vec![segment], location); ExpressionKind::Variable(path) } @@ -439,7 +439,8 @@ impl Type { TypeBinding::Bound(typ) => return typ.to_display_ast(), TypeBinding::Unbound(id, type_var_kind) => { let name = format!("var_{:?}_{}", type_var_kind, id); - let path = Path::from_single(name, Span::empty(0)); + let path = + Path::from_single(name, Location::new(Span::empty(0), FileId::dummy())); let expression = UnresolvedTypeExpression::Variable(path); UnresolvedTypeData::Expression(expression) } @@ -450,11 +451,11 @@ impl Type { (named_type.name.clone(), named_type.typ.to_display_ast()) }); let generics = GenericTypeArgs { ordered_args, named_args, kinds: Vec::new() }; - let name = Path::from_single(name.as_ref().clone(), Span::default()); + let name = Path::from_single(name.as_ref().clone(), Location::dummy()); UnresolvedTypeData::TraitAsType(name, generics) } Type::NamedGeneric(_var, name) => { - let name = Path::from_single(name.as_ref().clone(), Span::default()); + let name = Path::from_single(name.as_ref().clone(), Location::dummy()); UnresolvedTypeData::Named(name, GenericTypeArgs::default(), true) } Type::CheckedCast { to, .. } => to.to_display_ast().typ, @@ -479,23 +480,23 @@ impl Type { Type::InfixExpr(lhs, op, rhs, _) => { let lhs = Box::new(lhs.to_type_expression()); let rhs = Box::new(rhs.to_type_expression()); - let span = Span::default(); - let expr = UnresolvedTypeExpression::BinaryOperation(lhs, *op, rhs, span); + let location = Location::dummy(); + let expr = UnresolvedTypeExpression::BinaryOperation(lhs, *op, rhs, location); UnresolvedTypeData::Expression(expr) } }; - UnresolvedType { typ, span: Span::default() } + UnresolvedType { typ, location: Location::dummy() } } /// Convert to AST for display (some details lost) fn to_type_expression(&self) -> UnresolvedTypeExpression { - let span = Span::default(); + let location = Location::dummy(); match self.follow_bindings() { - Type::Constant(length, _kind) => UnresolvedTypeExpression::Constant(length, span), + Type::Constant(length, _kind) => UnresolvedTypeExpression::Constant(length, location), Type::NamedGeneric(_var, name) => { - let path = Path::from_single(name.as_ref().clone(), span); + let path = Path::from_single(name.as_ref().clone(), location); UnresolvedTypeExpression::Variable(path) } // TODO: This should be turned into a proper error. @@ -511,16 +512,16 @@ impl HirLValue { HirLValue::Ident(ident, _) => LValue::Ident(ident.to_display_ast(interner)), HirLValue::MemberAccess { object, field_name, field_index: _, typ: _, location } => { let object = Box::new(object.to_display_ast(interner)); - LValue::MemberAccess { object, field_name: field_name.clone(), span: location.span } + LValue::MemberAccess { object, field_name: field_name.clone(), location: *location } } HirLValue::Index { array, index, typ: _, location } => { let array = Box::new(array.to_display_ast(interner)); let index = index.to_display_ast(interner); - LValue::Index { array, index, span: location.span } + LValue::Index { array, index, location: *location } } HirLValue::Dereference { lvalue, element_type: _, location } => { let lvalue = Box::new(lvalue.to_display_ast(interner)); - LValue::Dereference(lvalue, location.span) + LValue::Dereference(lvalue, *location) } } } @@ -528,7 +529,7 @@ impl HirLValue { impl HirArrayLiteral { /// Convert to AST for display (some details lost) - fn to_display_ast(&self, interner: &NodeInterner, span: Span) -> ArrayLiteral { + fn to_display_ast(&self, interner: &NodeInterner, location: Location) -> ArrayLiteral { match self { HirArrayLiteral::Standard(elements) => { ArrayLiteral::Standard(vecmap(elements, |element| element.to_display_ast(interner))) @@ -537,11 +538,13 @@ impl HirArrayLiteral { let repeated_element = Box::new(repeated_element.to_display_ast(interner)); let length = match length { Type::Constant(length, _kind) => { - let literal = Literal::Integer(*length, false); + let literal = Literal::Integer(SignedField::positive(*length)); let expr_kind = ExpressionKind::Literal(literal); - Box::new(Expression::new(expr_kind, span)) + Box::new(Expression::new(expr_kind, location)) } - other => panic!("Cannot convert non-constant type for repeated array literal from Hir -> Ast: {other:?}"), + other => panic!( + "Cannot convert non-constant type for repeated array literal from Hir -> Ast: {other:?}" + ), }; ArrayLiteral::Repeated { repeated_element, length } } 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 279d176ad33b..5c87e70949a1 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -2,13 +2,13 @@ use std::collections::VecDeque; use std::{collections::hash_map::Entry, rc::Rc}; use acvm::blackbox_solver::BigIntSolverWithId; -use acvm::{acir::AcirField, FieldElement}; -use fm::FileId; +use acvm::{FieldElement, acir::AcirField}; use im::Vector; use iter_extended::try_vecmap; use noirc_errors::Location; use rustc_hash::FxHashMap as HashMap; +use crate::TypeVariable; use crate::ast::{BinaryOpKind, FunctionKind, IntegerBitSize, Signedness, UnaryOp}; use crate::elaborator::Elaborator; use crate::graph::CrateId; @@ -21,9 +21,10 @@ use crate::monomorphization::{ undo_instantiation_bindings, }; use crate::node_interner::GlobalValue; +use crate::signed_field::SignedField; use crate::token::{FmtStrFragment, Tokens}; -use crate::TypeVariable; use crate::{ + Shared, Type, TypeBinding, TypeBindings, hir_def::{ expr::{ HirArrayLiteral, HirBlockExpression, HirCallExpression, HirCastExpression, @@ -38,11 +39,10 @@ use crate::{ types::Kind, }, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, NodeInterner, StmtId}, - Shared, Type, TypeBinding, TypeBindings, }; use super::errors::{IResult, InterpreterError}; -use super::value::{unwrap_rc, Closure, Value}; +use super::value::{Closure, Value, unwrap_rc}; mod builtin; mod foreign; @@ -67,9 +67,6 @@ pub struct Interpreter<'local, 'interner> { /// Stateful bigint calculator. bigint_solver: BigIntSolverWithId, - - /// Use pedantic ACVM solving - pedantic_solving: bool, } #[allow(unused)] @@ -78,8 +75,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { elaborator: &'local mut Elaborator<'interner>, crate_id: CrateId, current_function: Option, - pedantic_solving: bool, ) -> Self { + let pedantic_solving = elaborator.pedantic_solving(); let bigint_solver = BigIntSolverWithId::with_pedantic_solving(pedantic_solving); Self { elaborator, @@ -88,7 +85,6 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { bound_generics: Vec::new(), in_loop: false, bigint_solver, - pedantic_solving, } } @@ -222,11 +218,10 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { fn elaborate_in_module( &mut self, module: ModuleId, - file: FileId, f: impl FnOnce(&mut Elaborator) -> T, ) -> T { self.unbind_generics_from_previous_function(); - let result = self.elaborator.elaborate_item_from_comptime_in_module(module, file, f); + let result = self.elaborator.elaborate_item_from_comptime_in_module(module, f); self.rebind_generics_from_previous_function(); result } @@ -623,9 +618,12 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Err(InterpreterError::NonIntegerArrayLength { typ, err: None, location }) } TypeBinding::Bound(binding) => { - let span = self.elaborator.interner.id_location(id).span; + let location = self.elaborator.interner.id_location(id); binding - .evaluate_to_field_element(&Kind::Numeric(numeric_typ.clone()), span) + .evaluate_to_field_element( + &Kind::Numeric(numeric_typ.clone()), + location, + ) .map_err(|err| { let typ = Type::TypeVariable(type_variable.clone()); let err = Some(Box::new(err)); @@ -635,7 +633,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } }?; - self.evaluate_integer(value, false, id) + self.evaluate_integer(value.into(), id) } } } @@ -644,9 +642,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { match literal { HirLiteral::Unit => Ok(Value::Unit), HirLiteral::Bool(value) => Ok(Value::Bool(value)), - HirLiteral::Integer(value, is_negative) => { - self.evaluate_integer(value, is_negative, id) - } + HirLiteral::Integer(value) => self.evaluate_integer(value, id), HirLiteral::Str(string) => Ok(Value::String(Rc::new(string))), HirLiteral::FmtStr(fragments, captures, _length) => { self.evaluate_format_string(fragments, captures, id) @@ -674,7 +670,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { FmtStrFragment::String(string) => { result.push_str(&string); } - FmtStrFragment::Interpolation(_, span) => { + FmtStrFragment::Interpolation(..) => { if let Some(value) = values.pop_front() { // When interpolating a quoted value inside a format string, we don't include the // surrounding `quote {` ... `}` as if we are unquoting the quoted value inside the string. @@ -683,8 +679,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { if index > 0 { result.push(' '); } - result - .push_str(&token.display(self.elaborator.interner).to_string()); + result.push_str( + &token.token().display(self.elaborator.interner).to_string(), + ); } } else { result.push_str(&value.display(self.elaborator.interner).to_string()); @@ -705,103 +702,85 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Ok(Value::FormatString(Rc::new(result), typ)) } - fn evaluate_integer( - &self, - value: FieldElement, - is_negative: bool, - id: ExprId, - ) -> IResult { + fn evaluate_integer(&self, value: SignedField, id: ExprId) -> IResult { let typ = self.elaborator.interner.id_type(id).follow_bindings(); let location = self.elaborator.interner.expr_location(&id); if let Type::FieldElement = &typ { - let value = if is_negative { -value } else { value }; - Ok(Value::Field(value)) + Ok(Value::Field(value.into())) } else if let Type::Integer(sign, bit_size) = &typ { match (sign, bit_size) { (Signedness::Unsigned, IntegerBitSize::One) => { return Err(InterpreterError::TypeUnsupported { typ, location }); } (Signedness::Unsigned, IntegerBitSize::Eight) => { - let value: u8 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( - InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; - let value = if is_negative { 0u8.wrapping_sub(value) } else { value }; + let value = value.try_to_unsigned().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::U8(value)) } (Signedness::Unsigned, IntegerBitSize::Sixteen) => { - let value: u16 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( - InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; - let value = if is_negative { 0u16.wrapping_sub(value) } else { value }; + let value = value.try_to_unsigned().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::U16(value)) } (Signedness::Unsigned, IntegerBitSize::ThirtyTwo) => { - let value: u32 = - value.try_to_u32().ok_or(InterpreterError::IntegerOutOfRangeForType { - value, - typ, - location, - })?; - let value = if is_negative { 0u32.wrapping_sub(value) } else { value }; + let value = value.try_to_unsigned().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::U32(value)) } (Signedness::Unsigned, IntegerBitSize::SixtyFour) => { - let value: u64 = - value.try_to_u64().ok_or(InterpreterError::IntegerOutOfRangeForType { - value, - typ, - location, - })?; - let value = if is_negative { 0u64.wrapping_sub(value) } else { value }; + let value = value.try_to_unsigned().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::U64(value)) } + (Signedness::Unsigned, IntegerBitSize::HundredTwentyEight) => { + let value: u128 = value.try_to_unsigned().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; + Ok(Value::U128(value)) + } (Signedness::Signed, IntegerBitSize::One) => { return Err(InterpreterError::TypeUnsupported { typ, location }); } (Signedness::Signed, IntegerBitSize::Eight) => { - let value: i8 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( - InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; - let value = if is_negative { -value } else { value }; + let value = value.try_to_signed().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::I8(value)) } (Signedness::Signed, IntegerBitSize::Sixteen) => { - let value: i16 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( - InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; - let value = if is_negative { -value } else { value }; + let value = value.try_to_signed().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::I16(value)) } (Signedness::Signed, IntegerBitSize::ThirtyTwo) => { - let value: i32 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( - InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; - let value = if is_negative { -value } else { value }; + let value = value.try_to_signed().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::I32(value)) } (Signedness::Signed, IntegerBitSize::SixtyFour) => { - let value: i64 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( - InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; - let value = if is_negative { -value } else { value }; + let value = value.try_to_signed().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::I64(value)) } + (Signedness::Signed, IntegerBitSize::HundredTwentyEight) => { + return Err(InterpreterError::TypeUnsupported { typ, location }); + } } } else if let Type::TypeVariable(variable) = &typ { if variable.is_integer_or_field() { - Ok(Value::Field(value)) + Ok(Value::Field(value.into())) } else if variable.is_integer() { - let value: u64 = value - .try_to_u64() + let value = value + .try_to_unsigned() .ok_or(InterpreterError::IntegerOutOfRangeForType { value, typ, location })?; - let value = if is_negative { 0u64.wrapping_sub(value) } else { value }; Ok(Value::U64(value)) } else { Err(InterpreterError::NonIntegerIntegerLiteral { typ, location }) @@ -844,8 +823,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { HirArrayLiteral::Repeated { repeated_element, length } => { let element = self.evaluate(repeated_element)?; - let span = self.elaborator.interner.id_location(id).span; - match length.evaluate_to_u32(span) { + let location = self.elaborator.interner.id_location(id); + match length.evaluate_to_u32(location) { Ok(length) => { let elements = (0..length).map(|_| element.clone()).collect(); Ok(Value::Array(elements, typ)) @@ -1248,6 +1227,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Value::Field(value) => { value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else(|| { let typ = Type::default_int_type(); + let value = SignedField::positive(value); InterpreterError::IntegerOutOfRangeForType { value, typ, location } })? } @@ -1377,11 +1357,11 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } fn unify_without_binding(&mut self, actual: &Type, expected: &Type, location: Location) { - self.elaborator.unify_without_applying_bindings(actual, expected, location.file, || { + self.elaborator.unify_without_applying_bindings(actual, expected, || { TypeCheckError::TypeMismatch { expected_typ: expected.to_string(), expr_typ: actual.to_string(), - expr_span: location.span, + expr_location: location, } }); } @@ -1399,10 +1379,11 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let typ = object.get_type().follow_bindings(); let method_name = &call.method.0.contents; + let check_self_param = true; let method = self .elaborator - .lookup_method(&typ, method_name, location.span, true) + .lookup_method(&typ, method_name, location, check_self_param) .and_then(|method| method.func_id(self.elaborator.interner)); if let Some(method) = method { @@ -1495,6 +1476,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Signedness::Unsigned, IntegerBitSize::SixtyFour) => { cast_to_int!(lhs, to_u128, u64, U64) } + (Signedness::Unsigned, IntegerBitSize::HundredTwentyEight) => { + cast_to_int!(lhs, to_u128, u128, U128) + } (Signedness::Signed, IntegerBitSize::One) => { Err(InterpreterError::TypeUnsupported { typ: typ.clone(), location }) } @@ -1508,6 +1492,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Signedness::Signed, IntegerBitSize::SixtyFour) => { cast_to_int!(lhs, to_i128, i64, I64) } + (Signedness::Signed, IntegerBitSize::HundredTwentyEight) => { + todo!() + } }, Type::Bool => Ok(Value::Bool(!lhs.is_zero() || lhs_is_negative)), typ => Err(InterpreterError::CastToNonNumericType { typ, location }), 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 ff46592f9ed1..34a5535f63c2 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 @@ -12,23 +12,24 @@ use builtin_helpers::{ }; use im::Vector; use iter_extended::{try_vecmap, vecmap}; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use num_bigint::BigUint; use rustc_hash::FxHashMap as HashMap; use crate::{ + Kind, QuotedType, ResolvedGeneric, Shared, Type, TypeVariable, ast::{ ArrayLiteral, BlockExpression, ConstrainKind, Expression, ExpressionKind, ForRange, FunctionKind, FunctionReturnType, Ident, IntegerBitSize, ItemVisibility, LValue, Literal, Pattern, Signedness, Statement, StatementKind, UnaryOp, UnresolvedType, UnresolvedTypeData, - Visibility, + UnsafeExpression, Visibility, }, - elaborator::Elaborator, + elaborator::{ElaborateReason, Elaborator}, hir::{ comptime::{ + InterpreterError, Value, errors::IResult, value::{ExprValue, TypedExpr}, - InterpreterError, Value, }, def_collector::dc_crate::CollectedItems, def_map::ModuleDefId, @@ -40,8 +41,7 @@ use crate::{ }, node_interner::{DefinitionKind, NodeInterner, TraitImplKind}, parser::{Parser, StatementOrExpressionOrLValue}, - token::{Attribute, Token}, - Kind, QuotedType, ResolvedGeneric, Shared, Type, TypeVariable, + token::{Attribute, LocatedToken, Token}, }; use self::builtin_helpers::{eq_item, get_array, get_ctstring, get_str, get_u8, hash_item, lex}; @@ -49,7 +49,7 @@ use super::Interpreter; pub(crate) mod builtin_helpers; -impl<'local, 'context> Interpreter<'local, 'context> { +impl Interpreter<'_, '_> { pub(super) fn call_builtin( &mut self, name: &str, @@ -247,7 +247,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "unresolved_type_is_bool" => unresolved_type_is_bool(interner, arguments, location), "unresolved_type_is_field" => unresolved_type_is_field(interner, arguments, location), "unresolved_type_is_unit" => unresolved_type_is_unit(interner, arguments, location), - "zeroed" => Ok(zeroed(return_type, location.span)), + "zeroed" => Ok(zeroed(return_type, location)), _ => { let item = format!("Comptime evaluation for builtin function '{name}'"); Err(InterpreterError::Unimplemented { item, location }) @@ -385,7 +385,7 @@ fn struct_def_add_attribute( let attribute_location = attribute.1; let attribute = get_str(interner, attribute)?; let attribute = format!("#[{}]", attribute); - let mut parser = Parser::for_str(&attribute); + let mut parser = Parser::for_str(&attribute, attribute_location.file); let Some((Attribute::Secondary(attribute), _span)) = parser.parse_attribute() else { return Err(InterpreterError::InvalidAttribute { attribute: attribute.to_string(), @@ -411,7 +411,7 @@ fn struct_def_add_generic( let generic_location = generic.1; let generic = get_str(interner, generic)?; - let mut tokens = lex(&generic); + let mut tokens = lex(&generic, location); if tokens.len() != 1 { return Err(InterpreterError::GenericNameShouldBeAnIdent { name: generic, @@ -419,7 +419,7 @@ fn struct_def_add_generic( }); } - let Token::Ident(generic_name) = tokens.remove(0) else { + let Token::Ident(generic_name) = tokens.remove(0).into_token() else { return Err(InterpreterError::GenericNameShouldBeAnIdent { name: generic, location: generic_location, @@ -436,7 +436,7 @@ fn struct_def_add_generic( return Err(InterpreterError::DuplicateGeneric { name, struct_name: the_struct.name.to_string(), - existing_location: Location::new(generic.span, the_struct.location.file), + existing_location: generic.location, duplicate_location: generic_location, }); } @@ -444,9 +444,8 @@ fn struct_def_add_generic( let type_var_kind = Kind::Normal; let type_var = TypeVariable::unbound(interner.next_type_variable_id(), type_var_kind); - let span = generic_location.span; let typ = Type::NamedGeneric(type_var.clone(), name.clone()); - let new_generic = ResolvedGeneric { name, type_var, span }; + let new_generic = ResolvedGeneric { name, type_var, location: generic_location }; the_struct.generics.push(new_generic); Ok(Value::Type(typ)) @@ -509,7 +508,7 @@ fn struct_def_generics( Kind::Numeric(numeric_type) => Some(Value::Type(*numeric_type)), _ => None, }; - let numeric_type = option(option_typ.clone(), numeric_type, location.span); + let numeric_type = option(option_typ.clone(), numeric_type, location); Value::Tuple(vec![Value::Type(generic_as_named), numeric_type]) }) .collect(); @@ -562,7 +561,10 @@ fn struct_def_fields( if actual != expected { let s = if expected == 1 { "" } else { "s" }; let was_were = if actual == 1 { "was" } else { "were" }; - let message = Some(format!("`StructDefinition::fields` expected {expected} generic{s} for `{}` but {actual} {was_were} given", struct_def.name)); + let message = Some(format!( + "`StructDefinition::fields` expected {expected} generic{s} for `{}` but {actual} {was_were} given", + struct_def.name + )); let location = args_location; let call_stack = call_stack.clone(); return Err(InterpreterError::FailingConstraint { message, location, call_stack }); @@ -572,7 +574,8 @@ fn struct_def_fields( if let Some(struct_fields) = struct_def.get_fields(&generic_args) { for (field_name, field_type) in struct_fields { - let name = Value::Quoted(Rc::new(vec![Token::Ident(field_name)])); + let token = LocatedToken::new(Token::Ident(field_name), location); + let name = Value::Quoted(Rc::new(vec![token])); fields.push_back(Value::Tuple(vec![name, Value::Type(field_type)])); } } @@ -602,7 +605,8 @@ fn struct_def_fields_as_written( if let Some(struct_fields) = struct_def.get_fields_as_written() { for field in struct_fields { - let name = Value::Quoted(Rc::new(vec![Token::Ident(field.name.to_string())])); + let token = LocatedToken::new(Token::Ident(field.name.to_string()), location); + let name = Value::Quoted(Rc::new(vec![token])); let typ = Value::Type(field.typ); fields.push_back(Value::Tuple(vec![name, typ])); } @@ -638,7 +642,8 @@ fn struct_def_name( let the_struct = interner.get_type(struct_id); let name = Token::Ident(the_struct.borrow().name.to_string()); - Ok(Value::Quoted(Rc::new(vec![name]))) + let token = LocatedToken::new(name, location); + Ok(Value::Quoted(Rc::new(vec![token]))) } /// fn set_fields(self, new_fields: [(Quoted, Type)]) {} @@ -669,11 +674,11 @@ fn struct_def_set_fields( let name_tokens = get_quoted((name_value.clone(), field_location))?; let typ = get_type((typ, field_location))?; - match name_tokens.first() { + match name_tokens.first().map(|t| t.token()) { Some(Token::Ident(name)) if name_tokens.len() == 1 => { Ok(hir_def::types::StructField { visibility: ItemVisibility::Public, - name: Ident::new(name.clone(), field_location.span), + name: Ident::new(name.clone(), field_location), typ, }) } @@ -812,7 +817,7 @@ fn quoted_as_expr( }, ); - Ok(option(return_type, value, location.span)) + Ok(option(return_type, value, location)) } // fn as_module(quoted: Quoted) -> Option @@ -835,7 +840,7 @@ fn quoted_as_module( module.map(Value::ModuleDefinition) }); - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn as_trait_constraint(quoted: Quoted) -> TraitConstraint @@ -953,11 +958,7 @@ fn to_le_radix( None => 0, }; // The only built-ins that use these either return `[u1; N]` or `[u8; N]` - if return_type_is_bits { - Value::U1(digit != 0) - } else { - Value::U8(digit) - } + if return_type_is_bits { Value::U1(digit != 0) } else { Value::U8(digit) } }); let result_type = Type::Array( @@ -1003,7 +1004,7 @@ fn type_as_constant( // Prefer to use `evaluate_to_u32` over matching on `Type::Constant` // since arithmetic generics may be `Type::InfixExpr`s which evaluate to // constants but are not actually the `Type::Constant` variant. - match typ.evaluate_to_u32(location.span) { + match typ.evaluate_to_u32(location) { Ok(constant) => Ok(Some(Value::U32(constant))), Err(err) => { // Evaluating to a non-constant returns 'None' in user code @@ -1040,11 +1041,7 @@ fn type_as_mutable_reference( location: Location, ) -> IResult { type_as(arguments, return_type, location, |typ| { - if let Type::MutableReference(typ) = typ { - Some(Value::Type(*typ)) - } else { - None - } + if let Type::MutableReference(typ) = typ { Some(Value::Type(*typ)) } else { None } }) } @@ -1055,11 +1052,7 @@ fn type_as_slice( location: Location, ) -> IResult { type_as(arguments, return_type, location, |typ| { - if let Type::Slice(slice_type) = typ { - Some(Value::Type(*slice_type)) - } else { - None - } + if let Type::Slice(slice_type) = typ { Some(Value::Type(*slice_type)) } else { None } }) } @@ -1070,11 +1063,7 @@ fn type_as_str( location: Location, ) -> IResult { type_as(arguments, return_type, location, |typ| { - if let Type::String(n) = typ { - Some(Value::Type(*n)) - } else { - None - } + if let Type::String(n) = typ { Some(Value::Type(*n)) } else { None } }) } @@ -1147,7 +1136,7 @@ where let option_value = f(typ)?; - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn type_eq(_first: Type, _second: Type) -> bool @@ -1182,7 +1171,7 @@ fn type_get_trait_impl( _ => None, }; - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn implements(self, constraint: TraitConstraint) -> bool @@ -1303,7 +1292,7 @@ fn typed_expr_as_function_definition( } else { None }; - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn get_type(self) -> Option @@ -1317,15 +1306,11 @@ fn typed_expr_get_type( let typed_expr = get_typed_expr(self_argument)?; let option_value = if let TypedExpr::ExprId(expr_id) = typed_expr { let typ = interner.id_type(expr_id); - if typ == Type::Error { - None - } else { - Some(Value::Type(typ)) - } + if typ == Type::Error { None } else { Some(Value::Type(typ)) } } else { None }; - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn as_mutable_reference(self) -> Option @@ -1408,16 +1393,16 @@ where let typ = get_unresolved_type(interner, value)?; let option_value = f(typ); - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn zeroed() -> T -fn zeroed(return_type: Type, span: Span) -> Value { +fn zeroed(return_type: Type, location: Location) -> Value { match return_type { Type::FieldElement => Value::Field(0u128.into()), Type::Array(length_type, elem) => { - if let Ok(length) = length_type.evaluate_to_u32(span) { - let element = zeroed(elem.as_ref().clone(), span); + if let Ok(length) = length_type.evaluate_to_u32(location) { + let element = zeroed(elem.as_ref().clone(), location); let array = std::iter::repeat(element).take(length as usize).collect(); Value::Array(array, Type::Array(length_type, elem)) } else { @@ -1432,15 +1417,17 @@ fn zeroed(return_type: Type, span: Span) -> Value { (Signedness::Unsigned, IntegerBitSize::Sixteen) => Value::U16(0), (Signedness::Unsigned, IntegerBitSize::ThirtyTwo) => Value::U32(0), (Signedness::Unsigned, IntegerBitSize::SixtyFour) => Value::U64(0), + (Signedness::Unsigned, IntegerBitSize::HundredTwentyEight) => Value::U128(0), (Signedness::Signed, IntegerBitSize::One) => Value::I8(0), (Signedness::Signed, IntegerBitSize::Eight) => Value::I8(0), (Signedness::Signed, IntegerBitSize::Sixteen) => Value::I16(0), (Signedness::Signed, IntegerBitSize::ThirtyTwo) => Value::I32(0), (Signedness::Signed, IntegerBitSize::SixtyFour) => Value::I64(0), + (Signedness::Signed, IntegerBitSize::HundredTwentyEight) => todo!(), }, Type::Bool => Value::Bool(false), Type::String(length_type) => { - if let Ok(length) = length_type.evaluate_to_u32(span) { + if let Ok(length) = length_type.evaluate_to_u32(location) { Value::String(Rc::new("\0".repeat(length as usize))) } else { // Assume we can resolve the length later @@ -1448,7 +1435,7 @@ fn zeroed(return_type: Type, span: Span) -> Value { } } Type::FmtString(length_type, captures) => { - let length = length_type.evaluate_to_u32(span); + let length = length_type.evaluate_to_u32(location); let typ = Type::FmtString(length_type, captures); if let Ok(length) = length { Value::FormatString(Rc::new("\0".repeat(length as usize)), typ) @@ -1458,7 +1445,7 @@ fn zeroed(return_type: Type, span: Span) -> Value { } } Type::Unit => Value::Unit, - Type::Tuple(fields) => Value::Tuple(vecmap(fields, |field| zeroed(field, span))), + Type::Tuple(fields) => Value::Tuple(vecmap(fields, |field| zeroed(field, location))), Type::DataType(data_type, generics) => { let typ = data_type.borrow(); @@ -1466,7 +1453,7 @@ fn zeroed(return_type: Type, span: Span) -> Value { let mut values = HashMap::default(); for (field_name, field_type) in fields { - let field_value = zeroed(field_type, span); + let field_value = zeroed(field_type, location); values.insert(Rc::new(field_name), field_value); } @@ -1480,7 +1467,7 @@ fn zeroed(return_type: Type, span: Span) -> Value { if !variants.is_empty() { // is_empty & swap_remove let us avoid a .clone() we'd need if we did .get(0) let (_name, params) = variants.swap_remove(0); - args = vecmap(params, |param| zeroed(param, span)); + args = vecmap(params, |param| zeroed(param, location)); } drop(typ); @@ -1490,14 +1477,14 @@ fn zeroed(return_type: Type, span: Span) -> Value { Value::Zeroed(Type::DataType(data_type, generics)) } } - Type::Alias(alias, generics) => zeroed(alias.borrow().get_type(&generics), span), - Type::CheckedCast { to, .. } => zeroed(*to, span), + Type::Alias(alias, generics) => zeroed(alias.borrow().get_type(&generics), location), + Type::CheckedCast { to, .. } => zeroed(*to, location), typ @ Type::Function(..) => { // Using Value::Zeroed here is probably safer than using FuncId::dummy_id() or similar Value::Zeroed(typ) } Type::MutableReference(element) => { - let element = zeroed(*element, span); + let element = zeroed(*element, location); Value::Pointer(Shared::new(element), false) } // Optimistically assume we can resolve this type later or that the value is unused @@ -1561,7 +1548,7 @@ fn expr_as_assert( let option_type = tuple_types.pop().unwrap(); let message = message.map(|msg| Value::expression(msg.kind)); - let message = option(option_type, message, location.span); + let message = option(option_type, message, location); Some(Value::Tuple(vec![predicate, message])) } else { @@ -1607,7 +1594,7 @@ fn expr_as_assert_eq( let option_type = tuple_types.pop().unwrap(); let message = message.map(|message| Value::expression(message.kind)); - let message = option(option_type, message, location.span); + let message = option(option_type, message, location); Some(Value::Tuple(vec![lhs, rhs, message])) } else { @@ -1770,7 +1757,7 @@ fn expr_as_constructor( let typ = Value::UnresolvedType(constructor.typ.typ); let fields = constructor.fields.into_iter(); let fields = fields.map(|(name, value)| { - Value::Tuple(vec![quote_ident(&name), Value::expression(value.kind)]) + Value::Tuple(vec![quote_ident(&name, location), Value::expression(value.kind)]) }); let fields = fields.collect(); let fields_type = Type::Slice(Box::new(Type::Tuple(vec![ @@ -1783,7 +1770,7 @@ fn expr_as_constructor( None }; - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn as_for(self) -> Option<(Quoted, Expr, Expr)> @@ -1796,8 +1783,9 @@ fn expr_as_for( expr_as(interner, arguments, return_type, location, |expr| { if let ExprValue::Statement(StatementKind::For(for_statement)) = expr { if let ForRange::Array(array) = for_statement.range { - let identifier = - Value::Quoted(Rc::new(vec![Token::Ident(for_statement.identifier.0.contents)])); + let token = Token::Ident(for_statement.identifier.0.contents); + let token = LocatedToken::new(token, location); + let identifier = Value::Quoted(Rc::new(vec![token])); let array = Value::expression(array.kind); let body = Value::expression(for_statement.block.kind); Some(Value::Tuple(vec![identifier, array, body])) @@ -1821,8 +1809,9 @@ fn expr_as_for_range( if let ExprValue::Statement(StatementKind::For(for_statement)) = expr { if let ForRange::Range(bounds) = for_statement.range { let (from, to) = bounds.into_half_open(); - let identifier = - Value::Quoted(Rc::new(vec![Token::Ident(for_statement.identifier.0.contents)])); + let token = Token::Ident(for_statement.identifier.0.contents); + let token = LocatedToken::new(token, location); + let identifier = Value::Quoted(Rc::new(vec![token])); let from = Value::expression(from.kind); let to = Value::expression(to.kind); let body = Value::expression(for_statement.block.kind); @@ -1877,7 +1866,7 @@ fn expr_as_if( let alternative = option( alternative_option_type, if_expr.alternative.map(|e| Value::expression(e.kind)), - location.span, + location, ); Some(Value::Tuple(vec![ @@ -1918,14 +1907,12 @@ fn expr_as_integer( location: Location, ) -> IResult { expr_as(interner, arguments, return_type.clone(), location, |expr| match expr { - ExprValue::Expression(ExpressionKind::Literal(Literal::Integer(field, sign))) => { - Some(Value::Tuple(vec![Value::Field(field), Value::Bool(sign)])) + ExprValue::Expression(ExpressionKind::Literal(Literal::Integer(field))) => { + Some(Value::Tuple(vec![Value::Field(field.field), Value::Bool(field.is_negative)])) } ExprValue::Expression(ExpressionKind::Resolved(id)) => { - if let HirExpression::Literal(HirLiteral::Integer(field, sign)) = - interner.expression(&id) - { - Some(Value::Tuple(vec![Value::Field(field), Value::Bool(sign)])) + if let HirExpression::Literal(HirLiteral::Integer(field)) = interner.expression(&id) { + Some(Value::Tuple(vec![Value::Field(field.field), Value::Bool(field.is_negative)])) } else { None } @@ -1966,7 +1953,7 @@ fn expr_as_lambda( } else { Some(Value::UnresolvedType(typ.typ)) }; - let typ = option(option_unresolved_type.clone(), typ, location.span); + let typ = option(option_unresolved_type.clone(), typ, location); Value::Tuple(vec![pattern, typ]) }) .collect(); @@ -1985,7 +1972,7 @@ fn expr_as_lambda( Some(return_type) }; let return_type = return_type.map(Value::UnresolvedType); - let return_type = option(option_unresolved_type, return_type, location.span); + let return_type = option(option_unresolved_type, return_type, location); let body = Value::expression(lambda.body.kind); @@ -2019,7 +2006,7 @@ fn expr_as_let( Some(Value::UnresolvedType(let_statement.r#type.typ)) }; - let typ = option(option_type, typ, location.span); + let typ = option(option_type, typ, location); Some(Value::Tuple(vec![ Value::pattern(let_statement.pattern), @@ -2042,11 +2029,11 @@ fn expr_as_member_access( ExprValue::Expression(ExpressionKind::MemberAccess(member_access)) => { Some(Value::Tuple(vec![ Value::expression(member_access.lhs.kind), - quote_ident(&member_access.rhs), + quote_ident(&member_access.rhs, location), ])) } - ExprValue::LValue(crate::ast::LValue::MemberAccess { object, field_name, span: _ }) => { - Some(Value::Tuple(vec![Value::lvalue(*object), quote_ident(&field_name)])) + ExprValue::LValue(crate::ast::LValue::MemberAccess { object, field_name, location: _ }) => { + Some(Value::Tuple(vec![Value::lvalue(*object), quote_ident(&field_name, location)])) } _ => None, }) @@ -2063,7 +2050,7 @@ fn expr_as_method_call( if let ExprValue::Expression(ExpressionKind::MethodCall(method_call)) = expr { let object = Value::expression(method_call.object.kind); - let name = quote_ident(&method_call.method_name); + let name = quote_ident(&method_call.method_name, location); let generics = method_call.generics.unwrap_or_default().into_iter(); let generics = generics.map(|generic| Value::UnresolvedType(generic.typ)).collect(); @@ -2214,8 +2201,9 @@ fn expr_as_unsafe( location: Location, ) -> IResult { expr_as(interner, arguments, return_type, location, |expr| { - if let ExprValue::Expression(ExpressionKind::Unsafe(block_expr, _)) = expr { - Some(block_expression_to_value(block_expr)) + if let ExprValue::Expression(ExpressionKind::Unsafe(UnsafeExpression { block, .. })) = expr + { + Some(block_expression_to_value(block)) } else { None } @@ -2271,7 +2259,7 @@ where let expr_value = unwrap_expr_value(interner, expr_value); let option_value = f(expr_value); - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn resolve(self, in_function: Option) -> TypedExpr @@ -2306,12 +2294,12 @@ fn expr_resolve( interpreter.elaborate_in_function(function_to_resolve_in, |elaborator| match expr_value { ExprValue::Expression(expression_kind) => { - let expr = Expression { kind: expression_kind, span: self_argument_location.span }; + let expr = Expression { kind: expression_kind, location: self_argument_location }; let (expr_id, _) = elaborator.elaborate_expression(expr); Ok(Value::TypedExpr(TypedExpr::ExprId(expr_id))) } ExprValue::Statement(statement_kind) => { - let statement = Statement { kind: statement_kind, span: self_argument_location.span }; + let statement = Statement { kind: statement_kind, location: self_argument_location }; let (stmt_id, _) = elaborator.elaborate_statement(statement); Ok(Value::TypedExpr(TypedExpr::StmtId(stmt_id))) } @@ -2380,7 +2368,7 @@ fn fmtstr_quoted_contents( ) -> IResult { let self_argument = check_one_argument(arguments, location)?; let (string, _) = get_format_string(interner, self_argument)?; - let tokens = lex(&string); + let tokens = lex(&string, location); Ok(Value::Quoted(Rc::new(tokens))) } @@ -2399,7 +2387,7 @@ fn function_def_add_attribute( let attribute_location = attribute.1; let attribute = get_str(interpreter.elaborator.interner, attribute)?; let attribute = format!("#[{}]", attribute); - let mut parser = Parser::for_str(&attribute); + let mut parser = Parser::for_str(&attribute, attribute_location.file); let Some((attribute, _span)) = parser.parse_attribute() else { return Err(InterpreterError::InvalidAttribute { attribute: attribute.to_string(), @@ -2437,7 +2425,7 @@ fn function_def_as_typed_expr( let generics = None; let hir_expr = HirExpression::Ident(hir_ident.clone(), generics.clone()); let expr_id = interpreter.elaborator.interner.push_expr(hir_expr); - interpreter.elaborator.interner.push_expr_location(expr_id, location.span, location.file); + interpreter.elaborator.interner.push_expr_location(expr_id, location); let typ = interpreter.elaborator.type_check_variable(hir_ident, expr_id, generics); interpreter.elaborator.interner.push_expr_type(expr_id, typ); Ok(Value::TypedExpr(TypedExpr::ExprId(expr_id))) @@ -2521,7 +2509,9 @@ fn function_def_name( let self_argument = check_one_argument(arguments, location)?; let func_id = get_function_def(self_argument)?; let name = interner.function_name(&func_id).to_string(); - let tokens = Rc::new(vec![Token::Ident(name)]); + let token = Token::Ident(name); + let token = LocatedToken::new(token, location); + let tokens = Rc::new(vec![token]); Ok(Value::Quoted(tokens)) } @@ -2539,7 +2529,9 @@ fn function_def_parameters( .parameters .iter() .map(|(hir_pattern, typ, _visibility)| { - let name = Value::Quoted(Rc::new(hir_pattern_to_tokens(interner, hir_pattern))); + let tokens = hir_pattern_to_tokens(interner, hir_pattern); + let tokens = vecmap(tokens, |token| LocatedToken::new(token, location)); + let name = Value::Quoted(Rc::new(tokens)); let typ = Value::Type(typ.clone()); Value::Tuple(vec![name, typ]) }) @@ -2580,10 +2572,9 @@ fn function_def_set_body( let body_argument = get_expr(interpreter.elaborator.interner, body_argument)?; let statement_kind = match body_argument { - ExprValue::Expression(expression_kind) => StatementKind::Expression(Expression { - kind: expression_kind, - span: body_location.span, - }), + ExprValue::Expression(expression_kind) => { + StatementKind::Expression(Expression { kind: expression_kind, location: body_location }) + } ExprValue::Statement(statement_kind) => statement_kind, ExprValue::LValue(lvalue) => StatementKind::Expression(lvalue.as_expression()), ExprValue::Pattern(pattern) => { @@ -2598,12 +2589,12 @@ fn function_def_set_body( } }; - let statement = Statement { kind: statement_kind, span: body_location.span }; + let statement = Statement { kind: statement_kind, location: body_location }; let body = BlockExpression { statements: vec![statement] }; let func_meta = interpreter.elaborator.interner.function_meta_mut(&func_id); func_meta.has_body = true; - func_meta.function_body = FunctionBody::Unresolved(FunctionKind::Normal, body, location.span); + func_meta.function_body = FunctionBody::Unresolved(FunctionKind::Normal, body, location); Ok(Value::Unit) } @@ -2681,7 +2672,7 @@ fn function_def_set_return_type( mutate_func_meta_type(interpreter.elaborator.interner, func_id, |func_meta| { func_meta.return_type = FunctionReturnType::Ty(UnresolvedType { typ: UnresolvedTypeData::Resolved(quoted_type_id), - span: location.span, + location, }); replace_func_meta_return_type(&mut func_meta.typ, return_type); }); @@ -2756,8 +2747,10 @@ fn module_add_item( let parser = Parser::parse_top_level_items; let top_level_statements = parse(interpreter.elaborator, item, parser, "a top-level item")?; - let module_data = interpreter.elaborator.get_module(module_id); - interpreter.elaborate_in_module(module_id, module_data.location.file, |elaborator| { + interpreter.elaborate_in_module(module_id, |elaborator| { + let previous_errors = elaborator + .push_elaborate_reason_and_take_errors(ElaborateReason::AddingItemToModule, location); + let mut generated_items = CollectedItems::default(); for top_level_statement in top_level_statements { @@ -2767,6 +2760,8 @@ fn module_add_item( if !generated_items.is_empty() { elaborator.elaborate_items(generated_items); } + + elaborator.pop_elaborate_reason(previous_errors); }); Ok(Value::Unit) @@ -2867,7 +2862,9 @@ fn module_name( let self_argument = check_one_argument(arguments, location)?; let module_id = get_module(self_argument)?; let name = &interner.module_attributes(&module_id).name; - let tokens = Rc::new(vec![Token::Ident(name.clone())]); + let token = Token::Ident(name.clone()); + let token = LocatedToken::new(token, location); + let tokens = Rc::new(vec![token]); Ok(Value::Quoted(tokens)) } @@ -2932,19 +2929,19 @@ fn trait_def_as_trait_constraint( let argument = check_one_argument(arguments, location)?; let trait_id = get_trait_def(argument)?; - let constraint = interner.get_trait(trait_id).as_constraint(location.span); + let constraint = interner.get_trait(trait_id).as_constraint(location); Ok(Value::TraitConstraint(trait_id, constraint.trait_bound.trait_generics)) } /// Creates a value that holds an `Option`. /// `option_type` must be a Type referencing the `Option` type. -pub(crate) fn option(option_type: Type, value: Option, span: Span) -> Value { +pub(crate) fn option(option_type: Type, value: Option, location: Location) -> Value { let t = extract_option_generic_type(option_type.clone()); let (is_some, value) = match value { Some(value) => (Value::Bool(true), value), - None => (Value::Bool(false), zeroed(t, span)), + None => (Value::Bool(false), zeroed(t, location)), }; let mut fields = HashMap::default(); @@ -2993,7 +2990,7 @@ fn derive_generators( _ => panic!("ICE: Should only have an array return type"), }; - let num_generators = size.evaluate_to_u32(location.span).map_err(|err| { + let num_generators = size.evaluate_to_u32(location).map_err(|err| { let err = Box::new(err); InterpreterError::UnknownArrayLength { length: *size, err, location } })?; @@ -3009,10 +3006,10 @@ fn derive_generators( let y_field_name: Rc = Rc::new("y".to_owned()); let is_infinite_field_name: Rc = Rc::new("is_infinite".to_owned()); let mut results = Vector::new(); - for gen in generators { - let x_big: BigUint = gen.x.into(); + for generator in generators { + let x_big: BigUint = generator.x.into(); let x = FieldElement::from_be_bytes_reduce(&x_big.to_bytes_be()); - let y_big: BigUint = gen.y.into(); + let y_big: BigUint = generator.y.into(); let y = FieldElement::from_be_bytes_reduce(&y_big.to_bytes_be()); let mut embedded_curve_point_fields = HashMap::default(); embedded_curve_point_fields.insert(x_field_name.clone(), Value::Field(x)); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs index 095f20b3f4c7..e552cf0c5a22 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs @@ -7,19 +7,23 @@ use noirc_errors::Location; use crate::elaborator::Elaborator; use crate::hir::comptime::display::tokens_to_string; -use crate::hir::comptime::value::add_token_spans; +use crate::hir::comptime::value::unwrap_rc; +use crate::hir::def_collector::dc_crate::CompilationError; use crate::lexer::Lexer; use crate::parser::{Parser, ParserError}; +use crate::token::LocatedToken; +use crate::{DataType, Kind, Shared}; use crate::{ + QuotedType, Type, ast::{ BlockExpression, ExpressionKind, Ident, IntegerBitSize, LValue, Pattern, Signedness, StatementKind, UnresolvedTypeData, }, hir::{ comptime::{ + Interpreter, InterpreterError, Value, errors::IResult, value::{ExprValue, TypedExpr}, - Interpreter, InterpreterError, Value, }, def_map::ModuleId, type_check::generics::TraitGenerics, @@ -30,9 +34,7 @@ use crate::{ }, node_interner::{FuncId, NodeInterner, TraitId, TraitImplId, TypeId}, token::{SecondaryAttribute, Token, Tokens}, - QuotedType, Type, }; -use crate::{DataType, Kind, Shared}; use rustc_hash::FxHashMap as HashMap; pub(crate) fn check_argument_count( @@ -110,7 +112,7 @@ pub(crate) fn get_struct_fields( _ => { let expected = DataType::new( TypeId::dummy_id(), - Ident::new(name.to_string(), location.span), + Ident::new(name.to_string(), location), location, Vec::new(), ); @@ -287,7 +289,7 @@ pub(crate) fn get_expr( Ok(ExprValue::Statement(interner.get_statement_kind(id).clone())) } ExprValue::LValue(LValue::Interned(id, _)) => { - Ok(ExprValue::LValue(interner.get_lvalue(id, location.span).clone())) + Ok(ExprValue::LValue(interner.get_lvalue(id, location).clone())) } ExprValue::Pattern(Pattern::Interned(id, _)) => { Ok(ExprValue::Pattern(interner.get_pattern(id).clone())) @@ -370,7 +372,7 @@ pub(crate) fn get_typed_expr((value, location): (Value, Location)) -> IResult IResult>> { +pub(crate) fn get_quoted((value, location): (Value, Location)) -> IResult>> { match value { Value::Quoted(tokens) => Ok(tokens), value => type_mismatch(value, Type::Quoted(QuotedType::Quoted), location), @@ -483,10 +485,11 @@ pub(super) fn check_function_not_yet_resolved( } } -pub(super) fn lex(input: &str) -> Vec { - let (tokens, _) = Lexer::lex(input); - let mut tokens: Vec<_> = tokens.0.into_iter().map(|token| token.into_token()).collect(); - if let Some(Token::EOF) = tokens.last() { +pub(super) fn lex(input: &str, location: Location) -> Vec { + let (tokens, _) = Lexer::lex(input, location.file); + let mut tokens: Vec<_> = + tokens.0.into_iter().map(|token| LocatedToken::new(token.into_token(), location)).collect(); + if let Some(Token::EOF) = tokens.last().map(|t| t.token()) { tokens.pop(); } tokens @@ -502,17 +505,18 @@ where F: FnOnce(&mut Parser<'a>) -> T, { let tokens = get_quoted((value, location))?; - let quoted = add_token_spans(tokens.clone(), location.span); + let quoted = Tokens(unwrap_rc(tokens.clone())); let (result, warnings) = parse_tokens(tokens, quoted, elaborator.interner, location, parser, rule)?; for warning in warnings { - elaborator.errors.push((warning.into(), location.file)); + let warning: CompilationError = warning.into(); + elaborator.push_err(warning); } Ok(result) } pub(super) fn parse_tokens<'a, T, F>( - tokens: Rc>, + tokens: Rc>, quoted: Tokens, interner: &NodeInterner, location: Location, @@ -524,8 +528,8 @@ where { Parser::for_tokens(quoted).parse_result(parsing_function).map_err(|mut errors| { let error = Box::new(errors.swap_remove(0)); - let tokens = tokens_to_string(tokens, interner); - InterpreterError::FailedToParseMacro { error, tokens, rule, file: location.file } + let tokens = tokens_to_string(&tokens, interner); + InterpreterError::FailedToParseMacro { error, tokens, rule, location } }) } @@ -582,12 +586,14 @@ pub(super) fn has_named_attribute(name: &str, attributes: &[SecondaryAttribute]) false } -pub(super) fn quote_ident(ident: &Ident) -> Value { - Value::Quoted(ident_to_tokens(ident)) +pub(super) fn quote_ident(ident: &Ident, location: Location) -> Value { + Value::Quoted(ident_to_tokens(ident, location)) } -pub(super) fn ident_to_tokens(ident: &Ident) -> Rc> { - Rc::new(vec![Token::Ident(ident.0.contents.clone())]) +fn ident_to_tokens(ident: &Ident, location: Location) -> Rc> { + let token = Token::Ident(ident.0.contents.clone()); + let token = LocatedToken::new(token, location); + Rc::new(vec![token]) } pub(super) fn hash_item( diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs index 0221280ae1b3..823e0297755c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs @@ -1,31 +1,31 @@ use acvm::{ + AcirField, BlackBoxResolutionError, FieldElement, acir::BlackBoxFunc, blackbox_solver::{BigIntSolverWithId, BlackBoxFunctionSolver}, - AcirField, BlackBoxResolutionError, FieldElement, }; use bn254_blackbox_solver::Bn254BlackBoxSolver; // Currently locked to only bn254! use im::Vector; use noirc_errors::Location; use crate::{ + Type, hir::comptime::{ - errors::IResult, interpreter::builtin::builtin_helpers::to_byte_array, InterpreterError, - Value, + InterpreterError, Value, errors::IResult, + interpreter::builtin::builtin_helpers::to_byte_array, }, node_interner::NodeInterner, - Type, }; use super::{ + Interpreter, builtin::builtin_helpers::{ check_arguments, check_one_argument, check_three_arguments, check_two_arguments, get_array_map, get_bool, get_field, get_fixed_array_map, get_slice_map, get_struct_field, - get_struct_fields, get_u32, get_u64, get_u8, to_byte_slice, to_field_array, to_struct, + get_struct_fields, get_u8, get_u32, get_u64, to_byte_slice, to_field_array, to_struct, }, - Interpreter, }; -impl<'local, 'context> Interpreter<'local, 'context> { +impl Interpreter<'_, '_> { pub(super) fn call_foreign( &mut self, name: &str, @@ -40,7 +40,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { arguments, return_type, location, - self.pedantic_solving, + self.elaborator.pedantic_solving(), ) } } @@ -234,7 +234,7 @@ fn blake_hash( /// signature: [u8; 64], /// message_hash: [u8; N], /// ) -> bool - +/// /// pub fn verify_signature_slice( /// public_key_x: [u8; 32], /// public_key_y: [u8; 32], @@ -422,11 +422,11 @@ mod tests { use noirc_errors::Location; use strum::IntoEnumIterator; - use crate::hir::comptime::tests::with_interpreter; + use crate::Type; use crate::hir::comptime::InterpreterError::{ ArgumentCountMismatch, InvalidInComptimeContext, Unimplemented, }; - use crate::Type; + use crate::hir::comptime::tests::with_interpreter; use super::call_foreign; 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 c7b1532c9b7e..fc4daa22edb7 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 @@ -2,12 +2,12 @@ use noirc_errors::Location; use crate::{ hir::comptime::errors::IResult, - token::{Token, Tokens}, + token::{LocatedToken, Token, Tokens}, }; use super::Interpreter; -impl<'local, 'interner> Interpreter<'local, 'interner> { +impl Interpreter<'_, '_> { /// Evaluates any expressions within UnquoteMarkers in the given token list /// and replaces the expression held by the marker with the evaluated value /// in expression form. @@ -15,17 +15,17 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { &mut self, tokens: Tokens, location: Location, - ) -> IResult> { + ) -> IResult> { let mut new_tokens = Vec::with_capacity(tokens.0.len()); for token in tokens.0 { - match token.into_token() { + match token.token() { Token::UnquoteMarker(id) => { - let value = self.evaluate(id)?; + let value = self.evaluate(*id)?; let tokens = value.into_tokens(self.elaborator.interner, location)?; new_tokens.extend(tokens); } - token => new_tokens.push(token), + _ => new_tokens.push(token), } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs index 2e2753001b48..c4a987e54195 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs @@ -5,6 +5,6 @@ mod interpreter; mod tests; mod value; -pub use errors::InterpreterError; +pub use errors::{ComptimeError, InterpreterError}; pub use interpreter::Interpreter; pub use value::Value; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs index 342d0a616a05..88a2bc8b52a8 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs @@ -7,10 +7,10 @@ use fm::{FileId, FileManager}; use noirc_arena::Index; use noirc_errors::Location; +use super::Interpreter; use super::errors::InterpreterError; use super::value::Value; -use super::Interpreter; -use crate::elaborator::Elaborator; +use crate::elaborator::{Elaborator, ElaboratorOptions}; use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector}; use crate::hir::def_collector::dc_mod::collect_defs; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleData}; @@ -23,7 +23,7 @@ use crate::parse_program; /// The stdlib is not made available as a dependency. pub(crate) fn with_interpreter( src: &str, - f: impl FnOnce(&mut Interpreter, FuncId, &[(CompilationError, FileId)]) -> T, + f: impl FnOnce(&mut Interpreter, FuncId, &[CompilationError]) -> T, ) -> T { let file = FileId::default(); @@ -48,7 +48,7 @@ pub(crate) fn with_interpreter( let krate = context.crate_graph.add_crate_root(FileId::dummy()); - let (module, errors) = parse_program(src); + let (module, errors) = parse_program(src, file); assert_eq!(errors.len(), 0); let ast = module.into_sorted(); @@ -60,13 +60,11 @@ pub(crate) fn with_interpreter( let main = context.get_main_function(&krate).expect("Expected 'main' function"); - let pedantic_solving = true; let mut elaborator = Elaborator::elaborate_and_return_self( &mut context, krate, collector.items, - None, - pedantic_solving, + ElaboratorOptions::test_default(), ); let errors = elaborator.errors.clone(); 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 a6668eae1b0a..8d07669497f6 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 @@ -3,25 +3,29 @@ use std::{borrow::Cow, rc::Rc, vec}; use acvm::FieldElement; use im::Vector; use iter_extended::{try_vecmap, vecmap}; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use strum_macros::Display; use crate::{ + Kind, QuotedType, Shared, Type, TypeBindings, ast::{ ArrayLiteral, BlockExpression, ConstructorExpression, Expression, ExpressionKind, Ident, - IntegerBitSize, LValue, Literal, Path, Pattern, Signedness, Statement, StatementKind, + IntegerBitSize, LValue, Literal, Pattern, Signedness, Statement, StatementKind, UnresolvedType, UnresolvedTypeData, }, elaborator::Elaborator, - hir::{def_map::ModuleId, type_check::generics::TraitGenerics}, + hir::{ + def_collector::dc_crate::CompilationError, def_map::ModuleId, + type_check::generics::TraitGenerics, + }, hir_def::expr::{ HirArrayLiteral, HirConstructorExpression, HirEnumConstructorExpression, HirExpression, HirIdent, HirLambda, HirLiteral, ImplKind, }, node_interner::{ExprId, FuncId, NodeInterner, StmtId, TraitId, TraitImplId, TypeId}, parser::{Item, Parser}, - token::{SpannedToken, Token, Tokens}, - Kind, QuotedType, Shared, Type, TypeBindings, + signed_field::SignedField, + token::{LocatedToken, Token, Tokens}, }; use rustc_hash::FxHashMap as HashMap; @@ -44,6 +48,7 @@ pub enum Value { U16(u16), U32(u32), U64(u64), + U128(u128), String(Rc), FormatString(Rc, Type), CtString(Rc), @@ -59,10 +64,7 @@ pub enum Value { Pointer(Shared, /* auto_deref */ bool), Array(Vector, Type), Slice(Vector, Type), - /// Quoted tokens don't have spans because otherwise inserting them in the middle of other - /// tokens can cause larger spans to be before lesser spans, causing an assert. They may also - /// be inserted into separate files entirely. - Quoted(Rc>), + Quoted(Rc>), StructDefinition(TypeId), TraitConstraint(TraitId, TraitGenerics), TraitDefinition(TraitId), @@ -130,6 +132,9 @@ impl Value { Value::U16(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::Sixteen), Value::U32(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo), Value::U64(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour), + Value::U128(_) => { + Type::Integer(Signedness::Unsigned, IntegerBitSize::HundredTwentyEight) + } Value::String(value) => { let length = Type::Constant(value.len().into(), Kind::u32()); Type::String(Box::new(length)) @@ -176,45 +181,38 @@ impl Value { let kind = match self { Value::Unit => ExpressionKind::Literal(Literal::Unit), Value::Bool(value) => ExpressionKind::Literal(Literal::Bool(value)), - Value::Field(value) => ExpressionKind::Literal(Literal::Integer(value, false)), + Value::Field(value) => { + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value))) + } Value::I8(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - ExpressionKind::Literal(Literal::Integer(value, negative)) + ExpressionKind::Literal(Literal::Integer(SignedField::from_signed(value))) } Value::I16(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - ExpressionKind::Literal(Literal::Integer(value, negative)) + ExpressionKind::Literal(Literal::Integer(SignedField::from_signed(value))) } Value::I32(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - ExpressionKind::Literal(Literal::Integer(value, negative)) + ExpressionKind::Literal(Literal::Integer(SignedField::from_signed(value))) } Value::I64(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - ExpressionKind::Literal(Literal::Integer(value, negative)) + ExpressionKind::Literal(Literal::Integer(SignedField::from_signed(value))) } Value::U1(value) => { - ExpressionKind::Literal(Literal::Integer((value as u128).into(), false)) + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value))) } Value::U8(value) => { - ExpressionKind::Literal(Literal::Integer((value as u128).into(), false)) + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value as u128))) } Value::U16(value) => { - ExpressionKind::Literal(Literal::Integer((value as u128).into(), false)) + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value as u128))) } Value::U32(value) => { - ExpressionKind::Literal(Literal::Integer((value as u128).into(), false)) + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value))) } Value::U64(value) => { - ExpressionKind::Literal(Literal::Integer((value as u128).into(), false)) + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value))) + } + Value::U128(value) => { + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value))) } Value::String(value) | Value::CtString(value) => { ExpressionKind::Literal(Literal::Str(unwrap_rc(value))) @@ -228,7 +226,7 @@ impl Value { let impl_kind = ImplKind::NotATraitMethod; let ident = HirIdent { location, id, impl_kind }; let expr_id = elaborator.interner.push_expr(HirExpression::Ident(ident, None)); - elaborator.interner.push_expr_location(expr_id, location.span, location.file); + elaborator.interner.push_expr_location(expr_id, location); elaborator.interner.push_expr_type(expr_id, typ); elaborator.interner.store_instantiation_bindings(expr_id, unwrap_rc(bindings)); ExpressionKind::Resolved(expr_id) @@ -241,21 +239,21 @@ impl Value { Value::Struct(fields, typ) => { let fields = try_vecmap(fields, |(name, field)| { let field = field.into_expression(elaborator, location)?; - Ok((Ident::new(unwrap_rc(name), location.span), field)) + Ok((Ident::new(unwrap_rc(name), location), field)) })?; - let struct_type = match typ.follow_bindings_shallow().as_ref() { - Type::DataType(def, _) => Some(def.borrow().id), + let typ = match typ.follow_bindings_shallow().as_ref() { + Type::DataType(data_type, generics) => { + Type::DataType(data_type.clone(), generics.clone()) + } _ => return Err(InterpreterError::NonStructInConstructor { typ, location }), }; - // Since we've provided the struct_type, the path should be ignored. - let type_name = Path::from_single(String::new(), location.span); - ExpressionKind::Constructor(Box::new(ConstructorExpression { - typ: UnresolvedType::from_path(type_name), - fields, - struct_type, - })) + let quoted_type_id = elaborator.interner.push_quoted_type(typ); + + let typ = UnresolvedTypeData::Resolved(quoted_type_id); + let typ = UnresolvedType { typ, location }; + ExpressionKind::Constructor(Box::new(ConstructorExpression { typ, fields })) } value @ Value::Enum(..) => { let hir = value.into_hir_expression(elaborator.interner, location)?; @@ -273,29 +271,30 @@ impl Value { } Value::Quoted(tokens) => { // Wrap the tokens in '{' and '}' so that we can parse statements as well. - let mut tokens_to_parse = add_token_spans(tokens.clone(), location.span); - tokens_to_parse.0.insert(0, SpannedToken::new(Token::LeftBrace, location.span)); - tokens_to_parse.0.push(SpannedToken::new(Token::RightBrace, location.span)); + let mut tokens_to_parse = unwrap_rc(tokens.clone()); + tokens_to_parse.insert(0, LocatedToken::new(Token::LeftBrace, location)); + tokens_to_parse.push(LocatedToken::new(Token::RightBrace, location)); + + let tokens_to_parse = Tokens(tokens_to_parse); let parser = Parser::for_tokens(tokens_to_parse); return match parser.parse_result(Parser::parse_expression_or_error) { Ok((expr, warnings)) => { for warning in warnings { - elaborator.errors.push((warning.into(), location.file)); + let warning: CompilationError = warning.into(); + elaborator.push_err(warning); } Ok(expr) } Err(mut errors) => { let error = Box::new(errors.swap_remove(0)); - let file = location.file; let rule = "an expression"; - let tokens = tokens_to_string(tokens, elaborator.interner); - Err(InterpreterError::FailedToParseMacro { error, file, tokens, rule }) + let tokens = tokens_to_string(&tokens, elaborator.interner); + Err(InterpreterError::FailedToParseMacro { error, tokens, rule, location }) } }; } - Value::Expr(ref expr) => { // We need to do some shenanigans to get around the borrow checker here due to using a boxed value. @@ -314,7 +313,7 @@ impl Value { match *expr { ExprValue::Expression(expr) => expr, ExprValue::Statement(statement) => ExpressionKind::Block(BlockExpression { - statements: vec![Statement { kind: statement, span: location.span }], + statements: vec![Statement { kind: statement, location }], }), ExprValue::LValue(lvalue) => lvalue.as_expression().kind, ExprValue::Pattern(_) => unreachable!("this case is handled above"), @@ -338,7 +337,7 @@ impl Value { } }; - Ok(Expression::new(kind, location.span)) + Ok(Expression::new(kind, location)) } pub(crate) fn into_hir_expression( @@ -351,45 +350,36 @@ impl Value { let expression = match self { Value::Unit => HirExpression::Literal(HirLiteral::Unit), Value::Bool(value) => HirExpression::Literal(HirLiteral::Bool(value)), - Value::Field(value) => HirExpression::Literal(HirLiteral::Integer(value, false)), + Value::Field(value) => HirExpression::Literal(HirLiteral::Integer(value.into())), Value::I8(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - HirExpression::Literal(HirLiteral::Integer(value, negative)) + HirExpression::Literal(HirLiteral::Integer(SignedField::from_signed(value))) } Value::I16(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - HirExpression::Literal(HirLiteral::Integer(value, negative)) + HirExpression::Literal(HirLiteral::Integer(SignedField::from_signed(value))) } Value::I32(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - HirExpression::Literal(HirLiteral::Integer(value, negative)) + HirExpression::Literal(HirLiteral::Integer(SignedField::from_signed(value))) } Value::I64(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - HirExpression::Literal(HirLiteral::Integer(value, negative)) + HirExpression::Literal(HirLiteral::Integer(SignedField::from_signed(value))) } Value::U1(value) => { - HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) + HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value))) } Value::U8(value) => { - HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) + HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value as u128))) } Value::U16(value) => { - HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) + HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value as u128))) } Value::U32(value) => { - HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) + HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value))) } Value::U64(value) => { - HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) + HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value))) + } + Value::U128(value) => { + HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value))) } Value::String(value) | Value::CtString(value) => { HirExpression::Literal(HirLiteral::Str(unwrap_rc(value))) @@ -403,7 +393,7 @@ impl Value { let impl_kind = ImplKind::NotATraitMethod; let ident = HirIdent { location, id, impl_kind }; let expr_id = interner.push_expr(HirExpression::Ident(ident, None)); - interner.push_expr_location(expr_id, location.span, location.file); + interner.push_expr_location(expr_id, location); interner.push_expr_type(expr_id, typ); interner.store_instantiation_bindings(expr_id, unwrap_rc(bindings)); return Ok(expr_id); @@ -416,7 +406,7 @@ impl Value { Value::Struct(fields, typ) => { let fields = try_vecmap(fields, |(name, field)| { let field = field.into_hir_expression(interner, location)?; - Ok((Ident::new(unwrap_rc(name), location.span), field)) + Ok((Ident::new(unwrap_rc(name), location), field)) })?; let (r#type, struct_generics) = match typ.follow_bindings() { @@ -458,7 +448,7 @@ impl Value { })?; HirExpression::Literal(HirLiteral::Slice(HirArrayLiteral::Standard(elements))) } - Value::Quoted(tokens) => HirExpression::Unquote(add_token_spans(tokens, location.span)), + Value::Quoted(tokens) => HirExpression::Unquote(Tokens(unwrap_rc(tokens))), Value::TypedExpr(TypedExpr::ExprId(expr_id)) => interner.expression(&expr_id), // Only convert pointers with auto_deref = true. These are mutable variables // and we don't need to wrap them in `&mut`. @@ -485,7 +475,7 @@ impl Value { }; let id = interner.push_expr(expression); - interner.push_expr_location(id, location.span, location.file); + interner.push_expr_location(id, location); interner.push_expr_type(id, typ); Ok(id) } @@ -494,74 +484,77 @@ impl Value { self, interner: &mut NodeInterner, location: Location, - ) -> IResult> { - let token = match self { + ) -> IResult> { + let tokens: Vec = match self { Value::Unit => { - return Ok(vec![Token::LeftParen, Token::RightParen]); + vec![Token::LeftParen, Token::RightParen] } Value::Quoted(tokens) => return Ok(unwrap_rc(tokens)), - Value::Type(typ) => Token::QuotedType(interner.push_quoted_type(typ)), + Value::Type(typ) => vec![Token::QuotedType(interner.push_quoted_type(typ))], Value::Expr(expr) => match *expr { ExprValue::Expression(expr) => { - Token::InternedExpr(interner.push_expression_kind(expr)) + vec![Token::InternedExpr(interner.push_expression_kind(expr))] } ExprValue::Statement(StatementKind::Expression(expr)) => { - Token::InternedExpr(interner.push_expression_kind(expr.kind)) + vec![Token::InternedExpr(interner.push_expression_kind(expr.kind))] } ExprValue::Statement(statement) => { - Token::InternedStatement(interner.push_statement_kind(statement)) + vec![Token::InternedStatement(interner.push_statement_kind(statement))] + } + ExprValue::LValue(lvalue) => { + vec![Token::InternedLValue(interner.push_lvalue(lvalue))] } - ExprValue::LValue(lvalue) => Token::InternedLValue(interner.push_lvalue(lvalue)), ExprValue::Pattern(pattern) => { - Token::InternedPattern(interner.push_pattern(pattern)) + vec![Token::InternedPattern(interner.push_pattern(pattern))] } }, Value::UnresolvedType(typ) => { - Token::InternedUnresolvedTypeData(interner.push_unresolved_type_data(typ)) + vec![Token::InternedUnresolvedTypeData(interner.push_unresolved_type_data(typ))] } Value::TraitConstraint(trait_id, generics) => { let name = Rc::new(interner.get_trait(trait_id).name.0.contents.clone()); let typ = Type::TraitAsType(trait_id, name, generics); - Token::QuotedType(interner.push_quoted_type(typ)) - } - Value::TypedExpr(TypedExpr::ExprId(expr_id)) => Token::UnquoteMarker(expr_id), - Value::U1(bool) => Token::Bool(bool), - Value::U8(value) => Token::Int((value as u128).into()), - Value::U16(value) => Token::Int((value as u128).into()), - Value::U32(value) => Token::Int((value as u128).into()), - Value::U64(value) => Token::Int((value as u128).into()), + vec![Token::QuotedType(interner.push_quoted_type(typ))] + } + Value::TypedExpr(TypedExpr::ExprId(expr_id)) => vec![Token::UnquoteMarker(expr_id)], + Value::U1(bool) => vec![Token::Bool(bool)], + Value::U8(value) => vec![Token::Int((value as u128).into())], + Value::U16(value) => vec![Token::Int((value as u128).into())], + Value::U32(value) => vec![Token::Int((value as u128).into())], + Value::U64(value) => vec![Token::Int((value as u128).into())], Value::I8(value) => { if value < 0 { - return Ok(vec![Token::Minus, Token::Int((-value as u128).into())]); + vec![Token::Minus, Token::Int((-value as u128).into())] } else { - Token::Int((value as u128).into()) + vec![Token::Int((value as u128).into())] } } Value::I16(value) => { if value < 0 { - return Ok(vec![Token::Minus, Token::Int((-value as u128).into())]); + vec![Token::Minus, Token::Int((-value as u128).into())] } else { - Token::Int((value as u128).into()) + vec![Token::Int((value as u128).into())] } } Value::I32(value) => { if value < 0 { - return Ok(vec![Token::Minus, Token::Int((-value as u128).into())]); + vec![Token::Minus, Token::Int((-value as u128).into())] } else { - Token::Int((value as u128).into()) + vec![Token::Int((value as u128).into())] } } Value::I64(value) => { if value < 0 { - return Ok(vec![Token::Minus, Token::Int((-value as u128).into())]); + vec![Token::Minus, Token::Int((-value as u128).into())] } else { - Token::Int((value as u128).into()) + vec![Token::Int((value as u128).into())] } } - Value::Field(value) => Token::Int(value), - other => Token::UnquoteMarker(other.into_hir_expression(interner, location)?), + Value::Field(value) => vec![Token::Int(value)], + other => vec![Token::UnquoteMarker(other.into_hir_expression(interner, location)?)], }; - Ok(vec![token]) + let tokens = vecmap(tokens, |token| LocatedToken::new(token, location)); + Ok(tokens) } /// Returns false for non-integral `Value`s. @@ -619,7 +612,7 @@ pub(crate) fn unwrap_rc(rc: Rc) -> T { } fn parse_tokens<'a, T, F>( - tokens: Rc>, + tokens: Rc>, elaborator: &mut Elaborator, parsing_function: F, location: Location, @@ -628,24 +621,19 @@ fn parse_tokens<'a, T, F>( where F: FnOnce(&mut Parser<'a>) -> T, { - let parser = Parser::for_tokens(add_token_spans(tokens.clone(), location.span)); + let parser = Parser::for_tokens(Tokens(unwrap_rc(tokens.clone()))); match parser.parse_result(parsing_function) { Ok((expr, warnings)) => { for warning in warnings { - elaborator.errors.push((warning.into(), location.file)); + let warning: CompilationError = warning.into(); + elaborator.push_err(warning); } Ok(expr) } Err(mut errors) => { let error = Box::new(errors.swap_remove(0)); - let file = location.file; - let tokens = tokens_to_string(tokens, elaborator.interner); - Err(InterpreterError::FailedToParseMacro { error, file, tokens, rule }) + let tokens = tokens_to_string(&tokens, elaborator.interner); + Err(InterpreterError::FailedToParseMacro { error, tokens, rule, location }) } } } - -pub(crate) fn add_token_spans(tokens: Rc>, span: Span) -> Tokens { - let tokens = unwrap_rc(tokens); - Tokens(vecmap(tokens, |token| SpannedToken::new(token, span))) -} 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 9d8c32fbc12d..b0c71f1ebe67 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 @@ -2,7 +2,7 @@ use super::dc_mod::collect_defs; use super::errors::{DefCollectorErrorKind, DuplicateType}; use crate::elaborator::Elaborator; use crate::graph::CrateId; -use crate::hir::comptime::InterpreterError; +use crate::hir::comptime::{ComptimeError, InterpreterError}; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}; use crate::hir::resolution::errors::ResolverError; use crate::hir::type_check::TypeCheckError; @@ -11,8 +11,8 @@ use crate::token::SecondaryAttribute; use crate::usage_tracker::UnusedItem; use crate::{Generics, Type}; -use crate::hir::resolution::import::{resolve_import, ImportDirective}; use crate::hir::Context; +use crate::hir::resolution::import::{ImportDirective, resolve_import}; use crate::ast::{Expression, NoirEnumeration}; use crate::node_interner::{ @@ -22,10 +22,11 @@ use crate::node_interner::{ use crate::ast::{ ExpressionKind, Ident, ItemVisibility, LetStatement, Literal, NoirFunction, NoirStruct, - NoirTrait, NoirTypeAlias, Path, PathKind, PathSegment, UnresolvedGenerics, - UnresolvedTraitConstraint, UnresolvedType, UnsupportedNumericGenericType, + NoirTrait, NoirTypeAlias, Path, PathSegment, UnresolvedGenerics, UnresolvedTraitConstraint, + UnresolvedType, UnsupportedNumericGenericType, }; +use crate::elaborator::FrontendOptions; use crate::parser::{ParserError, SortedModule}; use noirc_errors::{CustomDiagnostic, Location, Span}; @@ -176,17 +177,35 @@ impl CollectedItems { /// Note that because these are keyed by unresolved types, the impl map is one of the few instances /// of HashMap rather than BTreeMap. For this reason, we should be careful not to iterate over it /// since it would be non-deterministic. -pub(crate) type ImplMap = - HashMap<(UnresolvedType, LocalModuleId), Vec<(UnresolvedGenerics, Span, UnresolvedFunctions)>>; +pub(crate) type ImplMap = HashMap< + (UnresolvedType, LocalModuleId), + Vec<(UnresolvedGenerics, Location, UnresolvedFunctions)>, +>; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum CompilationError { ParseError(ParserError), DefinitionError(DefCollectorErrorKind), ResolverError(ResolverError), TypeError(TypeCheckError), InterpreterError(InterpreterError), - DebugComptimeScopeNotFound(Vec), + ComptimeError(ComptimeError), + DebugComptimeScopeNotFound(Vec, Location), +} + +impl CompilationError { + /// Returns the primary location where this error happened. + pub fn location(&self) -> Location { + match self { + CompilationError::ParseError(error) => error.location(), + CompilationError::DefinitionError(error) => error.location(), + CompilationError::ResolverError(error) => error.location(), + CompilationError::TypeError(error) => error.location(), + CompilationError::InterpreterError(error) => error.location(), + CompilationError::ComptimeError(error) => error.location(), + CompilationError::DebugComptimeScopeNotFound(_, location) => *location, + } + } } impl std::fmt::Display for CompilationError { @@ -197,7 +216,8 @@ impl std::fmt::Display for CompilationError { CompilationError::ResolverError(error) => write!(f, "{}", error), CompilationError::TypeError(error) => write!(f, "{}", error), CompilationError::InterpreterError(error) => write!(f, "{:?}", error), - CompilationError::DebugComptimeScopeNotFound(error) => write!(f, "{:?}", error), + CompilationError::DebugComptimeScopeNotFound(error, _) => write!(f, "{:?}", error), + CompilationError::ComptimeError(error) => write!(f, "{:?}", error), } } } @@ -210,15 +230,16 @@ impl<'a> From<&'a CompilationError> for CustomDiagnostic { CompilationError::ResolverError(error) => error.into(), CompilationError::TypeError(error) => error.into(), CompilationError::InterpreterError(error) => error.into(), - CompilationError::DebugComptimeScopeNotFound(error) => { + CompilationError::ComptimeError(error) => error.into(), + CompilationError::DebugComptimeScopeNotFound(error, _) => { let msg = "multiple files found matching --debug-comptime path".into(); let secondary = error.iter().fold(String::new(), |mut output, path| { let _ = writeln!(output, " {}", path.display()); output }); - // NOTE: this span is empty as it is not expected to be displayed - let dummy_span = Span::default(); - CustomDiagnostic::simple_error(msg, secondary, dummy_span) + // NOTE: this location is empty as it is not expected to be displayed + let dummy_location = Location::dummy(); + CustomDiagnostic::simple_error(msg, secondary, dummy_location) } } } @@ -282,10 +303,9 @@ impl DefCollector { context: &mut Context, ast: SortedModule, root_file_id: FileId, - debug_comptime_in_file: Option<&str>, - pedantic_solving: bool, - ) -> Vec<(CompilationError, FileId)> { - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + options: FrontendOptions, + ) -> Vec { + let mut errors: Vec = vec![]; let crate_id = def_map.krate; // Recursively resolve the dependencies @@ -296,12 +316,7 @@ impl DefCollector { let crate_graph = &context.crate_graph[crate_id]; for dep in crate_graph.dependencies.clone() { - errors.extend(CrateDefMap::collect_defs( - dep.crate_id, - context, - debug_comptime_in_file, - pedantic_solving, - )); + errors.extend(CrateDefMap::collect_defs(dep.crate_id, context, options)); let dep_def_map = context.def_map(&dep.crate_id).expect("ice: def map was just created"); @@ -358,15 +373,13 @@ impl DefCollector { for collected_import in std::mem::take(&mut def_collector.imports) { let local_module_id = collected_import.module_id; let module_id = ModuleId { krate: crate_id, local_id: local_module_id }; - let current_def_map = context.def_maps.get(&crate_id).unwrap(); - let file_id = current_def_map.file_id(local_module_id); let resolved_import = resolve_import( collected_import.path.clone(), module_id, &context.def_maps, &mut context.usage_tracker, - Some(ReferencesTracker::new(&mut context.def_interner, file_id)), + Some(ReferencesTracker::new(&mut context.def_interner)), ); match resolved_import { @@ -376,10 +389,7 @@ impl DefCollector { let has_path_resolution_error = !resolved_import.errors.is_empty(); for error in resolved_import.errors { - errors.push(( - DefCollectorErrorKind::PathResolutionError(error).into(), - file_id, - )); + errors.push(DefCollectorErrorKind::PathResolutionError(error).into()); } // Populate module namespaces according to the imports used @@ -390,14 +400,13 @@ impl DefCollector { resolved_import.namespace.iter_items() { if item_visibility < visibility { - errors.push(( + errors.push( DefCollectorErrorKind::CannotReexportItemWithLessVisibility { item_name: name.clone(), desired_visibility: visibility, } .into(), - file_id, - )); + ); } let visibility = visibility.min(item_visibility); @@ -454,34 +463,34 @@ impl DefCollector { first_def, second_def, }; - errors.push((err.into(), root_file_id)); + errors.push(err.into()); } } } Err(error) => { - let current_def_map = context.def_maps.get(&crate_id).unwrap(); - let file_id = current_def_map.file_id(collected_import.module_id); let error = DefCollectorErrorKind::PathResolutionError(error); - errors.push((error.into(), file_id)); + errors.push(error.into()); } } } - let debug_comptime_in_file = debug_comptime_in_file.and_then(|debug_comptime_in_file| { - let file = context.file_manager.find_by_path_suffix(debug_comptime_in_file); + let debug_comptime_in_file = options.debug_comptime_in_file.and_then(|file_suffix| { + let file = context.file_manager.find_by_path_suffix(file_suffix); file.unwrap_or_else(|error| { - errors.push((CompilationError::DebugComptimeScopeNotFound(error), root_file_id)); + let location = Location::new(Span::empty(0), root_file_id); + errors.push(CompilationError::DebugComptimeScopeNotFound(error, location)); None }) }); - let mut more_errors = Elaborator::elaborate( - context, - crate_id, - def_collector.items, + let cli_options = crate::elaborator::ElaboratorOptions { debug_comptime_in_file, - pedantic_solving, - ); + pedantic_solving: options.pedantic_solving, + enabled_unstable_features: options.enabled_unstable_features, + }; + + let mut more_errors = + Elaborator::elaborate(context, crate_id, def_collector.items, cli_options); errors.append(&mut more_errors); @@ -493,20 +502,18 @@ impl DefCollector { fn check_unused_items( context: &Context, crate_id: CrateId, - errors: &mut Vec<(CompilationError, FileId)>, + errors: &mut Vec, ) { let unused_imports = context.usage_tracker.unused_items().iter(); let unused_imports = unused_imports.filter(|(module_id, _)| module_id.krate == crate_id); - errors.extend(unused_imports.flat_map(|(module_id, usage_tracker)| { - let module = &context.def_maps[&crate_id].modules()[module_id.local_id.0]; + errors.extend(unused_imports.flat_map(|(_, usage_tracker)| { usage_tracker.iter().map(|(ident, unused_item)| { let ident = ident.clone(); - let error = CompilationError::ResolverError(ResolverError::UnusedItem { + CompilationError::ResolverError(ResolverError::UnusedItem { ident, item: *unused_item, - }); - (error, module.location.file) + }) }) })); } @@ -539,16 +546,12 @@ fn inject_prelude( .map(|segment| { crate::ast::PathSegment::from(crate::ast::Ident::new( segment.into(), - Span::default(), + Location::dummy(), )) }) .collect(); - let path = Path { - segments: segments.clone(), - kind: crate::ast::PathKind::Plain, - span: Span::default(), - }; + let path = Path::plain(segments.clone(), Location::dummy()); if let Ok(resolved_import) = resolve_import( path, @@ -566,14 +569,14 @@ fn inject_prelude( for path in prelude { let mut segments = segments.clone(); - segments.push(PathSegment::from(Ident::new(path.to_string(), Span::default()))); + segments.push(PathSegment::from(Ident::new(path.to_string(), Location::dummy()))); collected_imports.insert( 0, ImportDirective { visibility: ItemVisibility::Private, module_id: crate_root, - path: Path { segments, kind: PathKind::Plain, span: Span::default() }, + path: Path::plain(segments, Location::dummy()), alias: None, is_prelude: true, }, 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 59e1f2f6e329..8ba6be4fc4bd 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 @@ -4,7 +4,7 @@ use std::rc::Rc; use std::vec; use acvm::{AcirField, FieldElement}; -use fm::{FileId, FileManager, FILE_EXTENSION}; +use fm::{FILE_EXTENSION, FileId, FileManager}; use noirc_errors::{Location, Span}; use num_bigint::BigUint; use num_traits::Num; @@ -20,13 +20,13 @@ use crate::hir::resolution::errors::ResolverError; use crate::node_interner::{ModuleAttributes, NodeInterner, ReferenceId, TypeId}; use crate::token::SecondaryAttribute; use crate::usage_tracker::{UnusedItem, UsageTracker}; +use crate::{Generics, Kind, ResolvedGeneric, Type, TypeVariable}; use crate::{ graph::CrateId, hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait}, node_interner::{FunctionModifiers, TraitId, TypeAliasId}, parser::{SortedModule, SortedSubModule}, }; -use crate::{Generics, Kind, ResolvedGeneric, Type, TypeVariable}; use super::dc_crate::ModuleAttribute; use super::dc_crate::{CollectedItems, UnresolvedEnum}; @@ -37,9 +37,9 @@ use super::{ }, errors::{DefCollectorErrorKind, DuplicateType}, }; -use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleData, ModuleId, MAIN_FUNCTION}; -use crate::hir::resolution::import::ImportDirective; use crate::hir::Context; +use crate::hir::def_map::{CrateDefMap, LocalModuleId, MAIN_FUNCTION, ModuleData, ModuleId}; +use crate::hir::resolution::import::ImportDirective; /// Given a module collect all definitions into ModuleData struct ModCollector<'a> { @@ -58,9 +58,9 @@ pub fn collect_defs( module_id: LocalModuleId, crate_id: CrateId, context: &mut Context, -) -> Vec<(CompilationError, FileId)> { +) -> Vec { let mut collector = ModCollector { def_collector, file_id, module_id }; - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + let mut errors: Vec = vec![]; // First resolve the module declarations for decl in ast.module_decls { @@ -116,7 +116,7 @@ pub fn collect_defs( errors } -impl<'a> ModCollector<'a> { +impl ModCollector<'_> { fn collect_attributes( &mut self, attributes: Vec, @@ -143,7 +143,7 @@ impl<'a> ModCollector<'a> { context: &mut Context, globals: Vec<(Documented, ItemVisibility)>, crate_id: CrateId, - ) -> Vec<(CompilationError, fm::FileId)> { + ) -> Vec { let mut errors = vec![]; for (global, visibility) in globals { let (global, error) = collect_global( @@ -171,7 +171,7 @@ impl<'a> ModCollector<'a> { context: &mut Context, impls: Vec, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { + ) -> Vec { let mut errors = Vec::new(); let module_id = ModuleId { krate, local_id: self.module_id }; @@ -194,7 +194,7 @@ impl<'a> ModCollector<'a> { context: &mut Context, impls: Vec, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { + ) -> Vec { let mut errors = Vec::new(); for mut trait_impl in impls { @@ -212,19 +212,19 @@ impl<'a> ModCollector<'a> { for (_, func_id, noir_function) in &mut unresolved_functions.functions { if noir_function.def.attributes.is_test_function() { let error = DefCollectorErrorKind::TestOnAssociatedFunction { - span: noir_function.name_ident().span(), + location: noir_function.name_ident().location(), }; - errors.push((error.into(), self.file_id)); + errors.push(error.into()); } if noir_function.def.attributes.has_export() { let error = DefCollectorErrorKind::ExportOnAssociatedFunction { - span: noir_function.name_ident().span(), + location: noir_function.name_ident().location(), }; - errors.push((error.into(), self.file_id)); + errors.push(error.into()); } - let location = Location::new(noir_function.def.span, self.file_id); + let location = noir_function.def.location; context.def_interner.push_function(*func_id, &noir_function.def, module, location); } @@ -258,7 +258,7 @@ impl<'a> ModCollector<'a> { context: &mut Context, functions: Vec>, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { + ) -> Vec { let mut unresolved_functions = UnresolvedFunctions { file_id: self.file_id, functions: Vec::new(), @@ -276,7 +276,6 @@ impl<'a> ModCollector<'a> { &mut context.usage_tracker, &function.item, module, - self.file_id, function.doc_comments, &mut errors, ) else { @@ -304,7 +303,7 @@ impl<'a> ModCollector<'a> { context: &mut Context, types: Vec>, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { + ) -> Vec { let mut definition_errors = vec![]; for struct_definition in types { if let Some((id, the_struct)) = collect_struct( @@ -312,7 +311,6 @@ impl<'a> ModCollector<'a> { &mut self.def_collector.def_map, &mut context.usage_tracker, struct_definition, - self.file_id, self.module_id, krate, &mut definition_errors, @@ -331,7 +329,7 @@ impl<'a> ModCollector<'a> { context: &mut Context, types: Vec>, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { + ) -> Vec { let mut definition_errors = vec![]; for enum_definition in types { if let Some((id, the_enum)) = collect_enum( @@ -357,8 +355,8 @@ impl<'a> ModCollector<'a> { context: &mut Context, type_aliases: Vec>, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + ) -> Vec { + let mut errors: Vec = vec![]; for type_alias in type_aliases { let doc_comments = type_alias.doc_comments; let type_alias = type_alias.item; @@ -377,7 +375,6 @@ impl<'a> ModCollector<'a> { &context.def_interner, &unresolved.type_alias_def.generics, &mut errors, - self.file_id, ); let type_alias_id = @@ -406,7 +403,7 @@ impl<'a> ModCollector<'a> { first_def, second_def, }; - errors.push((err.into(), self.file_id)); + errors.push(err.into()); } self.def_collector.items.type_aliases.insert(type_alias_id, unresolved); @@ -433,20 +430,20 @@ impl<'a> ModCollector<'a> { context: &mut Context, traits: Vec>, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + ) -> Vec { + let mut errors: Vec = vec![]; for trait_definition in traits { let doc_comments = trait_definition.doc_comments; let trait_definition = trait_definition.item; let name = trait_definition.name.clone(); - let location = Location::new(trait_definition.name.span(), self.file_id); + let location = trait_definition.location; // Create the corresponding module for the trait namespace let trait_id = match self.push_child_module( context, &name, ItemVisibility::Public, - Location::new(name.span(), self.file_id), + name.location(), Vec::new(), Vec::new(), false, @@ -455,7 +452,7 @@ impl<'a> ModCollector<'a> { ) { Ok(module_id) => TraitId(ModuleId { krate, local_id: module_id.local_id }), Err(error) => { - errors.push((error.into(), self.file_id)); + errors.push(error.into()); continue; } }; @@ -484,7 +481,7 @@ impl<'a> ModCollector<'a> { first_def, second_def, }; - errors.push((error.into(), self.file_id)); + errors.push(error.into()); } // Add all functions that have a default implementation in the trait @@ -573,7 +570,7 @@ impl<'a> ModCollector<'a> { first_def, second_def, }; - errors.push((error.into(), self.file_id)); + errors.push(error.into()); } } } @@ -597,7 +594,7 @@ impl<'a> ModCollector<'a> { first_def, second_def, }; - errors.push((error.into(), self.file_id)); + errors.push(error.into()); } else { let type_variable_id = context.def_interner.next_type_variable_id(); let typ = self.resolve_associated_constant_type(typ, &mut errors); @@ -608,7 +605,7 @@ impl<'a> ModCollector<'a> { type_variable_id, Kind::numeric(typ), ), - span: name.span(), + location: name.location(), }); } } @@ -626,13 +623,13 @@ impl<'a> ModCollector<'a> { first_def, second_def, }; - errors.push((error.into(), self.file_id)); + errors.push(error.into()); } else { let type_variable_id = context.def_interner.next_type_variable_id(); associated_types.push(ResolvedGeneric { name: Rc::new(name.to_string()), type_var: TypeVariable::unbound(type_variable_id, Kind::Normal), - span: name.span(), + location: name.location(), }); } } @@ -643,7 +640,6 @@ impl<'a> ModCollector<'a> { &context.def_interner, &trait_definition.generics, &mut errors, - self.file_id, ); let unresolved = UnresolvedTrait { @@ -684,8 +680,8 @@ impl<'a> ModCollector<'a> { parent_module_id: LocalModuleId, submodules: Vec>, file_id: FileId, - ) -> Vec<(CompilationError, FileId)> { - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + ) -> Vec { + let mut errors: Vec = vec![]; for submodule in submodules { let mut doc_comments = submodule.doc_comments; let submodule = submodule.item; @@ -731,7 +727,7 @@ impl<'a> ModCollector<'a> { )); } Err(error) => { - errors.push((error.into(), file_id)); + errors.push(error.into()); } }; } @@ -749,16 +745,16 @@ impl<'a> ModCollector<'a> { crate_id: CrateId, parent_file_id: FileId, parent_module_id: LocalModuleId, - ) -> Vec<(CompilationError, FileId)> { + ) -> Vec { let mut doc_comments = mod_decl.doc_comments; let mod_decl = mod_decl.item; - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + let mut errors: Vec = vec![]; let child_file_id = match find_module(&context.file_manager, self.file_id, &mod_decl.ident) { Ok(child_file_id) => child_file_id, Err(err) => { - errors.push((err.into(), self.file_id)); + errors.push(err.into()); return errors; } }; @@ -768,15 +764,15 @@ impl<'a> ModCollector<'a> { if let Some(old_location) = context.visited_files.get(&child_file_id) { let error = DefCollectorErrorKind::ModuleAlreadyPartOfCrate { mod_name: mod_decl.ident.clone(), - span: location.span, + location, }; - errors.push((error.into(), location.file)); + errors.push(error.into()); let error = DefCollectorErrorKind::ModuleOriginallyDefined { mod_name: mod_decl.ident.clone(), - span: old_location.span, + location: *old_location, }; - errors.push((error.into(), old_location.file)); + errors.push(error.into()); return errors; } @@ -786,9 +782,7 @@ impl<'a> ModCollector<'a> { let (ast, parsing_errors) = context.parsed_file_results(child_file_id); let ast = ast.into_sorted(); - errors.extend( - parsing_errors.iter().map(|e| (e.clone().into(), child_file_id)).collect::>(), - ); + errors.extend(parsing_errors.iter().map(|e| e.clone().into()).collect::>()); // Add module into def collector and get a ModuleId match self.push_child_module( @@ -833,7 +827,7 @@ impl<'a> ModCollector<'a> { )); } Err(error) => { - errors.push((error.into(), child_file_id)); + errors.push(error.into()); } } errors @@ -872,15 +866,15 @@ impl<'a> ModCollector<'a> { fn resolve_associated_constant_type( &self, typ: &UnresolvedType, - errors: &mut Vec<(CompilationError, FileId)>, + errors: &mut Vec, ) -> Type { match &typ.typ { UnresolvedTypeData::FieldElement => Type::FieldElement, UnresolvedTypeData::Integer(sign, bits) => Type::Integer(*sign, *bits), _ => { - let span = typ.span; - let error = ResolverError::AssociatedConstantsMustBeNumeric { span }; - errors.push((error.into(), self.file_id)); + let error = + ResolverError::AssociatedConstantsMustBeNumeric { location: typ.location }; + errors.push(error.into()); Type::Error } } @@ -979,9 +973,8 @@ pub fn collect_function( usage_tracker: &mut UsageTracker, function: &NoirFunction, module: ModuleId, - file: FileId, doc_comments: Vec, - errors: &mut Vec<(CompilationError, FileId)>, + errors: &mut Vec, ) -> Option { if let Some(field) = function.attributes().get_field_attribute() { if !is_native_field(&field) { @@ -1002,7 +995,7 @@ pub fn collect_function( let name = function.name_ident().clone(); let func_id = interner.push_empty_fn(); let visibility = function.def.visibility; - let location = Location::new(function.span(), file); + let location = function.location(); interner.push_function(func_id, &function.def, module, location); if interner.is_in_lsp_mode() && !function.def.is_test() { interner.register_function(func_id, &function.def); @@ -1023,7 +1016,7 @@ pub fn collect_function( first_def, second_def, }; - errors.push((error.into(), file)); + errors.push(error.into()); } Some(func_id) } @@ -1034,26 +1027,22 @@ pub fn collect_struct( def_map: &mut CrateDefMap, usage_tracker: &mut UsageTracker, struct_definition: Documented, - file_id: FileId, module_id: LocalModuleId, krate: CrateId, - definition_errors: &mut Vec<(CompilationError, FileId)>, + definition_errors: &mut Vec, ) -> Option<(TypeId, UnresolvedStruct)> { let doc_comments = struct_definition.doc_comments; let struct_definition = struct_definition.item; + let file_id = struct_definition.location.file; - check_duplicate_field_names(&struct_definition, file_id, definition_errors); + check_duplicate_field_names(&struct_definition, definition_errors); let name = struct_definition.name.clone(); let unresolved = UnresolvedStruct { file_id, module_id, struct_def: struct_definition }; - let resolved_generics = Context::resolve_generics( - interner, - &unresolved.struct_def.generics, - definition_errors, - file_id, - ); + let resolved_generics = + Context::resolve_generics(interner, &unresolved.struct_def.generics, definition_errors); // Create the corresponding module for the struct namespace let location = Location::new(name.span(), file_id); @@ -1072,13 +1061,13 @@ pub fn collect_struct( ) { Ok(module_id) => { let name = unresolved.struct_def.name.clone(); - let span = unresolved.struct_def.span; + let span = unresolved.struct_def.location.span; let attributes = unresolved.struct_def.attributes.clone(); let local_id = module_id.local_id; interner.new_type(name, span, attributes, resolved_generics, krate, local_id, file_id) } Err(error) => { - definition_errors.push((error.into(), file_id)); + definition_errors.push(error.into()); return None; } }; @@ -1113,7 +1102,7 @@ pub fn collect_struct( first_def, second_def, }; - definition_errors.push((error.into(), file_id)); + definition_errors.push(error.into()); } if interner.is_in_lsp_mode() { @@ -1132,23 +1121,19 @@ pub fn collect_enum( file_id: FileId, module_id: LocalModuleId, krate: CrateId, - definition_errors: &mut Vec<(CompilationError, FileId)>, + definition_errors: &mut Vec, ) -> Option<(TypeId, UnresolvedEnum)> { let doc_comments = enum_def.doc_comments; let enum_def = enum_def.item; - check_duplicate_variant_names(&enum_def, file_id, definition_errors); + check_duplicate_variant_names(&enum_def, definition_errors); let name = enum_def.name.clone(); let unresolved = UnresolvedEnum { file_id, module_id, enum_def }; - let resolved_generics = Context::resolve_generics( - interner, - &unresolved.enum_def.generics, - definition_errors, - file_id, - ); + let resolved_generics = + Context::resolve_generics(interner, &unresolved.enum_def.generics, definition_errors); // Create the corresponding module for the enum namespace let location = Location::new(name.span(), file_id); @@ -1167,13 +1152,13 @@ pub fn collect_enum( ) { Ok(module_id) => { let name = unresolved.enum_def.name.clone(); - let span = unresolved.enum_def.span; + let span = unresolved.enum_def.location.span; let attributes = unresolved.enum_def.attributes.clone(); let local_id = module_id.local_id; interner.new_type(name, span, attributes, resolved_generics, krate, local_id, file_id) } Err(error) => { - definition_errors.push((error.into(), file_id)); + definition_errors.push(error.into()); return None; } }; @@ -1208,7 +1193,7 @@ pub fn collect_enum( first_def, second_def, }; - definition_errors.push((error.into(), file_id)); + definition_errors.push(error.into()); } if interner.is_in_lsp_mode() { @@ -1224,7 +1209,7 @@ pub fn collect_impl( r#impl: TypeImpl, file_id: FileId, module_id: ModuleId, - errors: &mut Vec<(CompilationError, FileId)>, + errors: &mut Vec, ) { let mut unresolved_functions = UnresolvedFunctions { file_id, functions: Vec::new(), trait_id: None, self_type: None }; @@ -1235,16 +1220,16 @@ pub fn collect_impl( if method.def.attributes.is_test_function() { let error = DefCollectorErrorKind::TestOnAssociatedFunction { - span: method.name_ident().span(), + location: method.name_ident().location(), }; - errors.push((error.into(), file_id)); + errors.push(error.into()); continue; } if method.def.attributes.has_export() { let error = DefCollectorErrorKind::ExportOnAssociatedFunction { - span: method.name_ident().span(), + location: method.name_ident().location(), }; - errors.push((error.into(), file_id)); + errors.push(error.into()); } let func_id = interner.push_empty_fn(); @@ -1257,7 +1242,7 @@ pub fn collect_impl( let key = (r#impl.object_type, module_id.local_id); let methods = items.impls.entry(key).or_default(); - methods.push((r#impl.generics, r#impl.type_span, unresolved_functions)); + methods.push((r#impl.generics, r#impl.type_location, unresolved_functions)); } fn find_module( @@ -1339,11 +1324,7 @@ fn is_native_field(str: &str) -> bool { } else { BigUint::from_str_radix(str, 10) }; - if let Ok(big_num) = big_num { - big_num == FieldElement::modulus() - } else { - CHOSEN_FIELD == str - } + if let Ok(big_num) = big_num { big_num == FieldElement::modulus() } else { CHOSEN_FIELD == str } } type AssociatedTypes = Vec<(Ident, UnresolvedType)>; @@ -1401,7 +1382,7 @@ pub(crate) fn collect_global( file_id: FileId, module_id: LocalModuleId, crate_id: CrateId, -) -> (UnresolvedGlobal, Option<(CompilationError, FileId)>) { +) -> (UnresolvedGlobal, Option) { let doc_comments = global.doc_comments; let global = global.item; @@ -1435,7 +1416,7 @@ pub(crate) fn collect_global( let error = result.err().map(|(first_def, second_def)| { let err = DefCollectorErrorKind::Duplicate { typ: DuplicateType::Global, first_def, second_def }; - (err.into(), file_id) + err.into() }); interner.set_doc_comments(ReferenceId::Global(global_id), doc_comments); @@ -1446,8 +1427,7 @@ pub(crate) fn collect_global( fn check_duplicate_field_names( struct_definition: &NoirStruct, - file: FileId, - definition_errors: &mut Vec<(CompilationError, FileId)>, + definition_errors: &mut Vec, ) { let mut seen_field_names = std::collections::HashSet::new(); for field in &struct_definition.fields { @@ -1463,14 +1443,13 @@ fn check_duplicate_field_names( first_def: previous_field_name.clone(), second_def: field_name.clone(), }; - definition_errors.push((error.into(), file)); + definition_errors.push(error.into()); } } fn check_duplicate_variant_names( enum_def: &NoirEnumeration, - file: FileId, - definition_errors: &mut Vec<(CompilationError, FileId)>, + definition_errors: &mut Vec, ) { let mut seen_variant_names = std::collections::HashSet::new(); for variant in &enum_def.variants { @@ -1486,7 +1465,7 @@ fn check_duplicate_variant_names( first_def: previous_variant_name.clone(), second_def: variant_name.clone(), }; - definition_errors.push((error.into(), file)); + definition_errors.push(error.into()); } } @@ -1494,7 +1473,6 @@ fn check_duplicate_variant_names( mod find_module_tests { use super::*; - use noirc_errors::Spanned; use std::path::{Path, PathBuf}; fn add_file(file_manager: &mut FileManager, dir: &Path, file_name: &str) -> FileId { @@ -1513,7 +1491,9 @@ mod find_module_tests { anchor: FileId, mod_name: &str, ) -> Result { - let mod_name = Ident(Spanned::from_position(0, 1, mod_name.to_string())); + let span = Span::from(0..1); + let location = Location::new(span, FileId::dummy()); + let mod_name = Ident::new(mod_name.to_string(), location); super::find_module(file_manager, anchor, &mod_name) } 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 1ca62acd29b1..7f17b1e30430 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 @@ -2,9 +2,7 @@ use crate::ast::{Ident, ItemVisibility, Path, UnsupportedNumericGenericType}; use crate::hir::resolution::import::PathResolutionError; use crate::hir::type_check::generics::TraitGenerics; -use noirc_errors::CustomDiagnostic as Diagnostic; -use noirc_errors::FileDiagnostic; -use noirc_errors::Span; +use noirc_errors::{CustomDiagnostic as Diagnostic, Location}; use thiserror::Error; use std::fmt; @@ -25,7 +23,7 @@ pub enum DuplicateType { EnumVariant, } -#[derive(Error, Debug, Clone)] +#[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum DefCollectorErrorKind { #[error("Duplicate {typ}")] Duplicate { typ: DuplicateType, first_def: Ident, second_def: Ident }, @@ -38,23 +36,13 @@ pub enum DefCollectorErrorKind { #[error("Cannot re-export {item_name} because it has less visibility than this use statement")] CannotReexportItemWithLessVisibility { item_name: Ident, desired_visibility: ItemVisibility }, #[error("Non-struct type used in impl")] - NonStructTypeInImpl { span: Span }, + NonStructTypeInImpl { location: Location }, #[error("Cannot implement trait on a mutable reference type")] - MutableReferenceInTraitImpl { span: Span }, + MutableReferenceInTraitImpl { location: Location }, #[error("Impl for type `{typ}` overlaps with existing impl")] - OverlappingImpl { span: Span, typ: crate::Type }, - #[error("Previous impl defined here")] - OverlappingImplNote { span: Span }, + OverlappingImpl { typ: crate::Type, location: Location, prev_location: Location }, #[error("Cannot `impl` a type defined outside the current crate")] - ForeignImpl { span: Span, type_name: String }, - #[error("Mismatched number of generics in {location}")] - MismatchGenericCount { - actual_generic_count: usize, - expected_generic_count: usize, - location: &'static str, - origin: String, - span: Span, - }, + ForeignImpl { location: Location, type_name: String }, #[error("Method is not defined in trait")] MethodNotInTrait { trait_name: Ident, impl_method: Ident }, #[error("Only traits can be implemented")] @@ -62,35 +50,66 @@ pub enum DefCollectorErrorKind { #[error("Trait not found")] TraitNotFound { trait_path: Path }, #[error("Missing Trait method implementation")] - TraitMissingMethod { trait_name: Ident, method_name: Ident, trait_impl_span: Span }, + TraitMissingMethod { trait_name: Ident, method_name: Ident, trait_impl_location: Location }, #[error("Module is already part of the crate")] - ModuleAlreadyPartOfCrate { mod_name: Ident, span: Span }, + ModuleAlreadyPartOfCrate { mod_name: Ident, location: Location }, #[error("Module was originally declared here")] - ModuleOriginallyDefined { mod_name: Ident, span: Span }, - #[error( - "Either the type or the trait must be from the same crate as the trait implementation" - )] - TraitImplOrphaned { span: Span }, + ModuleOriginallyDefined { mod_name: Ident, location: Location }, + #[error("Either the type or the trait must be from the same crate as the trait implementation")] + TraitImplOrphaned { location: Location }, #[error("impl has stricter requirements than trait")] ImplIsStricterThanTrait { constraint_typ: crate::Type, constraint_name: String, constraint_generics: TraitGenerics, - constraint_span: Span, + constraint_location: Location, trait_method_name: String, - trait_method_span: Span, + trait_method_location: Location, }, #[error("{0}")] UnsupportedNumericGenericType(#[from] UnsupportedNumericGenericType), #[error("The `#[test]` attribute may only be used on a non-associated function")] - TestOnAssociatedFunction { span: Span }, + TestOnAssociatedFunction { location: Location }, #[error("The `#[export]` attribute may only be used on a non-associated function")] - ExportOnAssociatedFunction { span: Span }, + ExportOnAssociatedFunction { location: Location }, } impl DefCollectorErrorKind { - pub fn into_file_diagnostic(&self, file: fm::FileId) -> FileDiagnostic { - Diagnostic::from(self).in_file(file) + pub fn location(&self) -> Location { + match self { + DefCollectorErrorKind::Duplicate { second_def: ident, .. } + | DefCollectorErrorKind::UnresolvedModuleDecl { mod_name: ident, .. } + | DefCollectorErrorKind::CannotReexportItemWithLessVisibility { + item_name: ident, + .. + } + | DefCollectorErrorKind::MethodNotInTrait { impl_method: ident, .. } + | DefCollectorErrorKind::OverlappingModuleDecls { mod_name: ident, .. } => { + ident.location() + } + DefCollectorErrorKind::PathResolutionError(path_resolution_error) => { + path_resolution_error.location() + } + DefCollectorErrorKind::ImplIsStricterThanTrait { + trait_method_location: location, + .. + } + | DefCollectorErrorKind::TestOnAssociatedFunction { location } + | DefCollectorErrorKind::ExportOnAssociatedFunction { location } + | DefCollectorErrorKind::NonStructTypeInImpl { location } + | DefCollectorErrorKind::MutableReferenceInTraitImpl { location } + | DefCollectorErrorKind::OverlappingImpl { location, .. } + | DefCollectorErrorKind::ModuleAlreadyPartOfCrate { location, .. } + | DefCollectorErrorKind::ModuleOriginallyDefined { location, .. } + | DefCollectorErrorKind::TraitImplOrphaned { location } + | DefCollectorErrorKind::TraitMissingMethod { trait_impl_location: location, .. } + | DefCollectorErrorKind::ForeignImpl { location, .. } => *location, + DefCollectorErrorKind::NotATrait { not_a_trait_name: path } + | DefCollectorErrorKind::TraitNotFound { trait_path: path } => path.location, + DefCollectorErrorKind::UnsupportedNumericGenericType( + unsupported_numeric_generic_type, + ) => unsupported_numeric_generic_type.ident.location(), + } } } @@ -100,9 +119,11 @@ impl<'a> From<&'a UnsupportedNumericGenericType> for Diagnostic { let typ = &error.typ; Diagnostic::simple_error( - format!("{name} has a type of {typ}. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`."), + format!( + "{name} has a type of {typ}. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`." + ), "Unsupported numeric generic type".to_string(), - error.ident.0.span(), + error.ident.0.location(), ) } } @@ -135,35 +156,35 @@ impl<'a> From<&'a DefCollectorErrorKind> for Diagnostic { &typ, &first_def.0.contents ); { - let first_span = first_def.0.span(); - let second_span = second_def.0.span(); + let first_location = first_def.0.location(); + let second_location = second_def.0.location(); let mut diag = Diagnostic::simple_error( primary_message, - format!("First {} found here", &typ), - first_span, + format!("Second {} found here", &typ), + second_location, ); - diag.add_secondary(format!("Second {} found here", &typ), second_span); + diag.add_secondary(format!("First {} found here", &typ), first_location); diag } } DefCollectorErrorKind::UnresolvedModuleDecl { mod_name, expected_path, alternative_path } => { - let span = mod_name.0.span(); + let location = mod_name.0.location(); let mod_name = &mod_name.0.contents; Diagnostic::simple_error( format!("No module `{mod_name}` at path `{expected_path}` or `{alternative_path}`"), String::new(), - span, + location, ) } DefCollectorErrorKind::OverlappingModuleDecls { mod_name, expected_path, alternative_path } => { - let span = mod_name.0.span(); + let location = mod_name.0.location(); let mod_name = &mod_name.0.contents; Diagnostic::simple_error( format!("Overlapping modules `{mod_name}` at path `{expected_path}` and `{alternative_path}`"), String::new(), - span, + location, ) } DefCollectorErrorKind::PathResolutionError(error) => error.into(), @@ -171,68 +192,48 @@ impl<'a> From<&'a DefCollectorErrorKind> for Diagnostic { Diagnostic::simple_error( format!("cannot re-export {item_name} because it has less visibility than this use statement"), format!("consider marking {item_name} as {desired_visibility}"), - item_name.span()) + item_name.location()) } - DefCollectorErrorKind::NonStructTypeInImpl { span } => Diagnostic::simple_error( + DefCollectorErrorKind::NonStructTypeInImpl { location } => Diagnostic::simple_error( "Non-struct type used in impl".into(), "Only struct types may have implementation methods".into(), - *span, + *location, ), - DefCollectorErrorKind::MutableReferenceInTraitImpl { span } => Diagnostic::simple_error( + DefCollectorErrorKind::MutableReferenceInTraitImpl { location } => Diagnostic::simple_error( "Trait impls are not allowed on mutable reference types".into(), "Try using a struct type here instead".into(), - *span, + *location, ), - DefCollectorErrorKind::OverlappingImpl { span, typ } => { - Diagnostic::simple_error( + DefCollectorErrorKind::OverlappingImpl { location, typ, prev_location } => { + let mut diagnostic = Diagnostic::simple_error( format!("Impl for type `{typ}` overlaps with existing impl"), "Overlapping impl".into(), - *span, - ) - } - DefCollectorErrorKind::OverlappingImplNote { span } => { - // This should be a note or part of the previous error eventually. - // This must be an error to appear next to the previous OverlappingImpl - // error since we sort warnings first. - Diagnostic::simple_error( - "Previous impl defined here".into(), - "Previous impl defined here".into(), - *span, - ) + *location, + ); + diagnostic.add_secondary("Previous impl defined here".into(), *prev_location); + diagnostic } - DefCollectorErrorKind::ForeignImpl { span, type_name } => Diagnostic::simple_error( + DefCollectorErrorKind::ForeignImpl { location, type_name } => Diagnostic::simple_error( "Cannot `impl` a type that was defined outside the current crate".into(), format!("{type_name} was defined outside the current crate"), - *span, + *location, ), DefCollectorErrorKind::TraitNotFound { trait_path } => Diagnostic::simple_error( format!("Trait {trait_path} not found"), "".to_string(), - trait_path.span(), + trait_path.location, ), - DefCollectorErrorKind::MismatchGenericCount { - actual_generic_count, - expected_generic_count, - location, - origin, - span, - } => { - let plural = if *expected_generic_count == 1 { "" } else { "s" }; - let primary_message = format!( - "`{origin}` expects {expected_generic_count} generic{plural}, but {location} has {actual_generic_count}"); - Diagnostic::simple_error(primary_message, "".to_string(), *span) - } DefCollectorErrorKind::MethodNotInTrait { trait_name, impl_method } => { let trait_name = &trait_name.0.contents; - let impl_method_span = impl_method.span(); + let impl_method_location = impl_method.location(); let impl_method_name = &impl_method.0.contents; let primary_message = format!("Method with name `{impl_method_name}` is not part of trait `{trait_name}`, therefore it can't be implemented"); - Diagnostic::simple_error(primary_message, "".to_owned(), impl_method_span) + Diagnostic::simple_error(primary_message, "".to_owned(), impl_method_location) } DefCollectorErrorKind::TraitMissingMethod { trait_name, method_name, - trait_impl_span, + trait_impl_location, } => { let trait_name = &trait_name.0.contents; let impl_method_name = &method_name.0.contents; @@ -242,53 +243,53 @@ impl<'a> From<&'a DefCollectorErrorKind> for Diagnostic { Diagnostic::simple_error( primary_message, format!("Please implement {impl_method_name} here"), - *trait_impl_span, + *trait_impl_location, ) } DefCollectorErrorKind::NotATrait { not_a_trait_name } => { - let span = not_a_trait_name.span(); + let location = not_a_trait_name.location; Diagnostic::simple_error( format!("{not_a_trait_name} is not a trait, therefore it can't be implemented"), String::new(), - span, + location, ) } - DefCollectorErrorKind::ModuleAlreadyPartOfCrate { mod_name, span } => { + DefCollectorErrorKind::ModuleAlreadyPartOfCrate { mod_name, location } => { let message = format!("Module '{mod_name}' is already part of the crate"); let secondary = String::new(); - Diagnostic::simple_error(message, secondary, *span) + Diagnostic::simple_error(message, secondary, *location) } - DefCollectorErrorKind::ModuleOriginallyDefined { mod_name, span } => { + DefCollectorErrorKind::ModuleOriginallyDefined { mod_name, location } => { let message = format!("Note: {mod_name} was originally declared here"); let secondary = String::new(); - Diagnostic::simple_error(message, secondary, *span) + Diagnostic::simple_error(message, secondary, *location) } - DefCollectorErrorKind::TraitImplOrphaned { span } => Diagnostic::simple_error( + DefCollectorErrorKind::TraitImplOrphaned { location } => Diagnostic::simple_error( "Orphaned trait implementation".into(), "Either the type or the trait must be from the same crate as the trait implementation".into(), - *span, + *location, ), - DefCollectorErrorKind::ImplIsStricterThanTrait { constraint_typ, constraint_name, constraint_generics, constraint_span, trait_method_name, trait_method_span } => { + DefCollectorErrorKind::ImplIsStricterThanTrait { constraint_typ, constraint_name, constraint_generics, constraint_location, trait_method_name, trait_method_location } => { let constraint = format!("{}{}", constraint_name, constraint_generics); let mut diag = Diagnostic::simple_error( "impl has stricter requirements than trait".to_string(), format!("impl has extra requirement `{constraint_typ}: {constraint}`"), - *constraint_span, + *constraint_location, ); - diag.add_secondary(format!("definition of `{trait_method_name}` from trait"), *trait_method_span); + diag.add_secondary(format!("definition of `{trait_method_name}` from trait"), *trait_method_location); diag } DefCollectorErrorKind::UnsupportedNumericGenericType(err) => err.into(), - DefCollectorErrorKind::TestOnAssociatedFunction { span } => Diagnostic::simple_error( + DefCollectorErrorKind::TestOnAssociatedFunction { location } => Diagnostic::simple_error( "The `#[test]` attribute is disallowed on `impl` methods".into(), String::new(), - *span, + *location, ), - DefCollectorErrorKind::ExportOnAssociatedFunction { span } => Diagnostic::simple_error( + DefCollectorErrorKind::ExportOnAssociatedFunction { location } => Diagnostic::simple_error( "The `#[export]` attribute is disallowed on `impl` methods".into(), String::new(), - *span, + *location, ), } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/item_scope.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/item_scope.rs index 3ca89e56bbc4..bbc2f59c655b 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/item_scope.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/item_scope.rs @@ -1,8 +1,8 @@ -use super::{namespace::PerNs, ModuleDefId, ModuleId}; +use super::{ModuleDefId, ModuleId, namespace::PerNs}; use crate::ast::{Ident, ItemVisibility}; use crate::node_interner::{FuncId, TraitId}; -use std::collections::{hash_map::Entry, HashMap}; +use std::collections::{HashMap, hash_map::Entry}; type Scope = HashMap, (ModuleDefId, ItemVisibility, bool /*is_prelude*/)>; @@ -50,11 +50,7 @@ impl ItemScope { let is_prelude = std::mem::replace(&mut n.get_mut().2, is_prelude); let old_ident = o.key(); - if is_prelude { - Ok(()) - } else { - Err((old_ident.clone(), name)) - } + if is_prelude { Ok(()) } else { Err((old_ident.clone(), name)) } } else { trait_hashmap.insert(trait_id, (mod_def, visibility, is_prelude)); Ok(()) 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 fae891a16475..5377ee7d42a6 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 @@ -1,6 +1,7 @@ +use crate::elaborator::FrontendOptions; use crate::graph::{CrateGraph, CrateId}; -use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector}; use crate::hir::Context; +use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector}; use crate::node_interner::{FuncId, GlobalId, NodeInterner, TypeId}; use crate::parse_program; use crate::parser::{ParsedModule, ParserError}; @@ -77,15 +78,14 @@ impl CrateDefMap { pub fn collect_defs( crate_id: CrateId, context: &mut Context, - debug_comptime_in_file: Option<&str>, - pedantic_solving: bool, - ) -> Vec<(CompilationError, FileId)> { + options: FrontendOptions, + ) -> Vec { // Check if this Crate has already been compiled // XXX: There is probably a better alternative for this. // Without this check, the compiler will panic as it does not // expect the same crate to be processed twice. It would not // make the implementation wrong, if the same crate was processed twice, it just makes it slow. - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + let mut errors: Vec = vec![]; if context.def_map(&crate_id).is_some() { return errors; } @@ -120,13 +120,10 @@ impl CrateDefMap { context, ast, root_file_id, - debug_comptime_in_file, - pedantic_solving, + options, )); - errors.extend( - parsing_errors.iter().map(|e| (e.clone().into(), root_file_id)).collect::>(), - ); + errors.extend(parsing_errors.iter().map(|e| e.clone().into()).collect::>()); errors } @@ -373,7 +370,7 @@ pub struct Contract { /// Given a FileId, fetch the File, from the FileManager and parse it's content pub fn parse_file(fm: &FileManager, file_id: FileId) -> (ParsedModule, Vec) { let file_source = fm.fetch_file(file_id).expect("File does not exist"); - parse_program(file_source) + parse_program(file_source, file_id) } impl std::ops::Index for CrateDefMap { 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 e85fa629d561..8f2c3c5f8675 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/mod.rs @@ -14,7 +14,7 @@ use crate::parser::ParserError; use crate::usage_tracker::UsageTracker; use crate::{Generics, Kind, ParsedModule, ResolvedGeneric, TypeVariable}; use def_collector::dc_crate::CompilationError; -use def_map::{fully_qualified_module_path, Contract, CrateDefMap}; +use def_map::{Contract, CrateDefMap, fully_qualified_module_path}; use fm::{FileId, FileManager}; use iter_extended::vecmap; use noirc_errors::Location; @@ -139,11 +139,7 @@ impl Context<'_, '_> { let parent = def_map.get_module_path_with_separator(module_id.local_id.0, module.parent, "::"); - if parent.is_empty() { - name.into() - } else { - format!("{parent}::{name}") - } + if parent.is_empty() { name.into() } else { format!("{parent}::{name}") } } /// Returns a fully-qualified path to the given [StructId] from the given [CrateId]. This function also @@ -232,26 +228,25 @@ impl Context<'_, '_> { pub(crate) fn resolve_generics( interner: &NodeInterner, generics: &UnresolvedGenerics, - errors: &mut Vec<(CompilationError, FileId)>, - file_id: FileId, + errors: &mut Vec, ) -> Generics { vecmap(generics, |generic| { // Map the generic to a fresh type variable let id = interner.next_type_variable_id(); let type_var_kind = generic.kind().unwrap_or_else(|err| { - errors.push((err.into(), file_id)); + errors.push(err.into()); // When there's an error, unify with any other kinds Kind::Any }); let type_var = TypeVariable::unbound(id, type_var_kind); let ident = generic.ident(); - let span = ident.0.span(); + let location = ident.0.location(); // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); - ResolvedGeneric { name, type_var, span } + ResolvedGeneric { name, type_var, location } }) } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs index 5af0cf41a627..bc1c519ed5d8 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -1,9 +1,10 @@ use acvm::FieldElement; pub use noirc_errors::Span; -use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic, Location}; +use noirc_errors::{CustomDiagnostic as Diagnostic, Location}; use thiserror::Error; use crate::{ + Kind, Type, ast::{Ident, UnsupportedNumericGenericType}, hir::{ comptime::{InterpreterError, Value}, @@ -11,7 +12,6 @@ use crate::{ }, parser::ParserError, usage_tracker::UnusedItem, - Kind, Type, }; use super::import::PathResolutionError; @@ -27,166 +27,257 @@ pub enum PubPosition { #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum ResolverError { #[error("Duplicate definition")] - DuplicateDefinition { name: String, first_span: Span, second_span: Span }, + DuplicateDefinition { name: String, first_location: Location, second_location: Location }, #[error("Unused variable")] UnusedVariable { ident: Ident }, #[error("Unused {}", item.item_type())] UnusedItem { ident: Ident, item: UnusedItem }, #[error("Unconditional recursion")] - UnconditionalRecursion { name: String, span: Span }, + UnconditionalRecursion { name: String, location: Location }, #[error("Could not find variable in this scope")] - VariableNotDeclared { name: String, span: Span }, + VariableNotDeclared { name: String, location: Location }, #[error("path is not an identifier")] - PathIsNotIdent { span: Span }, + PathIsNotIdent { location: Location }, #[error("could not resolve path")] PathResolutionError(#[from] PathResolutionError), #[error("Expected")] - Expected { span: Span, expected: &'static str, got: &'static str }, + Expected { location: Location, expected: &'static str, got: &'static str }, #[error("Duplicate field in constructor")] DuplicateField { field: Ident }, #[error("No such field in struct")] NoSuchField { field: Ident, struct_definition: Ident }, #[error("Missing fields from struct")] - MissingFields { span: Span, missing_fields: Vec, struct_definition: Ident }, + MissingFields { location: Location, missing_fields: Vec, struct_definition: Ident }, #[error("Unneeded 'mut', pattern is already marked as mutable")] - UnnecessaryMut { first_mut: Span, second_mut: Span }, + UnnecessaryMut { first_mut: Location, second_mut: Location }, #[error("Unneeded 'pub', function is not the main method")] UnnecessaryPub { ident: Ident, position: PubPosition }, #[error("Required 'pub', main function must return public value")] NecessaryPub { ident: Ident }, #[error("Missing expression for declared constant")] - MissingRhsExpr { name: String, span: Span }, + MissingRhsExpr { name: String, location: Location }, #[error("Expression invalid in an array length context")] - InvalidArrayLengthExpr { span: Span }, + InvalidArrayLengthExpr { location: Location }, #[error("Integer too large to be evaluated in an array length context")] - IntegerTooLarge { span: Span }, + IntegerTooLarge { location: Location }, #[error("No global or generic type parameter found with the given name")] NoSuchNumericTypeVariable { path: crate::ast::Path }, #[error("Closures cannot capture mutable variables")] - CapturedMutableVariable { span: Span }, + CapturedMutableVariable { location: Location }, #[error("Test functions are not allowed to have any parameters")] - TestFunctionHasParameters { span: Span }, + TestFunctionHasParameters { location: Location }, #[error("Only struct types can be used in constructor expressions")] - NonStructUsedInConstructor { typ: String, span: Span }, + NonStructUsedInConstructor { typ: String, location: Location }, #[error("Only struct types can have generics")] - NonStructWithGenerics { span: Span }, + NonStructWithGenerics { location: Location }, #[error("Cannot apply generics on Self type")] - GenericsOnSelfType { span: Span }, + GenericsOnSelfType { location: Location }, #[error("Cannot apply generics on an associated type")] - GenericsOnAssociatedType { span: Span }, + GenericsOnAssociatedType { location: Location }, #[error("{0}")] ParserError(Box), - #[error("Cannot create a mutable reference to {variable}, it was declared to be immutable")] - MutableReferenceToImmutableVariable { variable: String, span: Span }, - #[error("Mutable references to array indices are unsupported")] - MutableReferenceToArrayElement { span: Span }, #[error("Closure environment must be a tuple or unit type")] - InvalidClosureEnvironment { typ: Type, span: Span }, + InvalidClosureEnvironment { typ: Type, location: Location }, #[error("Nested slices, i.e. slices within an array or slice, are not supported")] - NestedSlices { span: Span }, + NestedSlices { location: Location }, #[error("#[abi(tag)] attribute is only allowed in contracts")] - AbiAttributeOutsideContract { span: Span }, - #[error("Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library")] - LowLevelFunctionOutsideOfStdlib { ident: Ident }, + AbiAttributeOutsideContract { location: Location }, #[error( - "Usage of the `#[oracle]` function attribute is only valid on unconstrained functions" + "Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library" )] + LowLevelFunctionOutsideOfStdlib { ident: Ident }, + #[error("Usage of the `#[oracle]` function attribute is only valid on unconstrained functions")] OracleMarkedAsConstrained { ident: Ident }, #[error("Oracle functions cannot be called directly from constrained functions")] - UnconstrainedOracleReturnToConstrained { span: Span }, + UnconstrainedOracleReturnToConstrained { location: Location }, #[error("Dependency cycle found, '{item}' recursively depends on itself: {cycle} ")] - DependencyCycle { span: Span, item: String, cycle: String }, + DependencyCycle { location: Location, item: String, cycle: String }, #[error("break/continue are only allowed in unconstrained functions")] - JumpInConstrainedFn { is_break: bool, span: Span }, + JumpInConstrainedFn { is_break: bool, location: Location }, #[error("`loop` is only allowed in unconstrained functions")] - LoopInConstrainedFn { span: Span }, + LoopInConstrainedFn { location: Location }, #[error("`loop` must have at least one `break` in it")] - LoopWithoutBreak { span: Span }, + LoopWithoutBreak { location: Location }, #[error("`while` is only allowed in unconstrained functions")] - WhileInConstrainedFn { span: Span }, + WhileInConstrainedFn { location: Location }, #[error("break/continue are only allowed within loops")] - JumpOutsideLoop { is_break: bool, span: Span }, + JumpOutsideLoop { is_break: bool, location: Location }, #[error("Only `comptime` globals can be mutable")] - MutableGlobal { span: Span }, + MutableGlobal { location: Location }, #[error("Globals must have a specified type")] - UnspecifiedGlobalType { span: Span, expected_type: Type }, + UnspecifiedGlobalType { + pattern_location: Location, + expr_location: Location, + expected_type: Type, + }, #[error("Global failed to evaluate")] - UnevaluatedGlobalType { span: Span }, + UnevaluatedGlobalType { location: Location }, #[error("Globals used in a type position must be non-negative")] - NegativeGlobalType { span: Span, global_value: Value }, + NegativeGlobalType { location: Location, global_value: Value }, #[error("Globals used in a type position must be integers")] - NonIntegralGlobalType { span: Span, global_value: Value }, + NonIntegralGlobalType { location: Location, global_value: Value }, #[error("Global value `{global_value}` is larger than its kind's maximum value")] - GlobalLargerThanKind { span: Span, global_value: FieldElement, kind: Kind }, + GlobalLargerThanKind { location: Location, global_value: FieldElement, kind: Kind }, #[error("Self-referential types are not supported")] - SelfReferentialType { span: Span }, + SelfReferentialType { location: Location }, #[error("#[no_predicates] attribute is only allowed on constrained functions")] NoPredicatesAttributeOnUnconstrained { ident: Ident }, #[error("#[fold] attribute is only allowed on constrained functions")] FoldAttributeOnUnconstrained { ident: Ident }, #[error("expected type, found numeric generic parameter")] - NumericGenericUsedForType { name: String, span: Span }, + NumericGenericUsedForType { name: String, location: Location }, #[error("Invalid array length construction")] ArrayLengthInterpreter { error: InterpreterError }, #[error("The unquote operator '$' can only be used within a quote expression")] - UnquoteUsedOutsideQuote { span: Span }, - #[error("\"as trait path\" not yet implemented")] - AsTraitPathNotYetImplemented { span: Span }, + UnquoteUsedOutsideQuote { location: Location }, #[error("Invalid syntax in macro call")] - InvalidSyntaxInMacroCall { span: Span }, + InvalidSyntaxInMacroCall { location: Location }, #[error("Macros must be comptime functions")] - MacroIsNotComptime { span: Span }, + MacroIsNotComptime { location: Location }, #[error("Annotation name must refer to a comptime function")] - NonFunctionInAnnotation { span: Span }, + NonFunctionInAnnotation { location: Location }, #[error("Type `{typ}` was inserted into the generics list from a macro, but is not a generic")] - MacroResultInGenericsListNotAGeneric { span: Span, typ: Type }, + MacroResultInGenericsListNotAGeneric { location: Location, typ: Type }, #[error("Named type arguments aren't allowed in a {item_kind}")] - NamedTypeArgs { span: Span, item_kind: &'static str }, + NamedTypeArgs { location: Location, item_kind: &'static str }, #[error("Associated constants may only be a field or integer type")] - AssociatedConstantsMustBeNumeric { span: Span }, + AssociatedConstantsMustBeNumeric { location: Location }, #[error("Computing `{lhs} {op} {rhs}` failed with error {err}")] BinaryOpError { lhs: FieldElement, op: crate::BinaryTypeOperator, rhs: FieldElement, err: Box, - span: Span, + location: Location, }, #[error("`quote` cannot be used in runtime code")] - QuoteInRuntimeCode { span: Span }, + QuoteInRuntimeCode { location: Location }, #[error("Comptime-only type `{typ}` cannot be used in runtime code")] - ComptimeTypeInRuntimeCode { typ: String, span: Span }, + ComptimeTypeInRuntimeCode { typ: String, location: Location }, #[error("Comptime variable `{name}` cannot be mutated in a non-comptime context")] - MutatingComptimeInNonComptimeContext { name: String, span: Span }, + MutatingComptimeInNonComptimeContext { name: String, location: Location }, #[error("Failed to parse `{statement}` as an expression")] - InvalidInternedStatementInExpr { statement: String, span: Span }, + InvalidInternedStatementInExpr { statement: String, location: Location }, #[error("{0}")] UnsupportedNumericGenericType(#[from] UnsupportedNumericGenericType), #[error("Type `{typ}` is more private than item `{item}`")] - TypeIsMorePrivateThenItem { typ: String, item: String, span: Span }, + TypeIsMorePrivateThenItem { typ: String, item: String, location: Location }, #[error("Unable to parse attribute `{attribute}`")] - UnableToParseAttribute { attribute: String, span: Span }, + UnableToParseAttribute { attribute: String, location: Location }, #[error("Attribute function `{function}` is not a path")] - AttributeFunctionIsNotAPath { function: String, span: Span }, + AttributeFunctionIsNotAPath { function: String, location: Location }, #[error("Attribute function `{name}` is not in scope")] - AttributeFunctionNotInScope { name: String, span: Span }, - #[error("The trait `{missing_trait}` is not implemented for `{type_missing_trait}")] + AttributeFunctionNotInScope { name: String, location: Location }, + #[error("The trait `{missing_trait}` is not implemented for `{type_missing_trait}`")] TraitNotImplemented { impl_trait: String, missing_trait: String, type_missing_trait: String, - span: Span, + location: Location, missing_trait_location: Location, }, #[error("`loop` statements are not yet implemented")] - LoopNotYetSupported { span: Span }, + LoopNotYetSupported { location: Location }, #[error("Expected a trait but found {found}")] - ExpectedTrait { found: String, span: Span }, + ExpectedTrait { found: String, location: Location }, + #[error("Invalid syntax in match pattern")] + InvalidSyntaxInPattern { location: Location }, + #[error("Variable '{existing}' was already defined in the same match pattern")] + VariableAlreadyDefinedInPattern { existing: Ident, new_location: Location }, + #[error("Only integer globals can be used in match patterns")] + NonIntegerGlobalUsedInPattern { location: Location }, + #[error("Cannot match on values of type `{typ}`")] + TypeUnsupportedInMatch { typ: Type, location: Location }, + #[error("Expected a struct, enum, or literal value in pattern, but found a {item}")] + UnexpectedItemInPattern { location: Location, item: &'static str }, + #[error("Trait `{trait_name}` doesn't have a method named `{method_name}`")] + NoSuchMethodInTrait { trait_name: String, method_name: String, location: Location }, } impl ResolverError { - pub fn into_file_diagnostic(&self, file: fm::FileId) -> FileDiagnostic { - Diagnostic::from(self).in_file(file) + pub fn location(&self) -> Location { + match self { + ResolverError::DuplicateDefinition { second_location: location, .. } + | ResolverError::UnconditionalRecursion { location, .. } + | ResolverError::PathIsNotIdent { location } + | ResolverError::Expected { location, .. } + | ResolverError::VariableNotDeclared { location, .. } + | ResolverError::MissingFields { location, .. } + | ResolverError::UnnecessaryMut { second_mut: location, .. } + | ResolverError::TypeIsMorePrivateThenItem { location, .. } + | ResolverError::UnableToParseAttribute { location, .. } + | ResolverError::AttributeFunctionIsNotAPath { location, .. } + | ResolverError::AttributeFunctionNotInScope { location, .. } + | ResolverError::TraitNotImplemented { location, .. } + | ResolverError::LoopNotYetSupported { location } + | ResolverError::ExpectedTrait { location, .. } + | ResolverError::MissingRhsExpr { location, .. } + | ResolverError::InvalidArrayLengthExpr { location } + | ResolverError::IntegerTooLarge { location } + | ResolverError::CapturedMutableVariable { location } + | ResolverError::TestFunctionHasParameters { location } + | ResolverError::NonStructUsedInConstructor { location, .. } + | ResolverError::NonStructWithGenerics { location } + | ResolverError::GenericsOnSelfType { location } + | ResolverError::GenericsOnAssociatedType { location } + | ResolverError::InvalidClosureEnvironment { location, .. } + | ResolverError::NestedSlices { location } + | ResolverError::AbiAttributeOutsideContract { location } + | ResolverError::UnconstrainedOracleReturnToConstrained { location } + | ResolverError::DependencyCycle { location, .. } + | ResolverError::JumpInConstrainedFn { location, .. } + | ResolverError::LoopInConstrainedFn { location } + | ResolverError::LoopWithoutBreak { location } + | ResolverError::WhileInConstrainedFn { location } + | ResolverError::JumpOutsideLoop { location, .. } + | ResolverError::MutableGlobal { location } + | ResolverError::UnspecifiedGlobalType { pattern_location: location, .. } + | ResolverError::UnevaluatedGlobalType { location } + | ResolverError::NegativeGlobalType { location, .. } + | ResolverError::NonIntegralGlobalType { location, .. } + | ResolverError::GlobalLargerThanKind { location, .. } + | ResolverError::SelfReferentialType { location } + | ResolverError::NumericGenericUsedForType { location, .. } + | ResolverError::UnquoteUsedOutsideQuote { location } + | ResolverError::InvalidSyntaxInMacroCall { location } + | ResolverError::MacroIsNotComptime { location } + | ResolverError::NonFunctionInAnnotation { location } + | ResolverError::MacroResultInGenericsListNotAGeneric { location, .. } + | ResolverError::NamedTypeArgs { location, .. } + | ResolverError::AssociatedConstantsMustBeNumeric { location } + | ResolverError::BinaryOpError { location, .. } + | ResolverError::QuoteInRuntimeCode { location } + | ResolverError::ComptimeTypeInRuntimeCode { location, .. } + | ResolverError::MutatingComptimeInNonComptimeContext { location, .. } + | ResolverError::InvalidInternedStatementInExpr { location, .. } + | ResolverError::InvalidSyntaxInPattern { location } + | ResolverError::NonIntegerGlobalUsedInPattern { location, .. } + | ResolverError::TypeUnsupportedInMatch { location, .. } + | ResolverError::UnexpectedItemInPattern { location, .. } + | ResolverError::NoSuchMethodInTrait { location, .. } + | ResolverError::VariableAlreadyDefinedInPattern { new_location: location, .. } => { + *location + } + ResolverError::UnusedVariable { ident } + | ResolverError::UnusedItem { ident, .. } + | ResolverError::DuplicateField { field: ident } + | ResolverError::NoSuchField { field: ident, .. } + | ResolverError::UnnecessaryPub { ident, .. } + | ResolverError::NecessaryPub { ident } + | ResolverError::LowLevelFunctionOutsideOfStdlib { ident } + | ResolverError::OracleMarkedAsConstrained { ident } + | ResolverError::NoPredicatesAttributeOnUnconstrained { ident } + | ResolverError::FoldAttributeOnUnconstrained { ident } => ident.location(), + ResolverError::ArrayLengthInterpreter { error } => error.location(), + ResolverError::PathResolutionError(path_resolution_error) => { + path_resolution_error.location() + } + ResolverError::NoSuchNumericTypeVariable { path } => path.location, + ResolverError::ParserError(parser_error) => parser_error.location(), + ResolverError::UnsupportedNumericGenericType(unsupported_numeric_generic_type) => { + unsupported_numeric_generic_type.ident.location() + } + } } } @@ -196,13 +287,13 @@ impl<'a> From<&'a ResolverError> for Diagnostic { /// soundness of the generated program fn from(error: &'a ResolverError) -> Diagnostic { match error { - ResolverError::DuplicateDefinition { name, first_span, second_span } => { + ResolverError::DuplicateDefinition { name, first_location, second_location} => { let mut diag = Diagnostic::simple_error( format!("duplicate definitions of {name} found"), - "first definition found here".to_string(), - *first_span, + "second definition found here".to_string(), + *second_location, ); - diag.add_secondary("second definition found here".to_string(), *second_span); + diag.add_secondary("first definition found here".to_string(), *first_location); diag } ResolverError::UnusedVariable { ident } => { @@ -210,8 +301,8 @@ impl<'a> From<&'a ResolverError> for Diagnostic { let mut diagnostic = Diagnostic::simple_warning( format!("unused variable {name}"), - "unused variable ".to_string(), - ident.span(), + "unused variable".to_string(), + ident.location(), ); diagnostic.unnecessary = true; diagnostic @@ -225,64 +316,64 @@ impl<'a> From<&'a ResolverError> for Diagnostic { Diagnostic::simple_warning( format!("{item_type} `{name}` is never constructed"), format!("{item_type} is never constructed"), - ident.span(), + ident.location(), ) } else { Diagnostic::simple_warning( format!("unused {item_type} {name}"), format!("unused {item_type}"), - ident.span(), + ident.location(), ) }; diagnostic.unnecessary = true; diagnostic } - ResolverError::UnconditionalRecursion { name, span} => { + ResolverError::UnconditionalRecursion { name, location} => { Diagnostic::simple_warning( format!("function `{name}` cannot return without recursing"), "function cannot return without recursing".to_string(), - *span, + *location, ) } - ResolverError::VariableNotDeclared { name, span } => { + ResolverError::VariableNotDeclared { name, location } => { if name == "_" { Diagnostic::simple_error( "in expressions, `_` can only be used on the left-hand side of an assignment".to_string(), "`_` not allowed here".to_string(), - *span, + *location, ) } else { Diagnostic::simple_error( format!("cannot find `{name}` in this scope"), "not found in this scope".to_string(), - *span, + *location, ) } }, - ResolverError::PathIsNotIdent { span } => Diagnostic::simple_error( + ResolverError::PathIsNotIdent { location } => Diagnostic::simple_error( "cannot use path as an identifier".to_string(), String::new(), - *span, + *location, ), ResolverError::PathResolutionError(error) => error.into(), - ResolverError::Expected { span, expected, got } => Diagnostic::simple_error( + ResolverError::Expected { location, expected, got } => Diagnostic::simple_error( format!("expected {expected} got {got}"), String::new(), - *span, + *location, ), ResolverError::DuplicateField { field } => Diagnostic::simple_error( format!("duplicate field {field}"), String::new(), - field.span(), + field.location(), ), ResolverError::NoSuchField { field, struct_definition } => { Diagnostic::simple_error( format!("no such field {field} defined in struct {struct_definition}"), String::new(), - field.span(), + field.location(), ) } - ResolverError::MissingFields { span, missing_fields, struct_definition } => { + ResolverError::MissingFields { location, missing_fields, struct_definition } => { let plural = if missing_fields.len() != 1 { "s" } else { "" }; let remaining_fields_names = match &missing_fields[..] { [field1] => field1.clone(), @@ -301,7 +392,7 @@ impl<'a> From<&'a ResolverError> for Diagnostic { Diagnostic::simple_error( format!("missing field{plural} {remaining_fields_names} in struct {struct_definition}"), String::new(), - *span, + *location, ) } ResolverError::UnnecessaryMut { first_mut, second_mut } => { @@ -322,7 +413,7 @@ impl<'a> From<&'a ResolverError> for Diagnostic { let mut diag = Diagnostic::simple_warning( format!("unnecessary pub keyword on {position} for function {name}"), format!("unnecessary pub {position}"), - ident.0.span(), + ident.0.location(), ); diag.add_note("The `pub` keyword only has effects on arguments to the entry-point function of a program. Thus, adding it to other function parameters can be deceiving and should be removed".to_owned()); @@ -334,192 +425,188 @@ impl<'a> From<&'a ResolverError> for Diagnostic { let mut diag = Diagnostic::simple_error( format!("missing pub keyword on return type of function {name}"), "missing pub on return type".to_string(), - ident.0.span(), + ident.0.location(), ); diag.add_note("The `pub` keyword is mandatory for the entry-point function return type because the verifier cannot retrieve private witness and thus the function will not be able to return a 'priv' value".to_owned()); diag } - ResolverError::MissingRhsExpr { name, span } => Diagnostic::simple_error( + ResolverError::MissingRhsExpr { name, location } => Diagnostic::simple_error( format!( "no expression specifying the value stored by the constant variable {name}" ), "expected expression to be stored for let statement".to_string(), - *span, + *location, ), - ResolverError::InvalidArrayLengthExpr { span } => Diagnostic::simple_error( + ResolverError::InvalidArrayLengthExpr { location } => Diagnostic::simple_error( "Expression invalid in an array-length context".into(), "Array-length expressions can only have simple integer operations and any variables used must be global constants".into(), - *span, + *location, ), - ResolverError::IntegerTooLarge { span } => Diagnostic::simple_error( + ResolverError::IntegerTooLarge { location } => Diagnostic::simple_error( "Integer too large to be evaluated to an array-length".into(), "Array-lengths may be a maximum size of usize::MAX, including intermediate calculations".into(), - *span, + *location, ), ResolverError::NoSuchNumericTypeVariable { path } => Diagnostic::simple_error( format!("Cannot find a global or generic type parameter named `{path}`"), "Only globals or generic type parameters are allowed to be used as an array type's length".to_string(), - path.span(), + path.location, ), - ResolverError::CapturedMutableVariable { span } => Diagnostic::simple_error( + ResolverError::CapturedMutableVariable { location } => Diagnostic::simple_error( "Closures cannot capture mutable variables".into(), "Mutable variable".into(), - *span, + *location, ), - ResolverError::TestFunctionHasParameters { span } => Diagnostic::simple_error( + ResolverError::TestFunctionHasParameters { location } => Diagnostic::simple_error( "Test functions cannot have any parameters".into(), "Try removing the parameters or moving the test into a wrapper function".into(), - *span, + *location, ), - ResolverError::NonStructUsedInConstructor { typ, span } => Diagnostic::simple_error( + ResolverError::NonStructUsedInConstructor { typ, location } => Diagnostic::simple_error( "Only struct types can be used in constructor expressions".into(), format!("{typ} has no fields to construct it with"), - *span, + *location, ), - ResolverError::NonStructWithGenerics { span } => Diagnostic::simple_error( + ResolverError::NonStructWithGenerics { location } => Diagnostic::simple_error( "Only struct types can have generic arguments".into(), "Try removing the generic arguments".into(), - *span, + *location, ), - ResolverError::GenericsOnSelfType { span } => Diagnostic::simple_error( + ResolverError::GenericsOnSelfType { location } => Diagnostic::simple_error( "Cannot apply generics to Self type".into(), "Use an explicit type name or apply the generics at the start of the impl instead".into(), - *span, + *location, ), - ResolverError::GenericsOnAssociatedType { span } => Diagnostic::simple_error( + ResolverError::GenericsOnAssociatedType { location } => Diagnostic::simple_error( "Generic Associated Types (GATs) are currently unsupported in Noir".into(), "Cannot apply generics to an associated type".into(), - *span, + *location, ), ResolverError::ParserError(error) => error.as_ref().into(), - ResolverError::MutableReferenceToImmutableVariable { variable, span } => { - Diagnostic::simple_error(format!("Cannot mutably reference the immutable variable {variable}"), format!("{variable} is immutable"), *span) - }, - ResolverError::MutableReferenceToArrayElement { span } => { - Diagnostic::simple_error("Mutable references to array elements are currently unsupported".into(), "Try storing the element in a fresh variable first".into(), *span) - }, - ResolverError::InvalidClosureEnvironment { span, typ } => Diagnostic::simple_error( + ResolverError::InvalidClosureEnvironment { location, typ } => Diagnostic::simple_error( format!("{typ} is not a valid closure environment type"), - "Closure environment must be a tuple or unit type".to_string(), *span), - ResolverError::NestedSlices { span } => Diagnostic::simple_error( + "Closure environment must be a tuple or unit type".to_string(), *location), + ResolverError::NestedSlices { location } => Diagnostic::simple_error( "Nested slices, i.e. slices within an array or slice, are not supported".into(), "Try to use a constant sized array or BoundedVec instead".into(), - *span, + *location, ), - ResolverError::AbiAttributeOutsideContract { span } => { + ResolverError::AbiAttributeOutsideContract { location } => { Diagnostic::simple_error( "#[abi(tag)] attributes can only be used in contracts".to_string(), "misplaced #[abi(tag)] attribute".to_string(), - *span, + *location, ) }, ResolverError::LowLevelFunctionOutsideOfStdlib { ident } => Diagnostic::simple_error( "Definition of low-level function outside of standard library".into(), "Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library".into(), - ident.span(), + ident.location(), ), ResolverError::OracleMarkedAsConstrained { ident } => Diagnostic::simple_error( error.to_string(), "Oracle functions must have the `unconstrained` keyword applied".into(), - ident.span(), + ident.location(), ), - ResolverError::UnconstrainedOracleReturnToConstrained { span } => Diagnostic::simple_error( + ResolverError::UnconstrainedOracleReturnToConstrained { location } => Diagnostic::simple_error( error.to_string(), "This oracle call must be wrapped in a call to another unconstrained function before being returned to a constrained runtime".into(), - *span, + *location, ), - ResolverError::DependencyCycle { span, item, cycle } => { + ResolverError::DependencyCycle { location, item, cycle } => { Diagnostic::simple_error( "Dependency cycle found".into(), format!("'{item}' recursively depends on itself: {cycle}"), - *span, + *location, ) }, - ResolverError::JumpInConstrainedFn { is_break, span } => { + ResolverError::JumpInConstrainedFn { is_break, location } => { let item = if *is_break { "break" } else { "continue" }; Diagnostic::simple_error( format!("{item} is only allowed in unconstrained functions"), "Constrained code must always have a known number of loop iterations".into(), - *span, + *location, ) }, - ResolverError::LoopInConstrainedFn { span } => { + ResolverError::LoopInConstrainedFn { location } => { Diagnostic::simple_error( "`loop` is only allowed in unconstrained functions".into(), "Constrained code must always have a known number of loop iterations".into(), - *span, + *location, ) }, - ResolverError::LoopWithoutBreak { span } => { + ResolverError::LoopWithoutBreak { location } => { Diagnostic::simple_error( "`loop` must have at least one `break` in it".into(), "Infinite loops are disallowed".into(), - *span, + *location, ) }, - ResolverError::WhileInConstrainedFn { span } => { + ResolverError::WhileInConstrainedFn { location } => { Diagnostic::simple_error( "`while` is only allowed in unconstrained functions".into(), "Constrained code must always have a known number of loop iterations".into(), - *span, + *location, ) }, - ResolverError::JumpOutsideLoop { is_break, span } => { + ResolverError::JumpOutsideLoop { is_break, location } => { let item = if *is_break { "break" } else { "continue" }; Diagnostic::simple_error( format!("{item} is only allowed within loops"), "".into(), - *span, + *location, ) }, - ResolverError::MutableGlobal { span } => { + ResolverError::MutableGlobal { location } => { Diagnostic::simple_error( "Only `comptime` globals may be mutable".into(), String::new(), - *span, + *location, ) }, - ResolverError::UnspecifiedGlobalType { span, expected_type } => { - Diagnostic::simple_error( + ResolverError::UnspecifiedGlobalType { pattern_location, expr_location, expected_type } => { + let mut diagnostic = Diagnostic::simple_error( "Globals must have a specified type".to_string(), - format!("Inferred type is `{expected_type}`"), - *span, - ) + String::new(), + *pattern_location, + ); + diagnostic.add_secondary(format!("Inferred type is `{expected_type}`"), *expr_location); + diagnostic }, - ResolverError::UnevaluatedGlobalType { span } => { + ResolverError::UnevaluatedGlobalType { location } => { Diagnostic::simple_error( "Global failed to evaluate".to_string(), String::new(), - *span, + *location, ) } - ResolverError::NegativeGlobalType { span, global_value } => { + ResolverError::NegativeGlobalType { location, global_value } => { Diagnostic::simple_error( "Globals used in a type position must be non-negative".to_string(), format!("But found value `{global_value:?}`"), - *span, + *location, ) } - ResolverError::NonIntegralGlobalType { span, global_value } => { + ResolverError::NonIntegralGlobalType { location, global_value } => { Diagnostic::simple_error( "Globals used in a type position must be integers".to_string(), format!("But found value `{global_value:?}`"), - *span, + *location, ) } - ResolverError::GlobalLargerThanKind { span, global_value, kind } => { + ResolverError::GlobalLargerThanKind { location, global_value, kind } => { Diagnostic::simple_error( format!("Global value `{global_value}` is larger than its kind's maximum value"), format!("Global's kind inferred to be `{kind}`"), - *span, + *location, ) } - ResolverError::SelfReferentialType { span } => { + ResolverError::SelfReferentialType { location } => { Diagnostic::simple_error( "Self-referential types are not supported".into(), "".into(), - *span, + *location, ) }, ResolverError::NoPredicatesAttributeOnUnconstrained { ident } => { @@ -528,7 +615,7 @@ impl<'a> From<&'a ResolverError> for Diagnostic { let mut diag = Diagnostic::simple_error( format!("misplaced #[no_predicates] attribute on unconstrained function {name}. Only allowed on constrained functions"), "misplaced #[no_predicates] attribute".to_string(), - ident.0.span(), + ident.0.location(), ); diag.add_note("The `#[no_predicates]` attribute specifies to the compiler whether it should diverge from auto-inlining constrained functions".to_owned()); @@ -540,159 +627,191 @@ impl<'a> From<&'a ResolverError> for Diagnostic { let mut diag = Diagnostic::simple_error( format!("misplaced #[fold] attribute on unconstrained function {name}. Only allowed on constrained functions"), "misplaced #[fold] attribute".to_string(), - ident.0.span(), + ident.0.location(), ); diag.add_note("The `#[fold]` attribute specifies whether a constrained function should be treated as a separate circuit rather than inlined into the program entry point".to_owned()); diag } - ResolverError::NumericGenericUsedForType { name, span } => { + ResolverError::NumericGenericUsedForType { name, location } => { Diagnostic::simple_error( format!("expected type, found numeric generic parameter {name}"), String::from("not a type"), - *span, + *location, ) } ResolverError::ArrayLengthInterpreter { error } => Diagnostic::from(error), - ResolverError::UnquoteUsedOutsideQuote { span } => { + ResolverError::UnquoteUsedOutsideQuote { location } => { Diagnostic::simple_error( "The unquote operator '$' can only be used within a quote expression".into(), "".into(), - *span, - ) - }, - ResolverError::AsTraitPathNotYetImplemented { span } => { - Diagnostic::simple_error( - "\"as trait path\" not yet implemented".into(), - "".into(), - *span, + *location, ) }, - ResolverError::InvalidSyntaxInMacroCall { span } => { + ResolverError::InvalidSyntaxInMacroCall { location } => { Diagnostic::simple_error( "Invalid syntax in macro call".into(), "Macro calls must call a comptime function directly, they cannot use higher-order functions".into(), - *span, + *location, ) }, - ResolverError::MacroIsNotComptime { span } => { + ResolverError::MacroIsNotComptime { location } => { Diagnostic::simple_error( "This macro call is to a non-comptime function".into(), "Macro calls must be to comptime functions".into(), - *span, + *location, ) }, - ResolverError::NonFunctionInAnnotation { span } => { + ResolverError::NonFunctionInAnnotation { location } => { Diagnostic::simple_error( "Unknown annotation".into(), "The name of an annotation must refer to a comptime function".into(), - *span, + *location, ) }, - ResolverError::MacroResultInGenericsListNotAGeneric { span, typ } => { + ResolverError::MacroResultInGenericsListNotAGeneric { location, typ } => { Diagnostic::simple_error( format!("Type `{typ}` was inserted into a generics list from a macro, but it is not a generic"), format!("Type `{typ}` is not a generic"), - *span, + *location, ) } - ResolverError::NamedTypeArgs { span, item_kind } => { + ResolverError::NamedTypeArgs { location, item_kind } => { Diagnostic::simple_error( format!("Named type arguments aren't allowed on a {item_kind}"), "Named type arguments are only allowed for associated types on traits".to_string(), - *span, + *location, ) } - ResolverError::AssociatedConstantsMustBeNumeric { span } => { + ResolverError::AssociatedConstantsMustBeNumeric { location } => { Diagnostic::simple_error( "Associated constants may only be a field or integer type".to_string(), "Only numeric constants are allowed".to_string(), - *span, + *location, ) } - ResolverError::BinaryOpError { lhs, op, rhs, err, span } => { + ResolverError::BinaryOpError { lhs, op, rhs, err, location } => { Diagnostic::simple_error( format!("Computing `{lhs} {op} {rhs}` failed with error {err}"), String::new(), - *span, + *location, ) } - ResolverError::QuoteInRuntimeCode { span } => { + ResolverError::QuoteInRuntimeCode { location } => { Diagnostic::simple_error( "`quote` cannot be used in runtime code".to_string(), "Wrap this in a `comptime` block or function to use it".to_string(), - *span, + *location, ) }, - ResolverError::ComptimeTypeInRuntimeCode { typ, span } => { + ResolverError::ComptimeTypeInRuntimeCode { typ, location } => { Diagnostic::simple_error( format!("Comptime-only type `{typ}` cannot be used in runtime code"), "Comptime-only type used here".to_string(), - *span, + *location, ) }, - ResolverError::MutatingComptimeInNonComptimeContext { name, span } => { + ResolverError::MutatingComptimeInNonComptimeContext { name, location } => { Diagnostic::simple_error( format!("Comptime variable `{name}` cannot be mutated in a non-comptime context"), format!("`{name}` mutated here"), - *span, + *location, ) }, - ResolverError::InvalidInternedStatementInExpr { statement, span } => { + ResolverError::InvalidInternedStatementInExpr { statement, location } => { Diagnostic::simple_error( format!("Failed to parse `{statement}` as an expression"), "The statement was used from a macro here".to_string(), - *span, + *location, ) }, ResolverError::UnsupportedNumericGenericType(err) => err.into(), - ResolverError::TypeIsMorePrivateThenItem { typ, item, span } => { + ResolverError::TypeIsMorePrivateThenItem { typ, item, location } => { Diagnostic::simple_error( format!("Type `{typ}` is more private than item `{item}`"), String::new(), - *span, + *location, ) }, - ResolverError::UnableToParseAttribute { attribute, span } => { + ResolverError::UnableToParseAttribute { attribute, location } => { Diagnostic::simple_error( format!("Unable to parse attribute `{attribute}`"), "Attribute should be a function or function call".into(), - *span, + *location, ) }, - ResolverError::AttributeFunctionIsNotAPath { function, span } => { + ResolverError::AttributeFunctionIsNotAPath { function, location } => { Diagnostic::simple_error( format!("Attribute function `{function}` is not a path"), "An attribute's function should be a single identifier or a path".into(), - *span, + *location, ) }, - ResolverError::AttributeFunctionNotInScope { name, span } => { + ResolverError::AttributeFunctionNotInScope { name, location } => { Diagnostic::simple_error( format!("Attribute function `{name}` is not in scope"), String::new(), - *span, + *location, ) }, - ResolverError::TraitNotImplemented { impl_trait, missing_trait: the_trait, type_missing_trait: typ, span, missing_trait_location} => { + ResolverError::TraitNotImplemented { impl_trait, missing_trait: the_trait, type_missing_trait: typ, location, missing_trait_location} => { let mut diagnostic = Diagnostic::simple_error( format!("The trait bound `{typ}: {the_trait}` is not satisfied"), - format!("The trait `{the_trait}` is not implemented for `{typ}") - , *span); - diagnostic.add_secondary_with_file(format!("required by this bound in `{impl_trait}"), missing_trait_location.span, missing_trait_location.file); + format!("The trait `{the_trait}` is not implemented for `{typ}`") + , *location); + diagnostic.add_secondary(format!("required by this bound in `{impl_trait}`"), *missing_trait_location); diagnostic }, - ResolverError::LoopNotYetSupported { span } => { + ResolverError::LoopNotYetSupported { location } => { let msg = "`loop` statements are not yet implemented".to_string(); - Diagnostic::simple_error(msg, String::new(), *span) + Diagnostic::simple_error(msg, String::new(), *location) } - ResolverError::ExpectedTrait { found, span } => { + ResolverError::ExpectedTrait { found, location } => { Diagnostic::simple_error( format!("Expected a trait, found {found}"), String::new(), - *span) + *location) } + ResolverError::InvalidSyntaxInPattern { location } => { + Diagnostic::simple_error( + "Invalid syntax in match pattern".into(), + "Only literal, constructor, and variable patterns are allowed".into(), + *location) + }, + ResolverError::VariableAlreadyDefinedInPattern { existing, new_location } => { + let message = format!("Variable `{existing}` was already defined in the same match pattern"); + let secondary = format!("`{existing}` redefined here"); + let mut error = Diagnostic::simple_error(message, secondary, *new_location); + error.add_secondary(format!("`{existing}` was previously defined here"), existing.location()); + error + }, + ResolverError::NonIntegerGlobalUsedInPattern { location } => { + let message = "Only integer or boolean globals can be used in match patterns".to_string(); + let secondary = "This global is not an integer or boolean".to_string(); + Diagnostic::simple_error(message, secondary, *location) + }, + ResolverError::TypeUnsupportedInMatch { typ, location } => { + Diagnostic::simple_error( + format!("Cannot match on values of type `{typ}`"), + String::new(), + *location, + ) + }, + ResolverError::UnexpectedItemInPattern { item, location } => { + Diagnostic::simple_error( + format!("Expected a struct, enum, or literal pattern, but found a {item}"), + String::new(), + *location, + ) + }, + ResolverError::NoSuchMethodInTrait { trait_name, method_name, location } => { + Diagnostic::simple_error( + format!("Trait `{trait_name}` has no method named `{method_name}`"), + String::new(), + *location, + ) + }, } } } 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 11b694aa61be..16e25b804651 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 @@ -1,5 +1,5 @@ use iter_extended::vecmap; -use noirc_errors::{CustomDiagnostic, Span}; +use noirc_errors::{CustomDiagnostic, Location}; use thiserror::Error; use crate::graph::CrateId; @@ -44,12 +44,14 @@ pub enum PathResolutionError { #[error("{0} is private and not visible from the current module")] Private(Ident), #[error("There is no super module")] - NoSuper(Span), + NoSuper(Location), #[error("turbofish (`::<_>`) not allowed on {item}")] - TurbofishNotAllowedOnItem { item: String, span: Span }, + TurbofishNotAllowedOnItem { item: String, location: Location }, #[error("{ident} is a {kind}, not a module")] NotAModule { ident: Ident, kind: &'static str }, - #[error("trait `{trait_name}` which provides `{ident}` is implemented but not in scope, please import it")] + #[error( + "trait `{trait_name}` which provides `{ident}` is implemented but not in scope, please import it" + )] TraitMethodNotInScope { ident: Ident, trait_name: String }, #[error("Could not resolve '{ident}' in path")] UnresolvedWithPossibleTraitsToImport { ident: Ident, traits: Vec }, @@ -57,6 +59,23 @@ pub enum PathResolutionError { MultipleTraitsInScope { ident: Ident, traits: Vec }, } +impl PathResolutionError { + pub fn location(&self) -> Location { + match self { + PathResolutionError::NoSuper(location) + | PathResolutionError::TurbofishNotAllowedOnItem { location, .. } => *location, + PathResolutionError::Unresolved(ident) + | PathResolutionError::Private(ident) + | PathResolutionError::NotAModule { ident, .. } + | PathResolutionError::TraitMethodNotInScope { ident, .. } + | PathResolutionError::MultipleTraitsInScope { ident, .. } + | PathResolutionError::UnresolvedWithPossibleTraitsToImport { ident, .. } => { + ident.location() + } + } + } +} + #[derive(Debug)] pub struct ResolvedImport { // The symbol which we have resolved to @@ -75,43 +94,48 @@ impl<'a> From<&'a PathResolutionError> for CustomDiagnostic { fn from(error: &'a PathResolutionError) -> Self { match &error { PathResolutionError::Unresolved(ident) => { - CustomDiagnostic::simple_error(error.to_string(), String::new(), ident.span()) + CustomDiagnostic::simple_error(error.to_string(), String::new(), ident.location()) } // This will be upgraded to an error in future versions PathResolutionError::Private(ident) => CustomDiagnostic::simple_warning( error.to_string(), format!("{ident} is private"), - ident.span(), + ident.location(), ), - PathResolutionError::NoSuper(span) => { - CustomDiagnostic::simple_error(error.to_string(), String::new(), *span) + PathResolutionError::NoSuper(location) => { + CustomDiagnostic::simple_error(error.to_string(), String::new(), *location) } - PathResolutionError::TurbofishNotAllowedOnItem { item: _, span } => { - CustomDiagnostic::simple_error(error.to_string(), String::new(), *span) + PathResolutionError::TurbofishNotAllowedOnItem { item: _, location } => { + CustomDiagnostic::simple_error(error.to_string(), String::new(), *location) } PathResolutionError::NotAModule { ident, kind: _ } => { - CustomDiagnostic::simple_error(error.to_string(), String::new(), ident.span()) + CustomDiagnostic::simple_error(error.to_string(), String::new(), ident.location()) } PathResolutionError::TraitMethodNotInScope { ident, .. } => { - CustomDiagnostic::simple_warning(error.to_string(), String::new(), ident.span()) + CustomDiagnostic::simple_warning(error.to_string(), String::new(), ident.location()) } PathResolutionError::UnresolvedWithPossibleTraitsToImport { ident, traits } => { - let traits = vecmap(traits, |trait_name| format!("`{}`", trait_name)); + let mut traits = vecmap(traits, |trait_name| format!("`{}`", trait_name)); + traits.sort(); CustomDiagnostic::simple_error( error.to_string(), - format!("The following traits which provide `{ident}` are implemented but not in scope: {}", traits.join(", ")), - ident.span(), + format!( + "The following traits which provide `{ident}` are implemented but not in scope: {}", + traits.join(", ") + ), + ident.location(), ) } PathResolutionError::MultipleTraitsInScope { ident, traits } => { - let traits = vecmap(traits, |trait_name| format!("`{}`", trait_name)); + let mut traits = vecmap(traits, |trait_name| format!("`{}`", trait_name)); + traits.sort(); CustomDiagnostic::simple_error( error.to_string(), format!( "All these trait which provide `{ident}` are implemented and in scope: {}", traits.join(", ") ), - ident.span(), + ident.location(), ) } } @@ -162,7 +186,7 @@ struct PathResolutionTargetResolver<'def_maps, 'references_tracker> { references_tracker: Option>, } -impl<'def_maps, 'references_tracker> PathResolutionTargetResolver<'def_maps, 'references_tracker> { +impl PathResolutionTargetResolver<'_, '_> { fn resolve(&mut self, path: Path) -> Result<(Path, ModuleId), PathResolutionError> { match path.kind { PathKind::Crate => self.resolve_crate_path(path), @@ -214,8 +238,8 @@ impl<'def_maps, 'references_tracker> PathResolutionTargetResolver<'def_maps, 're .ok_or_else(|| PathResolutionError::Unresolved(crate_name.to_owned()))?; if let Some(references_tracker) = &mut self.references_tracker { - let span = crate_name.span(); - references_tracker.add_reference(ModuleDefId::ModuleId(*dep_module), span, false); + let location = crate_name.location(); + references_tracker.add_reference(ModuleDefId::ModuleId(*dep_module), location, false); } // Now the path can be solved starting from the second segment as a plain path @@ -227,9 +251,7 @@ impl<'def_maps, 'references_tracker> PathResolutionTargetResolver<'def_maps, 're fn resolve_super_path(&mut self, path: Path) -> Result<(Path, ModuleId), PathResolutionError> { let Some(parent_module_id) = get_module(self.def_maps, self.importing_module).parent else { - let span_start = path.span.start(); - let span = Span::from(span_start..span_start + 5); // 5 == "super".len() - return Err(PathResolutionError::NoSuper(span)); + return Err(PathResolutionError::NoSuper(path.kind_location)); }; let current_module = @@ -297,7 +319,7 @@ impl<'def_maps, 'usage_tracker, 'references_tracker> Some((typ, visibility, _)) => (typ, visibility), }; - self.add_reference(typ, last_segment.span, last_segment.ident.is_self_type_name()); + self.add_reference(typ, last_segment.location, last_segment.ident.is_self_type_name()); // In the type namespace, only Mod can be used in a path. current_module_id = match typ { @@ -339,7 +361,7 @@ impl<'def_maps, 'usage_tracker, 'references_tracker> let (module_def_id, visibility, _) = current_ns.values.or(current_ns.types).expect("Found empty namespace"); - self.add_reference(module_def_id, path.segments.last().unwrap().ident.span(), false); + self.add_reference(module_def_id, path.segments.last().unwrap().ident.location(), false); if !self.item_in_module_is_visible(current_module_id, visibility) { errors.push(PathResolutionError::Private(path.last_ident())); @@ -348,9 +370,14 @@ impl<'def_maps, 'usage_tracker, 'references_tracker> Ok(ResolvedImport { namespace: current_ns, errors }) } - fn add_reference(&mut self, reference_id: ModuleDefId, span: Span, is_self_type_name: bool) { + fn add_reference( + &mut self, + reference_id: ModuleDefId, + location: Location, + is_self_type_name: bool, + ) { if let Some(references_tracker) = &mut self.references_tracker { - references_tracker.add_reference(reference_id, span, is_self_type_name); + references_tracker.add_reference(reference_id, location, is_self_type_name); } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/visibility.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/visibility.rs index c592175ffcb6..84badde9d35a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/visibility.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/visibility.rs @@ -1,6 +1,6 @@ +use crate::Type; use crate::graph::CrateId; use crate::node_interner::{FuncId, NodeInterner, TraitId, TypeId}; -use crate::Type; use std::collections::BTreeMap; @@ -61,7 +61,7 @@ pub(crate) fn module_descendent_of_target( def_map.modules[current.0] .parent - .map_or(false, |parent| module_descendent_of_target(def_map, target, parent)) + .is_some_and(|parent| module_descendent_of_target(def_map, target, parent)) } /// Returns true if `target` is a struct and its parent is `current`. 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 d29e1aa43397..9198182312df 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 @@ -2,7 +2,7 @@ use std::rc::Rc; use acvm::FieldElement; use noirc_errors::CustomDiagnostic as Diagnostic; -use noirc_errors::Span; +use noirc_errors::Location; use thiserror::Error; use crate::ast::{ @@ -13,6 +13,7 @@ use crate::hir_def::expr::HirBinaryOp; use crate::hir_def::traits::TraitConstraint; use crate::hir_def::types::{BinaryTypeOperator, Kind, Type}; use crate::node_interner::NodeInterner; +use crate::signed_field::SignedField; #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum Source { @@ -31,190 +32,219 @@ pub enum Source { #[error("{0}")] BinOp(BinaryOpKind), #[error("Return")] - Return(FunctionReturnType, Span), + Return(FunctionReturnType, Location), } #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum TypeCheckError { #[error("Operator {op:?} cannot be used in a {place:?}")] - OpCannotBeUsed { op: HirBinaryOp, place: &'static str, span: Span }, + OpCannotBeUsed { op: HirBinaryOp, place: &'static str, location: Location }, #[error("Division by zero: {lhs} / {rhs}")] - DivisionByZero { lhs: FieldElement, rhs: FieldElement, span: Span }, + DivisionByZero { lhs: FieldElement, rhs: FieldElement, location: Location }, #[error("Modulo on Field elements: {lhs} % {rhs}")] - ModuloOnFields { lhs: FieldElement, rhs: FieldElement, span: Span }, - #[error("The value `{expr:?}` cannot fit into `{ty}` which has range `{range}`")] - OverflowingAssignment { expr: FieldElement, ty: Type, range: String, span: Span }, + ModuloOnFields { lhs: FieldElement, rhs: FieldElement, location: Location }, + #[error("The value `{expr}` cannot fit into `{ty}` which has range `{range}`")] + OverflowingAssignment { expr: SignedField, ty: Type, range: String, location: Location }, #[error( "The value `{value}` cannot fit into `{kind}` which has a maximum size of `{maximum_size}`" )] - OverflowingConstant { value: FieldElement, kind: Kind, maximum_size: FieldElement, span: Span }, + OverflowingConstant { + value: FieldElement, + kind: Kind, + maximum_size: FieldElement, + location: Location, + }, #[error("Evaluating `{op}` on `{lhs}`, `{rhs}` failed")] - FailingBinaryOp { op: BinaryTypeOperator, lhs: i128, rhs: i128, span: Span }, + FailingBinaryOp { op: BinaryTypeOperator, lhs: i128, rhs: i128, location: Location }, #[error("Type {typ:?} cannot be used in a {place:?}")] - TypeCannotBeUsed { typ: Type, place: &'static str, span: Span }, + TypeCannotBeUsed { typ: Type, place: &'static str, location: Location }, #[error("Expected type {expected_typ:?} is not the same as {expr_typ:?}")] - TypeMismatch { expected_typ: String, expr_typ: String, expr_span: Span }, + TypeMismatch { expected_typ: String, expr_typ: String, expr_location: Location }, #[error("Expected type {expected} is not the same as {actual}")] - TypeMismatchWithSource { expected: Type, actual: Type, span: Span, source: Source }, + TypeMismatchWithSource { expected: Type, actual: Type, location: Location, source: Source }, #[error("Expected type {expected_kind:?} is not the same as {expr_kind:?}")] - TypeKindMismatch { expected_kind: String, expr_kind: String, expr_span: Span }, + TypeKindMismatch { expected_kind: Kind, expr_kind: Kind, expr_location: Location }, #[error("Evaluating {to} resulted in {to_value}, but {from_value} was expected")] TypeCanonicalizationMismatch { to: Type, from: Type, to_value: FieldElement, from_value: FieldElement, - span: Span, + location: Location, }, #[error("Expected {expected:?} found {found:?}")] - ArityMisMatch { expected: usize, found: usize, span: Span }, + ArityMisMatch { expected: usize, found: usize, location: Location }, #[error("Return type in a function cannot be public")] - PublicReturnType { typ: Type, span: Span }, + PublicReturnType { typ: Type, location: Location }, #[error("Cannot cast type {from}, 'as' is only for primitive field or integer types")] - InvalidCast { from: Type, span: Span, reason: String }, + InvalidCast { from: Type, location: Location, reason: String }, #[error("Casting value of type {from} to a smaller type ({to})")] - DownsizingCast { from: Type, to: Type, span: Span, reason: String }, + DownsizingCast { from: Type, to: Type, location: Location, reason: String }, #[error("Expected a function, but found a(n) {found}")] - ExpectedFunction { found: Type, span: Span }, + ExpectedFunction { found: Type, location: Location }, #[error("Type {lhs_type} has no member named {field_name}")] - AccessUnknownMember { lhs_type: Type, field_name: String, span: Span }, + AccessUnknownMember { lhs_type: Type, field_name: String, location: Location }, #[error("Function expects {expected} parameters but {found} were given")] - ParameterCountMismatch { expected: usize, found: usize, span: Span }, + ParameterCountMismatch { expected: usize, found: usize, location: Location }, #[error("{} expects {} or {} parameters but {found} were given", kind, kind.required_arguments_count(), kind.required_arguments_count() + 1)] - AssertionParameterCountMismatch { kind: ConstrainKind, found: usize, span: Span }, + AssertionParameterCountMismatch { kind: ConstrainKind, found: usize, location: Location }, #[error("{item} expects {expected} generics but {found} were given")] - GenericCountMismatch { item: String, expected: usize, found: usize, span: Span }, + GenericCountMismatch { item: String, expected: usize, found: usize, location: Location }, #[error("{item} has incompatible `unconstrained`")] - UnconstrainedMismatch { item: String, expected: bool, span: Span }, + UnconstrainedMismatch { item: String, expected: bool, location: Location }, #[error("Only integer and Field types may be casted to")] - UnsupportedCast { span: Span }, + UnsupportedCast { location: Location }, #[error("Index {index} is out of bounds for this tuple {lhs_type} of length {length}")] - TupleIndexOutOfBounds { index: usize, lhs_type: Type, length: usize, span: Span }, + TupleIndexOutOfBounds { index: usize, lhs_type: Type, length: usize, location: Location }, #[error("Variable `{name}` must be mutable to be assigned to")] - VariableMustBeMutable { name: String, span: Span }, + VariableMustBeMutable { name: String, location: Location }, #[error("Cannot mutate immutable variable `{name}`")] - CannotMutateImmutableVariable { name: String, span: Span }, + CannotMutateImmutableVariable { name: String, location: Location }, + #[error("Variable {name} captured in lambda must be a mutable reference")] + MutableCaptureWithoutRef { name: String, location: Location }, + #[error("Mutable references to array indices are unsupported")] + MutableReferenceToArrayElement { location: Location }, #[error("No method named '{method_name}' found for type '{object_type}'")] - UnresolvedMethodCall { method_name: String, object_type: Type, span: Span }, + UnresolvedMethodCall { method_name: String, object_type: Type, location: Location }, #[error("Cannot invoke function field '{method_name}' on type '{object_type}' as a method")] - CannotInvokeStructFieldFunctionType { method_name: String, object_type: Type, span: Span }, + CannotInvokeStructFieldFunctionType { + method_name: String, + object_type: Type, + location: Location, + }, #[error("Integers must have the same signedness LHS is {sign_x:?}, RHS is {sign_y:?}")] - IntegerSignedness { sign_x: Signedness, sign_y: Signedness, span: Span }, + IntegerSignedness { sign_x: Signedness, sign_y: Signedness, location: Location }, #[error("Integers must have the same bit width LHS is {bit_width_x}, RHS is {bit_width_y}")] - IntegerBitWidth { bit_width_x: IntegerBitSize, bit_width_y: IntegerBitSize, span: Span }, + IntegerBitWidth { bit_width_x: IntegerBitSize, bit_width_y: IntegerBitSize, location: Location }, #[error("{kind} cannot be used in an infix operation")] - InvalidInfixOp { kind: &'static str, span: Span }, + InvalidInfixOp { kind: &'static str, location: Location }, #[error("{kind} cannot be used in a unary operation")] - InvalidUnaryOp { kind: String, span: Span }, - #[error("Bitwise operations are invalid on Field types. Try casting the operands to a sized integer type first.")] - FieldBitwiseOp { span: Span }, + InvalidUnaryOp { kind: String, location: Location }, + #[error( + "Bitwise operations are invalid on Field types. Try casting the operands to a sized integer type first." + )] + FieldBitwiseOp { location: Location }, #[error("Integer cannot be used with type {typ}")] - IntegerTypeMismatch { typ: Type, span: Span }, - #[error("Cannot use an integer and a Field in a binary operation, try converting the Field into an integer first")] - IntegerAndFieldBinaryOperation { span: Span }, + IntegerTypeMismatch { typ: Type, location: Location }, + #[error( + "Cannot use an integer and a Field in a binary operation, try converting the Field into an integer first" + )] + IntegerAndFieldBinaryOperation { location: Location }, #[error("Cannot do modulo on Fields, try casting to an integer first")] - FieldModulo { span: Span }, + FieldModulo { location: Location }, #[error("Cannot do not (`!`) on Fields, try casting to an integer first")] - FieldNot { span: Span }, + FieldNot { location: Location }, #[error("Fields cannot be compared, try casting to an integer first")] - FieldComparison { span: Span }, - #[error("The bit count in a bit-shift operation must fit in a u8, try casting the right hand side into a u8 first")] - InvalidShiftSize { span: Span }, - #[error("The number of bits to use for this bitwise operation is ambiguous. Either the operand's type or return type should be specified")] - AmbiguousBitWidth { span: Span }, + FieldComparison { location: Location }, + #[error( + "The bit count in a bit-shift operation must fit in a u8, try casting the right hand side into a u8 first" + )] + InvalidShiftSize { location: Location }, + #[error( + "The number of bits to use for this bitwise operation is ambiguous. Either the operand's type or return type should be specified" + )] + AmbiguousBitWidth { location: Location }, #[error("Error with additional context")] Context { err: Box, ctx: &'static str }, #[error("Array is not homogeneous")] NonHomogeneousArray { - first_span: Span, + first_location: Location, first_type: String, first_index: usize, - second_span: Span, + second_location: Location, second_type: String, second_index: usize, }, #[error("Object type is unknown in method call")] - TypeAnnotationsNeededForMethodCall { span: Span }, + TypeAnnotationsNeededForMethodCall { location: Location }, #[error("Object type is unknown in field access")] - TypeAnnotationsNeededForFieldAccess { span: Span }, + TypeAnnotationsNeededForFieldAccess { location: Location }, #[error("Multiple trait impls may apply to this object type")] - MultipleMatchingImpls { object_type: Type, candidates: Vec, span: Span }, + MultipleMatchingImpls { object_type: Type, candidates: Vec, location: Location }, #[error("use of deprecated function {name}")] - CallDeprecated { name: String, note: Option, span: Span }, + CallDeprecated { name: String, note: Option, location: Location }, #[error("{0}")] ResolverError(ResolverError), #[error("Unused expression result of type {expr_type}")] - UnusedResultError { expr_type: Type, expr_span: Span }, + UnusedResultError { expr_type: Type, expr_location: Location }, #[error("Expected type {expected_typ:?} is not the same as {actual_typ:?}")] TraitMethodParameterTypeMismatch { method_name: String, expected_typ: String, actual_typ: String, - parameter_span: Span, + parameter_location: Location, parameter_index: usize, }, #[error("No matching impl found")] NoMatchingImplFound(NoMatchingImplFoundError), - #[error("Constraint for `{typ}: {trait_name}` is not needed, another matching impl is already in scope")] - UnneededTraitConstraint { trait_name: String, typ: Type, span: Span }, + #[error( + "Constraint for `{typ}: {trait_name}` is not needed, another matching impl is already in scope" + )] + UnneededTraitConstraint { trait_name: String, typ: Type, location: Location }, #[error( "Expected {expected_count} generic(s) from this function, but {actual_count} were provided" )] - IncorrectTurbofishGenericCount { expected_count: usize, actual_count: usize, span: Span }, + IncorrectTurbofishGenericCount { + expected_count: usize, + actual_count: usize, + location: Location, + }, #[error( "Cannot pass a mutable reference from a constrained runtime to an unconstrained runtime" )] - ConstrainedReferenceToUnconstrained { span: Span }, + ConstrainedReferenceToUnconstrained { location: Location }, #[error( "Cannot pass a mutable reference from a unconstrained runtime to an constrained runtime" )] - UnconstrainedReferenceToConstrained { span: Span }, + UnconstrainedReferenceToConstrained { location: Location }, #[error("Slices cannot be returned from an unconstrained runtime to a constrained runtime")] - UnconstrainedSliceReturnToConstrained { span: Span }, - #[error("Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block")] - Unsafe { span: Span }, + UnconstrainedSliceReturnToConstrained { location: Location }, + #[error( + "Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block" + )] + Unsafe { location: Location }, #[error("Converting an unconstrained fn to a non-unconstrained fn is unsafe")] - UnsafeFn { span: Span }, + UnsafeFn { location: Location }, #[error("Expected a constant, but found `{typ}`")] - NonConstantEvaluated { typ: Type, span: Span }, + NonConstantEvaluated { typ: Type, location: Location }, #[error("Slices must have constant length")] - NonConstantSliceLength { span: Span }, + NonConstantSliceLength { location: Location }, #[error("Only sized types may be used in the entry point to a program")] - InvalidTypeForEntryPoint { span: Span }, + InvalidTypeForEntryPoint { location: Location }, #[error("Mismatched number of parameters in trait implementation")] MismatchTraitImplNumParameters { actual_num_parameters: usize, expected_num_parameters: usize, trait_name: String, method_name: String, - span: Span, + location: Location, }, #[error("Strings do not support indexed assignment")] - StringIndexAssign { span: Span }, + StringIndexAssign { location: Location }, #[error("Macro calls may only return `Quoted` values")] - MacroReturningNonExpr { typ: Type, span: Span }, + MacroReturningNonExpr { typ: Type, location: Location }, #[error("`{name}` has already been specified")] - DuplicateNamedTypeArg { name: Ident, prev_span: Span }, + DuplicateNamedTypeArg { name: Ident, prev_location: Location }, #[error("`{item}` has no associated type named `{name}`")] NoSuchNamedTypeArg { name: Ident, item: String }, #[error("`{item}` is missing the associated type `{name}`")] - MissingNamedTypeArg { name: Rc, item: String, span: Span }, + MissingNamedTypeArg { name: Rc, item: String, location: Location }, #[error("Internal compiler error: type unspecified for value")] - UnspecifiedType { span: Span }, + UnspecifiedType { location: Location }, #[error("Binding `{typ}` here to the `_` inside would create a cyclic type")] - CyclicType { typ: Type, span: Span }, + CyclicType { typ: Type, location: Location }, #[error("Type annotations required before indexing this array or slice")] - TypeAnnotationsNeededForIndex { span: Span }, + TypeAnnotationsNeededForIndex { location: Location }, #[error("Unnecessary `unsafe` block")] - UnnecessaryUnsafeBlock { span: Span }, + UnnecessaryUnsafeBlock { location: Location }, #[error("Unnecessary `unsafe` block")] - NestedUnsafeBlock { span: Span }, + NestedUnsafeBlock { location: Location }, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct NoMatchingImplFoundError { pub(crate) constraints: Vec<(Type, String)>, - pub span: Span, + pub location: Location, } impl TypeCheckError { @@ -225,69 +255,179 @@ impl TypeCheckError { pub(crate) fn is_non_constant_evaluated(&self) -> bool { matches!(self, TypeCheckError::NonConstantEvaluated { .. }) } + + pub fn location(&self) -> Location { + match self { + TypeCheckError::OpCannotBeUsed { location, .. } + | TypeCheckError::DivisionByZero { location, .. } + | TypeCheckError::ModuloOnFields { location, .. } + | TypeCheckError::OverflowingAssignment { location, .. } + | TypeCheckError::OverflowingConstant { location, .. } + | TypeCheckError::FailingBinaryOp { location, .. } + | TypeCheckError::TypeCannotBeUsed { location, .. } + | TypeCheckError::TypeMismatch { expr_location: location, .. } + | TypeCheckError::TypeMismatchWithSource { location, .. } + | TypeCheckError::TypeKindMismatch { expr_location: location, .. } + | TypeCheckError::TypeCanonicalizationMismatch { location, .. } + | TypeCheckError::ArityMisMatch { location, .. } + | TypeCheckError::PublicReturnType { location, .. } + | TypeCheckError::InvalidCast { location, .. } + | TypeCheckError::DownsizingCast { location, .. } + | TypeCheckError::ExpectedFunction { location, .. } + | TypeCheckError::AccessUnknownMember { location, .. } + | TypeCheckError::ParameterCountMismatch { location, .. } + | TypeCheckError::AssertionParameterCountMismatch { location, .. } + | TypeCheckError::GenericCountMismatch { location, .. } + | TypeCheckError::UnconstrainedMismatch { location, .. } + | TypeCheckError::UnsupportedCast { location } + | TypeCheckError::TupleIndexOutOfBounds { location, .. } + | TypeCheckError::VariableMustBeMutable { location, .. } + | TypeCheckError::CannotMutateImmutableVariable { location, .. } + | TypeCheckError::MutableCaptureWithoutRef { location, .. } + | TypeCheckError::MutableReferenceToArrayElement { location } + | TypeCheckError::UnresolvedMethodCall { location, .. } + | TypeCheckError::CannotInvokeStructFieldFunctionType { location, .. } + | TypeCheckError::IntegerSignedness { location, .. } + | TypeCheckError::IntegerBitWidth { location, .. } + | TypeCheckError::InvalidInfixOp { location, .. } + | TypeCheckError::InvalidUnaryOp { location, .. } + | TypeCheckError::FieldBitwiseOp { location } + | TypeCheckError::IntegerTypeMismatch { location, .. } + | TypeCheckError::IntegerAndFieldBinaryOperation { location } + | TypeCheckError::FieldModulo { location } + | TypeCheckError::FieldNot { location } + | TypeCheckError::FieldComparison { location } + | TypeCheckError::InvalidShiftSize { location } + | TypeCheckError::AmbiguousBitWidth { location } + | TypeCheckError::NonHomogeneousArray { first_location: location, .. } + | TypeCheckError::TypeAnnotationsNeededForMethodCall { location } + | TypeCheckError::TypeAnnotationsNeededForFieldAccess { location } + | TypeCheckError::MultipleMatchingImpls { location, .. } + | TypeCheckError::CallDeprecated { location, .. } + | TypeCheckError::UnusedResultError { expr_location: location, .. } + | TypeCheckError::TraitMethodParameterTypeMismatch { + parameter_location: location, + .. + } + | TypeCheckError::UnneededTraitConstraint { location, .. } + | TypeCheckError::IncorrectTurbofishGenericCount { location, .. } + | TypeCheckError::ConstrainedReferenceToUnconstrained { location } + | TypeCheckError::UnconstrainedReferenceToConstrained { location } + | TypeCheckError::UnconstrainedSliceReturnToConstrained { location } + | TypeCheckError::Unsafe { location } + | TypeCheckError::UnsafeFn { location } + | TypeCheckError::NonConstantEvaluated { location, .. } + | TypeCheckError::NonConstantSliceLength { location } + | TypeCheckError::InvalidTypeForEntryPoint { location } + | TypeCheckError::MismatchTraitImplNumParameters { location, .. } + | TypeCheckError::StringIndexAssign { location } + | TypeCheckError::MacroReturningNonExpr { location, .. } + | TypeCheckError::MissingNamedTypeArg { location, .. } + | TypeCheckError::UnspecifiedType { location } + | TypeCheckError::CyclicType { location, .. } + | TypeCheckError::TypeAnnotationsNeededForIndex { location } + | TypeCheckError::UnnecessaryUnsafeBlock { location } + | TypeCheckError::NestedUnsafeBlock { location } => *location, + + TypeCheckError::DuplicateNamedTypeArg { name: ident, .. } + | TypeCheckError::NoSuchNamedTypeArg { name: ident, .. } => ident.location(), + + TypeCheckError::NoMatchingImplFound(no_matching_impl_found_error) => { + no_matching_impl_found_error.location + } + TypeCheckError::Context { err, .. } => err.location(), + TypeCheckError::ResolverError(resolver_error) => resolver_error.location(), + } + } } impl<'a> From<&'a TypeCheckError> for Diagnostic { fn from(error: &'a TypeCheckError) -> Diagnostic { match error { - TypeCheckError::TypeCannotBeUsed { typ, place, span } => Diagnostic::simple_error( + TypeCheckError::TypeCannotBeUsed { typ, place, location } => Diagnostic::simple_error( format!("The type {} cannot be used in a {}", &typ, place), String::new(), - *span, + *location, ), TypeCheckError::Context { err, ctx } => { let mut diag = Diagnostic::from(err.as_ref()); diag.add_note(ctx.to_string()); diag } - TypeCheckError::OpCannotBeUsed { op, place, span } => Diagnostic::simple_error( + TypeCheckError::OpCannotBeUsed { op, place, location } => Diagnostic::simple_error( format!("The operator {op:?} cannot be used in a {place}"), String::new(), - *span, + *location, ), - TypeCheckError::DivisionByZero { lhs, rhs, span } => Diagnostic::simple_error( + TypeCheckError::DivisionByZero { lhs, rhs, location } => Diagnostic::simple_error( format!("Division by zero: {lhs} / {rhs}"), String::new(), - *span, + *location, ), - TypeCheckError::ModuloOnFields { lhs, rhs, span } => Diagnostic::simple_error( + TypeCheckError::ModuloOnFields { lhs, rhs, location } => Diagnostic::simple_error( format!("Modulo on Field elements: {lhs} % {rhs}"), String::new(), - *span, + *location, ), - TypeCheckError::TypeMismatch { expected_typ, expr_typ, expr_span } => { + TypeCheckError::TypeMismatch { expected_typ, expr_typ, expr_location } => { Diagnostic::simple_error( format!("Expected type {expected_typ}, found type {expr_typ}"), String::new(), - *expr_span, + *expr_location, ) } - TypeCheckError::TypeKindMismatch { expected_kind, expr_kind, expr_span } => { - Diagnostic::simple_error( - format!("Expected kind {expected_kind}, found kind {expr_kind}"), - String::new(), - *expr_span, - ) + TypeCheckError::TypeKindMismatch { expected_kind, expr_kind, expr_location } => { + // Try to improve the error message for some kind combinations + match (expected_kind, expr_kind) { + (Kind::Normal, Kind::Numeric(_)) => { + Diagnostic::simple_error( + "Expected type, found numeric generic".into(), + "not a type".into(), + *expr_location, + ) + } + (Kind::Numeric(typ), Kind::Normal) => { + Diagnostic::simple_error( + "Type provided when a numeric generic was expected".into(), + format!("the numeric generic is not of type `{typ}`"), + *expr_location, + ) + } + (Kind::Numeric(expected_type), Kind::Numeric(found_type)) => { + Diagnostic::simple_error( + format!("The numeric generic is not of type `{expected_type}`"), + format!("expected `{expected_type}`, found `{found_type}`"), + *expr_location, + ) + } + _ => { + Diagnostic::simple_error( + format!("Expected kind {expected_kind}, found kind {expr_kind}"), + String::new(), + *expr_location, + ) + } + } } - TypeCheckError::TypeCanonicalizationMismatch { to, from, to_value, from_value, span } => { + TypeCheckError::TypeCanonicalizationMismatch { to, from, to_value, from_value, location } => { Diagnostic::simple_error( format!("Evaluating {to} resulted in {to_value}, but {from_value} was expected"), format!("from evaluating {from} without simplifications"), - *span, + *location, ) } - TypeCheckError::TraitMethodParameterTypeMismatch { method_name, expected_typ, actual_typ, parameter_index, parameter_span } => { + TypeCheckError::TraitMethodParameterTypeMismatch { method_name, expected_typ, actual_typ, parameter_index, parameter_location } => { Diagnostic::simple_error( format!("Parameter #{parameter_index} of method `{method_name}` must be of type {expected_typ}, not {actual_typ}"), String::new(), - *parameter_span, + *parameter_location, ) } TypeCheckError::NonHomogeneousArray { - first_span, + first_location, first_type, first_index, - second_span, + second_location, second_type, second_index, } => { @@ -296,114 +436,122 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { "Non homogeneous array, different element types found at indices ({first_index},{second_index})" ), format!("Found type {first_type}"), - *first_span, + *first_location, ); - diag.add_secondary(format!("but then found type {second_type}"), *second_span); + diag.add_secondary(format!("but then found type {second_type}"), *second_location); diag } - TypeCheckError::ArityMisMatch { expected, found, span } => { + TypeCheckError::ArityMisMatch { expected, found, location } => { let plural = if *expected == 1 { "" } else { "s" }; let msg = format!("Expected {expected} argument{plural}, but found {found}"); - Diagnostic::simple_error(msg, String::new(), *span) + Diagnostic::simple_error(msg, String::new(), *location) } - TypeCheckError::ParameterCountMismatch { expected, found, span } => { + TypeCheckError::ParameterCountMismatch { expected, found, location } => { let empty_or_s = if *expected == 1 { "" } else { "s" }; let was_or_were = if *found == 1 { "was" } else { "were" }; let msg = format!("Function expects {expected} parameter{empty_or_s} but {found} {was_or_were} given"); - Diagnostic::simple_error(msg, String::new(), *span) + Diagnostic::simple_error(msg, String::new(), *location) } - TypeCheckError::AssertionParameterCountMismatch { kind, found, span } => { + TypeCheckError::AssertionParameterCountMismatch { kind, found, location } => { let was_or_were = if *found == 1 { "was" } else { "were" }; let min = kind.required_arguments_count(); let max = min + 1; let msg = format!("{kind} expects {min} or {max} parameters but {found} {was_or_were} given"); - Diagnostic::simple_error(msg, String::new(), *span) + Diagnostic::simple_error(msg, String::new(), *location) } - TypeCheckError::GenericCountMismatch { item, expected, found, span } => { + TypeCheckError::GenericCountMismatch { item, expected, found, location } => { let empty_or_s = if *expected == 1 { "" } else { "s" }; let was_or_were = if *found == 1 { "was" } else { "were" }; let msg = format!("{item} expects {expected} generic{empty_or_s} but {found} {was_or_were} given"); - Diagnostic::simple_error(msg, String::new(), *span) + Diagnostic::simple_error(msg, String::new(), *location) } - TypeCheckError::UnconstrainedMismatch { item, expected, span } => { + TypeCheckError::UnconstrainedMismatch { item, expected, location } => { let msg = if *expected { format!("{item} is expected to be unconstrained") } else { format!("{item} is not expected to be unconstrained") }; - Diagnostic::simple_error(msg, String::new(), *span) + Diagnostic::simple_error(msg, String::new(), *location) } - TypeCheckError::InvalidCast { span, reason, .. } => { - Diagnostic::simple_error(error.to_string(), reason.clone(), *span) + TypeCheckError::InvalidCast { location, reason, .. } => { + Diagnostic::simple_error(error.to_string(), reason.clone(), *location) } - TypeCheckError::DownsizingCast { span, reason, .. } => { - Diagnostic::simple_warning(error.to_string(), reason.clone(), *span) + TypeCheckError::DownsizingCast { location, reason, .. } => { + Diagnostic::simple_warning(error.to_string(), reason.clone(), *location) } - TypeCheckError::ExpectedFunction { span, .. } - | TypeCheckError::AccessUnknownMember { span, .. } - | TypeCheckError::UnsupportedCast { span } - | TypeCheckError::TupleIndexOutOfBounds { span, .. } - | TypeCheckError::VariableMustBeMutable { span, .. } - | TypeCheckError::CannotMutateImmutableVariable { span, .. } - | TypeCheckError::UnresolvedMethodCall { span, .. } - | TypeCheckError::IntegerSignedness { span, .. } - | TypeCheckError::IntegerBitWidth { span, .. } - | TypeCheckError::InvalidInfixOp { span, .. } - | TypeCheckError::InvalidUnaryOp { span, .. } - | TypeCheckError::FieldBitwiseOp { span, .. } - | TypeCheckError::IntegerTypeMismatch { span, .. } - | TypeCheckError::FieldComparison { span, .. } - | TypeCheckError::AmbiguousBitWidth { span, .. } - | TypeCheckError::IntegerAndFieldBinaryOperation { span } - | TypeCheckError::OverflowingAssignment { span, .. } - | TypeCheckError::OverflowingConstant { span, .. } - | TypeCheckError::FailingBinaryOp { span, .. } - | TypeCheckError::FieldModulo { span } - | TypeCheckError::FieldNot { span } - | TypeCheckError::ConstrainedReferenceToUnconstrained { span } - | TypeCheckError::UnconstrainedReferenceToConstrained { span } - | TypeCheckError::UnconstrainedSliceReturnToConstrained { span } - | TypeCheckError::NonConstantEvaluated { span, .. } - | TypeCheckError::NonConstantSliceLength { span } - | TypeCheckError::StringIndexAssign { span } - | TypeCheckError::InvalidShiftSize { span } => { - Diagnostic::simple_error(error.to_string(), String::new(), *span) + TypeCheckError::ExpectedFunction { location, .. } + | TypeCheckError::AccessUnknownMember { location, .. } + | TypeCheckError::UnsupportedCast { location } + | TypeCheckError::TupleIndexOutOfBounds { location, .. } + | TypeCheckError::VariableMustBeMutable { location, .. } + | TypeCheckError::CannotMutateImmutableVariable { location, .. } + | TypeCheckError::UnresolvedMethodCall { location, .. } + | TypeCheckError::IntegerSignedness { location, .. } + | TypeCheckError::IntegerBitWidth { location, .. } + | TypeCheckError::InvalidInfixOp { location, .. } + | TypeCheckError::InvalidUnaryOp { location, .. } + | TypeCheckError::FieldBitwiseOp { location, .. } + | TypeCheckError::IntegerTypeMismatch { location, .. } + | TypeCheckError::FieldComparison { location, .. } + | TypeCheckError::AmbiguousBitWidth { location, .. } + | TypeCheckError::IntegerAndFieldBinaryOperation { location } + | TypeCheckError::OverflowingAssignment { location, .. } + | TypeCheckError::OverflowingConstant { location, .. } + | TypeCheckError::FailingBinaryOp { location, .. } + | TypeCheckError::FieldModulo { location } + | TypeCheckError::FieldNot { location } + | TypeCheckError::ConstrainedReferenceToUnconstrained { location } + | TypeCheckError::UnconstrainedReferenceToConstrained { location } + | TypeCheckError::UnconstrainedSliceReturnToConstrained { location } + | TypeCheckError::NonConstantEvaluated { location, .. } + | TypeCheckError::NonConstantSliceLength { location } + | TypeCheckError::StringIndexAssign { location } + | TypeCheckError::InvalidShiftSize { location } => { + Diagnostic::simple_error(error.to_string(), String::new(), *location) } - TypeCheckError::PublicReturnType { typ, span } => Diagnostic::simple_error( + TypeCheckError::MutableCaptureWithoutRef { name, location } => Diagnostic::simple_error( + format!("Mutable variable {name} captured in lambda must be a mutable reference"), + "Use '&mut' instead of 'mut' to capture a mutable variable.".to_string(), + *location, + ), + TypeCheckError::MutableReferenceToArrayElement { location } => { + Diagnostic::simple_error("Mutable references to array elements are currently unsupported".into(), "Try storing the element in a fresh variable first".into(), *location) + }, + TypeCheckError::PublicReturnType { typ, location } => Diagnostic::simple_error( "Functions cannot declare a public return type".to_string(), format!("return type is {typ}"), - *span, + *location, ), - TypeCheckError::TypeAnnotationsNeededForMethodCall { span } => { + TypeCheckError::TypeAnnotationsNeededForMethodCall { location } => { let mut error = Diagnostic::simple_error( "Object type is unknown in method call".to_string(), "Type must be known by this point to know which method to call".to_string(), - *span, + *location, ); error.add_note("Try adding a type annotation for the object type before this method call".to_string()); error }, - TypeCheckError::TypeAnnotationsNeededForFieldAccess { span } => { + TypeCheckError::TypeAnnotationsNeededForFieldAccess { location } => { let mut error = Diagnostic::simple_error( "Object type is unknown in field access".to_string(), "Type must be known by this point".to_string(), - *span, + *location, ); error.add_note("Try adding a type annotation for the object type before this expression".to_string()); error }, - TypeCheckError::MultipleMatchingImpls { object_type, candidates, span } => { + TypeCheckError::MultipleMatchingImpls { object_type, candidates, location } => { let message = format!("Multiple trait impls match the object type `{object_type}`"); let secondary = "Ambiguous impl".to_string(); - let mut error = Diagnostic::simple_error(message, secondary, *span); + let mut error = Diagnostic::simple_error(message, secondary, *location); for (i, candidate) in candidates.iter().enumerate() { error.add_note(format!("Candidate {}: `{candidate}`", i + 1)); } error }, TypeCheckError::ResolverError(error) => error.into(), - TypeCheckError::TypeMismatchWithSource { expected, actual, span, source } => { + TypeCheckError::TypeMismatchWithSource { expected, actual, location, source } => { let message = match source { Source::Binary => format!("Types in a binary operation should match, but found {expected} and {actual}"), Source::Assignment => { @@ -414,125 +562,125 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { Source::StringLen => format!("Can only compare strings of the same length. Here LHS is of length {expected}, and RHS is {actual}"), Source::Comparison => format!("Unsupported types for comparison: {expected} and {actual}"), Source::BinOp(kind) => format!("Unsupported types for operator `{kind}`: {expected} and {actual}"), - Source::Return(ret_ty, expr_span) => { - let ret_ty_span = match ret_ty.clone() { - FunctionReturnType::Default(span) => span, - FunctionReturnType::Ty(ty) => ty.span, + Source::Return(ret_ty, expr_location) => { + let ret_ty_location = match ret_ty.clone() { + FunctionReturnType::Default(location) => location, + FunctionReturnType::Ty(ty) => ty.location, }; - let mut diagnostic = Diagnostic::simple_error(format!("expected type {expected}, found type {actual}"), format!("expected {expected} because of return type"), ret_ty_span); + let mut diagnostic = Diagnostic::simple_error(format!("expected type {expected}, found type {actual}"), format!("expected {expected} because of return type"), ret_ty_location); if let FunctionReturnType::Default(_) = ret_ty { diagnostic.add_note(format!("help: try adding a return type: `-> {actual}`")); } - diagnostic.add_secondary(format!("{actual} returned here"), *expr_span); + diagnostic.add_secondary(format!("{actual} returned here"), *expr_location); return diagnostic }, }; - Diagnostic::simple_error(message, String::new(), *span) + Diagnostic::simple_error(message, String::new(), *location) } - TypeCheckError::CallDeprecated { span, ref note, .. } => { + TypeCheckError::CallDeprecated { location, note, .. } => { let primary_message = error.to_string(); let secondary_message = note.clone().unwrap_or_default(); - let mut diagnostic = Diagnostic::simple_warning(primary_message, secondary_message, *span); + let mut diagnostic = Diagnostic::simple_warning(primary_message, secondary_message, *location); diagnostic.deprecated = true; diagnostic } - TypeCheckError::UnusedResultError { expr_type, expr_span } => { + TypeCheckError::UnusedResultError { expr_type, expr_location } => { let msg = format!("Unused expression result of type {expr_type}"); - Diagnostic::simple_warning(msg, String::new(), *expr_span) + Diagnostic::simple_warning(msg, String::new(), *expr_location) } TypeCheckError::NoMatchingImplFound(error) => error.into(), - TypeCheckError::UnneededTraitConstraint { trait_name, typ, span } => { + TypeCheckError::UnneededTraitConstraint { trait_name, typ, location } => { let msg = format!("Constraint for `{typ}: {trait_name}` is not needed, another matching impl is already in scope"); - Diagnostic::simple_warning(msg, "Unnecessary trait constraint in where clause".into(), *span) + Diagnostic::simple_warning(msg, "Unnecessary trait constraint in where clause".into(), *location) } - TypeCheckError::InvalidTypeForEntryPoint { span } => Diagnostic::simple_error( + TypeCheckError::InvalidTypeForEntryPoint { location } => Diagnostic::simple_error( "Only sized types may be used in the entry point to a program".to_string(), - "Slices, references, or any type containing them may not be used in main, contract functions, or foldable functions".to_string(), *span), + "Slices, references, or any type containing them may not be used in main, contract functions, or foldable functions".to_string(), *location), TypeCheckError::MismatchTraitImplNumParameters { expected_num_parameters, actual_num_parameters, trait_name, method_name, - span, + location, } => { let plural = if *expected_num_parameters == 1 { "" } else { "s" }; let primary_message = format!( "`{trait_name}::{method_name}` expects {expected_num_parameters} parameter{plural}, but this method has {actual_num_parameters}"); - Diagnostic::simple_error(primary_message, "".to_string(), *span) + Diagnostic::simple_error(primary_message, "".to_string(), *location) } - TypeCheckError::IncorrectTurbofishGenericCount { expected_count, actual_count, span } => { + TypeCheckError::IncorrectTurbofishGenericCount { expected_count, actual_count, location } => { let expected_plural = if *expected_count == 1 { "" } else { "s" }; let actual_plural = if *actual_count == 1 { "was" } else { "were" }; let msg = format!("Expected {expected_count} generic{expected_plural} from this function, but {actual_count} {actual_plural} provided"); - Diagnostic::simple_error(msg, "".into(), *span) + Diagnostic::simple_error(msg, "".into(), *location) }, - TypeCheckError::MacroReturningNonExpr { typ, span } => { + TypeCheckError::MacroReturningNonExpr { typ, location } => { let mut error = Diagnostic::simple_error( format!("Expected macro call to return a `Quoted` but found a(n) `{typ}`"), "Macro calls must return quoted values, otherwise there is no code to insert.".into(), - *span, + *location, ); - error.add_secondary("Hint: remove the `!` from the end of the function name.".to_string(), *span); + error.add_secondary("Hint: remove the `!` from the end of the function name.".to_string(), *location); error }, - TypeCheckError::DuplicateNamedTypeArg { name, prev_span } => { + TypeCheckError::DuplicateNamedTypeArg { name, prev_location } => { let msg = format!("`{name}` has already been specified"); - let mut error = Diagnostic::simple_error(msg.to_string(), "".to_string(), name.span()); - error.add_secondary(format!("`{name}` previously specified here"), *prev_span); + let mut error = Diagnostic::simple_error(msg.to_string(), "".to_string(), name.location()); + error.add_secondary(format!("`{name}` previously specified here"), *prev_location); error }, TypeCheckError::NoSuchNamedTypeArg { name, item } => { let msg = format!("`{item}` has no associated type named `{name}`"); - Diagnostic::simple_error(msg.to_string(), "".to_string(), name.span()) + Diagnostic::simple_error(msg.to_string(), "".to_string(), name.location()) }, - TypeCheckError::MissingNamedTypeArg { name, item, span } => { + TypeCheckError::MissingNamedTypeArg { name, item, location } => { let msg = format!("`{item}` is missing the associated type `{name}`"); - Diagnostic::simple_error(msg.to_string(), "".to_string(), *span) + Diagnostic::simple_error(msg.to_string(), "".to_string(), *location) }, - TypeCheckError::Unsafe { span } => { - Diagnostic::simple_error(error.to_string(), String::new(), *span) + TypeCheckError::Unsafe { location } => { + Diagnostic::simple_error(error.to_string(), String::new(), *location) } - TypeCheckError::UnsafeFn { span } => { - Diagnostic::simple_error(error.to_string(), String::new(), *span) + TypeCheckError::UnsafeFn { location } => { + Diagnostic::simple_error(error.to_string(), String::new(), *location) } - TypeCheckError::UnspecifiedType { span } => { - Diagnostic::simple_error(error.to_string(), String::new(), *span) + TypeCheckError::UnspecifiedType { location } => { + Diagnostic::simple_error(error.to_string(), String::new(), *location) } - TypeCheckError::CyclicType { typ: _, span } => { - Diagnostic::simple_error(error.to_string(), "Cyclic types have unlimited size and are prohibited in Noir".into(), *span) + TypeCheckError::CyclicType { typ: _, location } => { + Diagnostic::simple_error(error.to_string(), "Cyclic types have unlimited size and are prohibited in Noir".into(), *location) } - TypeCheckError::CannotInvokeStructFieldFunctionType { method_name, object_type, span } => { + TypeCheckError::CannotInvokeStructFieldFunctionType { method_name, object_type, location } => { Diagnostic::simple_error( format!("Cannot invoke function field '{method_name}' on type '{object_type}' as a method"), format!("to call the function stored in '{method_name}', surround the field access with parentheses: '(', ')'"), - *span, + *location, ) }, - TypeCheckError::TypeAnnotationsNeededForIndex { span } => { + TypeCheckError::TypeAnnotationsNeededForIndex { location } => { Diagnostic::simple_error( "Type annotations required before indexing this array or slice".into(), "Type annotations needed before this point, can't decide if this is an array or slice".into(), - *span, + *location, ) }, - TypeCheckError::UnnecessaryUnsafeBlock { span } => { + TypeCheckError::UnnecessaryUnsafeBlock { location } => { Diagnostic::simple_warning( "Unnecessary `unsafe` block".into(), "".into(), - *span, + *location, ) }, - TypeCheckError::NestedUnsafeBlock { span } => { + TypeCheckError::NestedUnsafeBlock { location } => { Diagnostic::simple_warning( "Unnecessary `unsafe` block".into(), "Because it's nested inside another `unsafe` block".into(), - *span, + *location, ) }, } @@ -542,15 +690,15 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { impl<'a> From<&'a NoMatchingImplFoundError> for Diagnostic { fn from(error: &'a NoMatchingImplFoundError) -> Self { let constraints = &error.constraints; - let span = error.span; + let location = error.location; assert!(!constraints.is_empty()); let msg = format!("No matching impl found for `{}: {}`", constraints[0].0, constraints[0].1); - let mut diagnostic = Diagnostic::from_message(&msg); + let mut diagnostic = Diagnostic::from_message(&msg, location.file); let secondary = format!("No impl for `{}: {}`", constraints[0].0, constraints[0].1); - diagnostic.add_secondary(secondary, span); + diagnostic.add_secondary(secondary, location); // These must be notes since secondaries are unordered for (typ, trait_name) in &constraints[1..] { @@ -565,7 +713,7 @@ impl NoMatchingImplFoundError { pub fn new( interner: &NodeInterner, failing_constraints: Vec, - span: Span, + location: Location, ) -> Option { // Don't show any errors where try_get_trait returns None. // This can happen if a trait is used that was never declared. @@ -578,6 +726,6 @@ impl NoMatchingImplFoundError { }) .collect::>>()?; - Some(Self { constraints, span }) + Some(Self { constraints, location }) } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/generics.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/generics.rs index f823b495040a..29baee8c30a4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/generics.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/generics.rs @@ -3,9 +3,9 @@ use std::cell::Ref; use iter_extended::vecmap; use crate::{ + DataType, ResolvedGeneric, Type, hir_def::traits::NamedType, node_interner::{FuncId, NodeInterner, TraitId, TypeAliasId}, - DataType, ResolvedGeneric, Type, }; /// Represents something that can be generic over type variables diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs index 74028fa38093..0076cab8de5d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs @@ -1,14 +1,14 @@ -use acvm::FieldElement; use fm::FileId; use noirc_errors::Location; +use crate::Shared; use crate::ast::{BinaryOp, BinaryOpKind, Ident, UnaryOp}; use crate::hir::type_check::generics::TraitGenerics; use crate::node_interner::{ DefinitionId, DefinitionKind, ExprId, FuncId, NodeInterner, StmtId, TraitMethodId, }; +use crate::signed_field::SignedField; use crate::token::{FmtStrFragment, Tokens}; -use crate::Shared; use super::stmt::HirPattern; use super::traits::{ResolvedTraitBound, TraitConstraint}; @@ -115,7 +115,7 @@ pub enum HirLiteral { Array(HirArrayLiteral), Slice(HirArrayLiteral), Bool(bool), - Integer(FieldElement, bool), //true for negative integer and false for positive + Integer(SignedField), Str(String), FmtStr(Vec, Vec, u32 /* length */), Unit, @@ -249,13 +249,10 @@ impl HirMethodReference { } HirMethodReference::TraitMethodId(method_id, trait_generics, assumed) => { let id = interner.trait_method_id(method_id); + let trait_id = method_id.trait_id; let constraint = TraitConstraint { typ: object_type, - trait_bound: ResolvedTraitBound { - trait_id: method_id.trait_id, - trait_generics, - span: location.span, - }, + trait_bound: ResolvedTraitBound { trait_id, trait_generics, location }, }; (id, ImplKind::TraitMethod(TraitMethod { method_id, constraint, assumed })) @@ -263,7 +260,7 @@ impl HirMethodReference { }; let func_var = HirIdent { location, id, impl_kind }; let func = interner.push_expr(HirExpression::Ident(func_var.clone(), generics)); - interner.push_expr_location(func, location.span, location.file); + interner.push_expr_location(func, location); (func, func_var) } } @@ -390,49 +387,6 @@ impl Case { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct SignedField { - pub field: FieldElement, - pub is_negative: bool, -} - -impl SignedField { - pub fn new(field: FieldElement, is_negative: bool) -> Self { - Self { field, is_negative } - } -} - -impl std::ops::Neg for SignedField { - type Output = Self; - - fn neg(mut self) -> Self::Output { - self.is_negative = !self.is_negative; - self - } -} - -impl std::cmp::PartialOrd for SignedField { - fn partial_cmp(&self, other: &Self) -> Option { - if self.is_negative != other.is_negative { - if self.is_negative { - return Some(std::cmp::Ordering::Less); - } else { - return Some(std::cmp::Ordering::Greater); - } - } - self.field.partial_cmp(&other.field) - } -} - -impl std::fmt::Display for SignedField { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.is_negative { - write!(f, "-")?; - } - write!(f, "{}", self.field) - } -} - #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum Constructor { True, 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 75bb4f505417..b84e511bd447 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 @@ -171,7 +171,7 @@ pub struct FuncMeta { #[derive(Debug, Clone)] pub enum FunctionBody { - Unresolved(FunctionKind, BlockExpression, Span), + Unresolved(FunctionKind, BlockExpression, Location), Resolving, Resolved, } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/stmt.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/stmt.rs index b0e004349032..21db5971bf0f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -1,8 +1,8 @@ use super::expr::HirIdent; +use crate::Type; use crate::ast::Ident; use crate::node_interner::{ExprId, StmtId}; use crate::token::SecondaryAttribute; -use crate::Type; use noirc_errors::{Location, Span}; /// A HirStatement is the result of performing name resolution on diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/traits.rs index a80c25492a35..a344b2769133 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/traits.rs @@ -1,13 +1,13 @@ use iter_extended::vecmap; use rustc_hash::FxHashMap as HashMap; +use crate::ResolvedGeneric; use crate::ast::{Ident, ItemVisibility, NoirFunction}; use crate::hir::type_check::generics::TraitGenerics; -use crate::ResolvedGeneric; use crate::{ + Generics, Type, TypeBindings, TypeVariable, graph::CrateId, node_interner::{FuncId, TraitId, TraitMethodId}, - Generics, Type, TypeBindings, TypeVariable, }; use fm::FileId; use noirc_errors::{Location, Span}; @@ -121,7 +121,7 @@ impl TraitConstraint { pub struct ResolvedTraitBound { pub trait_id: TraitId, pub trait_generics: TraitGenerics, - pub span: Span, + pub location: Location, } impl ResolvedTraitBound { @@ -186,10 +186,10 @@ impl Trait { (ordered, named) } - pub fn get_trait_generics(&self, span: Span) -> TraitGenerics { + pub fn get_trait_generics(&self, location: Location) -> TraitGenerics { let ordered = vecmap(&self.generics, |generic| generic.clone().as_named_generic()); let named = vecmap(&self.associated_types, |generic| { - let name = Ident::new(generic.name.to_string(), span); + let name = Ident::new(generic.name.to_string(), location); NamedType { name, typ: generic.clone().as_named_generic() } }); TraitGenerics { ordered, named } @@ -197,11 +197,11 @@ impl Trait { /// Returns a TraitConstraint for this trait using Self as the object /// type and the uninstantiated generics for any trait generics. - pub fn as_constraint(&self, span: Span) -> TraitConstraint { - let trait_generics = self.get_trait_generics(span); + pub fn as_constraint(&self, location: Location) -> TraitConstraint { + let trait_generics = self.get_trait_generics(location); TraitConstraint { typ: Type::TypeVariable(self.self_type_typevar.clone()), - trait_bound: ResolvedTraitBound { trait_generics, trait_id: self.id, span }, + trait_bound: ResolvedTraitBound { trait_generics, trait_id: self.id, location }, } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs index 2afaced32c71..e7960c59521f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs @@ -12,11 +12,11 @@ use acvm::{AcirField, FieldElement}; use crate::{ ast::{IntegerBitSize, ItemVisibility}, - hir::type_check::{generics::TraitGenerics, TypeCheckError}, + hir::type_check::{TypeCheckError, generics::TraitGenerics}, node_interner::{ExprId, NodeInterner, TraitId, TypeAliasId}, }; use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use noirc_printable_type::PrintableType; use crate::{ @@ -249,11 +249,7 @@ impl Kind { } pub(crate) fn unify(&self, other: &Kind) -> Result<(), UnificationError> { - if self.unifies(other) { - Ok(()) - } else { - Err(UnificationError) - } + if self.unifies(other) { Ok(()) } else { Err(UnificationError) } } /// Returns the default type this type variable should be bound to if it is still unbound @@ -278,7 +274,7 @@ impl Kind { pub(crate) fn ensure_value_fits( &self, value: FieldElement, - span: Span, + location: Location, ) -> Result { match self.integral_maximum_size() { None => Ok(value), @@ -287,7 +283,7 @@ impl Kind { value, kind: self.clone(), maximum_size, - span, + location, } }), } @@ -395,7 +391,7 @@ pub type Generics = Vec; pub struct ResolvedGeneric { pub name: Rc, pub type_var: TypeVariable, - pub span: Span, + pub location: Location, } impl ResolvedGeneric { @@ -854,12 +850,17 @@ impl TypeVariable { *self.1.borrow_mut() = TypeBinding::Bound(typ); } - pub fn try_bind(&self, binding: Type, kind: &Kind, span: Span) -> Result<(), TypeCheckError> { + pub fn try_bind( + &self, + binding: Type, + kind: &Kind, + location: Location, + ) -> Result<(), TypeCheckError> { if !binding.kind().unifies(kind) { return Err(TypeCheckError::TypeKindMismatch { - expected_kind: format!("{}", kind), - expr_kind: format!("{}", binding.kind()), - expr_span: span, + expected_kind: kind.clone(), + expr_kind: binding.kind(), + expr_location: location, }); } @@ -871,7 +872,7 @@ impl TypeVariable { }; if binding.occurs(id) { - Err(TypeCheckError::CyclicType { span, typ: binding }) + Err(TypeCheckError::CyclicType { location, typ: binding }) } else { *self.1.borrow_mut() = TypeBinding::Bound(binding); Ok(()) @@ -1053,11 +1054,7 @@ impl std::fmt::Display for Type { let this = self.canonicalize_checked(); // Prevent infinite recursion - if this != *self { - write!(f, "{this}") - } else { - write!(f, "({lhs} {op} {rhs})") - } + if this != *self { write!(f, "{this}") } else { write!(f, "({lhs} {op} {rhs})") } } } } @@ -1245,6 +1242,10 @@ impl Type { } } + pub(crate) fn is_mutable_ref(&self) -> bool { + matches!(self.follow_bindings_shallow().as_ref(), Type::MutableReference(_)) + } + /// True if this type can be used as a parameter to `main` or a contract function. /// This is only false for unsized types like slices or slices that do not make sense /// as a program input such as named generics or mutable references. @@ -1457,8 +1458,8 @@ impl Type { Type::NamedGeneric(var, _) => var.kind(), Type::Constant(_, kind) => kind.clone(), Type::TypeVariable(var) => match &*var.borrow() { - TypeBinding::Bound(ref typ) => typ.kind(), - TypeBinding::Unbound(_, ref type_var_kind) => type_var_kind.clone(), + TypeBinding::Bound(typ) => typ.kind(), + TypeBinding::Unbound(_, type_var_kind) => type_var_kind.clone(), }, Type::InfixExpr(lhs, _op, rhs, _) => lhs.infix_kind(rhs), Type::Alias(def, generics) => def.borrow().get_type(generics).kind(), @@ -1486,11 +1487,7 @@ impl Type { fn infix_kind(&self, other: &Self) -> Kind { let self_kind = self.kind(); let other_kind = other.kind(); - if self_kind.unifies(&other_kind) { - self_kind - } else { - Kind::numeric(Type::Error) - } + if self_kind.unifies(&other_kind) { self_kind } else { Kind::numeric(Type::Error) } } /// Creates an `InfixExpr`. @@ -1541,7 +1538,7 @@ impl Type { Type::FieldElement | Type::Integer { .. } | Type::Bool => 1, Type::Array(size, typ) => { let length = size - .evaluate_to_u32(location.span) + .evaluate_to_u32(*location) .expect("Cannot have variable sized arrays as a parameter to main"); let typ = typ.as_ref(); length * typ.field_count(location) @@ -1568,7 +1565,7 @@ impl Type { fields.iter().fold(0, |acc, field_typ| acc + field_typ.field_count(location)) } Type::String(size) => size - .evaluate_to_u32(location.span) + .evaluate_to_u32(*location) .expect("Cannot have variable sized strings as a parameter to main"), Type::FmtString(_, _) | Type::Unit @@ -1658,8 +1655,8 @@ impl Type { typ.try_bind_to_polymorphic_int(var, bindings, only_integer) } // Avoid infinitely recursive bindings - TypeBinding::Unbound(ref id, _) if *id == target_id => Ok(()), - TypeBinding::Unbound(ref new_target_id, Kind::IntegerOrField) => { + TypeBinding::Unbound(id, _) if *id == target_id => Ok(()), + TypeBinding::Unbound(new_target_id, Kind::IntegerOrField) => { let type_var_kind = Kind::IntegerOrField; if only_integer { let var_clone = var.clone(); @@ -1682,7 +1679,7 @@ impl Type { bindings.insert(target_id, (var.clone(), Kind::Integer, this.clone())); Ok(()) } - TypeBinding::Unbound(new_target_id, ref type_var_kind) => { + TypeBinding::Unbound(new_target_id, type_var_kind) => { let var_clone = var.clone(); // Bind to the most specific type variable kind let clone_kind = @@ -1934,8 +1931,8 @@ impl Type { } (Constant(value, kind), other) | (other, Constant(value, kind)) => { - let dummy_span = Span::default(); - if let Ok(other_value) = other.evaluate_to_field_element(kind, dummy_span) { + let dummy_location = Location::dummy(); + if let Ok(other_value) = other.evaluate_to_field_element(kind, dummy_location) { if *value == other_value && kind.unifies(&other.kind()) { Ok(()) } else { @@ -2012,7 +2009,7 @@ impl Type { &self, expected: &Type, expression: ExprId, - span: Span, + location: Location, interner: &mut NodeInterner, errors: &mut Vec, make_error: impl FnOnce() -> TypeCheckError, @@ -2032,14 +2029,16 @@ impl Type { match self.try_fn_to_unconstrained_fn_coercion(expected) { FunctionCoercionResult::NoCoercion => errors.push(make_error()), FunctionCoercionResult::Coerced(coerced_self) => { - coerced_self - .unify_with_coercions(expected, expression, span, interner, errors, make_error); + coerced_self.unify_with_coercions( + expected, expression, location, interner, errors, make_error, + ); } FunctionCoercionResult::UnconstrainedMismatch(coerced_self) => { - errors.push(TypeCheckError::UnsafeFn { span }); + errors.push(TypeCheckError::UnsafeFn { location }); - coerced_self - .unify_with_coercions(expected, expression, span, interner, errors, make_error); + coerced_self.unify_with_coercions( + expected, expression, location, interner, errors, make_error, + ); } } } @@ -2104,8 +2103,8 @@ impl Type { /// If this type is a Type::Constant (used in array lengths), or is bound /// to a Type::Constant, return the constant as a u32. - pub fn evaluate_to_u32(&self, span: Span) -> Result { - self.evaluate_to_field_element(&Kind::u32(), span).map(|field_element| { + pub fn evaluate_to_u32(&self, location: Location) -> Result { + self.evaluate_to_field_element(&Kind::u32(), location).map(|field_element| { field_element .try_to_u32() .expect("ICE: size should have already been checked by evaluate_to_field_element") @@ -2117,17 +2116,17 @@ impl Type { pub(crate) fn evaluate_to_field_element( &self, kind: &Kind, - span: Span, + location: Location, ) -> Result { let run_simplifications = true; - self.evaluate_to_field_element_helper(kind, span, run_simplifications) + self.evaluate_to_field_element_helper(kind, location, run_simplifications) } /// evaluate_to_field_element with optional generic arithmetic simplifications pub(crate) fn evaluate_to_field_element_helper( &self, kind: &Kind, - span: Span, + location: Location, run_simplifications: bool, ) -> Result { if let Some((binding, binding_kind)) = self.get_inner_type_variable() { @@ -2135,7 +2134,7 @@ impl Type { if kind.unifies(&binding_kind) { return binding.evaluate_to_field_element_helper( &binding_kind, - span, + location, run_simplifications, ); } @@ -2146,12 +2145,12 @@ impl Type { match self.canonicalize_helper(could_be_checked_cast, run_simplifications) { Type::Constant(x, constant_kind) => { if kind.unifies(&constant_kind) { - kind.ensure_value_fits(x, span) + kind.ensure_value_fits(x, location) } else { Err(TypeCheckError::TypeKindMismatch { - expected_kind: format!("{}", constant_kind), - expr_kind: format!("{}", kind), - expr_span: span, + expected_kind: constant_kind, + expr_kind: kind.clone(), + expr_location: location, }) } } @@ -2160,31 +2159,31 @@ impl Type { if kind.unifies(&infix_kind) { let lhs_value = lhs.evaluate_to_field_element_helper( &infix_kind, - span, + location, run_simplifications, )?; let rhs_value = rhs.evaluate_to_field_element_helper( &infix_kind, - span, + location, run_simplifications, )?; - op.function(lhs_value, rhs_value, &infix_kind, span) + op.function(lhs_value, rhs_value, &infix_kind, location) } else { Err(TypeCheckError::TypeKindMismatch { - expected_kind: format!("{}", kind), - expr_kind: format!("{}", infix_kind), - expr_span: span, + expected_kind: kind.clone(), + expr_kind: infix_kind, + expr_location: location, }) } } Type::CheckedCast { from, to } => { - let to_value = to.evaluate_to_field_element(kind, span)?; + let to_value = to.evaluate_to_field_element(kind, location)?; // if both 'to' and 'from' evaluate to a constant, // return None unless they match let skip_simplifications = false; if let Ok(from_value) = - from.evaluate_to_field_element_helper(kind, span, skip_simplifications) + from.evaluate_to_field_element_helper(kind, location, skip_simplifications) { if to_value == from_value { Ok(to_value) @@ -2196,14 +2195,14 @@ impl Type { from, to_value, from_value, - span, + location, }) } } else { Ok(to_value) } } - other => Err(TypeCheckError::NonConstantEvaluated { typ: other, span }), + other => Err(TypeCheckError::NonConstantEvaluated { typ: other, location }), } } @@ -2287,7 +2286,11 @@ impl Type { ) -> (Type, TypeBindings) { match self { Type::Forall(typevars, typ) => { - assert_eq!(types.len() + implicit_generic_count, typevars.len(), "Turbofish operator used with incorrect generic count which was not caught by name resolution"); + assert_eq!( + types.len() + implicit_generic_count, + typevars.len(), + "Turbofish operator used with incorrect generic count which was not caught by name resolution" + ); let bindings = (0..implicit_generic_count).map(|_| interner.next_type_variable()).chain(types); @@ -2724,7 +2727,8 @@ impl Type { if sign == &Signedness::Signed { max_bit_size -= 1; } - Some(((1u128 << max_bit_size) - 1).into()) + let max = if max_bit_size == 128 { u128::MAX } else { (1u128 << max_bit_size) - 1 }; + Some(max.into()) } Type::Bool => Some(FieldElement::one()), Type::TypeVariable(var) => { @@ -2781,14 +2785,14 @@ fn convert_array_expression_to_slice( let argument = interner.expression(&expression); let argument = interner.push_expr(argument); interner.push_expr_type(argument, array_type.clone()); - interner.push_expr_location(argument, location.span, location.file); + interner.push_expr_location(argument, location); let arguments = vec![argument]; let is_macro_call = false; let call = HirExpression::Call(HirCallExpression { func, arguments, location, is_macro_call }); interner.replace_expr(&expression, call); - interner.push_expr_location(func, location.span, location.file); + interner.push_expr_location(func, location); interner.push_expr_type(expression, target_type.clone()); let func_type = @@ -2803,7 +2807,7 @@ impl BinaryTypeOperator { a: FieldElement, b: FieldElement, kind: &Kind, - span: Span, + location: Location, ) -> Result { match kind.follow_bindings().integral_maximum_size() { None => match self { @@ -2812,16 +2816,16 @@ impl BinaryTypeOperator { BinaryTypeOperator::Multiplication => Ok(a * b), BinaryTypeOperator::Division => (b != FieldElement::zero()) .then(|| a / b) - .ok_or(TypeCheckError::DivisionByZero { lhs: a, rhs: b, span }), + .ok_or(TypeCheckError::DivisionByZero { lhs: a, rhs: b, location }), BinaryTypeOperator::Modulo => { - Err(TypeCheckError::ModuloOnFields { lhs: a, rhs: b, span }) + Err(TypeCheckError::ModuloOnFields { lhs: a, rhs: b, location }) } }, Some(_maximum_size) => { let a = a.to_i128(); let b = b.to_i128(); - let err = TypeCheckError::FailingBinaryOp { op: self, lhs: a, rhs: b, span }; + let err = TypeCheckError::FailingBinaryOp { op: self, lhs: a, rhs: b, location }; let result = match self { BinaryTypeOperator::Addition => a.checked_add(b).ok_or(err)?, BinaryTypeOperator::Subtraction => a.checked_sub(b).ok_or(err)?, @@ -2875,9 +2879,10 @@ impl From<&Type> for PrintableType { match value { Type::FieldElement => PrintableType::Field, Type::Array(size, typ) => { - let dummy_span = Span::default(); - let length = - size.evaluate_to_u32(dummy_span).expect("Cannot print variable sized arrays"); + let dummy_location = Location::dummy(); + let length = size + .evaluate_to_u32(dummy_location) + .expect("Cannot print variable sized arrays"); let typ = typ.as_ref(); PrintableType::Array { length, typ: Box::new(typ.into()) } } @@ -2902,16 +2907,17 @@ impl From<&Type> for PrintableType { }, Type::Bool => PrintableType::Boolean, Type::String(size) => { - let dummy_span = Span::default(); - let size = - size.evaluate_to_u32(dummy_span).expect("Cannot print variable sized strings"); + let dummy_location = Location::dummy(); + let size = size + .evaluate_to_u32(dummy_location) + .expect("Cannot print variable sized strings"); PrintableType::String { length: size } } Type::FmtString(_, _) => unreachable!("format strings cannot be printed"), Type::Error => unreachable!(), Type::Unit => PrintableType::Unit, Type::Constant(_, _) => unreachable!(), - Type::DataType(def, ref args) => { + Type::DataType(def, args) => { let data_type = def.borrow(); let name = data_type.name.to_string(); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index ce9125cd5f01..83618bf37077 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use acvm::{AcirField, FieldElement}; -use noirc_errors::Span; +use noirc_errors::Location; use crate::{BinaryTypeOperator, Type, TypeBindings, UnificationError}; @@ -60,16 +60,19 @@ impl Type { match self.follow_bindings() { Type::InfixExpr(lhs, op, rhs, inversion) => { let kind = lhs.infix_kind(&rhs); - let dummy_span = Span::default(); + let dummy_location = Location::dummy(); // evaluate_to_field_element also calls canonicalize so if we just called // `self.evaluate_to_field_element(..)` we'd get infinite recursion. if let Ok(lhs_value) = - lhs.evaluate_to_field_element_helper(&kind, dummy_span, run_simplifications) + lhs.evaluate_to_field_element_helper(&kind, dummy_location, run_simplifications) { - if let Ok(rhs_value) = - rhs.evaluate_to_field_element_helper(&kind, dummy_span, run_simplifications) - { - if let Ok(result) = op.function(lhs_value, rhs_value, &kind, dummy_span) { + if let Ok(rhs_value) = rhs.evaluate_to_field_element_helper( + &kind, + dummy_location, + run_simplifications, + ) { + if let Ok(result) = op.function(lhs_value, rhs_value, &kind, dummy_location) + { return Type::Constant(result, kind); } } @@ -139,9 +142,9 @@ impl Type { queue.push(*rhs_inner); } Type::Constant(new_constant, new_constant_kind) => { - let dummy_span = Span::default(); + let dummy_location = Location::dummy(); if let Ok(result) = - op.function(constant, new_constant, &new_constant_kind, dummy_span) + op.function(constant, new_constant, &new_constant_kind, dummy_location) { constant = result; } else { @@ -268,15 +271,14 @@ impl Type { rhs: &Type, ) -> Option<(Box, BinaryTypeOperator, FieldElement, FieldElement)> { let kind = lhs.infix_kind(rhs); - let dummy_span = Span::default(); - let rhs = rhs.evaluate_to_field_element(&kind, dummy_span).ok()?; + let dummy_location = Location::dummy(); + let rhs = rhs.evaluate_to_field_element(&kind, dummy_location).ok()?; let Type::InfixExpr(l_type, l_op, l_rhs, _) = lhs.follow_bindings() else { return None; }; - let dummy_span = Span::default(); - let l_rhs = l_rhs.evaluate_to_field_element(&kind, dummy_span).ok()?; + let l_rhs = l_rhs.evaluate_to_field_element(&kind, dummy_location).ok()?; Some((l_type, l_op, l_rhs, rhs)) } @@ -301,9 +303,9 @@ impl Type { if l_op == Subtraction { op = op.inverse()?; } - let dummy_span = Span::default(); + let dummy_location = Location::dummy(); let result = - op.function(l_const, r_const, &lhs.infix_kind(rhs), dummy_span).ok()?; + op.function(l_const, r_const, &lhs.infix_kind(rhs), dummy_location).ok()?; let constant = Type::Constant(result, lhs.infix_kind(rhs)); Some(Type::infix_expr(l_type, l_op, Box::new(constant))) } @@ -316,9 +318,9 @@ impl Type { if op == Division && (r_const == FieldElement::zero() || !divides_evenly) { None } else { - let dummy_span = Span::default(); + let dummy_location = Location::dummy(); let result = - op.function(l_const, r_const, &lhs.infix_kind(rhs), dummy_span).ok()?; + op.function(l_const, r_const, &lhs.infix_kind(rhs), dummy_location).ok()?; let constant = Box::new(Type::Constant(result, lhs.infix_kind(rhs))); Some(Type::infix_expr(l_type, l_op, constant)) } @@ -337,8 +339,8 @@ impl Type { if let Type::InfixExpr(lhs_a, op_a, rhs_a, _) = self { if let Some(inverse) = op_a.approx_inverse() { let kind = lhs_a.infix_kind(rhs_a); - let dummy_span = Span::default(); - if let Ok(rhs_a_value) = rhs_a.evaluate_to_field_element(&kind, dummy_span) { + let dummy_location = Location::dummy(); + if let Ok(rhs_a_value) = rhs_a.evaluate_to_field_element(&kind, dummy_location) { let rhs_a = Box::new(Type::Constant(rhs_a_value, kind)); let new_other = Type::inverted_infix_expr(Box::new(other.clone()), inverse, rhs_a); @@ -355,8 +357,8 @@ impl Type { if let Type::InfixExpr(lhs_b, op_b, rhs_b, inversion) = other { if let Some(inverse) = op_b.approx_inverse() { let kind = lhs_b.infix_kind(rhs_b); - let dummy_span = Span::default(); - if let Ok(rhs_b_value) = rhs_b.evaluate_to_field_element(&kind, dummy_span) { + let dummy_location = Location::dummy(); + if let Ok(rhs_b_value) = rhs_b.evaluate_to_field_element(&kind, dummy_location) { let rhs_b = Box::new(Type::Constant(rhs_b_value, kind)); let new_self = Type::InfixExpr(Box::new(self.clone()), inverse, rhs_b, !inversion); @@ -598,7 +600,7 @@ mod proptests { #[test] // Expect cases that don't resolve to constants, e.g. see // `arithmetic_generics_checked_cast_indirect_zeros` - #[should_panic(expected = "matches!(infix, Type :: Constant(..))")] + #[should_panic(expected = "matches!(infix, Type::Constant(..))")] fn instantiate_before_or_after_canonicalize(infix_type_bindings in arbitrary_infix_expr_with_bindings(10)) { let (infix, typ, bindings) = infix_type_bindings; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/errors.rs index f95ccba061a6..9adae78febde 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/errors.rs @@ -1,55 +1,55 @@ use crate::hir::def_collector::dc_crate::CompilationError; use crate::parser::ParserError; use crate::parser::ParserErrorReason; -use crate::token::SpannedToken; +use super::token::LocatedToken; use super::token::Token; use noirc_errors::CustomDiagnostic as Diagnostic; -use noirc_errors::Span; +use noirc_errors::Location; use thiserror::Error; #[derive(Error, Clone, Debug, PartialEq, Eq)] pub enum LexerErrorKind { #[error("An unexpected character {:?} was found.", found)] - UnexpectedCharacter { span: Span, expected: String, found: Option }, + UnexpectedCharacter { location: Location, expected: String, found: Option }, #[error("Internal error: Tried to lex {:?} as a double char token", found)] - NotADoubleChar { span: Span, found: Token }, + NotADoubleChar { location: Location, found: Token }, #[error("Invalid integer literal, {:?} is not a integer", found)] - InvalidIntegerLiteral { span: Span, found: String }, + InvalidIntegerLiteral { location: Location, found: String }, #[error("Integer literal is too large")] - IntegerLiteralTooLarge { span: Span, limit: String }, + IntegerLiteralTooLarge { location: Location, limit: String }, #[error("{:?} is not a valid attribute", found)] - MalformedFuncAttribute { span: Span, found: String }, + MalformedFuncAttribute { location: Location, found: String }, #[error("Malformed test attribute")] - MalformedTestAttribute { span: Span }, + MalformedTestAttribute { location: Location }, #[error("{:?} is not a valid inner attribute", found)] - InvalidInnerAttribute { span: Span, found: String }, + InvalidInnerAttribute { location: Location, found: String }, #[error("Logical and used instead of bitwise and")] - LogicalAnd { span: Span }, + LogicalAnd { location: Location }, #[error("Unterminated block comment")] - UnterminatedBlockComment { span: Span }, + UnterminatedBlockComment { location: Location }, #[error("Unterminated string literal")] - UnterminatedStringLiteral { span: Span }, + UnterminatedStringLiteral { location: Location }, #[error("Invalid format string: expected '}}', found {found:?}")] - InvalidFormatString { found: char, span: Span }, + InvalidFormatString { found: char, location: Location }, #[error("Invalid format string: expected letter or underscore, found '}}'")] - EmptyFormatStringInterpolation { span: Span }, + EmptyFormatStringInterpolation { location: Location }, #[error( "'\\{escaped}' is not a valid escape sequence. Use '\\' for a literal backslash character." )] - InvalidEscape { escaped: char, span: Span }, + InvalidEscape { escaped: char, location: Location }, #[error("Invalid quote delimiter `{delimiter}`, valid delimiters are `{{`, `[`, and `(`")] - InvalidQuoteDelimiter { delimiter: SpannedToken }, + InvalidQuoteDelimiter { delimiter: LocatedToken }, #[error("Non-ASCII characters are invalid in comments")] - NonAsciiComment { span: Span }, + NonAsciiComment { location: Location }, #[error("Expected `{end_delim}` to close this {start_delim}")] - UnclosedQuote { start_delim: SpannedToken, end_delim: Token }, + UnclosedQuote { start_delim: LocatedToken, end_delim: Token }, } impl From for ParserError { fn from(value: LexerErrorKind) -> Self { - let span = value.span(); - ParserError::with_reason(ParserErrorReason::Lexer(value), span) + let location = value.location(); + ParserError::with_reason(ParserErrorReason::Lexer(value), location) } } @@ -60,31 +60,31 @@ impl From for CompilationError { } impl LexerErrorKind { - pub fn span(&self) -> Span { + pub fn location(&self) -> Location { match self { - LexerErrorKind::UnexpectedCharacter { span, .. } => *span, - LexerErrorKind::NotADoubleChar { span, .. } => *span, - LexerErrorKind::InvalidIntegerLiteral { span, .. } => *span, - LexerErrorKind::IntegerLiteralTooLarge { span, .. } => *span, - LexerErrorKind::MalformedFuncAttribute { span, .. } => *span, - LexerErrorKind::MalformedTestAttribute { span, .. } => *span, - LexerErrorKind::InvalidInnerAttribute { span, .. } => *span, - LexerErrorKind::LogicalAnd { span } => *span, - LexerErrorKind::UnterminatedBlockComment { span } => *span, - LexerErrorKind::UnterminatedStringLiteral { span } => *span, - LexerErrorKind::InvalidFormatString { span, .. } => *span, - LexerErrorKind::EmptyFormatStringInterpolation { span, .. } => *span, - LexerErrorKind::InvalidEscape { span, .. } => *span, - LexerErrorKind::InvalidQuoteDelimiter { delimiter } => delimiter.to_span(), - LexerErrorKind::NonAsciiComment { span, .. } => *span, - LexerErrorKind::UnclosedQuote { start_delim, .. } => start_delim.to_span(), + LexerErrorKind::UnexpectedCharacter { location, .. } => *location, + LexerErrorKind::NotADoubleChar { location, .. } => *location, + LexerErrorKind::InvalidIntegerLiteral { location, .. } => *location, + LexerErrorKind::IntegerLiteralTooLarge { location, .. } => *location, + LexerErrorKind::MalformedFuncAttribute { location, .. } => *location, + LexerErrorKind::MalformedTestAttribute { location, .. } => *location, + LexerErrorKind::InvalidInnerAttribute { location, .. } => *location, + LexerErrorKind::LogicalAnd { location } => *location, + LexerErrorKind::UnterminatedBlockComment { location } => *location, + LexerErrorKind::UnterminatedStringLiteral { location } => *location, + LexerErrorKind::InvalidFormatString { location, .. } => *location, + LexerErrorKind::EmptyFormatStringInterpolation { location, .. } => *location, + LexerErrorKind::InvalidEscape { location, .. } => *location, + LexerErrorKind::InvalidQuoteDelimiter { delimiter } => delimiter.location(), + LexerErrorKind::NonAsciiComment { location, .. } => *location, + LexerErrorKind::UnclosedQuote { start_delim, .. } => start_delim.location(), } } - fn parts(&self) -> (String, String, Span) { + fn parts(&self) -> (String, String, Location) { match self { LexerErrorKind::UnexpectedCharacter { - span, + location, expected, found, } => { @@ -93,55 +93,55 @@ impl LexerErrorKind { ( "An unexpected character was found".to_string(), format!("Expected {expected}, but found {found}"), - *span, + *location, ) }, - LexerErrorKind::NotADoubleChar { span, found } => ( + LexerErrorKind::NotADoubleChar { location, found } => ( format!("Tried to parse {found} as double char"), format!( " {found:?} is not a double char, this is an internal error" ), - *span, + *location, ), - LexerErrorKind::InvalidIntegerLiteral { span, found } => ( + LexerErrorKind::InvalidIntegerLiteral { location, found } => ( "Invalid integer literal".to_string(), format!(" {found} is not an integer"), - *span, + *location, ), - LexerErrorKind::IntegerLiteralTooLarge { span, limit } => ( + LexerErrorKind::IntegerLiteralTooLarge { location, limit } => ( "Integer literal is too large".to_string(), format!("value exceeds limit of {limit}"), - *span, + *location, ), - LexerErrorKind::MalformedFuncAttribute { span, found } => ( + LexerErrorKind::MalformedFuncAttribute { location, found } => ( "Malformed function attribute".to_string(), format!(" {found} is not a valid attribute"), - *span, + *location, ), - LexerErrorKind::MalformedTestAttribute { span } => ( + LexerErrorKind::MalformedTestAttribute { location } => ( "Malformed test attribute".to_string(), "The test attribute can be written in one of these forms: `#[test]`, `#[test(should_fail)]` or `#[test(should_fail_with = \"message\")]`".to_string(), - *span, + *location, ), - LexerErrorKind::InvalidInnerAttribute { span, found } => ( + LexerErrorKind::InvalidInnerAttribute { location, found } => ( "Invalid inner attribute".to_string(), format!(" {found} is not a valid inner attribute"), - *span, + *location, ), - LexerErrorKind::LogicalAnd { span } => ( + LexerErrorKind::LogicalAnd { location } => ( "Noir has no logical-and (&&) operator since short-circuiting is much less efficient when compiling to circuits".to_string(), "Try `&` instead, or use `if` only if you require short-circuiting".to_string(), - *span, + *location, ), - LexerErrorKind::UnterminatedBlockComment { span } => ("Unterminated block comment".to_string(), "Unterminated block comment".to_string(), *span), - LexerErrorKind::UnterminatedStringLiteral { span } => - ("Unterminated string literal".to_string(), "Unterminated string literal".to_string(), *span), - LexerErrorKind::InvalidFormatString { found, span } => { + LexerErrorKind::UnterminatedBlockComment { location } => ("Unterminated block comment".to_string(), "Unterminated block comment".to_string(), *location), + LexerErrorKind::UnterminatedStringLiteral { location } => + ("Unterminated string literal".to_string(), "Unterminated string literal".to_string(), *location), + LexerErrorKind::InvalidFormatString { found, location } => { if found == &'}' { ( "Invalid format string: unmatched '}}' found".to_string(), "If you intended to print '}', you can escape it using '}}'".to_string(), - *span, + *location, ) } else { ( @@ -151,27 +151,27 @@ impl LexerErrorKind { } else { "If you intended to print '{', you can escape it using '{{'".to_string() }, - *span, + *location, ) } } - LexerErrorKind::EmptyFormatStringInterpolation { span } => { + LexerErrorKind::EmptyFormatStringInterpolation { location } => { ( "Invalid format string: expected letter or underscore, found '}}'".to_string(), "If you intended to print '{' or '}', you can escape them using '{{' and '}}' respectively".to_string(), - *span, + *location, ) } - LexerErrorKind::InvalidEscape { escaped, span } => - (format!("'\\{escaped}' is not a valid escape sequence. Use '\\' for a literal backslash character."), "Invalid escape sequence".to_string(), *span), + LexerErrorKind::InvalidEscape { escaped, location } => + (format!("'\\{escaped}' is not a valid escape sequence. Use '\\' for a literal backslash character."), "Invalid escape sequence".to_string(), *location), LexerErrorKind::InvalidQuoteDelimiter { delimiter } => { - (format!("Invalid quote delimiter `{delimiter}`"), "Valid delimiters are `{`, `[`, and `(`".to_string(), delimiter.to_span()) + (format!("Invalid quote delimiter `{delimiter}`"), "Valid delimiters are `{`, `[`, and `(`".to_string(), delimiter.location()) }, - LexerErrorKind::NonAsciiComment { span } => { - ("Non-ASCII character in comment".to_string(), "Invalid comment character: only ASCII is currently supported.".to_string(), *span) + LexerErrorKind::NonAsciiComment { location } => { + ("Non-ASCII character in comment".to_string(), "Invalid comment character: only ASCII is currently supported.".to_string(), *location) } LexerErrorKind::UnclosedQuote { start_delim, end_delim } => { - ("Unclosed `quote` expression".to_string(), format!("Expected a `{end_delim}` to close this `{start_delim}`"), start_delim.to_span()) + ("Unclosed `quote` expression".to_string(), format!("Expected a `{end_delim}` to close this `{start_delim}`"), start_delim.location()) } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs index ef5706b4d49b..630f192c1095 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs @@ -2,10 +2,11 @@ use crate::token::DocStyle; use super::{ errors::LexerErrorKind, - token::{FmtStrFragment, IntType, Keyword, SpannedToken, Token, Tokens}, + token::{FmtStrFragment, IntType, Keyword, LocatedToken, SpannedToken, Token, Tokens}, }; use acvm::{AcirField, FieldElement}; -use noirc_errors::{Position, Span}; +use fm::FileId; +use noirc_errors::{Location, Position, Span}; use num_bigint::BigInt; use num_traits::{Num, One}; use std::str::{CharIndices, FromStr}; @@ -14,6 +15,7 @@ use std::str::{CharIndices, FromStr}; /// into an iterator of `SpannedToken`. Each `Token` corresponds roughly to 1 word or operator. /// Tokens are tagged with their location in the source file (a `Span`) for use in error reporting. pub struct Lexer<'a> { + file_id: FileId, chars: CharIndices<'a>, position: Position, done: bool, @@ -24,11 +26,13 @@ pub struct Lexer<'a> { pub type SpannedTokenResult = Result; +pub type LocatedTokenResult = Result; + impl<'a> Lexer<'a> { /// Given a source file of noir code, return all the tokens in the file /// in order, along with any lexing errors that occurred. - pub fn lex(source: &'a str) -> (Tokens, Vec) { - let lexer = Lexer::new(source); + pub fn lex(source: &'a str, file_id: FileId) -> (Tokens, Vec) { + let lexer = Lexer::new(source, file_id); let mut tokens = vec![]; let mut errors = vec![]; for result in lexer { @@ -40,8 +44,9 @@ impl<'a> Lexer<'a> { (Tokens(tokens), errors) } - pub fn new(source: &'a str) -> Self { + pub fn new(source: &'a str, file_id: FileId) -> Self { Lexer { + file_id, chars: source.char_indices(), position: 0, done: false, @@ -52,6 +57,10 @@ impl<'a> Lexer<'a> { } } + pub fn new_with_dummy_file(source: &'a str) -> Self { + Self::new(source, FileId::dummy()) + } + pub fn skip_comments(mut self, flag: bool) -> Self { self.skip_comments = flag; self @@ -96,21 +105,28 @@ impl<'a> Lexer<'a> { // When we issue this error the first '&' will already be consumed // and the next token issued will be the next '&'. let span = Span::inclusive(self.position, self.position + 1); - Err(LexerErrorKind::LogicalAnd { span }) + Err(LexerErrorKind::LogicalAnd { location: self.location(span) }) } else { self.single_char_token(Token::Ampersand) } } - fn next_token(&mut self) -> SpannedTokenResult { + fn next_token(&mut self) -> LocatedTokenResult { + self.next_spanned_token().map(|token| { + let span = token.span(); + LocatedToken::new(token.into_token(), Location::new(span, self.file_id)) + }) + } + + fn next_spanned_token(&mut self) -> SpannedTokenResult { if !self.skip_comments { - return self.next_token_without_checking_comments(); + return self.next_spanned_token_without_checking_comments(); } // Read tokens and skip comments. This is done like this to avoid recursion // and hitting stack overflow when there are many comments in a row. loop { - let token = self.next_token_without_checking_comments()?; + let token = self.next_spanned_token_without_checking_comments()?; if matches!(token.token(), Token::LineComment(_, None) | Token::BlockComment(_, None)) { continue; } @@ -119,12 +135,12 @@ impl<'a> Lexer<'a> { } /// Reads the next token, which might be a comment token (these aren't skipped in this method) - fn next_token_without_checking_comments(&mut self) -> SpannedTokenResult { + fn next_spanned_token_without_checking_comments(&mut self) -> SpannedTokenResult { match self.next_char() { Some(x) if Self::is_code_whitespace(x) => { let spanned = self.eat_whitespace(x); if self.skip_whitespaces { - self.next_token_without_checking_comments() + self.next_spanned_token_without_checking_comments() } else { Ok(spanned) } @@ -261,7 +277,7 @@ impl<'a> Lexer<'a> { Ok(prev_token.into_single_span(start)) } _ => Err(LexerErrorKind::NotADoubleChar { - span: Span::single_char(self.position), + location: self.location(Span::single_char(self.position)), found: prev_token, }), } @@ -303,7 +319,7 @@ impl<'a> Lexer<'a> { 'A'..='Z' | 'a'..='z' | '_' => Ok(self.eat_word(initial_char)?), '0'..='9' => self.eat_digit(initial_char), _ => Err(LexerErrorKind::UnexpectedCharacter { - span: Span::single_char(self.position), + location: self.location(Span::single_char(self.position)), found: initial_char.into(), expected: "an alpha numeric character".to_owned(), }), @@ -322,7 +338,7 @@ impl<'a> Lexer<'a> { if !self.peek_char_is('[') { return Err(LexerErrorKind::UnexpectedCharacter { - span: Span::single_char(self.position), + location: self.location(Span::single_char(self.position)), found: self.next_char(), expected: "[".to_owned(), }); @@ -399,7 +415,7 @@ impl<'a> Lexer<'a> { let consecutive_underscores = integer_str.contains("__"); if invalid_underscore_location || consecutive_underscores { return Err(LexerErrorKind::InvalidIntegerLiteral { - span: Span::inclusive(start, end), + location: self.location(Span::inclusive(start, end)), found: integer_str, }); } @@ -416,7 +432,7 @@ impl<'a> Lexer<'a> { Ok(bigint) => { if bigint > self.max_integer { return Err(LexerErrorKind::IntegerLiteralTooLarge { - span: Span::inclusive(start, end), + location: self.location(Span::inclusive(start, end)), limit: self.max_integer.to_string(), }); } @@ -425,9 +441,9 @@ impl<'a> Lexer<'a> { } Err(_) => { return Err(LexerErrorKind::InvalidIntegerLiteral { - span: Span::inclusive(start, end), + location: self.location(Span::inclusive(start, end)), found: integer_str, - }) + }); } }; @@ -452,11 +468,16 @@ impl<'a> Lexer<'a> { Some('\\') => '\\', Some(escaped) => { let span = Span::inclusive(start, self.position); - return Err(LexerErrorKind::InvalidEscape { escaped, span }); + return Err(LexerErrorKind::InvalidEscape { + escaped, + location: self.location(span), + }); } None => { let span = Span::inclusive(start, self.position); - return Err(LexerErrorKind::UnterminatedStringLiteral { span }); + return Err(LexerErrorKind::UnterminatedStringLiteral { + location: self.location(span), + }); } }, other => other, @@ -465,7 +486,9 @@ impl<'a> Lexer<'a> { string.push(char); } else { let span = Span::inclusive(start, self.position); - return Err(LexerErrorKind::UnterminatedStringLiteral { span }); + return Err(LexerErrorKind::UnterminatedStringLiteral { + location: self.location(span), + }); } } @@ -499,11 +522,16 @@ impl<'a> Lexer<'a> { Some('\\') => '\\', Some(escaped) => { let span = Span::inclusive(start, self.position); - return Err(LexerErrorKind::InvalidEscape { escaped, span }); + return Err(LexerErrorKind::InvalidEscape { + escaped, + location: self.location(span), + }); } None => { let span = Span::inclusive(start, self.position); - return Err(LexerErrorKind::UnterminatedStringLiteral { span }); + return Err(LexerErrorKind::UnterminatedStringLiteral { + location: self.location(span), + }); } }, '{' if self.peek_char_is('{') => { @@ -521,7 +549,10 @@ impl<'a> Lexer<'a> { self.skip_until_string_end(); let span = Span::inclusive(error_position, error_position); - return Err(LexerErrorKind::InvalidFormatString { found: '}', span }); + return Err(LexerErrorKind::InvalidFormatString { + found: '}', + location: self.location(span), + }); } '{' => { found_curly = true; @@ -545,7 +576,9 @@ impl<'a> Lexer<'a> { } } else { let span = Span::inclusive(start, self.position); - return Err(LexerErrorKind::UnterminatedStringLiteral { span }); + return Err(LexerErrorKind::UnterminatedStringLiteral { + location: self.location(span), + }); } } @@ -573,7 +606,9 @@ impl<'a> Lexer<'a> { self.skip_until_string_end(); let span = Span::inclusive(error_position, error_position); - return Err(LexerErrorKind::EmptyFormatStringInterpolation { span }); + return Err(LexerErrorKind::EmptyFormatStringInterpolation { + location: self.location(span), + }); } break; @@ -594,7 +629,10 @@ impl<'a> Lexer<'a> { } let span = Span::inclusive(error_position, error_position); - return Err(LexerErrorKind::InvalidFormatString { found: other, span }); + return Err(LexerErrorKind::InvalidFormatString { + found: other, + location: self.location(span), + }); } first_char = false; other @@ -606,8 +644,9 @@ impl<'a> Lexer<'a> { length += 1; // for the closing curly brace - let interpolation_span = Span::from(interpolation_start..self.position); - fragments.push(FmtStrFragment::Interpolation(string, interpolation_span)); + let span = Span::from(interpolation_start..self.position); + let location = Location::new(span, self.file_id); + fragments.push(FmtStrFragment::Interpolation(string, location)); } let token = Token::FmtStr(fragments, length); @@ -626,11 +665,7 @@ impl<'a> Lexer<'a> { } fn eat_format_string_or_alpha_numeric(&mut self) -> SpannedTokenResult { - if self.peek_char_is('"') { - self.eat_fmt_string() - } else { - self.eat_alpha_numeric('f') - } + if self.peek_char_is('"') { self.eat_fmt_string() } else { self.eat_alpha_numeric('f') } } fn eat_raw_string(&mut self) -> SpannedTokenResult { @@ -642,7 +677,7 @@ impl<'a> Lexer<'a> { // too many hashes (unlikely in practice) // also, Rust disallows 256+ hashes as well return Err(LexerErrorKind::UnexpectedCharacter { - span: Span::single_char(start + 255), + location: self.location(Span::single_char(start + 255)), found: Some('#'), expected: "\"".to_owned(), }); @@ -650,7 +685,7 @@ impl<'a> Lexer<'a> { if !self.peek_char_is('"') { return Err(LexerErrorKind::UnexpectedCharacter { - span: Span::single_char(self.position), + location: self.location(Span::single_char(self.position)), found: self.next_char(), expected: "\"".to_owned(), }); @@ -663,7 +698,7 @@ impl<'a> Lexer<'a> { str_literal.push_str(&chars[..]); if !self.peek_char_is('"') { return Err(LexerErrorKind::UnexpectedCharacter { - span: Span::single_char(self.position), + location: self.location(Span::single_char(self.position)), found: self.next_char(), expected: "\"".to_owned(), }); @@ -769,7 +804,7 @@ impl<'a> Lexer<'a> { if !comment.is_ascii() { let span = Span::from(start..self.position); - return Err(LexerErrorKind::NonAsciiComment { span }); + return Err(LexerErrorKind::NonAsciiComment { location: self.location(span) }); } Ok(Token::LineComment(comment, doc_style).into_span(start, self.position)) @@ -814,13 +849,13 @@ impl<'a> Lexer<'a> { if depth == 0 { if !content.is_ascii() { let span = Span::from(start..self.position); - return Err(LexerErrorKind::NonAsciiComment { span }); + return Err(LexerErrorKind::NonAsciiComment { location: self.location(span) }); } Ok(Token::BlockComment(content, doc_style).into_span(start, self.position)) } else { let span = Span::inclusive(start, self.position); - Err(LexerErrorKind::UnterminatedBlockComment { span }) + Err(LexerErrorKind::UnterminatedBlockComment { location: self.location(span) }) } } @@ -834,17 +869,17 @@ impl<'a> Lexer<'a> { let whitespace = self.eat_while(initial_char.into(), Self::is_code_whitespace); SpannedToken::new(Token::Whitespace(whitespace), Span::inclusive(start, self.position)) } + + fn location(&self, span: Span) -> Location { + Location::new(span, self.file_id) + } } -impl<'a> Iterator for Lexer<'a> { - type Item = SpannedTokenResult; +impl Iterator for Lexer<'_> { + type Item = LocatedTokenResult; fn next(&mut self) -> Option { - if self.done { - None - } else { - Some(self.next_token()) - } + if self.done { None } else { Some(self.next_token()) } } } @@ -894,7 +929,7 @@ mod tests { Token::EOF, ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); @@ -905,7 +940,7 @@ mod tests { #[test] fn invalid_attribute() { let input = "#"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next().unwrap(); assert!(token.is_err()); @@ -914,7 +949,7 @@ mod tests { #[test] fn test_attribute_start() { let input = r#"#[something]"#; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next_token().unwrap(); assert_eq!(token.token(), &Token::AttributeStart { is_inner: false, is_tag: false }); @@ -923,7 +958,7 @@ mod tests { #[test] fn test_attribute_start_with_tag() { let input = r#"#['something]"#; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next_token().unwrap(); assert_eq!(token.token(), &Token::AttributeStart { is_inner: false, is_tag: true }); @@ -932,7 +967,7 @@ mod tests { #[test] fn test_inner_attribute_start() { let input = r#"#![something]"#; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next_token().unwrap(); assert_eq!(token.token(), &Token::AttributeStart { is_inner: true, is_tag: false }); @@ -941,7 +976,7 @@ mod tests { #[test] fn test_inner_attribute_start_with_tag() { let input = r#"#!['something]"#; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next_token().unwrap(); assert_eq!(token.token(), &Token::AttributeStart { is_inner: true, is_tag: true }); @@ -960,7 +995,7 @@ mod tests { Token::Int(5_i128.into()), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); assert_eq!(got, token); @@ -972,7 +1007,7 @@ mod tests { let modulus = FieldElement::modulus(); let input = modulus.to_string(); - let mut lexer = Lexer::new(&input); + let mut lexer = Lexer::new_with_dummy_file(&input); let token = lexer.next_token(); assert!( matches!(token, Err(LexerErrorKind::IntegerLiteralTooLarge { .. })), @@ -997,7 +1032,7 @@ mod tests { Token::Assign, ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); assert_eq!(got, token); @@ -1008,7 +1043,7 @@ mod tests { fn unterminated_block_comment() { let input = "/*/"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next().unwrap(); assert!(token.is_err()); @@ -1027,7 +1062,7 @@ mod tests { Token::Int(FieldElement::from(5_i128)), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let first_lexer_output = lexer.next_token().unwrap(); assert_eq!(first_lexer_output, token); @@ -1049,7 +1084,7 @@ mod tests { Token::Int(FieldElement::from(5_i128)), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let first_lexer_output = lexer.next_token().unwrap(); assert_eq!(first_lexer_output, token); @@ -1075,7 +1110,7 @@ mod tests { Token::BlockComment(" inner doc block ".into(), DocStyle::Inner.into()), ]; - let mut lexer = Lexer::new(input).skip_comments(false); + let mut lexer = Lexer::new_with_dummy_file(input).skip_comments(false); for token in expected { let first_lexer_output = lexer.next_token().unwrap(); assert_eq!(token, first_lexer_output); @@ -1097,7 +1132,7 @@ mod tests { Token::Int(FieldElement::from(5_i128)), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let first_lexer_output = lexer.next_token().unwrap(); assert_eq!(first_lexer_output, token); @@ -1113,7 +1148,7 @@ mod tests { Token::Assign, Token::Str("hello".to_string()), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); @@ -1131,7 +1166,7 @@ mod tests { Token::Assign, Token::Str("hello\n\t".to_string()), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); @@ -1142,7 +1177,7 @@ mod tests { #[test] fn test_eat_string_literal_missing_double_quote() { let input = "\"hello"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); assert!(matches!( lexer.next_token(), Err(LexerErrorKind::UnterminatedStringLiteral { .. }) @@ -1159,7 +1194,7 @@ mod tests { Token::Assign, Token::FmtStr(vec![FmtStrFragment::String("hello".to_string())], 5), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); @@ -1177,7 +1212,7 @@ mod tests { Token::Assign, Token::FmtStr(vec![FmtStrFragment::String("hello\n\t{x}".to_string())], 12), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); @@ -1188,6 +1223,7 @@ mod tests { #[test] fn test_eat_fmt_string_literal_with_interpolations() { let input = "let _word = f\"hello {world} and {_another} {vAr_123}\""; + let file = FileId::dummy(); let expected = vec![ Token::Keyword(Keyword::Let), @@ -1196,16 +1232,25 @@ mod tests { Token::FmtStr( vec![ FmtStrFragment::String("hello ".to_string()), - FmtStrFragment::Interpolation("world".to_string(), Span::from(21..26)), + FmtStrFragment::Interpolation( + "world".to_string(), + Location::new(Span::from(21..26), file), + ), FmtStrFragment::String(" and ".to_string()), - FmtStrFragment::Interpolation("_another".to_string(), Span::from(33..41)), + FmtStrFragment::Interpolation( + "_another".to_string(), + Location::new(Span::from(33..41), file), + ), FmtStrFragment::String(" ".to_string()), - FmtStrFragment::Interpolation("vAr_123".to_string(), Span::from(44..51)), + FmtStrFragment::Interpolation( + "vAr_123".to_string(), + Location::new(Span::from(44..51), file), + ), ], 38, ), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap().into_token(); @@ -1216,7 +1261,7 @@ mod tests { #[test] fn test_eat_fmt_string_literal_missing_double_quote() { let input = "f\"hello"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); assert!(matches!( lexer.next_token(), Err(LexerErrorKind::UnterminatedStringLiteral { .. }) @@ -1226,7 +1271,7 @@ mod tests { #[test] fn test_eat_fmt_string_literal_invalid_char_in_interpolation() { let input = "f\"hello {foo.bar}\" true"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); assert!(matches!(lexer.next_token(), Err(LexerErrorKind::InvalidFormatString { .. }))); // Make sure the lexer went past the ending double quote for better recovery @@ -1237,7 +1282,7 @@ mod tests { #[test] fn test_eat_fmt_string_literal_double_quote_inside_interpolation() { let input = "f\"hello {world\" true"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); assert!(matches!(lexer.next_token(), Err(LexerErrorKind::InvalidFormatString { .. }))); // Make sure the lexer stopped parsing the string literal when it found \" inside the interpolation @@ -1248,7 +1293,7 @@ mod tests { #[test] fn test_eat_fmt_string_literal_unmatched_closing_curly() { let input = "f\"hello }\" true"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); assert!(matches!(lexer.next_token(), Err(LexerErrorKind::InvalidFormatString { .. }))); // Make sure the lexer went past the ending double quote for better recovery @@ -1259,7 +1304,7 @@ mod tests { #[test] fn test_eat_fmt_string_literal_empty_interpolation() { let input = "f\"{}\" true"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); assert!(matches!( lexer.next_token(), Err(LexerErrorKind::EmptyFormatStringInterpolation { .. }) @@ -1281,7 +1326,7 @@ mod tests { ]; for (input, expected_token) in test_cases { - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let got = lexer.next_token().unwrap(); assert_eq!(got.token(), &expected_token); } @@ -1292,7 +1337,7 @@ mod tests { let test_cases: Vec<&str> = vec!["0x05_", "5_", "5__5", "0x5__5"]; for input in test_cases { - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next_token(); assert!( matches!(token, Err(LexerErrorKind::InvalidIntegerLiteral { .. })), @@ -1332,12 +1377,12 @@ mod tests { let int_token = Token::Int(5_i128.into()).into_single_span(int_position); let expected = vec![let_token, ident_token, assign_token, int_token]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for spanned_token in expected.into_iter() { let got = lexer.next_token().unwrap(); - assert_eq!(got.to_span(), spanned_token.to_span()); - assert_eq!(got, spanned_token); + assert_eq!(got.span(), spanned_token.span()); + assert_eq!(got.into_spanned_token(), spanned_token); } } @@ -1403,7 +1448,7 @@ mod tests { Token::Semicolon, Token::EOF, ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); @@ -1481,7 +1526,7 @@ mod tests { for (token_discriminator_opt, blns_program_strs) in statements { for blns_program_str in blns_program_strs { let mut expected_token_found = false; - let mut lexer = Lexer::new(&blns_program_str); + let mut lexer = Lexer::new_with_dummy_file(&blns_program_str); let mut result_tokens = Vec::new(); loop { match lexer.next_token() { @@ -1542,7 +1587,8 @@ mod tests { ]; for (source, expected_stream_length) in cases { - let mut tokens = vecmap(Lexer::new(source), |result| result.unwrap().into_token()); + let mut tokens = + vecmap(Lexer::new_with_dummy_file(source), |result| result.unwrap().into_token()); // All examples should be a single TokenStream token followed by an EOF token. assert_eq!(tokens.len(), 2, "Unexpected token count: {tokens:?}"); @@ -1550,7 +1596,9 @@ mod tests { tokens.pop(); match tokens.pop().unwrap() { Token::Quote(stream) => assert_eq!(stream.0.len(), expected_stream_length), - other => panic!("test_quote test failure! Expected a single TokenStream token, got {other} for input `{source}`") + other => panic!( + "test_quote test failure! Expected a single TokenStream token, got {other} for input `{source}`" + ), } } } @@ -1562,7 +1610,7 @@ mod tests { for source in cases { // `quote` is not itself a keyword so if the token stream fails to // parse we don't expect any valid tokens from the quote construct - for token in Lexer::new(source) { + for token in Lexer::new_with_dummy_file(source) { assert!(token.is_err(), "Expected Err, found {token:?}"); } } @@ -1573,7 +1621,7 @@ mod tests { let cases = vec!["// 🙂", "// schön", "/* in the middle 🙂 of a comment */"]; for source in cases { - let mut lexer = Lexer::new(source); + let mut lexer = Lexer::new_with_dummy_file(source); assert!( lexer.any(|token| matches!(token, Err(LexerErrorKind::NonAsciiComment { .. }))), "Expected NonAsciiComment error" diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs index ef90ff4e5818..7367489f6251 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs @@ -1,5 +1,5 @@ use acvm::FieldElement; -use noirc_errors::{Position, Span, Spanned}; +use noirc_errors::{Located, Location, Position, Span, Spanned}; use std::fmt::{self, Display}; use crate::{ @@ -255,18 +255,18 @@ pub enum Token { pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { match token { - Token::Ident(ref s) => BorrowedToken::Ident(s), + Token::Ident(s) => BorrowedToken::Ident(s), Token::Int(n) => BorrowedToken::Int(*n), Token::Bool(b) => BorrowedToken::Bool(*b), - Token::Str(ref b) => BorrowedToken::Str(b), - Token::FmtStr(ref b, length) => BorrowedToken::FmtStr(b, *length), - Token::RawStr(ref b, hashes) => BorrowedToken::RawStr(b, *hashes), + Token::Str(b) => BorrowedToken::Str(b), + Token::FmtStr(b, length) => BorrowedToken::FmtStr(b, *length), + Token::RawStr(b, hashes) => BorrowedToken::RawStr(b, *hashes), Token::Keyword(k) => BorrowedToken::Keyword(*k), Token::AttributeStart { is_inner, is_tag } => { BorrowedToken::AttributeStart { is_inner: *is_inner, is_tag: *is_tag } } - Token::LineComment(ref s, _style) => BorrowedToken::LineComment(s, *_style), - Token::BlockComment(ref s, _style) => BorrowedToken::BlockComment(s, *_style), + Token::LineComment(s, _style) => BorrowedToken::LineComment(s, *_style), + Token::BlockComment(s, _style) => BorrowedToken::BlockComment(s, *_style), Token::Quote(stream) => BorrowedToken::Quote(stream), Token::QuotedType(id) => BorrowedToken::QuotedType(*id), Token::InternedExpr(id) => BorrowedToken::InternedExpression(*id), @@ -274,7 +274,7 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { Token::InternedLValue(id) => BorrowedToken::InternedLValue(*id), Token::InternedUnresolvedTypeData(id) => BorrowedToken::InternedUnresolvedTypeData(*id), Token::InternedPattern(id) => BorrowedToken::InternedPattern(*id), - Token::IntType(ref i) => BorrowedToken::IntType(i.clone()), + Token::IntType(i) => BorrowedToken::IntType(i.clone()), Token::Less => BorrowedToken::Less, Token::LessEqual => BorrowedToken::LessEqual, Token::Greater => BorrowedToken::Greater, @@ -312,7 +312,7 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { Token::DollarSign => BorrowedToken::DollarSign, Token::EOF => BorrowedToken::EOF, Token::Invalid(c) => BorrowedToken::Invalid(*c), - Token::Whitespace(ref s) => BorrowedToken::Whitespace(s), + Token::Whitespace(s) => BorrowedToken::Whitespace(s), Token::UnquoteMarker(id) => BorrowedToken::UnquoteMarker(*id), } } @@ -320,7 +320,7 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { #[derive(Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] pub enum FmtStrFragment { String(String), - Interpolation(String, Span), + Interpolation(String, Location), } impl Display for FmtStrFragment { @@ -339,7 +339,7 @@ impl Display for FmtStrFragment { .replace('\"', "\\\""); write!(f, "{}", string) } - FmtStrFragment::Interpolation(string, _span) => { + FmtStrFragment::Interpolation(string, _) => { write!(f, "{{{}}}", string) } } @@ -352,6 +352,63 @@ pub enum DocStyle { Inner, } +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct LocatedToken(Located); + +impl PartialEq for Token { + fn eq(&self, other: &LocatedToken) -> bool { + self == &other.0.contents + } +} +impl PartialEq for LocatedToken { + fn eq(&self, other: &Token) -> bool { + &self.0.contents == other + } +} + +impl From for Token { + fn from(spt: LocatedToken) -> Self { + spt.0.contents + } +} + +impl<'a> From<&'a LocatedToken> for &'a Token { + fn from(spt: &'a LocatedToken) -> Self { + &spt.0.contents + } +} + +impl LocatedToken { + pub fn new(token: Token, location: Location) -> LocatedToken { + LocatedToken(Located::from(location, token)) + } + pub fn location(&self) -> Location { + self.0.location() + } + pub fn span(&self) -> Span { + self.0.span() + } + pub fn token(&self) -> &Token { + &self.0.contents + } + pub fn into_token(self) -> Token { + self.0.contents + } + pub fn kind(&self) -> TokenKind { + self.token().kind() + } + pub fn into_spanned_token(self) -> SpannedToken { + let span = self.span(); + SpannedToken::new(self.into_token(), span) + } +} + +impl std::fmt::Display for LocatedToken { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.token().fmt(f) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct SpannedToken(Spanned); @@ -382,7 +439,7 @@ impl SpannedToken { pub fn new(token: Token, span: Span) -> SpannedToken { SpannedToken(Spanned::from(span, token)) } - pub fn to_span(&self) -> Span { + pub fn span(&self) -> Span { self.0.span() } pub fn token(&self) -> &Token { @@ -521,7 +578,7 @@ pub enum TokenKind { impl fmt::Display for TokenKind { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - TokenKind::Token(ref tok) => write!(f, "{tok}"), + TokenKind::Token(tok) => write!(f, "{tok}"), TokenKind::Ident => write!(f, "identifier"), TokenKind::Literal => write!(f, "literal"), TokenKind::Keyword => write!(f, "keyword"), @@ -743,11 +800,11 @@ impl Attributes { } pub fn is_foldable(&self) -> bool { - self.function().map_or(false, |func_attribute| func_attribute.is_foldable()) + self.function().is_some_and(|func_attribute| func_attribute.is_foldable()) } pub fn is_no_predicates(&self) -> bool { - self.function().map_or(false, |func_attribute| func_attribute.is_no_predicates()) + self.function().is_some_and(|func_attribute| func_attribute.is_no_predicates()) } pub fn has_varargs(&self) -> bool { @@ -869,9 +926,9 @@ impl fmt::Display for FunctionAttribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { FunctionAttribute::Test(scope) => write!(f, "#[test{scope}]"), - FunctionAttribute::Foreign(ref k) => write!(f, "#[foreign({k})]"), - FunctionAttribute::Builtin(ref k) => write!(f, "#[builtin({k})]"), - FunctionAttribute::Oracle(ref k) => write!(f, "#[oracle({k})]"), + FunctionAttribute::Foreign(k) => write!(f, "#[foreign({k})]"), + FunctionAttribute::Builtin(k) => write!(f, "#[builtin({k})]"), + FunctionAttribute::Oracle(k) => write!(f, "#[oracle({k})]"), FunctionAttribute::Fold => write!(f, "#[fold]"), FunctionAttribute::NoPredicates => write!(f, "#[no_predicates]"), FunctionAttribute::InlineAlways => write!(f, "#[inline_always]"), @@ -944,18 +1001,18 @@ impl SecondaryAttribute { pub(crate) fn contents(&self) -> String { match self { SecondaryAttribute::Deprecated(None) => "deprecated".to_string(), - SecondaryAttribute::Deprecated(Some(ref note)) => { + SecondaryAttribute::Deprecated(Some(note)) => { format!("deprecated({note:?})") } - SecondaryAttribute::Tag(ref attribute) => format!("'{}", attribute.contents), - SecondaryAttribute::Meta(ref meta) => meta.to_string(), + SecondaryAttribute::Tag(attribute) => format!("'{}", attribute.contents), + SecondaryAttribute::Meta(meta) => meta.to_string(), SecondaryAttribute::ContractLibraryMethod => "contract_library_method".to_string(), SecondaryAttribute::Export => "export".to_string(), - SecondaryAttribute::Field(ref k) => format!("field({k})"), - SecondaryAttribute::Abi(ref k) => format!("abi({k})"), + SecondaryAttribute::Field(k) => format!("field({k})"), + SecondaryAttribute::Abi(k) => format!("abi({k})"), SecondaryAttribute::Varargs => "varargs".to_string(), SecondaryAttribute::UseCallersScope => "use_callers_scope".to_string(), - SecondaryAttribute::Allow(ref k) => format!("allow({k})"), + SecondaryAttribute::Allow(k) => format!("allow({k})"), } } } @@ -970,7 +1027,7 @@ impl fmt::Display for SecondaryAttribute { pub struct MetaAttribute { pub name: Path, pub arguments: Vec, - pub span: Span, + pub location: Location, } impl Display for MetaAttribute { @@ -996,13 +1053,9 @@ pub struct CustomAttribute { impl CustomAttribute { fn name(&self) -> Option { - let mut lexer = Lexer::new(&self.contents); + let mut lexer = Lexer::new_with_dummy_file(&self.contents); let token = lexer.next()?.ok()?; - if let Token::Ident(ident) = token.into_token() { - Some(ident) - } else { - None - } + if let Token::Ident(ident) = token.into_token() { Some(ident) } else { None } } } @@ -1202,7 +1255,7 @@ impl Keyword { } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Tokens(pub Vec); +pub struct Tokens(pub Vec); #[cfg(test)] mod keywords { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lib.rs b/noir/noir-repo/compiler/noirc_frontend/src/lib.rs index 9d98b125e324..88a32b2717cc 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lib.rs @@ -9,6 +9,8 @@ #![warn(unused_crate_dependencies, unused_extern_crates)] #![warn(unreachable_pub)] #![warn(clippy::semicolon_if_nothing_returned)] +// Temporary allows. +#![allow(clippy::mutable_key_type, clippy::result_large_err)] pub mod ast; pub mod debug; @@ -20,6 +22,7 @@ pub mod monomorphization; pub mod node_interner; pub mod parser; pub mod resolve_locations; +pub mod signed_field; pub mod usage_tracker; pub mod hir; @@ -29,7 +32,7 @@ pub mod hir_def; pub use lexer::token; // Parser API -pub use parser::{parse_program, ParsedModule}; +pub use parser::{ParsedModule, parse_program, parse_program_with_dummy_file}; // Type API pub use hir_def::types::*; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs index e68a5d8c5d88..6c3925f3e41c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs @@ -1,5 +1,5 @@ use fm::FileId; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use rangemap::RangeMap; use rustc_hash::FxHashMap as HashMap; @@ -36,21 +36,19 @@ impl LocationIndices { pub struct ReferencesTracker<'a> { interner: &'a mut NodeInterner, - file_id: FileId, } impl<'a> ReferencesTracker<'a> { - pub fn new(interner: &'a mut NodeInterner, file_id: FileId) -> Self { - Self { interner, file_id } + pub fn new(interner: &'a mut NodeInterner) -> Self { + Self { interner } } pub(crate) fn add_reference( &mut self, module_def_id: ModuleDefId, - span: Span, + location: Location, is_self_type: bool, ) { - let location = Location::new(span, self.file_id); self.interner.add_module_def_id_reference(module_def_id, location, is_self_type); } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/ast.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/ast.rs index 6cd5073aadb9..da86a466fdbc 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -1,15 +1,15 @@ use std::{collections::BTreeMap, fmt::Display}; -use acvm::FieldElement; use iter_extended::vecmap; use noirc_errors::{ - debug_info::{DebugFunctions, DebugTypes, DebugVariables}, Location, + debug_info::{DebugFunctions, DebugTypes, DebugVariables}, }; use crate::{ ast::{BinaryOpKind, IntegerBitSize, Signedness, Visibility}, hir_def::expr::Constructor, + signed_field::SignedField, token::{Attributes, FunctionAttribute}, }; use crate::{hir_def::function::FunctionSignature, token::FmtStrFragment}; @@ -123,7 +123,7 @@ pub struct While { pub enum Literal { Array(ArrayLiteral), Slice(ArrayLiteral), - Integer(FieldElement, /*sign*/ bool, Type, Location), // false for positive integer and true for negative + Integer(SignedField, Type, Location), Bool(bool), Unit, Str(String), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/debug.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/debug.rs index 3b399c757063..680e78828115 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/debug.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/debug.rs @@ -1,12 +1,13 @@ use acvm::acir::AcirField; use iter_extended::vecmap; -use noirc_errors::debug_info::DebugVarId; use noirc_errors::Location; +use noirc_errors::debug_info::DebugVarId; use noirc_printable_type::PrintableType; use crate::debug::{SourceFieldId, SourceVarId}; use crate::hir_def::expr::*; use crate::node_interner::ExprId; +use crate::signed_field::SignedField; use super::ast::{Expression, Ident}; use super::{MonomorphizationError, Monomorphizer}; @@ -28,7 +29,7 @@ impl From for SourceFieldId { } } -impl<'interner> Monomorphizer<'interner> { +impl Monomorphizer<'_> { /// Patch instrumentation calls inserted for debugging. This will record /// tracked variables and their types, and assign them an ID to use at /// runtime. This ID is different from the source ID assigned at @@ -68,14 +69,14 @@ impl<'interner> Monomorphizer<'interner> { ) -> Result<(), MonomorphizationError> { let hir_arguments = vecmap(&call.arguments, |id| self.interner.expression(id)); let var_id_arg = hir_arguments.get(DEBUG_VAR_ID_ARG_SLOT); - let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id, _))) = var_id_arg else { + let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id))) = var_id_arg else { unreachable!("Missing source_var_id in __debug_var_assign call"); }; // instantiate tracked variable for the value type and associate it with // the ID used by the injected instrumentation code let var_type = self.interner.id_type(call.arguments[DEBUG_VALUE_ARG_SLOT]); - let source_var_id = source_var_id.to_u128().into(); + let source_var_id = source_var_id.field.to_u128().into(); // then update the ID used for tracking at runtime let var_id = self.debug_type_tracker.insert_var(source_var_id, &var_type); let interned_var_id = self.intern_var_id(var_id, &call.location); @@ -93,11 +94,11 @@ impl<'interner> Monomorphizer<'interner> { ) -> Result<(), MonomorphizationError> { let hir_arguments = vecmap(&call.arguments, |id| self.interner.expression(id)); let var_id_arg = hir_arguments.get(DEBUG_VAR_ID_ARG_SLOT); - let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id, _))) = var_id_arg else { + let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id))) = var_id_arg else { unreachable!("Missing source_var_id in __debug_var_drop call"); }; // update variable ID for tracked drops (ie. when the var goes out of scope) - let source_var_id = source_var_id.to_u128().into(); + let source_var_id = source_var_id.field.to_u128().into(); let var_id = self .debug_type_tracker .get_var_id(source_var_id) @@ -121,11 +122,11 @@ impl<'interner> Monomorphizer<'interner> { ) -> Result<(), MonomorphizationError> { let hir_arguments = vecmap(&call.arguments, |id| self.interner.expression(id)); let var_id_arg = hir_arguments.get(DEBUG_VAR_ID_ARG_SLOT); - let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id, _))) = var_id_arg else { + let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id))) = var_id_arg else { unreachable!("Missing source_var_id in __debug_member_assign call"); }; // update variable member assignments - let source_var_id = source_var_id.to_u128().into(); + let source_var_id = source_var_id.field.to_u128().into(); let var_type = self .debug_type_tracker @@ -134,11 +135,11 @@ impl<'interner> Monomorphizer<'interner> { .clone(); let mut cursor_type = &var_type; for i in 0..arity { - if let Some(HirExpression::Literal(HirLiteral::Integer(fe_i, i_neg))) = + if let Some(HirExpression::Literal(HirLiteral::Integer(fe_i))) = hir_arguments.get(DEBUG_MEMBER_FIELD_INDEX_ARG_SLOT + i) { - let index = fe_i.to_i128().unsigned_abs(); - if *i_neg { + let index = fe_i.field.to_i128().unsigned_abs(); + if fe_i.is_negative { // We use negative indices at instrumentation time to indicate // and reference member accesses by name which cannot be // resolved until we have a type. This strategy is also used @@ -152,15 +153,10 @@ impl<'interner> Monomorphizer<'interner> { }); cursor_type = element_type_at_index(cursor_type, field_index); - let index_id = self.interner.push_expr(HirExpression::Literal( - HirLiteral::Integer(field_index.into(), false), - )); + let integer = HirLiteral::Integer(SignedField::positive(field_index)); + let index_id = self.interner.push_expr(HirExpression::Literal(integer)); self.interner.push_expr_type(index_id, crate::Type::FieldElement); - self.interner.push_expr_location( - index_id, - call.location.span, - call.location.file, - ); + self.interner.push_expr_location(index_id, call.location); arguments[DEBUG_MEMBER_FIELD_INDEX_ARG_SLOT + i] = self.expr(index_id)?; } else { // array/string element using constant index @@ -182,10 +178,11 @@ impl<'interner> Monomorphizer<'interner> { } fn intern_var_id(&mut self, var_id: DebugVarId, location: &Location) -> ExprId { - let var_id_literal = HirLiteral::Integer((var_id.0 as u128).into(), false); + let value = SignedField::positive(var_id.0); + let var_id_literal = HirLiteral::Integer(value); let expr_id = self.interner.push_expr(HirExpression::Literal(var_id_literal)); self.interner.push_expr_type(expr_id, crate::Type::FieldElement); - self.interner.push_expr_location(expr_id, location.span, location.file); + self.interner.push_expr_location(expr_id, *location); expr_id } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs index c137a6fc90ab..93a12a46591b 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs @@ -1,8 +1,8 @@ -use noirc_errors::{CustomDiagnostic, FileDiagnostic, Location}; +use noirc_errors::{CustomDiagnostic, Location}; use crate::{ - hir::{comptime::InterpreterError, type_check::TypeCheckError}, Type, + hir::{comptime::InterpreterError, type_check::TypeCheckError}, }; #[derive(Debug)] @@ -29,23 +29,14 @@ impl MonomorphizationError { | MonomorphizationError::CheckedTransmuteFailed { location, .. } | MonomorphizationError::CheckedCastFailed { location, .. } | MonomorphizationError::NoDefaultType { location, .. } => *location, - MonomorphizationError::InterpreterError(error) => error.get_location(), + MonomorphizationError::InterpreterError(error) => error.location(), } } } -impl From for FileDiagnostic { - fn from(error: MonomorphizationError) -> FileDiagnostic { - let location = error.location(); - let call_stack = vec![location]; - let diagnostic = error.into_diagnostic(); - diagnostic.with_call_stack(call_stack).in_file(location.file) - } -} - -impl MonomorphizationError { - fn into_diagnostic(self) -> CustomDiagnostic { - let message = match &self { +impl From for CustomDiagnostic { + fn from(error: MonomorphizationError) -> CustomDiagnostic { + let message = match &error { MonomorphizationError::UnknownArrayLength { length, err, .. } => { format!("Could not determine array length `{length}`, encountered error: `{err}`") } @@ -61,7 +52,7 @@ impl MonomorphizationError { MonomorphizationError::NoDefaultType { location } => { let message = "Type annotation needed".into(); let secondary = "Could not determine type of generic argument".into(); - return CustomDiagnostic::simple_error(message, secondary, location.span); + return CustomDiagnostic::simple_error(message, secondary, *location); } MonomorphizationError::InterpreterError(error) => return error.into(), MonomorphizationError::InternalError { message, .. } => message.to_string(), @@ -69,16 +60,16 @@ impl MonomorphizationError { let message = format!("Comptime function {name} used in runtime code"); let secondary = "Comptime functions must be in a comptime block to be called".into(); - return CustomDiagnostic::simple_error(message, secondary, location.span); + return CustomDiagnostic::simple_error(message, secondary, *location); } MonomorphizationError::ComptimeTypeInRuntimeCode { typ, location } => { let message = format!("Comptime-only type `{typ}` used in runtime code"); let secondary = "Comptime type used here".into(); - return CustomDiagnostic::simple_error(message, secondary, location.span); + return CustomDiagnostic::simple_error(message, secondary, *location); } }; - let location = self.location(); - CustomDiagnostic::simple_error(message, String::new(), location.span) + let location = error.location(); + CustomDiagnostic::simple_error(message, String::new(), location) } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs index d30229ce97d5..62ed1ef2e684 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -12,8 +12,10 @@ use crate::ast::{FunctionKind, IntegerBitSize, Signedness, UnaryOp, Visibility}; use crate::hir::comptime::InterpreterError; use crate::hir::type_check::{NoMatchingImplFoundError, TypeCheckError}; use crate::node_interner::{ExprId, GlobalValue, ImplSearchErrorKind}; +use crate::signed_field::SignedField; use crate::token::FmtStrFragment; use crate::{ + Kind, Type, TypeBinding, TypeBindings, debug::DebugInstrumenter, hir_def::{ expr::*, @@ -22,9 +24,8 @@ use crate::{ types, }, node_interner::{self, DefinitionKind, NodeInterner, StmtId, TraitImplKind, TraitMethodId}, - Kind, Type, TypeBinding, TypeBindings, }; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use ast::{GlobalId, While}; use fxhash::FxHashMap as HashMap; use iter_extended::{btree_map, try_vecmap, vecmap}; @@ -481,10 +482,10 @@ impl<'interner> Monomorphizer<'interner> { )) } HirExpression::Literal(HirLiteral::Bool(value)) => Literal(Bool(value)), - HirExpression::Literal(HirLiteral::Integer(value, sign)) => { + HirExpression::Literal(HirLiteral::Integer(value)) => { let location = self.interner.id_location(expr); let typ = Self::convert_type(&self.interner.id_type(expr), location)?; - Literal(Integer(value, sign, typ, location)) + Literal(Integer(value, typ, location)) } HirExpression::Literal(HirLiteral::Array(array)) => match array { HirArrayLiteral::Standard(array) => self.standard_array(expr, array, false)?, @@ -602,7 +603,9 @@ impl<'interner> Monomorphizer<'interner> { HirExpression::Lambda(lambda) => self.lambda(lambda, expr)?, HirExpression::MethodCall(hir_method_call) => { - unreachable!("Encountered HirExpression::MethodCall during monomorphization {hir_method_call:?}") + unreachable!( + "Encountered HirExpression::MethodCall during monomorphization {hir_method_call:?}" + ) } HirExpression::Error => unreachable!("Encountered Error node during monomorphization"), HirExpression::Quote(_) => unreachable!("quote expression remaining in runtime code"), @@ -646,7 +649,7 @@ impl<'interner> Monomorphizer<'interner> { let location = self.interner.expr_location(&array); let typ = Self::convert_type(&self.interner.id_type(array), location)?; - let length = length.evaluate_to_u32(location.span).map_err(|err| { + let length = length.evaluate_to_u32(location).map_err(|err| { let location = self.interner.expr_location(&array); MonomorphizationError::UnknownArrayLength { location, err, length } })?; @@ -819,7 +822,8 @@ impl<'interner> Monomorphizer<'interner> { })?; let tag_value = FieldElement::from(constructor.variant_index); - let tag = ast::Literal::Integer(tag_value, false, ast::Type::Field, location); + let tag_value = SignedField::positive(tag_value); + let tag = ast::Literal::Integer(tag_value, ast::Type::Field, location); fields.insert(0, ast::Expression::Literal(tag)); Ok(ast::Expression::Tuple(fields)) @@ -1027,7 +1031,7 @@ impl<'interner> Monomorphizer<'interner> { binding .evaluate_to_field_element( &Kind::Numeric(numeric_typ.clone()), - location.span, + location, ) .map_err(|err| MonomorphizationError::UnknownArrayLength { length: binding.clone(), @@ -1044,7 +1048,8 @@ impl<'interner> Monomorphizer<'interner> { } let typ = Self::convert_type(&typ, ident.location)?; - ast::Expression::Literal(ast::Literal::Integer(value, false, typ, location)) + let value = SignedField::positive(value); + ast::Expression::Literal(ast::Literal::Integer(value, typ, location)) } }; @@ -1078,7 +1083,9 @@ impl<'interner> Monomorphizer<'interner> { .map_err(MonomorphizationError::InterpreterError)?; (expr, is_closure) } else { - unreachable!("All global values should be resolved at compile time and before monomorphization"); + unreachable!( + "All global values should be resolved at compile time and before monomorphization" + ); }; let expr = self.expr(expr)?; @@ -1119,7 +1126,7 @@ impl<'interner> Monomorphizer<'interner> { HirType::Integer(sign, bits) => ast::Type::Integer(*sign, *bits), HirType::Bool => ast::Type::Bool, HirType::String(size) => { - let size = match size.evaluate_to_u32(location.span) { + let size = match size.evaluate_to_u32(location) { Ok(size) => size, // only default variable sizes to size 0 Err(TypeCheckError::NonConstantEvaluated { .. }) => 0, @@ -1135,7 +1142,7 @@ impl<'interner> Monomorphizer<'interner> { ast::Type::String(size) } HirType::FmtString(size, fields) => { - let size = match size.evaluate_to_u32(location.span) { + let size = match size.evaluate_to_u32(location) { Ok(size) => size, // only default variable sizes to size 0 Err(TypeCheckError::NonConstantEvaluated { .. }) => 0, @@ -1154,7 +1161,7 @@ impl<'interner> Monomorphizer<'interner> { HirType::Unit => ast::Type::Unit, HirType::Array(length, element) => { let element = Box::new(Self::convert_type(element.as_ref(), location)?); - let length = match length.evaluate_to_u32(location.span) { + let length = match length.evaluate_to_u32(location) { Ok(length) => length, Err(err) => { let length = length.as_ref().clone(); @@ -1175,7 +1182,7 @@ impl<'interner> Monomorphizer<'interner> { unreachable!("All TraitAsType should be replaced before calling convert_type"); } HirType::NamedGeneric(binding, _) => { - if let TypeBinding::Bound(ref binding) = &*binding.borrow() { + if let TypeBinding::Bound(binding) = &*binding.borrow() { return Self::convert_type(binding, location); } @@ -1191,12 +1198,12 @@ impl<'interner> Monomorphizer<'interner> { Self::convert_type(to, location)? } - HirType::TypeVariable(ref binding) => { + HirType::TypeVariable(binding) => { let type_var_kind = match &*binding.borrow() { - TypeBinding::Bound(ref binding) => { + TypeBinding::Bound(binding) => { return Self::convert_type(binding, location); } - TypeBinding::Unbound(_, ref type_var_kind) => type_var_kind.clone(), + TypeBinding::Unbound(_, type_var_kind) => type_var_kind.clone(), }; // Default any remaining unbound type variables. @@ -1321,18 +1328,18 @@ impl<'interner> Monomorphizer<'interner> { 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(ref binding) = &*binding.borrow() { + if let TypeBinding::Bound(binding) = &*binding.borrow() { return Self::check_type(binding, location); } Ok(()) } - HirType::TypeVariable(ref binding) => { + HirType::TypeVariable(binding) => { let type_var_kind = match &*binding.borrow() { TypeBinding::Bound(binding) => { return Self::check_type(binding, location); } - TypeBinding::Unbound(_, ref type_var_kind) => type_var_kind.clone(), + TypeBinding::Unbound(_, type_var_kind) => type_var_kind.clone(), }; // Default any remaining unbound type variables. @@ -1402,14 +1409,11 @@ impl<'interner> Monomorphizer<'interner> { location, }); } - let to_value = to.evaluate_to_field_element(&to.kind(), location.span); + let to_value = to.evaluate_to_field_element(&to.kind(), location); if to_value.is_ok() { let skip_simplifications = false; - let from_value = from.evaluate_to_field_element_helper( - &to.kind(), - location.span, - skip_simplifications, - ); + let from_value = + from.evaluate_to_field_element_helper(&to.kind(), location, skip_simplifications); if from_value.is_err() || from_value.unwrap() != to_value.clone().unwrap() { return Err(MonomorphizationError::CheckedCastFailed { actual: HirType::Constant(to_value.unwrap(), to.kind()), @@ -1628,12 +1632,11 @@ impl<'interner> Monomorphizer<'interner> { let location = self.interner.expr_location(expr_id); return Ok(match opcode.as_str() { "modulus_num_bits" => { - let bits = (FieldElement::max_num_bits() as u128).into(); + let bits = FieldElement::max_num_bits(); let typ = ast::Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour); - Some(ast::Expression::Literal(ast::Literal::Integer( - bits, false, typ, location, - ))) + let bits = SignedField::positive(bits); + Some(ast::Expression::Literal(ast::Literal::Integer(bits, typ, location))) } "zeroed" => { let location = self.interner.expr_location(expr_id); @@ -1697,12 +1700,8 @@ impl<'interner> Monomorphizer<'interner> { let int_type = Type::Integer(crate::ast::Signedness::Unsigned, arr_elem_bits); let bytes_as_expr = vecmap(bytes, |byte| { - Expression::Literal(Literal::Integer( - (byte as u128).into(), - false, - int_type.clone(), - location, - )) + let value = SignedField::positive(byte as u32); + Expression::Literal(Literal::Integer(value, int_type.clone(), location)) }); let typ = Type::Slice(Box::new(int_type)); @@ -2062,7 +2061,8 @@ impl<'interner> Monomorphizer<'interner> { match typ { ast::Type::Field | ast::Type::Integer(..) => { let typ = typ.clone(); - ast::Expression::Literal(ast::Literal::Integer(0_u128.into(), false, typ, location)) + let zero = SignedField::positive(0u32); + ast::Expression::Literal(ast::Literal::Integer(zero, typ, location)) } ast::Type::Bool => ast::Expression::Literal(ast::Literal::Bool(false)), ast::Type::Unit => ast::Expression::Literal(ast::Literal::Unit), @@ -2080,7 +2080,9 @@ impl<'interner> Monomorphizer<'interner> { let zeroed_tuple = self.zeroed_value_of_type(fields, location); let fields_len = match &zeroed_tuple { ast::Expression::Tuple(fields) => fields.len() as u64, - _ => unreachable!("ICE: format string fields should be structured in a tuple, but got a {zeroed_tuple}"), + _ => unreachable!( + "ICE: format string fields should be structured in a tuple, but got a {zeroed_tuple}" + ), }; ast::Expression::Literal(ast::Literal::FmtStr( vec![FmtStrFragment::String("\0".repeat(*length as usize))], @@ -2217,8 +2219,8 @@ impl<'interner> Monomorphizer<'interner> { let operator = if matches!(operator.kind, Less | Greater) { Equal } else { NotEqual }; - let int_value = - ast::Literal::Integer(ordering_value, false, ast::Type::Field, location); + let ordering_value = SignedField::positive(ordering_value); + let int_value = ast::Literal::Integer(ordering_value, ast::Type::Field, location); let rhs = Box::new(ast::Expression::Literal(int_value)); let lhs = Box::new(ast::Expression::ExtractTupleField(Box::new(result), 0)); @@ -2375,10 +2377,9 @@ pub fn resolve_trait_method( } Err(ImplSearchErrorKind::Nested(constraints)) => { if let Some(error) = - NoMatchingImplFoundError::new(interner, constraints, location.span) + NoMatchingImplFoundError::new(interner, constraints, location) { - let file = location.file; - return Err(InterpreterError::NoMatchingImplFound { error, file }); + return Err(InterpreterError::NoMatchingImplFound { error }); } else { return Err(InterpreterError::NoImpl { location }); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/printer.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/printer.rs index df4340b4e0d3..bf783f50f000 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/printer.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/printer.rs @@ -105,7 +105,7 @@ impl AstPrinter { self.print_comma_separated(&array.contents, f)?; write!(f, "]") } - super::ast::Literal::Integer(x, _, _, _) => x.fmt(f), + super::ast::Literal::Integer(x, _, _) => x.fmt(f), super::ast::Literal::Bool(x) => x.fmt(f), super::ast::Literal::Str(s) => write!(f, "\"{s}\""), super::ast::Literal::FmtStr(fragments, _, _) => { 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 f995b20bc1a7..e599b99b3fda 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -6,12 +6,14 @@ use std::marker::Copy; use fm::FileId; use iter_extended::vecmap; use noirc_arena::{Arena, Index}; -use noirc_errors::{Location, Span, Spanned}; +use noirc_errors::Located; +use noirc_errors::{Location, Span}; use petgraph::algo::tarjan_scc; use petgraph::prelude::DiGraph; use petgraph::prelude::NodeIndex as PetGraphIndex; use rustc_hash::FxHashMap as HashMap; +use crate::QuotedType; use crate::ast::{ ExpressionKind, Ident, LValue, Pattern, StatementKind, UnaryOp, UnresolvedTypeData, }; @@ -25,8 +27,9 @@ use crate::hir::type_check::generics::TraitGenerics; use crate::hir_def::traits::NamedType; use crate::hir_def::traits::ResolvedTraitBound; use crate::locations::AutoImportEntry; -use crate::QuotedType; +use crate::GenericTypeVars; +use crate::Generics; use crate::ast::{BinaryOpKind, FunctionDefinition, ItemVisibility}; use crate::hir::resolution::errors::ResolverError; use crate::hir_def::expr::HirIdent; @@ -41,8 +44,6 @@ use crate::hir_def::{ }; use crate::locations::LocationIndices; use crate::token::{Attributes, SecondaryAttribute}; -use crate::GenericTypeVars; -use crate::Generics; use crate::{Shared, TypeAlias, TypeBindings, TypeVariable, TypeVariableId}; /// An arbitrary number to limit the recursion depth when searching for trait impls. @@ -358,16 +359,19 @@ pub enum ImplSearchErrorKind { /// as long as these specialized impls do not overlap. E.g. `impl Struct` and `impl Struct` #[derive(Default, Debug, Clone)] pub struct Methods { - pub direct: Vec, + pub direct: Vec, pub trait_impl_methods: Vec, } +#[derive(Debug, Clone)] +pub struct ImplMethod { + pub typ: Type, + pub method: FuncId, +} + #[derive(Debug, Clone)] pub struct TraitImplMethod { - // This type is only stored for primitive types to be able to - // select the correct static methods between multiple options keyed - // under TypeMethodKey::FieldOrInt - pub typ: Option, + pub typ: Type, pub method: FuncId, pub trait_id: TraitId, } @@ -721,9 +725,18 @@ impl NodeInterner { ExprId(self.nodes.insert(Node::Expression(expr))) } + /// Intern an expression with everything needed for it (location & Type) + /// instead of requiring they be pushed later. + pub fn push_expr_full(&mut self, expr: HirExpression, location: Location, typ: Type) -> ExprId { + let id = self.push_expr(expr); + self.push_expr_location(id, location); + self.push_expr_type(id, typ); + id + } + /// Stores the span for an interned expression. - pub fn push_expr_location(&mut self, expr_id: ExprId, span: Span, file: FileId) { - self.id_to_location.insert(expr_id.into(), Location::new(span, file)); + pub fn push_expr_location(&mut self, expr_id: ExprId, location: Location) { + self.id_to_location.insert(expr_id.into(), location); } /// Interns a HIR Function. @@ -752,7 +765,7 @@ impl NodeInterner { id: type_id, name: unresolved_trait.trait_def.name.clone(), crate_id: unresolved_trait.crate_id, - location: Location::new(unresolved_trait.trait_def.span, unresolved_trait.file_id), + location: unresolved_trait.trait_def.name.location(), generics, visibility: ItemVisibility::Private, self_type_typevar: TypeVariable::unbound(self.next_type_variable_id(), Kind::Normal), @@ -797,7 +810,7 @@ impl NodeInterner { self.type_aliases.push(Shared::new(TypeAlias::new( type_id, typ.type_alias_def.name.clone(), - Location::new(typ.type_alias_def.span, typ.file_id), + typ.type_alias_def.location, Type::Error, generics, ))); @@ -910,11 +923,11 @@ impl NodeInterner { comptime: bool, ) -> GlobalId { let statement = self.push_stmt(HirStatement::Error); - let span = name.span(); + let location = name.location(); let id = self .push_global(name, local_id, crate_id, statement, file, attributes, mutable, comptime); - self.push_stmt_location(statement, span, file); + self.push_stmt_location(statement, location); id } @@ -1089,8 +1102,8 @@ impl NodeInterner { pub fn function_ident(&self, func_id: &FuncId) -> crate::ast::Ident { let name = self.function_name(func_id).to_owned(); - let span = self.function_meta(func_id).name.location.span; - crate::ast::Ident(Spanned::from(span, name)) + let location = self.function_meta(func_id).name.location; + crate::ast::Ident(Located::from(location, name)) } pub fn function_name(&self, func_id: &FuncId) -> &str { @@ -1154,7 +1167,9 @@ impl NodeInterner { HirStatement::Let(let_stmt) => Some(let_stmt.clone()), HirStatement::Error => None, other => { - panic!("ice: all globals should correspond to a let statement in the interner: {other:?}") + panic!( + "ice: all globals should correspond to a let statement in the interner: {other:?}" + ) } }, _ => panic!("ice: all globals should correspond to a statement in the interner"), @@ -1222,8 +1237,8 @@ impl NodeInterner { self.id_location(stmt_id) } - pub fn push_stmt_location(&mut self, id: StmtId, span: Span, file: FileId) { - self.id_to_location.insert(id.into(), Location::new(span, file)); + pub fn push_stmt_location(&mut self, id: StmtId, location: Location) { + self.id_to_location.insert(id.into(), location); } pub fn get_type(&self, id: TypeId) -> Shared { @@ -1401,7 +1416,9 @@ impl NodeInterner { }); if trait_id.is_none() && matches!(self_type, Type::DataType(..)) { - if let Some(existing) = self.lookup_direct_method(self_type, &method_name, true) + let check_self_param = false; + if let Some(existing) = + self.lookup_direct_method(self_type, &method_name, check_self_param) { return Some(existing); } @@ -1409,8 +1426,7 @@ impl NodeInterner { // Only remember the actual type if it's FieldOrInt, // so later we can disambiguate on calls like `u32::call`. - let typ = - if key == TypeMethodKey::FieldOrInt { Some(self_type.clone()) } else { None }; + let typ = self_type.clone(); self.methods .entry(key) .or_default() @@ -1512,7 +1528,7 @@ impl NodeInterner { trait_bound: ResolvedTraitBound { trait_id, trait_generics: TraitGenerics { ordered, named }, - span: Span::default(), + location: Location::dummy(), }, } }; @@ -1596,7 +1612,7 @@ impl NodeInterner { trait_bound: ResolvedTraitBound { trait_id, trait_generics, - span: Span::default(), + location: Location::dummy(), }, }; matching_impls.push((impl_kind.clone(), fresh_bindings, constraint)); @@ -1707,7 +1723,7 @@ impl NodeInterner { impl_id: TraitImplId, impl_generics: GenericTypeVars, trait_impl: Shared, - ) -> Result<(), (Span, FileId)> { + ) -> Result<(), Location> { self.trait_implementations.insert(impl_id, trait_impl.clone()); // Avoid adding error types to impls since they'll conflict with every other type. @@ -1759,7 +1775,7 @@ impl NodeInterner { ) { let existing_impl = self.get_trait_implementation(existing); let existing_impl = existing_impl.borrow(); - return Err((existing_impl.ident.span(), existing_impl.file)); + return Err(existing_impl.ident.location()); } for method in &trait_impl.borrow().methods { @@ -1777,18 +1793,20 @@ impl NodeInterner { } /// Looks up a method that's directly defined in the given type. + /// If `check_self_param` is `true`, only a method that has a `self` parameter with a type + /// that unifies with `typ` will be returned. pub fn lookup_direct_method( &self, typ: &Type, method_name: &str, - has_self_arg: bool, + check_self_param: bool, ) -> Option { let key = get_type_method_key(typ)?; self.methods .get(&key) .and_then(|h| h.get(method_name)) - .and_then(|methods| methods.find_direct_method(typ, has_self_arg, self)) + .and_then(|methods| methods.find_direct_method(typ, check_self_param, self)) } /// Looks up a methods that apply to the given type but are defined in traits. @@ -2029,15 +2047,14 @@ impl NodeInterner { index } - pub(crate) fn check_for_dependency_cycles(&self) -> Vec<(CompilationError, FileId)> { + pub(crate) fn check_for_dependency_cycles(&self) -> Vec { let strongly_connected_components = tarjan_scc(&self.dependency_graph); let mut errors = Vec::new(); let mut push_error = |item: String, scc: &[_], i, location: Location| { let cycle = self.get_cycle_error_string(scc, i); - let span = location.span; - let error = ResolverError::DependencyCycle { item, cycle, span }; - errors.push((error.into(), location.file)); + let error = ResolverError::DependencyCycle { item, cycle, location }; + errors.push(error.into()); }; for scc in strongly_connected_components { @@ -2146,8 +2163,8 @@ impl NodeInterner { self.push_expression_kind(lvalue.as_expression().kind) } - pub fn get_lvalue(&self, id: InternedExpressionKind, span: Span) -> LValue { - LValue::from_expression_kind(self.get_expression_kind(id).clone(), span) + pub fn get_lvalue(&self, id: InternedExpressionKind, location: Location) -> LValue { + LValue::from_expression_kind(self.get_expression_kind(id).clone(), location) .expect("Called LValue::from_expression with an invalid expression") } @@ -2310,20 +2327,21 @@ impl NodeInterner { } impl Methods { - fn add_method(&mut self, method: FuncId, typ: Option, trait_id: Option) { + fn add_method(&mut self, method: FuncId, typ: Type, trait_id: Option) { if let Some(trait_id) = trait_id { let trait_impl_method = TraitImplMethod { typ, method, trait_id }; self.trait_impl_methods.push(trait_impl_method); } else { - self.direct.push(method); + let impl_method = ImplMethod { typ, method }; + self.direct.push(impl_method); } } /// Iterate through each method, starting with the direct methods - pub fn iter(&self) -> impl Iterator, Option)> { + pub fn iter(&self) -> impl Iterator)> { let trait_impl_methods = - self.trait_impl_methods.iter().map(|m| (m.method, m.typ.as_ref(), Some(m.trait_id))); - let direct = self.direct.iter().copied().map(|func_id| (func_id, None, None)); + self.trait_impl_methods.iter().map(|m| (m.method, &m.typ, Some(m.trait_id))); + let direct = self.direct.iter().map(|method| (method.method, &method.typ, None)); direct.chain(trait_impl_methods) } @@ -2345,12 +2363,12 @@ impl Methods { pub fn find_direct_method( &self, typ: &Type, - has_self_param: bool, + check_self_param: bool, interner: &NodeInterner, ) -> Option { for method in &self.direct { - if Self::method_matches(typ, has_self_param, *method, None, interner) { - return Some(*method); + if Self::method_matches(typ, check_self_param, method.method, &method.typ, interner) { + return Some(method.method); } } @@ -2367,7 +2385,7 @@ impl Methods { for trait_impl_method in &self.trait_impl_methods { let method = trait_impl_method.method; - let method_type = trait_impl_method.typ.as_ref(); + let method_type = &trait_impl_method.typ; let trait_id = trait_impl_method.trait_id; if Self::method_matches(typ, has_self_param, method, method_type, interner) { @@ -2380,14 +2398,14 @@ impl Methods { fn method_matches( typ: &Type, - has_self_param: bool, + check_self_param: bool, method: FuncId, - method_type: Option<&Type>, + method_type: &Type, interner: &NodeInterner, ) -> bool { match interner.function_meta(&method).typ.instantiate(interner).0 { Type::Function(args, _, _, _) => { - if has_self_param { + if check_self_param { if let Some(object) = args.first() { if object.unify(typ).is_ok() { return true; @@ -2401,21 +2419,18 @@ impl Methods { } } } else { - // If we recorded the concrete type this trait impl method belongs to, - // and it matches typ, it's an exact match and we return that. - if let Some(method_type) = method_type { + // We still need to make sure the method is for the given type + // (this might be false if for example a method for `Struct` was added but + // now we are looking for a method in `Struct`) + if method_type.unify(typ).is_ok() { + return true; + } + + // Handle auto-dereferencing `&mut T` into `T` + if let Type::MutableReference(method_type) = method_type { if method_type.unify(typ).is_ok() { return true; } - - // Handle auto-dereferencing `&mut T` into `T` - if let Type::MutableReference(method_type) = method_type { - if method_type.unify(typ).is_ok() { - return true; - } - } - } else { - return true; } } } 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 8345b75dbabf..76e2958f668d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/errors.rs @@ -5,9 +5,10 @@ use crate::token::TokenKind; use small_ord_set::SmallOrdSet; use thiserror::Error; +use crate::elaborator::UnstableFeature; use iter_extended::vecmap; -use noirc_errors::CustomDiagnostic as Diagnostic; use noirc_errors::Span; +use noirc_errors::{CustomDiagnostic as Diagnostic, Location}; use super::labels::ParsingRuleLabel; @@ -61,7 +62,9 @@ pub enum ParserErrorReason { MissingSeparatingSemi, #[error("constrain keyword is deprecated")] ConstrainDeprecated, - #[error("Invalid type expression: '{0}'. Only unsigned integer constants up to `u32`, globals, generics, +, -, *, /, and % may be used in this context.")] + #[error( + "Invalid type expression: '{0}'. Only unsigned integer constants up to `u32`, globals, generics, +, -, *, /, and % may be used in this context." + )] InvalidTypeExpression(Expression), #[error("Early 'return' is unsupported")] EarlyReturn, @@ -75,8 +78,8 @@ pub enum ParserErrorReason { TraitImplVisibilityIgnored, #[error("comptime keyword is deprecated")] ComptimeDeprecated, - #[error("{0} are experimental and aren't fully supported yet")] - ExperimentalFeature(&'static str), + #[error("This requires the unstable feature '{0}' which is not enabled")] + ExperimentalFeature(UnstableFeature), #[error( "Multiple primary attributes found. Only one function attribute is allowed per function" )] @@ -132,42 +135,50 @@ pub struct ParserError { expected_labels: SmallOrdSet<[ParsingRuleLabel; 1]>, found: Token, reason: Option, - span: Span, + location: Location, } impl ParserError { - pub fn empty(found: Token, span: Span) -> ParserError { + pub fn empty(found: Token, location: Location) -> ParserError { ParserError { expected_tokens: SmallOrdSet::new(), expected_labels: SmallOrdSet::new(), found, reason: None, - span, + location, } } - pub fn expected_token(token: Token, found: Token, span: Span) -> ParserError { - let mut error = ParserError::empty(found, span); + pub fn expected_token(token: Token, found: Token, location: Location) -> ParserError { + let mut error = ParserError::empty(found, location); error.expected_tokens.insert(token); error } - pub fn expected_one_of_tokens(tokens: &[Token], found: Token, span: Span) -> ParserError { - let mut error = ParserError::empty(found, span); + pub fn expected_one_of_tokens( + tokens: &[Token], + found: Token, + location: Location, + ) -> ParserError { + let mut error = ParserError::empty(found, location); for token in tokens { error.expected_tokens.insert(token.clone()); } error } - pub fn expected_label(label: ParsingRuleLabel, found: Token, span: Span) -> ParserError { - let mut error = ParserError::empty(found, span); + pub fn expected_label( + label: ParsingRuleLabel, + found: Token, + location: Location, + ) -> ParserError { + let mut error = ParserError::empty(found, location); error.expected_labels.insert(label); error } - pub fn with_reason(reason: ParserErrorReason, span: Span) -> ParserError { - let mut error = ParserError::empty(Token::EOF, span); + pub fn with_reason(reason: ParserErrorReason, location: Location) -> ParserError { + let mut error = ParserError::empty(Token::EOF, location); error.reason = Some(reason); error } @@ -177,7 +188,11 @@ impl ParserError { } pub fn span(&self) -> Span { - self.span + self.location.span + } + + pub fn location(&self) -> Location { + self.location } pub fn reason(&self) -> Option<&ParserErrorReason> { @@ -234,7 +249,7 @@ impl<'a> From<&'a ParserError> for Diagnostic { let mut diagnostic = Diagnostic::simple_error( "Use of deprecated keyword 'constrain'".into(), "The 'constrain' keyword is deprecated. Please use the 'assert' function instead.".into(), - error.span, + error.location(), ); diagnostic.deprecated = true; diagnostic @@ -243,7 +258,7 @@ impl<'a> From<&'a ParserError> for Diagnostic { let mut diagnostic = Diagnostic::simple_warning( "Use of deprecated keyword 'comptime'".into(), "The 'comptime' keyword has been deprecated. It can be removed without affecting your program".into(), - error.span, + error.location(), ) ; diagnostic.deprecated = true; diagnostic @@ -258,46 +273,51 @@ impl<'a> From<&'a ParserError> for Diagnostic { .collect::>() .join(", ") ), - error.span, + error.location(), ), - ParserErrorReason::ExperimentalFeature(_) => { - Diagnostic::simple_warning(reason.to_string(), "".into(), error.span) + ParserErrorReason::ExperimentalFeature(feature) => { + let secondary = format!( + "Pass -Z{feature} to nargo to enable this feature at your own risk." + ); + Diagnostic::simple_error(reason.to_string(), secondary, error.location()) } ParserErrorReason::TraitVisibilityIgnored => { - Diagnostic::simple_warning(reason.to_string(), "".into(), error.span) + Diagnostic::simple_warning(reason.to_string(), "".into(), error.location()) } ParserErrorReason::TraitImplVisibilityIgnored => { - Diagnostic::simple_warning(reason.to_string(), "".into(), error.span) + Diagnostic::simple_warning(reason.to_string(), "".into(), error.location()) } ParserErrorReason::ExpectedPatternButFoundType(ty) => Diagnostic::simple_error( format!("Expected a pattern but found a type - {ty}"), format!("{ty} is a type and cannot be used as a variable name"), - error.span, + error.location(), ), ParserErrorReason::Lexer(error) => error.into(), ParserErrorReason::ExpectedMutAfterAmpersand { found } => Diagnostic::simple_error( format!("Expected `mut` after `&`, found `{found}`"), "Noir doesn't have immutable references, only mutable references".to_string(), - error.span, + error.location(), ), ParserErrorReason::MissingSafetyComment => Diagnostic::simple_warning( "Unsafe block must have a safety comment above it".into(), "The comment must start with the \"Safety: \" word".into(), - error.span, + error.location(), ), ParserErrorReason::MissingParametersForFunctionDefinition => { Diagnostic::simple_error( "Missing parameters for function definition".into(), "Add a parameter list: `()`".into(), - error.span, + error.location(), ) } ParserErrorReason::DocCommentDoesNotDocumentAnything => { let primary = "This doc comment doesn't document anything".to_string(); let secondary = "Consider changing it to a regular `//` comment".to_string(); - Diagnostic::simple_warning(primary, secondary, error.span) + Diagnostic::simple_warning(primary, secondary, error.location()) + } + other => { + Diagnostic::simple_error(format!("{other}"), String::new(), error.location()) } - other => Diagnostic::simple_error(format!("{other}"), String::new(), error.span), }, None => { if matches!( @@ -306,10 +326,10 @@ impl<'a> From<&'a ParserError> for Diagnostic { ) { let primary = "This doc comment doesn't document anything".to_string(); let secondary = "Consider changing it to a regular `//` comment".to_string(); - Diagnostic::simple_warning(primary, secondary, error.span) + Diagnostic::simple_warning(primary, secondary, error.location()) } else { let primary = error.to_string(); - Diagnostic::simple_error(primary, String::new(), error.span) + Diagnostic::simple_error(primary, String::new(), error.location()) } } } 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 c433adbfdfb1..0cecbe0814ab 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs @@ -20,8 +20,10 @@ use crate::token::SecondaryAttribute; pub use errors::ParserError; pub use errors::ParserErrorReason; -use noirc_errors::Span; -pub use parser::{parse_program, Parser, StatementOrExpressionOrLValue}; +use noirc_errors::Location; +pub use parser::{ + Parser, StatementOrExpressionOrLValue, parse_program, parse_program_with_dummy_file, +}; #[derive(Clone, Default)] pub struct SortedModule { @@ -128,7 +130,7 @@ impl ParsedModule { #[derive(Clone, Debug)] pub struct Item { pub kind: ItemKind, - pub span: Span, + pub location: Location, pub doc_comments: Vec, } 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 f4491e84471a..a5ea2ea5fe9e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs @@ -1,14 +1,15 @@ use acvm::FieldElement; +use fm::FileId; use modifiers::Modifiers; -use noirc_errors::Span; +use noirc_errors::{Location, Span}; use crate::{ ast::{Ident, ItemVisibility}, - lexer::{Lexer, SpannedTokenResult}, - token::{FmtStrFragment, IntType, Keyword, SpannedToken, Token, TokenKind, Tokens}, + lexer::{Lexer, lexer::LocatedTokenResult}, + token::{FmtStrFragment, IntType, Keyword, LocatedToken, Token, TokenKind, Tokens}, }; -use super::{labels::ParsingRuleLabel, ParsedModule, ParserError, ParserErrorReason}; +use super::{ParsedModule, ParserError, ParserErrorReason, labels::ParsingRuleLabel}; mod arguments; mod attributes; @@ -47,21 +48,25 @@ pub use statement_or_expression_or_lvalue::StatementOrExpressionOrLValue; /// of the program along with any parsing errors encountered. If the parsing errors /// Vec is non-empty, there may be Error nodes in the Ast to fill in the gaps that /// failed to parse. Otherwise the Ast is guaranteed to have 0 Error nodes. -pub fn parse_program(source_program: &str) -> (ParsedModule, Vec) { - let lexer = Lexer::new(source_program); +pub fn parse_program(source_program: &str, file_id: FileId) -> (ParsedModule, Vec) { + let lexer = Lexer::new(source_program, file_id); let mut parser = Parser::for_lexer(lexer); let program = parser.parse_program(); let errors = parser.errors; (program, errors) } +pub fn parse_program_with_dummy_file(source_program: &str) -> (ParsedModule, Vec) { + parse_program(source_program, FileId::dummy()) +} + enum TokenStream<'a> { Lexer(Lexer<'a>), Tokens(Tokens), } -impl<'a> TokenStream<'a> { - fn next(&mut self) -> Option { +impl TokenStream<'_> { + fn next(&mut self) -> Option { match self { TokenStream::Lexer(lexer) => lexer.next(), TokenStream::Tokens(tokens) => { @@ -80,10 +85,10 @@ pub struct Parser<'a> { // We always have one look-ahead token for these cases: // - check if we get `&` or `&mut` // - check if we get `>` or `>>` - token: SpannedToken, - next_token: SpannedToken, - current_token_span: Span, - previous_token_span: Span, + token: LocatedToken, + next_token: LocatedToken, + current_token_location: Location, + previous_token_location: Location, // We also keep track of comments that appear right before a token, // because `unsafe { }` requires one before it. @@ -111,18 +116,22 @@ impl<'a> Parser<'a> { Self::new(TokenStream::Tokens(tokens)) } - pub fn for_str(str: &'a str) -> Self { - Self::for_lexer(Lexer::new(str)) + pub fn for_str(str: &'a str, file_id: FileId) -> Self { + Self::for_lexer(Lexer::new(str, file_id)) + } + + pub fn for_str_with_dummy_file(str: &'a str) -> Self { + Self::for_str(str, FileId::dummy()) } fn new(tokens: TokenStream<'a>) -> Self { let mut parser = Self { errors: Vec::new(), tokens, - token: eof_spanned_token(), - next_token: eof_spanned_token(), - current_token_span: Default::default(), - previous_token_span: Default::default(), + token: eof_located_token(), + next_token: eof_located_token(), + current_token_location: Location::dummy(), + previous_token_location: Location::dummy(), current_token_comments: String::new(), next_token_comments: String::new(), statement_comments: None, @@ -163,16 +172,12 @@ impl<'a> Parser<'a> { } let all_warnings = self.errors.iter().all(|error| error.is_warning()); - if all_warnings { - Ok((item, self.errors)) - } else { - Err(self.errors) - } + if all_warnings { Ok((item, self.errors)) } else { Err(self.errors) } } /// Bumps this parser by one token. Returns the token that was previously the "current" token. - fn bump(&mut self) -> SpannedToken { - self.previous_token_span = self.current_token_span; + fn bump(&mut self) -> LocatedToken { + self.previous_token_location = self.current_token_location; let (next_next_token, next_next_token_comments) = self.read_token_internal(); let next_token = std::mem::replace(&mut self.next_token, next_next_token); let token = std::mem::replace(&mut self.token, next_token); @@ -181,7 +186,7 @@ impl<'a> Parser<'a> { std::mem::replace(&mut self.next_token_comments, next_next_token_comments); let _ = std::mem::replace(&mut self.current_token_comments, next_comments); - self.current_token_span = self.token.to_span(); + self.current_token_location = self.token.location(); token } @@ -189,20 +194,23 @@ impl<'a> Parser<'a> { let (token, comments) = self.read_token_internal(); self.token = token; self.current_token_comments = comments; - self.current_token_span = self.token.to_span(); + self.current_token_location = self.token.location(); let (token, comments) = self.read_token_internal(); self.next_token = token; self.next_token_comments = comments; } - fn read_token_internal(&mut self) -> (SpannedToken, String) { + fn read_token_internal(&mut self) -> (LocatedToken, String) { let mut last_comments = String::new(); loop { match self.tokens.next() { Some(Ok(token)) => match token.token() { Token::LineComment(comment, None) | Token::BlockComment(comment, None) => { + if !last_comments.is_empty() { + last_comments.push('\n'); + } last_comments.push_str(comment); continue; } @@ -211,17 +219,13 @@ impl<'a> Parser<'a> { } }, Some(Err(lexer_error)) => self.errors.push(lexer_error.into()), - None => return (eof_spanned_token(), last_comments), + None => return (eof_located_token(), last_comments), } } } - fn eat_kind(&mut self, kind: TokenKind) -> Option { - if self.token.kind() == kind { - Some(self.bump()) - } else { - None - } + fn eat_kind(&mut self, kind: TokenKind) -> Option { + if self.token.kind() == kind { Some(self.bump()) } else { None } } fn eat_keyword(&mut self, keyword: Keyword) -> bool { @@ -240,7 +244,7 @@ impl<'a> Parser<'a> { fn eat_ident(&mut self) -> Option { if let Some(token) = self.eat_kind(TokenKind::Ident) { match token.into_token() { - Token::Ident(ident) => Some(Ident::new(ident, self.previous_token_span)), + Token::Ident(ident) => Some(Ident::new(ident, self.previous_token_location)), _ => unreachable!(), } } else { @@ -375,7 +379,7 @@ impl<'a> Parser<'a> { fn eat_commas(&mut self) -> bool { if self.eat_comma() { while self.eat_comma() { - self.push_error(ParserErrorReason::UnexpectedComma, self.previous_token_span); + self.push_error(ParserErrorReason::UnexpectedComma, self.previous_token_location); } true } else { @@ -390,7 +394,10 @@ impl<'a> Parser<'a> { fn eat_semicolons(&mut self) -> bool { if self.eat_semicolon() { while self.eat_semicolon() { - self.push_error(ParserErrorReason::UnexpectedSemicolon, self.previous_token_span); + self.push_error( + ParserErrorReason::UnexpectedSemicolon, + self.previous_token_location, + ); } true } else { @@ -479,17 +486,35 @@ impl<'a> Parser<'a> { self.token.token() == &Token::EOF } - fn span_since(&self, start_span: Span) -> Span { - if self.current_token_span == start_span { + fn location_since(&self, start_location: Location) -> Location { + // When taking the span between locations in different files, just keep the first one + if self.current_token_location.file != start_location.file { + return start_location; + } + + let start_span = start_location.span; + + let span = if self.current_token_location.span == start_location.span { start_span } else { - let end_span = self.previous_token_span; - Span::from(start_span.start()..end_span.end()) - } + let end_span = self.previous_token_location.span; + if start_span.start() <= end_span.end() { + Span::from(start_span.start()..end_span.end()) + } else { + // TODO: workaround for now + start_span + } + }; + + Location::new(span, start_location.file) + } + + fn location_at_previous_token_end(&self) -> Location { + Location::new(self.span_at_previous_token_end(), self.previous_token_location.file) } fn span_at_previous_token_end(&self) -> Span { - Span::from(self.previous_token_span.end()..self.previous_token_span.end()) + Span::from(self.previous_token_location.span.end()..self.previous_token_location.span.end()) } fn expected_identifier(&mut self) { @@ -500,7 +525,7 @@ impl<'a> Parser<'a> { self.errors.push(ParserError::expected_token( token, self.token.token().clone(), - self.current_token_span, + self.current_token_location, )); } @@ -508,7 +533,7 @@ impl<'a> Parser<'a> { self.errors.push(ParserError::expected_one_of_tokens( tokens, self.token.token().clone(), - self.current_token_span, + self.current_token_location, )); } @@ -516,18 +541,26 @@ impl<'a> Parser<'a> { self.errors.push(ParserError::expected_label( label, self.token.token().clone(), - self.current_token_span, + self.current_token_location, )); } - fn expected_token_separating_items(&mut self, token: Token, items: &'static str, span: Span) { - self.push_error(ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items }, span); + fn expected_token_separating_items( + &mut self, + token: Token, + items: &'static str, + location: Location, + ) { + self.push_error( + ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items }, + location, + ); } fn expected_mut_after_ampersand(&mut self) { self.push_error( ParserErrorReason::ExpectedMutAfterAmpersand { found: self.token.token().clone() }, - self.current_token_span, + self.current_token_location, ); } @@ -543,20 +576,20 @@ impl<'a> Parser<'a> { ParserErrorReason::VisibilityNotFollowedByAnItem { visibility: modifiers.visibility, }, - modifiers.visibility_span, + modifiers.visibility_location, ); } } fn unconstrained_not_followed_by_an_item(&mut self, modifiers: Modifiers) { - if let Some(span) = modifiers.unconstrained { - self.push_error(ParserErrorReason::UnconstrainedNotFollowedByAnItem, span); + if let Some(location) = modifiers.unconstrained { + self.push_error(ParserErrorReason::UnconstrainedNotFollowedByAnItem, location); } } fn comptime_not_followed_by_an_item(&mut self, modifiers: Modifiers) { - if let Some(span) = modifiers.comptime { - self.push_error(ParserErrorReason::ComptimeNotFollowedByAnItem, span); + if let Some(location) = modifiers.comptime { + self.push_error(ParserErrorReason::ComptimeNotFollowedByAnItem, location); } } @@ -567,28 +600,28 @@ impl<'a> Parser<'a> { } fn mutable_not_applicable(&mut self, modifiers: Modifiers) { - if let Some(span) = modifiers.mutable { - self.push_error(ParserErrorReason::MutableNotApplicable, span); + if let Some(location) = modifiers.mutable { + self.push_error(ParserErrorReason::MutableNotApplicable, location); } } fn comptime_not_applicable(&mut self, modifiers: Modifiers) { - if let Some(span) = modifiers.comptime { - self.push_error(ParserErrorReason::ComptimeNotApplicable, span); + if let Some(location) = modifiers.comptime { + self.push_error(ParserErrorReason::ComptimeNotApplicable, location); } } fn unconstrained_not_applicable(&mut self, modifiers: Modifiers) { - if let Some(span) = modifiers.unconstrained { - self.push_error(ParserErrorReason::UnconstrainedNotApplicable, span); + if let Some(location) = modifiers.unconstrained { + self.push_error(ParserErrorReason::UnconstrainedNotApplicable, location); } } - fn push_error(&mut self, reason: ParserErrorReason, span: Span) { - self.errors.push(ParserError::with_reason(reason, span)); + fn push_error(&mut self, reason: ParserErrorReason, location: Location) { + self.errors.push(ParserError::with_reason(reason, location)); } } -fn eof_spanned_token() -> SpannedToken { - SpannedToken::new(Token::EOF, Default::default()) +fn eof_located_token() -> LocatedToken { + LocatedToken::new(Token::EOF, Location::dummy()) } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/arguments.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/arguments.rs index 380f42809a6e..6b7dd8559c8e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/arguments.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/arguments.rs @@ -1,13 +1,13 @@ use crate::{ast::Expression, token::Token}; -use super::{parse_many::separated_by_comma_until_right_paren, Parser}; +use super::{Parser, parse_many::separated_by_comma_until_right_paren}; pub(crate) struct CallArguments { pub(crate) arguments: Vec, pub(crate) is_macro_call: bool, } -impl<'a> Parser<'a> { +impl Parser<'_> { /// Arguments = '(' ArgumentsList? ')' /// /// ArgumentsList = Expression ( ',' Expression )? ','? diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/attributes.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/attributes.rs index e32e7d3cb235..32aa974fcafe 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -1,31 +1,31 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::ast::{Expression, ExpressionKind, Ident, Literal, Path}; use crate::lexer::errors::LexerErrorKind; -use crate::parser::labels::ParsingRuleLabel; use crate::parser::ParserErrorReason; +use crate::parser::labels::ParsingRuleLabel; use crate::token::{Attribute, FunctionAttribute, MetaAttribute, TestScope, Token}; use crate::token::{CustomAttribute, SecondaryAttribute}; -use super::parse_many::without_separator; use super::Parser; +use super::parse_many::without_separator; -impl<'a> Parser<'a> { +impl Parser<'_> { /// InnerAttribute = '#![' SecondaryAttribute ']' pub(super) fn parse_inner_attribute(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let is_tag = self.eat_inner_attribute_start()?; let attribute = if is_tag { - self.parse_tag_attribute(start_span) + self.parse_tag_attribute(start_location) } else { - self.parse_non_tag_attribute(start_span) + self.parse_non_tag_attribute(start_location) }; match attribute { Attribute::Function(function_attribute) => { self.errors.push( LexerErrorKind::InvalidInnerAttribute { - span: self.span_since(start_span), + location: self.location_since(start_location), found: function_attribute.to_string(), } .into(), @@ -37,7 +37,7 @@ impl<'a> Parser<'a> { } /// Attributes = Attribute* - pub(super) fn parse_attributes(&mut self) -> Vec<(Attribute, Span)> { + pub(super) fn parse_attributes(&mut self) -> Vec<(Attribute, Location)> { self.parse_many("attributes", without_separator(), Self::parse_attribute) } @@ -73,26 +73,26 @@ impl<'a> Parser<'a> { /// AttributeValue /// = Path /// | integer - pub(crate) fn parse_attribute(&mut self) -> Option<(Attribute, Span)> { - let start_span = self.current_token_span; + pub(crate) fn parse_attribute(&mut self) -> Option<(Attribute, Location)> { + let start_location = self.current_token_location; let is_tag = self.eat_attribute_start()?; let attribute = if is_tag { - self.parse_tag_attribute(start_span) + self.parse_tag_attribute(start_location) } else { - self.parse_non_tag_attribute(start_span) + self.parse_non_tag_attribute(start_location) }; - Some((attribute, self.span_since(start_span))) + Some((attribute, self.location_since(start_location))) } pub(super) fn validate_secondary_attributes( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, ) -> Vec { attributes .into_iter() - .filter_map(|(attribute, span)| match attribute { + .filter_map(|(attribute, location)| match attribute { Attribute::Function(..) => { - self.push_error(ParserErrorReason::NoFunctionAttributesAllowedOnType, span); + self.push_error(ParserErrorReason::NoFunctionAttributesAllowedOnType, location); None } Attribute::Secondary(attr) => Some(attr), @@ -100,9 +100,9 @@ impl<'a> Parser<'a> { .collect() } - fn parse_tag_attribute(&mut self, start_span: Span) -> Attribute { - let contents_start_span = self.current_token_span; - let mut contents_span = contents_start_span; + fn parse_tag_attribute(&mut self, start_location: Location) -> Attribute { + let contents_start_location = self.current_token_location; + let mut contents_location = contents_start_location; let mut contents = String::new(); let mut brackets_count = 1; // 1 because of the starting `#[` @@ -113,7 +113,7 @@ impl<'a> Parser<'a> { } else if self.at(Token::RightBracket) { brackets_count -= 1; if brackets_count == 0 { - contents_span = self.span_since(contents_start_span); + contents_location = self.location_since(contents_start_location); self.bump(); break; } @@ -125,66 +125,68 @@ impl<'a> Parser<'a> { Attribute::Secondary(SecondaryAttribute::Tag(CustomAttribute { contents, - span: self.span_since(start_span), - contents_span, + span: self.location_since(start_location).span, + contents_span: contents_location.span, })) } - fn parse_non_tag_attribute(&mut self, start_span: Span) -> Attribute { + fn parse_non_tag_attribute(&mut self, start_location: Location) -> Attribute { if matches!(&self.token.token(), Token::Keyword(..)) && (self.next_is(Token::LeftParen) || self.next_is(Token::RightBracket)) { // This is a Meta attribute with the syntax `keyword(arg1, arg2, .., argN)` - let path = Path::from_single(self.token.to_string(), self.current_token_span); + let path = Path::from_single(self.token.to_string(), self.current_token_location); self.bump(); - self.parse_meta_attribute(path, start_span) + self.parse_meta_attribute(path, start_location) } else if let Some(path) = self.parse_path_no_turbofish() { if let Some(ident) = path.as_ident() { if ident.0.contents == "test" { // The test attribute is the only secondary attribute that has `a = b` in its syntax // (`should_fail_with = "..."``) so we parse it differently. - self.parse_test_attribute(start_span) + self.parse_test_attribute(start_location) } else { // Every other attribute has the form `name(arg1, arg2, .., argN)` - self.parse_ident_attribute_other_than_test(ident, start_span) + self.parse_ident_attribute_other_than_test(ident, start_location) } } else { // This is a Meta attribute with the syntax `path(arg1, arg2, .., argN)` - self.parse_meta_attribute(path, start_span) + self.parse_meta_attribute(path, start_location) } } else { self.expected_label(ParsingRuleLabel::Path); - self.parse_tag_attribute(start_span) + self.parse_tag_attribute(start_location) } } - fn parse_meta_attribute(&mut self, name: Path, start_span: Span) -> Attribute { + fn parse_meta_attribute(&mut self, name: Path, start_location: Location) -> Attribute { let arguments = self.parse_arguments().unwrap_or_default(); self.skip_until_right_bracket(); Attribute::Secondary(SecondaryAttribute::Meta(MetaAttribute { name, arguments, - span: self.span_since(start_span), + location: self.location_since(start_location), })) } fn parse_ident_attribute_other_than_test( &mut self, ident: &Ident, - start_span: Span, + start_location: Location, ) -> Attribute { let arguments = self.parse_arguments().unwrap_or_default(); self.skip_until_right_bracket(); match ident.0.contents.as_str() { - "abi" => self.parse_single_name_attribute(ident, arguments, start_span, |name| { + "abi" => self.parse_single_name_attribute(ident, arguments, start_location, |name| { Attribute::Secondary(SecondaryAttribute::Abi(name)) }), - "allow" => self.parse_single_name_attribute(ident, arguments, start_span, |name| { + "allow" => self.parse_single_name_attribute(ident, arguments, start_location, |name| { Attribute::Secondary(SecondaryAttribute::Allow(name)) }), - "builtin" => self.parse_single_name_attribute(ident, arguments, start_span, |name| { - Attribute::Function(FunctionAttribute::Builtin(name)) - }), + "builtin" => { + self.parse_single_name_attribute(ident, arguments, start_location, |name| { + Attribute::Function(FunctionAttribute::Builtin(name)) + }) + } "deprecated" => self.parse_deprecated_attribute(ident, arguments), "contract_library_method" => { let attr = Attribute::Secondary(SecondaryAttribute::ContractLibraryMethod); @@ -194,16 +196,18 @@ impl<'a> Parser<'a> { let attr = Attribute::Secondary(SecondaryAttribute::Export); self.parse_no_args_attribute(ident, arguments, attr) } - "field" => self.parse_single_name_attribute(ident, arguments, start_span, |name| { + "field" => self.parse_single_name_attribute(ident, arguments, start_location, |name| { Attribute::Secondary(SecondaryAttribute::Field(name)) }), "fold" => { let attr = Attribute::Function(FunctionAttribute::Fold); self.parse_no_args_attribute(ident, arguments, attr) } - "foreign" => self.parse_single_name_attribute(ident, arguments, start_span, |name| { - Attribute::Function(FunctionAttribute::Foreign(name)) - }), + "foreign" => { + self.parse_single_name_attribute(ident, arguments, start_location, |name| { + Attribute::Function(FunctionAttribute::Foreign(name)) + }) + } "inline_always" => { let attr = Attribute::Function(FunctionAttribute::InlineAlways); self.parse_no_args_attribute(ident, arguments, attr) @@ -212,9 +216,11 @@ impl<'a> Parser<'a> { let attr = Attribute::Function(FunctionAttribute::NoPredicates); self.parse_no_args_attribute(ident, arguments, attr) } - "oracle" => self.parse_single_name_attribute(ident, arguments, start_span, |name| { - Attribute::Function(FunctionAttribute::Oracle(name)) - }), + "oracle" => { + self.parse_single_name_attribute(ident, arguments, start_location, |name| { + Attribute::Function(FunctionAttribute::Oracle(name)) + }) + } "use_callers_scope" => { let attr = Attribute::Secondary(SecondaryAttribute::UseCallersScope); self.parse_no_args_attribute(ident, arguments, attr) @@ -226,7 +232,7 @@ impl<'a> Parser<'a> { _ => Attribute::Secondary(SecondaryAttribute::Meta(MetaAttribute { name: Path::from_ident(ident.clone()), arguments, - span: self.span_since(start_span), + location: self.location_since(start_location), })), } } @@ -248,7 +254,7 @@ impl<'a> Parser<'a> { max: 1, found: arguments.len(), }, - ident.span(), + ident.location(), ); return Attribute::Secondary(SecondaryAttribute::Deprecated(None)); } @@ -257,7 +263,7 @@ impl<'a> Parser<'a> { let ExpressionKind::Literal(Literal::Str(message)) = argument.kind else { self.push_error( ParserErrorReason::DeprecatedAttributeExpectsAStringArgument, - argument.span, + argument.location, ); return Attribute::Secondary(SecondaryAttribute::Deprecated(None)); }; @@ -265,7 +271,7 @@ impl<'a> Parser<'a> { Attribute::Secondary(SecondaryAttribute::Deprecated(Some(message))) } - fn parse_test_attribute(&mut self, start_span: Span) -> Attribute { + fn parse_test_attribute(&mut self, start_location: Location) -> Attribute { let scope = if self.eat_left_paren() { let scope = if let Some(ident) = self.eat_ident() { match ident.0.contents.as_str() { @@ -295,7 +301,10 @@ impl<'a> Parser<'a> { scope } else { self.errors.push( - LexerErrorKind::MalformedTestAttribute { span: self.span_since(start_span) }.into(), + LexerErrorKind::MalformedTestAttribute { + location: self.location_since(start_location), + } + .into(), ); TestScope::None }; @@ -307,7 +316,7 @@ impl<'a> Parser<'a> { &mut self, ident: &Ident, mut arguments: Vec, - start_span: Span, + start_location: Location, f: F, ) -> Attribute where @@ -321,7 +330,7 @@ impl<'a> Parser<'a> { max: 1, found: arguments.len(), }, - self.current_token_span, + self.current_token_location, ); return f(String::new()); } @@ -332,10 +341,13 @@ impl<'a> Parser<'a> { f(argument.to_string()) } _ => { - let span = self.span_since(start_span); + let location = self.location_since(start_location); self.errors.push( - LexerErrorKind::MalformedFuncAttribute { span, found: argument.to_string() } - .into(), + LexerErrorKind::MalformedFuncAttribute { + location, + found: argument.to_string(), + } + .into(), ); f(String::new()) } @@ -356,7 +368,7 @@ impl<'a> Parser<'a> { max: 0, found: arguments.len(), }, - ident.span(), + ident.location(), ); } @@ -388,19 +400,19 @@ mod tests { use noirc_errors::Span; use crate::{ - parser::{parser::tests::expect_no_errors, Parser}, + parser::{Parser, parser::tests::expect_no_errors}, token::{Attribute, FunctionAttribute, SecondaryAttribute, TestScope}, }; fn parse_inner_secondary_attribute_no_errors(src: &str, expected: SecondaryAttribute) { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let attribute = parser.parse_inner_attribute(); expect_no_errors(&parser.errors); assert_eq!(attribute.unwrap(), expected); } fn parse_attribute_no_errors(src: &str, expected: Attribute) { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); assert_eq!(attribute, expected); @@ -409,7 +421,7 @@ mod tests { #[test] fn parses_inner_attribute_as_tag() { let src = "#!['hello]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let Some(SecondaryAttribute::Tag(custom)) = parser.parse_inner_attribute() else { panic!("Expected inner tag attribute"); }; @@ -422,7 +434,7 @@ mod tests { #[test] fn parses_inner_attribute_as_tag_with_nested_brackets() { let src = "#!['hello[1]]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let Some(SecondaryAttribute::Tag(custom)) = parser.parse_inner_attribute() else { panic!("Expected inner tag attribute"); }; @@ -570,7 +582,7 @@ mod tests { #[test] fn parses_meta_attribute_single_identifier_no_arguments() { let src = "#[foo]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); let Attribute::Secondary(SecondaryAttribute::Meta(meta)) = attribute else { @@ -583,7 +595,7 @@ mod tests { #[test] fn parses_meta_attribute_single_identifier_as_keyword() { let src = "#[dep]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); let Attribute::Secondary(SecondaryAttribute::Meta(meta)) = attribute else { @@ -596,7 +608,7 @@ mod tests { #[test] fn parses_meta_attribute_single_identifier_with_arguments() { let src = "#[foo(1, 2, 3)]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); let Attribute::Secondary(SecondaryAttribute::Meta(meta)) = attribute else { @@ -610,7 +622,7 @@ mod tests { #[test] fn parses_meta_attribute_path_with_arguments() { let src = "#[foo::bar(1, 2, 3)]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); let Attribute::Secondary(SecondaryAttribute::Meta(meta)) = attribute else { @@ -624,7 +636,7 @@ mod tests { #[test] fn parses_attributes() { let src = "#[test] #[deprecated]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let mut attributes = parser.parse_attributes(); expect_no_errors(&parser.errors); assert_eq!(attributes.len(), 2); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/doc_comments.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/doc_comments.rs index 578a49641f64..0c9329d7027f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/doc_comments.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/doc_comments.rs @@ -1,8 +1,11 @@ -use crate::token::{DocStyle, Token, TokenKind}; +use crate::{ + parser::ParserErrorReason, + token::{DocStyle, Token, TokenKind}, +}; -use super::{parse_many::without_separator, Parser}; +use super::{Parser, parse_many::without_separator}; -impl<'a> Parser<'a> { +impl Parser<'_> { /// InnerDocComments = inner_doc_comment* pub(super) fn parse_inner_doc_comments(&mut self) -> Vec { self.parse_many("inner doc comments", without_separator(), Self::parse_inner_doc_comment) @@ -28,16 +31,28 @@ impl<'a> Parser<'a> { _ => unreachable!(), }) } + + /// Skips any outer doc comments but produces a warning saying that they don't document anything. + pub(super) fn warn_on_outer_doc_comments(&mut self) { + let location_before_doc_comments = self.current_token_location; + let doc_comments = self.parse_outer_doc_comments(); + if !doc_comments.is_empty() { + self.push_error( + ParserErrorReason::DocCommentDoesNotDocumentAnything, + location_before_doc_comments, + ); + } + } } #[cfg(test)] mod tests { - use crate::parser::{parser::tests::expect_no_errors, Parser}; + use crate::parser::{Parser, parser::tests::expect_no_errors}; #[test] fn parses_inner_doc_comments() { let src = "//! Hello\n//! World"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let comments = parser.parse_inner_doc_comments(); expect_no_errors(&parser.errors); assert_eq!(comments.len(), 2); @@ -48,7 +63,7 @@ mod tests { #[test] fn parses_outer_doc_comments() { let src = "/// Hello\n/// World"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let comments = parser.parse_outer_doc_comments(); expect_no_errors(&parser.errors); assert_eq!(comments.len(), 2); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/enums.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/enums.rs index 3b496a438cf3..885bfa871eeb 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/enums.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/enums.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{Documented, EnumVariant, Ident, ItemVisibility, NoirEnumeration, UnresolvedGenerics}, @@ -7,24 +7,22 @@ use crate::{ }; use super::{ - parse_many::{separated_by_comma_until_right_brace, separated_by_comma_until_right_paren}, Parser, + parse_many::{separated_by_comma_until_right_brace, separated_by_comma_until_right_paren}, }; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Enum = 'enum' identifier Generics '{' EnumVariant* '}' /// /// EnumField = OuterDocComments identifier ':' Type pub(crate) fn parse_enum( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, visibility: ItemVisibility, - start_span: Span, + start_location: Location, ) -> NoirEnumeration { let attributes = self.validate_secondary_attributes(attributes); - self.push_error(ParserErrorReason::ExperimentalFeature("Enums"), start_span); - let Some(name) = self.eat_ident() else { self.expected_identifier(); return self.empty_enum( @@ -32,7 +30,7 @@ impl<'a> Parser<'a> { attributes, visibility, Vec::new(), - start_span, + start_location, ); }; @@ -40,7 +38,7 @@ impl<'a> Parser<'a> { if !self.eat_left_brace() { self.expected_token(Token::LeftBrace); - return self.empty_enum(name, attributes, visibility, generics, start_span); + return self.empty_enum(name, attributes, visibility, generics, start_location); } let comma_separated = separated_by_comma_until_right_brace(); @@ -52,7 +50,7 @@ impl<'a> Parser<'a> { visibility, generics, variants, - span: self.span_since(start_span), + location: self.location_since(start_location), } } @@ -62,7 +60,7 @@ impl<'a> Parser<'a> { // Loop until we find an identifier, skipping anything that's not one loop { - let doc_comments_start_span = self.current_token_span; + let doc_comments_start_location = self.current_token_location; doc_comments = self.parse_outer_doc_comments(); if let Some(ident) = self.eat_ident() { @@ -73,7 +71,7 @@ impl<'a> Parser<'a> { if !doc_comments.is_empty() { self.push_error( ParserErrorReason::DocCommentDoesNotDocumentAnything, - self.span_since(doc_comments_start_span), + self.location_since(doc_comments_start_location), ); } @@ -106,7 +104,7 @@ impl<'a> Parser<'a> { attributes: Vec, visibility: ItemVisibility, generics: UnresolvedGenerics, - start_span: Span, + start_location: Location, ) -> NoirEnumeration { NoirEnumeration { name, @@ -114,7 +112,7 @@ impl<'a> Parser<'a> { visibility, generics, variants: Vec::new(), - span: self.span_since(start_span), + location: self.location_since(start_location), } } } @@ -123,17 +121,15 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{IntegerBitSize, NoirEnumeration, Signedness, UnresolvedGeneric, UnresolvedTypeData}, + parse_program_with_dummy_file, parser::{ - parser::{ - parse_program, - tests::{expect_no_errors, get_source_with_error_span}, - }, ItemKind, ParserErrorReason, + parser::tests::{expect_no_errors, get_source_with_error_span}, }, }; fn parse_enum_no_errors(src: &str) -> NoirEnumeration { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -205,7 +201,7 @@ mod tests { #[test] fn parse_empty_enum_with_doc_comments() { let src = "/// Hello\nenum Foo {}"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -219,8 +215,8 @@ mod tests { #[test] fn parse_unclosed_enum() { let src = "enum Foo {"; - let (module, errors) = parse_program(src); - assert_eq!(errors.len(), 2); + let (module, errors) = parse_program_with_dummy_file(src); + assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Enum(noir_enum) = &item.kind else { @@ -236,7 +232,7 @@ mod tests { ^^^^^^^ "; let (src, _) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let reason = errors[0].reason().unwrap(); assert!(matches!(reason, ParserErrorReason::NoFunctionAttributesAllowedOnType)); } @@ -248,7 +244,7 @@ mod tests { ^^ "; let (src, _) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -258,7 +254,8 @@ mod tests { assert_eq!("Foo", noir_enum.name.to_string()); assert_eq!(noir_enum.variants.len(), 1); - let error = &errors[1]; + assert_eq!(errors.len(), 1); + let error = &errors[0]; assert_eq!(error.to_string(), "Expected an identifier but found '42'"); } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/expression.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/expression.rs index b2ddc200ef25..d0f335414da4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1,26 +1,26 @@ use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{ ArrayLiteral, BlockExpression, CallExpression, CastExpression, ConstrainExpression, ConstrainKind, ConstructorExpression, Expression, ExpressionKind, Ident, IfExpression, IndexExpression, Literal, MatchExpression, MemberAccessExpression, MethodCallExpression, - Statement, TypePath, UnaryOp, UnresolvedType, + Statement, TypePath, UnaryOp, UnresolvedType, UnsafeExpression, }, - parser::{labels::ParsingRuleLabel, parser::parse_many::separated_by_comma, ParserErrorReason}, + parser::{ParserErrorReason, labels::ParsingRuleLabel, parser::parse_many::separated_by_comma}, token::{Keyword, Token, TokenKind}, }; use super::{ + Parser, parse_many::{ separated_by_comma_until_right_brace, separated_by_comma_until_right_paren, without_separator, }, - Parser, }; -impl<'a> Parser<'a> { +impl Parser<'_> { pub(crate) fn parse_expression_or_error(&mut self) -> Expression { self.parse_expression_or_error_impl(true) // allow constructors } @@ -47,7 +47,10 @@ impl<'a> Parser<'a> { expr } else { self.push_expected_expression(); - Expression { kind: ExpressionKind::Error, span: self.span_at_previous_token_end() } + Expression { + kind: ExpressionKind::Error, + location: self.location_at_previous_token_end(), + } } } @@ -59,7 +62,7 @@ impl<'a> Parser<'a> { /// = UnaryOp Term /// | AtomOrUnaryRightExpression pub(super) fn parse_term(&mut self, allow_constructors: bool) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if let Some(operator) = self.parse_unary_op() { let Some(rhs) = self.parse_term(allow_constructors) else { @@ -67,8 +70,8 @@ impl<'a> Parser<'a> { return None; }; let kind = ExpressionKind::prefix(operator, rhs); - let span = self.span_since(start_span); - return Some(Expression { kind, span }); + let location = self.location_since(start_location); + return Some(Expression { kind, location }); } self.parse_atom_or_unary_right(allow_constructors) @@ -94,12 +97,12 @@ impl<'a> Parser<'a> { /// AtomOrUnaryRightExpression /// = Atom UnaryRightExpression* fn parse_atom_or_unary_right(&mut self, allow_constructors: bool) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let mut atom = self.parse_atom(allow_constructors)?; let mut parsed; loop { - (atom, parsed) = self.parse_unary_right(atom, start_span); + (atom, parsed) = self.parse_unary_right(atom, start_location); if parsed { continue; } else { @@ -115,37 +118,41 @@ impl<'a> Parser<'a> { /// | MemberAccessOrMethodCallExpression /// | CastExpression /// | IndexExpression - fn parse_unary_right(&mut self, mut atom: Expression, start_span: Span) -> (Expression, bool) { + fn parse_unary_right( + &mut self, + mut atom: Expression, + start_location: Location, + ) -> (Expression, bool) { let mut parsed; - (atom, parsed) = self.parse_call(atom, start_span); + (atom, parsed) = self.parse_call(atom, start_location); if parsed { return (atom, parsed); } - (atom, parsed) = self.parse_member_access_or_method_call(atom, start_span); + (atom, parsed) = self.parse_member_access_or_method_call(atom, start_location); if parsed { return (atom, parsed); } - (atom, parsed) = self.parse_cast(atom, start_span); + (atom, parsed) = self.parse_cast(atom, start_location); if parsed { return (atom, parsed); } - self.parse_index(atom, start_span) + self.parse_index(atom, start_location) } /// CallExpression = Atom CallArguments - fn parse_call(&mut self, atom: Expression, start_span: Span) -> (Expression, bool) { + fn parse_call(&mut self, atom: Expression, start_location: Location) -> (Expression, bool) { if let Some(call_arguments) = self.parse_call_arguments() { let kind = ExpressionKind::Call(Box::new(CallExpression { func: Box::new(atom), arguments: call_arguments.arguments, is_macro_call: call_arguments.is_macro_call, })); - let span = self.span_since(start_span); - let atom = Expression { kind, span }; + let location = self.location_since(start_location); + let atom = Expression { kind, location }; (atom, true) } else { (atom, false) @@ -162,7 +169,7 @@ impl<'a> Parser<'a> { fn parse_member_access_or_method_call( &mut self, atom: Expression, - start_span: Span, + start_location: Location, ) -> (Expression, bool) { if !self.eat_dot() { return (atom, false); @@ -187,8 +194,8 @@ impl<'a> Parser<'a> { })) }; - let span = self.span_since(start_span); - let atom = Expression { kind, span }; + let location = self.location_since(start_location); + let atom = Expression { kind, location }; (atom, true) } @@ -196,31 +203,31 @@ impl<'a> Parser<'a> { if let Some(ident) = self.eat_ident() { Some(ident) } else if let Some(int) = self.eat_int() { - Some(Ident::new(int.to_string(), self.previous_token_span)) + Some(Ident::new(int.to_string(), self.previous_token_location)) } else { self.push_error( ParserErrorReason::ExpectedFieldName(self.token.token().clone()), - self.current_token_span, + self.current_token_location, ); None } } /// CastExpression = Atom 'as' Type - fn parse_cast(&mut self, atom: Expression, start_span: Span) -> (Expression, bool) { + fn parse_cast(&mut self, atom: Expression, start_location: Location) -> (Expression, bool) { if !self.eat_keyword(Keyword::As) { return (atom, false); } let typ = self.parse_type_or_error(); let kind = ExpressionKind::Cast(Box::new(CastExpression { lhs: atom, r#type: typ })); - let span = self.span_since(start_span); - let atom = Expression { kind, span }; + let location = self.location_since(start_location); + let atom = Expression { kind, location }; (atom, true) } /// IndexExpression = Atom '[' Expression ']' - fn parse_index(&mut self, atom: Expression, start_span: Span) -> (Expression, bool) { + fn parse_index(&mut self, atom: Expression, start_location: Location) -> (Expression, bool) { if !self.eat_left_bracket() { return (atom, false); } @@ -228,8 +235,8 @@ impl<'a> Parser<'a> { let index = self.parse_expression_or_error(); self.eat_or_error(Token::RightBracket); let kind = ExpressionKind::Index(Box::new(IndexExpression { collection: atom, index })); - let span = self.span_since(start_span); - let atom = Expression { kind, span }; + let location = self.location_since(start_location); + let atom = Expression { kind, location }; (atom, true) } @@ -261,20 +268,14 @@ impl<'a> Parser<'a> { /// | InternedExpression /// | InternedStatementExpression fn parse_atom(&mut self, allow_constructors: bool) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let kind = self.parse_atom_kind(allow_constructors)?; - Some(Expression { kind, span: self.span_since(start_span) }) + Some(Expression { kind, location: self.location_since(start_location) }) } fn parse_atom_kind(&mut self, allow_constructors: bool) -> Option { - let span_before_doc_comments = self.current_token_span; - let doc_comments = self.parse_outer_doc_comments(); - if !doc_comments.is_empty() { - self.push_error( - ParserErrorReason::DocCommentDoesNotDocumentAnything, - span_before_doc_comments, - ); - } + // Like in Rust, we allow parsing doc comments on top of an expression but they always produce a warning. + self.warn_on_outer_doc_comments(); if let Some(kind) = self.parse_unsafe_expr() { return Some(kind); @@ -298,10 +299,10 @@ impl<'a> Parser<'a> { Token::InternedUnresolvedTypeData(..) | Token::QuotedType(..) ) && self.next_is(Token::LeftBrace) { - let span = self.current_token_span; + let location = self.current_token_location; let typ = self.parse_interned_type().or_else(|| self.parse_resolved_type()).unwrap(); self.eat_or_error(Token::LeftBrace); - let typ = UnresolvedType { typ, span }; + let typ = UnresolvedType { typ, location }; return Some(self.parse_constructor(typ)); } @@ -386,26 +387,32 @@ impl<'a> Parser<'a> { /// UnsafeExpression = 'unsafe' Block fn parse_unsafe_expr(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; + let comments_before_unsafe = self.current_token_comments.clone(); if !self.eat_keyword(Keyword::Unsafe) { return None; } - if self.current_token_comments.is_empty() { - if let Some(statement_comments) = &mut self.statement_comments { - if !statement_comments.trim().to_lowercase().starts_with("safety:") { - self.push_error(ParserErrorReason::MissingSafetyComment, start_span); - } + let comments: &str = if comments_before_unsafe.is_empty() { + if let Some(statement_comments) = &self.statement_comments { + statement_comments } else { - self.push_error(ParserErrorReason::MissingSafetyComment, start_span); + "" } - } else if !self.current_token_comments.trim().to_lowercase().starts_with("safety:") { - self.push_error(ParserErrorReason::MissingSafetyComment, start_span); + } else { + &comments_before_unsafe + }; + + if !comments.lines().any(|line| line.trim().to_lowercase().starts_with("safety:")) { + self.push_error(ParserErrorReason::MissingSafetyComment, start_location); } if let Some(block) = self.parse_block() { - Some(ExpressionKind::Unsafe(block, self.span_since(start_span))) + Some(ExpressionKind::Unsafe(UnsafeExpression { + block, + unsafe_keyword_location: start_location, + })) } else { Some(ExpressionKind::Error) } @@ -439,11 +446,7 @@ impl<'a> Parser<'a> { Self::parse_constructor_field, ); - ExpressionKind::Constructor(Box::new(ConstructorExpression { - typ, - fields, - struct_type: None, - })) + ExpressionKind::Constructor(Box::new(ConstructorExpression { typ, fields })) } fn parse_constructor_field(&mut self) -> Option<(Ident, Expression)> { @@ -471,26 +474,26 @@ impl<'a> Parser<'a> { let condition = self.parse_expression_except_constructor_or_error(); - let start_span = self.current_token_span; + let start_location = self.current_token_location; let Some(consequence) = self.parse_block() else { self.expected_token(Token::LeftBrace); - let span = self.span_at_previous_token_end(); + let location = self.location_at_previous_token_end(); return Some(ExpressionKind::If(Box::new(IfExpression { condition, - consequence: Expression { kind: ExpressionKind::Error, span }, + consequence: Expression { kind: ExpressionKind::Error, location }, alternative: None, }))); }; - let span = self.span_since(start_span); - let consequence = Expression { kind: ExpressionKind::Block(consequence), span }; + let location = self.location_since(start_location); + let consequence = Expression { kind: ExpressionKind::Block(consequence), location }; let alternative = if self.eat_keyword(Keyword::Else) { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if let Some(block) = self.parse_block() { - let span = self.span_since(start_span); - Some(Expression { kind: ExpressionKind::Block(block), span }) + let location = self.location_since(start_location); + Some(Expression { kind: ExpressionKind::Block(block), location }) } else if let Some(if_expr) = self.parse_if_expr() { - Some(Expression { kind: if_expr, span: self.span_since(start_span) }) + Some(Expression { kind: if_expr, location: self.location_since(start_location) }) } else { self.expected_token(Token::LeftBrace); None @@ -504,7 +507,6 @@ impl<'a> Parser<'a> { /// MatchExpression = 'match' ExpressionExceptConstructor '{' MatchRule* '}' pub(super) fn parse_match_expr(&mut self) -> Option { - let start_span = self.current_token_span; if !self.eat_keyword(Keyword::Match) { return None; } @@ -519,7 +521,6 @@ impl<'a> Parser<'a> { Self::parse_match_rule, ); - self.push_error(ParserErrorReason::ExperimentalFeature("Match expressions"), start_span); Some(ExpressionKind::Match(Box::new(MatchExpression { expression, rules }))) } @@ -528,11 +529,11 @@ impl<'a> Parser<'a> { let pattern = self.parse_expression()?; self.eat_or_error(Token::FatArrow); - let start_span = self.current_token_span; + let start_location = self.current_token_location; let branch = match self.parse_block() { Some(block) => { - let span = self.span_since(start_span); - let block = Expression::new(ExpressionKind::Block(block), span); + let location = self.location_since(start_location); + let block = Expression::new(ExpressionKind::Block(block), location); self.eat_comma(); // comma is optional if we have a block block } @@ -551,21 +552,21 @@ impl<'a> Parser<'a> { return None; } - let start_span = self.current_token_span; + let start_location = self.current_token_location; let Some(block) = self.parse_block() else { self.expected_token(Token::LeftBrace); return None; }; - Some(ExpressionKind::Comptime(block, self.span_since(start_span))) + Some(ExpressionKind::Comptime(block, self.location_since(start_location))) } /// UnquoteExpression /// = '$' identifier /// | '$' '(' Expression ')' fn parse_unquote_expr(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.eat(Token::DollarSign) { return None; @@ -574,25 +575,25 @@ impl<'a> Parser<'a> { if let Some(path) = self.parse_path() { let expr = Expression { kind: ExpressionKind::Variable(path), - span: self.span_since(start_span), + location: self.location_since(start_location), }; return Some(ExpressionKind::Unquote(Box::new(expr))); } - let span_at_left_paren = self.current_token_span; + let location_at_left_paren = self.current_token_location; if self.eat_left_paren() { let expr = self.parse_expression_or_error(); self.eat_or_error(Token::RightParen); let expr = Expression { kind: ExpressionKind::Parenthesized(Box::new(expr)), - span: self.span_since(span_at_left_paren), + location: self.location_since(location_at_left_paren), }; return Some(ExpressionKind::Unquote(Box::new(expr))); } self.push_error( ParserErrorReason::ExpectedIdentifierOrLeftParenAfterDollar, - self.current_token_span, + self.current_token_location, ); None @@ -600,9 +601,9 @@ impl<'a> Parser<'a> { /// TypePathExpression = PrimitiveType '::' identifier ( '::' GenericTypeArgs )? fn parse_type_path_expr(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let typ = self.parse_primitive_type()?; - let typ = UnresolvedType { typ, span: self.span_since(start_span) }; + let typ = UnresolvedType { typ, location: self.location_since(start_location) }; self.eat_or_error(Token::DoubleColon); @@ -610,7 +611,7 @@ impl<'a> Parser<'a> { ident } else { self.expected_identifier(); - Ident::new(String::new(), self.span_at_previous_token_end()) + Ident::new(String::new(), self.location_at_previous_token_end()) }; let turbofish = self.eat_double_colon().then(|| { @@ -718,7 +719,7 @@ impl<'a> Parser<'a> { } let comma_after_first_expr = self.eat_comma(); - let second_expr_span = self.current_token_span; + let second_expr_location = self.current_token_location; let mut exprs = self.parse_many( "expressions", @@ -727,7 +728,7 @@ impl<'a> Parser<'a> { ); if !exprs.is_empty() && !comma_after_first_expr { - self.expected_token_separating_items(Token::Comma, "expressions", second_expr_span); + self.expected_token_separating_items(Token::Comma, "expressions", second_expr_location); } exprs.insert(0, first_expr); @@ -791,7 +792,7 @@ impl<'a> Parser<'a> { /// | 'assert' Arguments /// | 'assert_eq' Arguments pub(super) fn parse_constrain_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let kind = self.parse_constrain_kind()?; Some(match kind { @@ -802,16 +803,23 @@ impl<'a> Parser<'a> { } let arguments = arguments.unwrap_or_default(); - ConstrainExpression { kind, arguments, span: self.span_since(start_span) } + ConstrainExpression { + kind, + arguments, + location: self.location_since(start_location), + } } ConstrainKind::Constrain => { - self.push_error(ParserErrorReason::ConstrainDeprecated, self.previous_token_span); + self.push_error( + ParserErrorReason::ConstrainDeprecated, + self.previous_token_location, + ); let expression = self.parse_expression_or_error(); ConstrainExpression { kind, arguments: vec![expression], - span: self.span_since(start_span), + location: self.location_since(start_location), } } }) @@ -846,7 +854,7 @@ impl<'a> Parser<'a> { Some(BlockExpression { statements }) } - fn parse_statement_in_block(&mut self) -> Option<(Statement, (Option, Span))> { + fn parse_statement_in_block(&mut self) -> Option<(Statement, (Option, Location))> { if let Some(statement) = self.parse_statement() { Some(statement) } else { @@ -857,13 +865,13 @@ impl<'a> Parser<'a> { fn check_statements_require_semicolon( &mut self, - statements: Vec<(Statement, (Option, Span))>, + statements: Vec<(Statement, (Option, Location))>, ) -> Vec { let last = statements.len().saturating_sub(1); let iter = statements.into_iter().enumerate(); - vecmap(iter, |(i, (statement, (semicolon, span)))| { + vecmap(iter, |(i, (statement, (semicolon, location)))| { statement - .add_semicolon(semicolon, span, i == last, &mut |error| self.errors.push(error)) + .add_semicolon(semicolon, location, i == last, &mut |error| self.errors.push(error)) }) } @@ -882,19 +890,20 @@ mod tests { StatementKind, UnaryOp, UnresolvedTypeData, }, parser::{ + Parser, ParserErrorReason, parser::tests::{ expect_no_errors, get_single_error, get_single_error_reason, get_source_with_error_span, }, - Parser, ParserErrorReason, }, + signed_field::SignedField, token::Token, }; fn parse_expression_no_errors(src: &str) -> Expression { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let expr = parser.parse_expression_or_error(); - assert_eq!(expr.span.end() as usize, src.len()); + assert_eq!(expr.location.span.end() as usize, src.len()); expect_no_errors(&parser.errors); expr } @@ -914,22 +923,20 @@ mod tests { fn parses_integer_literal() { let src = "42"; let expr = parse_expression_no_errors(src); - let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + let ExpressionKind::Literal(Literal::Integer(value)) = expr.kind else { panic!("Expected integer literal"); }; - assert_eq!(field, 42_u128.into()); - assert!(!negative); + assert_eq!(value, SignedField::positive(42_u128)); } #[test] fn parses_negative_integer_literal() { let src = "-42"; let expr = parse_expression_no_errors(src); - let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + let ExpressionKind::Literal(Literal::Integer(value)) = expr.kind else { panic!("Expected integer literal"); }; - assert_eq!(field, 42_u128.into()); - assert!(negative); + assert_eq!(value, SignedField::negative(42_u128)); } #[test] @@ -939,11 +946,10 @@ mod tests { let ExpressionKind::Parenthesized(expr) = expr.kind else { panic!("Expected parenthesized expression"); }; - let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + let ExpressionKind::Literal(Literal::Integer(value)) = expr.kind else { panic!("Expected integer literal"); }; - assert_eq!(field, 42_u128.into()); - assert!(!negative); + assert_eq!(value, SignedField::positive(42_u128)); } #[test] @@ -995,18 +1001,16 @@ mod tests { assert_eq!(exprs.len(), 2); let expr = exprs.remove(0); - let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + let ExpressionKind::Literal(Literal::Integer(value)) = expr.kind else { panic!("Expected integer literal"); }; - assert_eq!(field, 1_u128.into()); - assert!(!negative); + assert_eq!(value, SignedField::positive(1_u128)); let expr = exprs.remove(0); - let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + let ExpressionKind::Literal(Literal::Integer(value)) = expr.kind else { panic!("Expected integer literal"); }; - assert_eq!(field, 2_u128.into()); - assert!(!negative); + assert_eq!(value, SignedField::positive(2_u128)); } #[test] @@ -1023,11 +1027,10 @@ mod tests { panic!("Expected expression statement"); }; - let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + let ExpressionKind::Literal(Literal::Integer(value)) = expr.kind else { panic!("Expected integer literal"); }; - assert_eq!(field, 1_u128.into()); - assert!(!negative); + assert_eq!(value, SignedField::positive(1_u128)); } #[test] @@ -1056,9 +1059,9 @@ mod tests { 2 3 }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let expr = parser.parse_expression_or_error(); - assert_eq!(expr.span.end() as usize, src.len()); + assert_eq!(expr.location.span.end() as usize, src.len()); assert_eq!(parser.errors.len(), 2); assert!(matches!( parser.errors[0].reason(), @@ -1079,11 +1082,13 @@ mod tests { let src = " // Safety: test unsafe { 1 }"; - let expr = parse_expression_no_errors(src); - let ExpressionKind::Unsafe(block, _) = expr.kind else { + let mut parser = Parser::for_str_with_dummy_file(src); + let expr = parser.parse_expression_or_error(); + assert!(parser.errors.is_empty()); + let ExpressionKind::Unsafe(unsafe_expression) = expr.kind else { panic!("Expected unsafe expression"); }; - assert_eq!(block.statements.len(), 1); + assert_eq!(unsafe_expression.block.statements.len(), 1); } #[test] @@ -1092,12 +1097,12 @@ mod tests { /// Safety: test unsafe { 1 }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let expr = parser.parse_expression().unwrap(); - let ExpressionKind::Unsafe(block, _) = expr.kind else { + let ExpressionKind::Unsafe(unsafe_expression) = expr.kind else { panic!("Expected unsafe expression"); }; - assert_eq!(block.statements.len(), 1); + assert_eq!(unsafe_expression.block.statements.len(), 1); } #[test] @@ -1107,7 +1112,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); parser.parse_expression(); let error = get_single_error(&parser.errors, span); assert_eq!(error.to_string(), "Expected an expression but found end of input"); @@ -1120,7 +1125,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); parser.parse_expression(); let reason = get_single_error_reason(&parser.errors, span); let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { @@ -1173,9 +1178,9 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let expr = parser.parse_expression_or_error(); - assert_eq!(expr.span.end() as usize, src.len()); + assert_eq!(expr.location.span.end() as usize, src.len()); let reason = get_single_error_reason(&parser.errors, span); let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { @@ -1334,9 +1339,9 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let expr = parser.parse_expression_or_error(); - assert_eq!(expr.span.end() as usize, src.len()); + assert_eq!(expr.location.span.end() as usize, src.len()); let reason = get_single_error_reason(&parser.errors, span); let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { panic!("Expected a different error"); @@ -1355,7 +1360,7 @@ mod tests { #[test] fn parses_call_with_wrong_expression() { let src = "foo(]) "; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); parser.parse_expression_or_error(); assert!(!parser.errors.is_empty()); } @@ -1478,7 +1483,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let expr = parser.parse_expression_or_error(); let error = get_single_error(&parser.errors, span); @@ -1506,7 +1511,7 @@ mod tests { ^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let expr = parser.parse_expression_or_error(); let error = get_single_error(&parser.errors, span); @@ -1595,7 +1600,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); parser.parse_expression(); let error = get_single_error(&parser.errors, span); assert_eq!(error.to_string(), "Expected a type but found end of input"); @@ -1616,9 +1621,9 @@ mod tests { fn parses_operators() { for operator in BinaryOpKind::iter() { let src = format!("1 {operator} 2"); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let expr = parser.parse_expression_or_error(); - assert_eq!(expr.span.end() as usize, src.len()); + assert_eq!(expr.location.span.end() as usize, src.len()); assert!(parser.errors.is_empty(), "Expected no errors for {operator}"); let ExpressionKind::Infix(infix_expr) = expr.kind else { panic!("Expected infix for {operator}"); @@ -1637,14 +1642,22 @@ mod tests { let multiply_or_divide_or_modulo = "1 * 2 / 3 % 4"; let expected_multiply_or_divide_or_modulo = "(((1 * 2) / 3) % 4)"; - let add_or_subtract = format!("{multiply_or_divide_or_modulo} + {multiply_or_divide_or_modulo} - {multiply_or_divide_or_modulo}"); - let expected_add_or_subtract = format!("(({expected_multiply_or_divide_or_modulo} + {expected_multiply_or_divide_or_modulo}) - {expected_multiply_or_divide_or_modulo})"); + let add_or_subtract = format!( + "{multiply_or_divide_or_modulo} + {multiply_or_divide_or_modulo} - {multiply_or_divide_or_modulo}" + ); + let expected_add_or_subtract = format!( + "(({expected_multiply_or_divide_or_modulo} + {expected_multiply_or_divide_or_modulo}) - {expected_multiply_or_divide_or_modulo})" + ); let shift = format!("{add_or_subtract} << {add_or_subtract} >> {add_or_subtract}"); - let expected_shift = format!("(({expected_add_or_subtract} << {expected_add_or_subtract}) >> {expected_add_or_subtract})"); + let expected_shift = format!( + "(({expected_add_or_subtract} << {expected_add_or_subtract}) >> {expected_add_or_subtract})" + ); let less_or_greater = format!("{shift} < {shift} > {shift} <= {shift} >= {shift}"); - let expected_less_or_greater = format!("(((({expected_shift} < {expected_shift}) > {expected_shift}) <= {expected_shift}) >= {expected_shift})"); + let expected_less_or_greater = format!( + "(((({expected_shift} < {expected_shift}) > {expected_shift}) <= {expected_shift}) >= {expected_shift})" + ); let xor = format!("{less_or_greater} ^ {less_or_greater}"); let expected_xor = format!("({expected_less_or_greater} ^ {expected_less_or_greater})"); @@ -1810,7 +1823,7 @@ mod tests { ^^^^^^^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let expression = parser.parse_expression_or_error(); let ExpressionKind::Constrain(constrain) = expression.kind else { panic!("Expected constrain expression"); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs index 29e864200f3e..f10b790e63f6 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs @@ -13,28 +13,28 @@ use crate::{ }; use acvm::AcirField; -use noirc_errors::Span; +use noirc_errors::{Location, Span}; use super::parse_many::separated_by_comma_until_right_paren; use super::pattern::SelfPattern; -use super::{pattern::PatternOrSelf, Parser}; +use super::{Parser, pattern::PatternOrSelf}; pub(crate) struct FunctionDefinitionWithOptionalBody { pub(crate) name: Ident, pub(crate) generics: UnresolvedGenerics, pub(crate) parameters: Vec, pub(crate) body: Option, - pub(crate) span: Span, + pub(crate) location: Location, pub(crate) where_clause: Vec, pub(crate) return_type: FunctionReturnType, pub(crate) return_visibility: Visibility, } -impl<'a> Parser<'a> { +impl Parser<'_> { /// Function = 'fn' identifier Generics FunctionParameters ( '->' Visibility Type )? WhereClause ( Block | ';' ) pub(crate) fn parse_function( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, visibility: ItemVisibility, is_comptime: bool, is_unconstrained: bool, @@ -52,7 +52,7 @@ impl<'a> Parser<'a> { pub(crate) fn parse_function_definition( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, visibility: ItemVisibility, is_comptime: bool, is_unconstrained: bool, @@ -74,7 +74,7 @@ impl<'a> Parser<'a> { generics: func.generics, parameters: func.parameters, body: func.body.unwrap_or_else(empty_body), - span: func.span, + location: func.location, where_clause: func.where_clause, return_type: func.return_type, return_visibility: func.return_visibility, @@ -88,7 +88,7 @@ impl<'a> Parser<'a> { ) -> FunctionDefinitionWithOptionalBody { let Some(name) = self.eat_ident() else { self.expected_identifier(); - return empty_function(self.previous_token_span); + return empty_function(self.previous_token_location); }; let generics = self.parse_generics(); @@ -99,7 +99,7 @@ impl<'a> Parser<'a> { None => { self.push_error( ParserErrorReason::MissingParametersForFunctionDefinition, - name.span(), + name.location(), ); Vec::new() } @@ -109,15 +109,32 @@ impl<'a> Parser<'a> { let visibility = self.parse_visibility(); (FunctionReturnType::Ty(self.parse_type_or_error()), visibility) } else { - (FunctionReturnType::Default(self.span_at_previous_token_end()), Visibility::Private) + // This will return the span between `)` and `{` + // + // fn foo() { } + // ^^^ + let mut location = self.previous_token_location.merge(self.current_token_location); + + // Here we change it to this (if there's space) + // + // fn foo() { } + // ^ + if location.span.end() - location.span.start() >= 3 { + location = Location::new( + Span::from(location.span.start() + 1..location.span.end() - 1), + location.file, + ); + } + + (FunctionReturnType::Default(location), Visibility::Private) }; let where_clause = self.parse_where_clause(); - let body_start_span = self.current_token_span; + let body_start_location = self.current_token_location; let body = if self.eat_semicolons() { if !allow_optional_body { - self.push_error(ParserErrorReason::ExpectedFunctionBody, body_start_span); + self.push_error(ParserErrorReason::ExpectedFunctionBody, body_start_location); } None @@ -130,7 +147,7 @@ impl<'a> Parser<'a> { generics, parameters, body, - span: self.span_since(body_start_span), + location: self.location_since(body_start_location), where_clause, return_type, return_visibility, @@ -154,7 +171,7 @@ impl<'a> Parser<'a> { fn parse_function_parameter(&mut self, allow_self: bool) -> Option { loop { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let pattern_or_self = if allow_self { self.parse_pattern_or_self() @@ -175,49 +192,54 @@ impl<'a> Parser<'a> { }; return Some(match pattern_or_self { - PatternOrSelf::Pattern(pattern) => self.pattern_param(pattern, start_span), + PatternOrSelf::Pattern(pattern) => self.pattern_param(pattern, start_location), PatternOrSelf::SelfPattern(self_pattern) => self.self_pattern_param(self_pattern), }); } } - fn pattern_param(&mut self, pattern: Pattern, start_span: Span) -> Param { + fn pattern_param(&mut self, pattern: Pattern, start_location: Location) -> Param { let (visibility, typ) = if !self.eat_colon() { self.push_error( ParserErrorReason::MissingTypeForFunctionParameter, - Span::from(pattern.span().start()..self.current_token_span.end()), + pattern.location().merge(self.current_token_location), ); let visibility = Visibility::Private; - let typ = UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }; + let typ = + UnresolvedType { typ: UnresolvedTypeData::Error, location: Location::dummy() }; (visibility, typ) } else { - (self.parse_visibility(), self.parse_type_or_error()) + ( + self.parse_visibility(), + self.parse_type_or_error_with_recovery(&[Token::Comma, Token::RightParen]), + ) }; - Param { visibility, pattern, typ, span: self.span_since(start_span) } + Param { visibility, pattern, typ, location: self.location_since(start_location) } } fn self_pattern_param(&mut self, self_pattern: SelfPattern) -> Param { - let ident_span = self.previous_token_span; - let ident = Ident::new("self".to_string(), ident_span); - let path = Path::from_single("Self".to_owned(), ident_span); + let ident_location = self.previous_token_location; + let ident = Ident::new("self".to_string(), ident_location); + let path = Path::from_single("Self".to_owned(), ident_location); let no_args = GenericTypeArgs::default(); - let mut self_type = UnresolvedTypeData::Named(path, no_args, true).with_span(ident_span); + let mut self_type = + UnresolvedTypeData::Named(path, no_args, true).with_location(ident_location); let mut pattern = Pattern::Identifier(ident); if self_pattern.reference { - self_type = - UnresolvedTypeData::MutableReference(Box::new(self_type)).with_span(ident_span); + self_type = UnresolvedTypeData::MutableReference(Box::new(self_type)) + .with_location(ident_location); } else if self_pattern.mutable { - pattern = Pattern::Mutable(Box::new(pattern), ident_span, true); + pattern = Pattern::Mutable(Box::new(pattern), ident_location, true); } Param { visibility: Visibility::Private, pattern, typ: self_type, - span: self.span_since(ident_span), + location: self.location_since(ident_location), } } @@ -256,17 +278,20 @@ impl<'a> Parser<'a> { Visibility::Private } - fn validate_attributes(&mut self, attributes: Vec<(Attribute, Span)>) -> Attributes { + fn validate_attributes(&mut self, attributes: Vec<(Attribute, Location)>) -> Attributes { let mut function = None; let mut secondary = Vec::new(); - for (index, (attribute, span)) in attributes.into_iter().enumerate() { + for (index, (attribute, location)) in attributes.into_iter().enumerate() { match attribute { Attribute::Function(attr) => { if function.is_none() { function = Some((attr, index)); } else { - self.push_error(ParserErrorReason::MultipleFunctionAttributesFound, span); + self.push_error( + ParserErrorReason::MultipleFunctionAttributesFound, + location, + ); } } Attribute::Secondary(attr) => secondary.push(attr), @@ -277,15 +302,16 @@ impl<'a> Parser<'a> { } } -fn empty_function(span: Span) -> FunctionDefinitionWithOptionalBody { +fn empty_function(location: Location) -> FunctionDefinitionWithOptionalBody { + let span = Span::from(location.span.end()..location.span.end()); FunctionDefinitionWithOptionalBody { name: Ident::default(), generics: Vec::new(), parameters: Vec::new(), body: None, - span: Span::from(span.end()..span.end()), + location: Location::new(span, location.file), where_clause: Vec::new(), - return_type: FunctionReturnType::Default(Span::default()), + return_type: FunctionReturnType::Default(Location::dummy()), return_visibility: Visibility::Private, } } @@ -297,21 +323,22 @@ fn empty_body() -> BlockExpression { #[cfg(test)] mod tests { use crate::{ - ast::{ItemVisibility, NoirFunction, UnresolvedTypeData, Visibility}, + ast::{ + IntegerBitSize, ItemVisibility, NoirFunction, Signedness, UnresolvedTypeData, + Visibility, + }, + parse_program_with_dummy_file, parser::{ - parser::{ - parse_program, - tests::{ - expect_no_errors, get_single_error, get_single_error_reason, - get_source_with_error_span, - }, - }, ItemKind, ParserErrorReason, + parser::tests::{ + expect_no_errors, get_single_error, get_single_error_reason, + get_source_with_error_span, + }, }, }; fn parse_function_no_error(src: &str) -> NoirFunction { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -405,7 +432,7 @@ mod tests { #[test] fn parse_function_unclosed_parentheses() { let src = "fn foo(x: i32,"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -422,7 +449,7 @@ mod tests { ^^^^^^^^^^^^^^ "; let (src, span) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::MultipleFunctionAttributesFound)); } @@ -434,7 +461,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::ExpectedFunctionBody)); } @@ -446,7 +473,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let ItemKind::Function(noir_function) = &module.items[0].kind else { panic!("Expected function"); @@ -464,7 +491,7 @@ mod tests { ^^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let ItemKind::Function(noir_function) = &module.items[0].kind else { panic!("Expected function"); @@ -482,7 +509,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let ItemKind::Function(noir_function) = &module.items[0].kind else { panic!("Expected function"); @@ -509,8 +536,38 @@ mod tests { ^^^ "; let (src, span) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::MissingParametersForFunctionDefinition)); } + + #[test] + fn parse_function_with_keyword_before_type() { + let src = " + fn foo(x: mut i32, y: i64) {} + ^^^ + "; + let (src, span) = get_source_with_error_span(src); + let (mut module, errors) = parse_program_with_dummy_file(&src); + let error = get_single_error(&errors, span); + assert_eq!(error.to_string(), "Expected a type but found 'mut'"); + + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Function(noir_function) = item.kind else { + panic!("Expected function"); + }; + + let params = noir_function.parameters(); + assert_eq!(params.len(), 2); + + assert_eq!( + params[0].typ.typ, + UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::ThirtyTwo) + ); + assert_eq!( + params[1].typ.typ, + UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::SixtyFour) + ); + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/generics.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/generics.rs index f577a237615a..0bac5d5f34ba 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -3,13 +3,13 @@ use crate::{ GenericTypeArg, GenericTypeArgs, IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, }, - parser::{labels::ParsingRuleLabel, ParserErrorReason}, + parser::{ParserErrorReason, labels::ParsingRuleLabel}, token::{Keyword, Token, TokenKind}, }; -use super::{parse_many::separated_by_comma, Parser}; +use super::{Parser, parse_many::separated_by_comma}; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Generics = ( '<' GenericsList? '>' )? /// /// GenericsList = Generic ( ',' Generic )* ','? @@ -71,11 +71,11 @@ impl<'a> Parser<'a> { // If we didn't get a type after the colon, error and assume it's u32 self.push_error( ParserErrorReason::MissingTypeForNumericGeneric, - self.current_token_span, + self.current_token_location, ); let typ = UnresolvedType { typ: UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo), - span: self.span_at_previous_token_end(), + location: self.location_at_previous_token_end(), }; return Some(UnresolvedGeneric::Numeric { ident, typ }); } @@ -85,7 +85,7 @@ impl<'a> Parser<'a> { if matches!(signedness, Signedness::Signed) || matches!(bit_size, IntegerBitSize::SixtyFour) { - self.push_error(ParserErrorReason::ForbiddenNumericGenericType, typ.span); + self.push_error(ParserErrorReason::ForbiddenNumericGenericType, typ.location); } } @@ -97,7 +97,7 @@ impl<'a> Parser<'a> { let token = self.eat_kind(TokenKind::QuotedType)?; match token.into_token() { Token::QuotedType(id) => { - Some(UnresolvedGeneric::Resolved(id, self.previous_token_span)) + Some(UnresolvedGeneric::Resolved(id, self.previous_token_location)) } _ => unreachable!(), } @@ -167,22 +167,22 @@ mod tests { use crate::{ ast::{GenericTypeArgs, IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedTypeData}, parser::{ + Parser, ParserErrorReason, parser::tests::{ expect_no_errors, get_single_error_reason, get_source_with_error_span, }, - Parser, ParserErrorReason, }, }; fn parse_generics_no_errors(src: &str) -> Vec { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let generics = parser.parse_generics(); expect_no_errors(&parser.errors); generics } fn parse_generic_type_args_no_errors(src: &str) -> GenericTypeArgs { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let generics = parser.parse_generic_type_args(); expect_no_errors(&parser.errors); generics @@ -263,7 +263,7 @@ mod tests { ^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); parser.parse_generics(); let reason = get_single_error_reason(&parser.errors, span); assert!(matches!(reason, ParserErrorReason::ForbiddenNumericGenericType)); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/global.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/global.rs index 2cd8343fe31d..2edb3eeaa187 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/global.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/global.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{ @@ -11,11 +11,11 @@ use crate::{ use super::Parser; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Global = 'global' identifier OptionalTypeAnnotation '=' Expression ';' pub(crate) fn parse_global( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, comptime: bool, mutable: bool, ) -> LetStatement { @@ -31,9 +31,9 @@ impl<'a> Parser<'a> { pattern: ident_to_pattern(Ident::default(), mutable), r#type: UnresolvedType { typ: UnresolvedTypeData::Unspecified, - span: Span::default(), + location: Location::dummy(), }, - expression: Expression { kind: ExpressionKind::Error, span: Span::default() }, + expression: Expression { kind: ExpressionKind::Error, location: Location::dummy() }, attributes, comptime, is_global_let, @@ -47,8 +47,8 @@ impl<'a> Parser<'a> { let expression = if self.eat_assign() { self.parse_expression_or_error() } else { - self.push_error(ParserErrorReason::GlobalWithoutValue, pattern.span()); - Expression { kind: ExpressionKind::Error, span: Span::default() } + self.push_error(ParserErrorReason::GlobalWithoutValue, pattern.location()); + Expression { kind: ExpressionKind::Error, location: Location::dummy() } }; if !self.eat_semicolons() { @@ -61,8 +61,8 @@ impl<'a> Parser<'a> { fn ident_to_pattern(ident: Ident, mutable: bool) -> Pattern { if mutable { - let span = ident.span(); - Pattern::Mutable(Box::new(Pattern::Identifier(ident)), span, false) + let location = ident.location(); + Pattern::Mutable(Box::new(Pattern::Identifier(ident)), location, false) } else { Pattern::Identifier(ident) } @@ -77,20 +77,18 @@ mod tests { ExpressionKind, IntegerBitSize, ItemVisibility, LetStatement, Literal, Pattern, Signedness, UnresolvedTypeData, }, + parse_program_with_dummy_file, parser::{ - parser::{ - parse_program, - tests::{ - expect_no_errors, get_single_error, get_single_error_reason, - get_source_with_error_span, - }, - }, ItemKind, ParserErrorReason, + parser::tests::{ + expect_no_errors, get_single_error, get_single_error_reason, + get_source_with_error_span, + }, }, }; fn parse_global_no_errors(src: &str) -> (LetStatement, ItemVisibility) { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -158,7 +156,7 @@ mod tests { ^^^ "; let (src, span) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::GlobalWithoutValue)); } @@ -170,7 +168,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let error = get_single_error(&errors, span); assert_eq!(error.to_string(), "Expected a ';' but found end of input"); } @@ -188,13 +186,11 @@ mod tests { assert_eq!(let_statement.pattern.span().start(), 16); assert_eq!(let_statement.pattern.span().end(), 19); - let ExpressionKind::Literal(Literal::Integer(abs_value, is_negative)) = - let_statement.expression.kind - else { + let ExpressionKind::Literal(Literal::Integer(value)) = let_statement.expression.kind else { panic!("Expected integer literal expression, got {:?}", let_statement.expression.kind); }; - assert!(is_negative); - assert_eq!(abs_value, FieldElement::from(17u128)); + assert!(value.is_negative); + assert_eq!(value.field, FieldElement::from(17u128)); } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/impls.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/impls.rs index 4b5054984d4f..3fdfbc7d28ad 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{ @@ -6,32 +6,32 @@ use crate::{ TraitImplItem, TraitImplItemKind, TypeImpl, UnresolvedGeneric, UnresolvedType, UnresolvedTypeData, }, - parser::{labels::ParsingRuleLabel, ParserErrorReason}, + parser::{ParserErrorReason, labels::ParsingRuleLabel}, token::{Keyword, Token}, }; -use super::{parse_many::without_separator, Parser}; +use super::{Parser, parse_many::without_separator}; pub(crate) enum Impl { Impl(TypeImpl), TraitImpl(NoirTraitImpl), } -impl<'a> Parser<'a> { +impl Parser<'_> { /// Impl /// = TypeImpl /// | TraitImpl pub(crate) fn parse_impl(&mut self) -> Impl { let generics = self.parse_generics(); - let type_span_start = self.current_token_span; + let type_location_start = self.current_token_location; let object_type = self.parse_type_or_error(); - let type_span = self.span_since(type_span_start); + let type_location = self.location_since(type_location_start); if self.eat_keyword(Keyword::For) { Impl::TraitImpl(self.parse_trait_impl(generics, object_type)) } else { - Impl::Impl(self.parse_type_impl(object_type, type_span, generics)) + Impl::Impl(self.parse_type_impl(object_type, type_location, generics)) } } @@ -39,18 +39,18 @@ impl<'a> Parser<'a> { fn parse_type_impl( &mut self, object_type: UnresolvedType, - type_span: Span, + type_location: Location, generics: Vec, ) -> TypeImpl { let where_clause = self.parse_where_clause(); let methods = self.parse_type_impl_body(); - TypeImpl { object_type, type_span, generics, where_clause, methods } + TypeImpl { object_type, type_location, generics, where_clause, methods } } /// TypeImplBody = '{' TypeImplItem* '}' /// /// TypeImplItem = OuterDocComments Attributes Modifiers Function - fn parse_type_impl_body(&mut self) -> Vec<(Documented, Span)> { + fn parse_type_impl_body(&mut self) -> Vec<(Documented, Location)> { if !self.eat_left_brace() { self.expected_token(Token::LeftBrace); return Vec::new(); @@ -63,10 +63,10 @@ impl<'a> Parser<'a> { ) } - fn parse_type_impl_method(&mut self) -> Option<(Documented, Span)> { + fn parse_type_impl_method(&mut self) -> Option<(Documented, Location)> { self.parse_item_in_list(ParsingRuleLabel::Function, |parser| { let doc_comments = parser.parse_outer_doc_comments(); - let start_span = parser.current_token_span; + let start_location = parser.current_token_location; let attributes = parser.parse_attributes(); let modifiers = parser.parse_modifiers( false, // allow mutable @@ -80,7 +80,7 @@ impl<'a> Parser<'a> { modifiers.unconstrained.is_some(), true, // allow_self ); - Some((Documented::new(method, doc_comments), parser.span_since(start_span))) + Some((Documented::new(method, doc_comments), parser.location_since(start_location))) } else { parser.modifiers_not_followed_by_an_item(modifiers); None @@ -118,11 +118,11 @@ impl<'a> Parser<'a> { fn parse_trait_impl_item(&mut self) -> Option> { self.parse_item_in_list(ParsingRuleLabel::TraitImplItem, |parser| { - let start_span = parser.current_token_span; + let start_location = parser.current_token_location; let doc_comments = parser.parse_outer_doc_comments(); if let Some(kind) = parser.parse_trait_impl_item_kind() { - let item = TraitImplItem { kind, span: parser.span_since(start_span) }; + let item = TraitImplItem { kind, location: parser.location_since(start_location) }; Some(Documented::new(item, doc_comments)) } else { None @@ -157,14 +157,17 @@ impl<'a> Parser<'a> { self.eat_semicolons(); return Some(TraitImplItemKind::Type { name: Ident::default(), - alias: UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }, + alias: UnresolvedType { + typ: UnresolvedTypeData::Error, + location: Location::dummy(), + }, }); }; let alias = if self.eat_assign() { self.parse_type_or_error() } else { - UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() } + UnresolvedType { typ: UnresolvedTypeData::Error, location: Location::dummy() } }; self.eat_semicolons(); @@ -192,7 +195,7 @@ impl<'a> Parser<'a> { self.parse_expression_or_error() } else { self.expected_token(Token::Assign); - Expression { kind: ExpressionKind::Error, span: Span::default() } + Expression { kind: ExpressionKind::Error, location: Location::dummy() } }; self.eat_semicolons(); @@ -210,7 +213,7 @@ impl<'a> Parser<'a> { if modifiers.visibility != ItemVisibility::Private { self.push_error( ParserErrorReason::TraitImplVisibilityIgnored, - modifiers.visibility_span, + modifiers.visibility_location, ); } @@ -236,17 +239,15 @@ mod tests { ast::{ ItemVisibility, NoirTraitImpl, Pattern, TraitImplItemKind, TypeImpl, UnresolvedTypeData, }, + parse_program_with_dummy_file, parser::{ - parser::{ - parse_program, - tests::{expect_no_errors, get_single_error, get_source_with_error_span}, - }, ItemKind, + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, }, }; fn parse_type_impl_no_errors(src: &str) -> TypeImpl { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -257,7 +258,7 @@ mod tests { } fn parse_trait_impl_no_errors(src: &str) -> NoirTraitImpl { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -402,7 +403,7 @@ mod tests { #[test] fn parse_empty_impl_missing_right_brace() { let src = "impl Foo {"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -415,7 +416,7 @@ mod tests { #[test] fn parse_empty_impl_incorrect_body() { let src = "impl Foo { hello fn foo() {} }"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -538,7 +539,7 @@ mod tests { ^^^^^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -558,7 +559,7 @@ mod tests { ^^^^^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let item = &module.items[0]; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/infix.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/infix.rs index f006923b8a25..c900228cc866 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/infix.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/infix.rs @@ -1,4 +1,4 @@ -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Located, Location}; use crate::{ ast::{BinaryOpKind, Expression, ExpressionKind, InfixExpression}, @@ -157,22 +157,22 @@ impl<'a> Parser<'a> { Next: FnMut(&mut Parser<'a>, bool) -> Option, Op: FnMut(&mut Parser<'a>) -> Option, { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let mut lhs = next(self, allow_constructors)?; loop { - let operator_start_span = self.current_token_span; + let operator_start_location = self.current_token_location; let Some(operator) = op(self) else { break; }; - let operator = Spanned::from(operator_start_span, operator); + let operator = Located::from(operator_start_location, operator); let Some(rhs) = next(self, allow_constructors) else { self.push_expected_expression(); break; }; - lhs = self.new_infix_expression(lhs, operator, rhs, start_span); + lhs = self.new_infix_expression(lhs, operator, rhs, start_location); } Some(lhs) @@ -181,13 +181,13 @@ impl<'a> Parser<'a> { fn new_infix_expression( &self, lhs: Expression, - operator: Spanned, + operator: Located, rhs: Expression, - start_span: Span, + start_location: Location, ) -> Expression { let infix_expr = InfixExpression { lhs, operator, rhs }; let kind = ExpressionKind::Infix(Box::new(infix_expr)); - let span = self.span_since(start_span); - Expression { kind, span } + let location = self.location_since(start_location); + Expression { kind, location } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item.rs index 6fa67ff08533..121a1e749f2a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item.rs @@ -1,11 +1,11 @@ use iter_extended::vecmap; use crate::{ - parser::{labels::ParsingRuleLabel, Item, ItemKind, ParserErrorReason}, + parser::{Item, ItemKind, ParserErrorReason, labels::ParsingRuleLabel}, token::{Keyword, Token}, }; -use super::{impls::Impl, parse_many::without_separator, Parser}; +use super::{Parser, impls::Impl, parse_many::without_separator}; impl<'a> Parser<'a> { pub(crate) fn parse_top_level_items(&mut self) -> Vec { @@ -89,16 +89,16 @@ impl<'a> Parser<'a> { /// Item = OuterDocComments ItemKind fn parse_item(&mut self) -> Vec { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let doc_comments = self.parse_outer_doc_comments(); let kinds = self.parse_item_kind(); - let span = self.span_since(start_span); + let location = self.location_since(start_location); if kinds.is_empty() && !doc_comments.is_empty() { - self.push_error(ParserErrorReason::DocCommentDoesNotDocumentAnything, start_span); + self.push_error(ParserErrorReason::DocCommentDoesNotDocumentAnything, start_location); } - vecmap(kinds, |kind| Item { kind, span, doc_comments: doc_comments.clone() }) + vecmap(kinds, |kind| Item { kind, location, doc_comments: doc_comments.clone() }) } /// This method returns one 'ItemKind' in the majority of cases. @@ -123,7 +123,7 @@ impl<'a> Parser<'a> { return vec![ItemKind::InnerAttribute(kind)]; } - let start_span = self.current_token_span; + let start_location = self.current_token_location; let attributes = self.parse_attributes(); let modifiers = self.parse_modifiers( @@ -149,7 +149,7 @@ impl<'a> Parser<'a> { return vec![ItemKind::Struct(self.parse_struct( attributes, modifiers.visibility, - start_span, + start_location, ))]; } @@ -159,7 +159,7 @@ impl<'a> Parser<'a> { return vec![ItemKind::Enum(self.parse_enum( attributes, modifiers.visibility, - start_span, + start_location, ))]; } @@ -176,7 +176,7 @@ impl<'a> Parser<'a> { self.comptime_mutable_and_unconstrained_not_applicable(modifiers); let (noir_trait, noir_impl) = - self.parse_trait(attributes, modifiers.visibility, start_span); + self.parse_trait(attributes, modifiers.visibility, start_location); let mut output = vec![ItemKind::Trait(noir_trait)]; if let Some(noir_impl) = noir_impl { output.push(ItemKind::TraitImpl(noir_impl)); @@ -202,7 +202,7 @@ impl<'a> Parser<'a> { self.comptime_mutable_and_unconstrained_not_applicable(modifiers); return vec![ItemKind::TypeAlias( - self.parse_type_alias(modifiers.visibility, start_span), + self.parse_type_alias(modifiers.visibility, start_location), )]; } @@ -235,7 +235,7 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use crate::{ - parse_program, + parse_program_with_dummy_file, parser::parser::tests::{get_single_error, get_source_with_error_span}, }; @@ -246,7 +246,7 @@ mod tests { ^^^^^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 2); let error = get_single_error(&errors, span); assert_eq!(error.to_string(), "Expected an item but found 'hello'"); @@ -259,7 +259,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let error = get_single_error(&errors, span); assert_eq!(error.to_string(), "Expected a '}' but found end of input"); @@ -273,7 +273,7 @@ mod tests { ^^^^^^^^^^^^^^^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let error = get_single_error(&errors, span); assert!(error.to_string().contains("This doc comment doesn't document anything")); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item_visibility.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item_visibility.rs index 5aea5f6a45f8..91ace6a62ce3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item_visibility.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item_visibility.rs @@ -5,7 +5,7 @@ use crate::{ use super::Parser; -impl<'a> Parser<'a> { +impl Parser<'_> { /// ItemVisibility /// = 'pub' // ItemVisibility::Public /// | 'pub' '(' 'crate' ')' // ItemVisibility::PublicCrate @@ -39,15 +39,15 @@ mod tests { use crate::{ ast::ItemVisibility, parser::{ - parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, Parser, + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, }, }; #[test] fn parses_private_visibility() { let src = "("; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let visibility = parser.parse_item_visibility(); expect_no_errors(&parser.errors); assert_eq!(visibility, ItemVisibility::Private); @@ -56,7 +56,7 @@ mod tests { #[test] fn parses_public_visibility() { let src = "pub"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let visibility = parser.parse_item_visibility(); expect_no_errors(&parser.errors); assert_eq!(visibility, ItemVisibility::Public); @@ -69,7 +69,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let visibility = parser.parse_item_visibility(); assert_eq!(visibility, ItemVisibility::Public); let error = get_single_error(&parser.errors, span); @@ -83,7 +83,7 @@ mod tests { ^^^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let visibility = parser.parse_item_visibility(); assert_eq!(visibility, ItemVisibility::Public); let error = get_single_error(&parser.errors, span); @@ -96,7 +96,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let visibility = parser.parse_item_visibility(); assert_eq!(visibility, ItemVisibility::PublicCrate); let error = get_single_error(&parser.errors, span); @@ -106,7 +106,7 @@ mod tests { #[test] fn parses_public_crate_visibility() { let src = "pub(crate)"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let visibility = parser.parse_item_visibility(); expect_no_errors(&parser.errors); assert_eq!(visibility, ItemVisibility::PublicCrate); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/lambda.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/lambda.rs index a6eeb4286218..25f803c8e1de 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/lambda.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/lambda.rs @@ -4,9 +4,9 @@ use crate::{ token::Token, }; -use super::{parse_many::separated_by_comma, Parser}; +use super::{Parser, parse_many::separated_by_comma}; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Lambda = '|' LambdaParameters? '|' ( '->' Type )? Expression /// /// LambdaParameters = LambdaParameter ( ',' LambdaParameter )? ','? diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/modifiers.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/modifiers.rs index a668d3bae6a7..896a27c04169 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/modifiers.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/modifiers.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ast::ItemVisibility, token::Keyword}; @@ -7,31 +7,31 @@ use super::Parser; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub(crate) struct Modifiers { pub(crate) visibility: ItemVisibility, - pub(crate) visibility_span: Span, - pub(crate) unconstrained: Option, - pub(crate) comptime: Option, - pub(crate) mutable: Option, + pub(crate) visibility_location: Location, + pub(crate) unconstrained: Option, + pub(crate) comptime: Option, + pub(crate) mutable: Option, } -impl<'a> Parser<'a> { +impl Parser<'_> { /// Modifiers = ItemVisibility 'unconstrained'? 'comptime'? 'mut'? /// /// NOTE: we also allow `unconstrained` before the visibility for backwards compatibility. /// The formatter will put it after the visibility. pub(crate) fn parse_modifiers(&mut self, allow_mutable: bool) -> Modifiers { let unconstrained = if self.eat_keyword(Keyword::Unconstrained) { - Some(self.previous_token_span) + Some(self.previous_token_location) } else { None }; - let start_span = self.current_token_span; + let start_location = self.current_token_location; let visibility = self.parse_item_visibility(); - let visibility_span = self.span_since(start_span); + let visibility_location = self.location_since(start_location); let unconstrained = if unconstrained.is_none() { if self.eat_keyword(Keyword::Unconstrained) { - Some(self.previous_token_span) + Some(self.previous_token_location) } else { None } @@ -39,14 +39,17 @@ impl<'a> Parser<'a> { unconstrained }; - let comptime = - if self.eat_keyword(Keyword::Comptime) { Some(self.previous_token_span) } else { None }; + let comptime = if self.eat_keyword(Keyword::Comptime) { + Some(self.previous_token_location) + } else { + None + }; let mutable = if allow_mutable && self.eat_keyword(Keyword::Mut) { - Some(self.previous_token_span) + Some(self.previous_token_location) } else { None }; - Modifiers { visibility, visibility_span, unconstrained, comptime, mutable } + Modifiers { visibility, visibility_location, unconstrained, comptime, mutable } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/module.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/module.rs index 1bc3d7b5bebb..cae0a328d756 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/module.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/module.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{Ident, ItemVisibility, ModuleDeclaration}, @@ -8,12 +8,12 @@ use crate::{ use super::Parser; -impl<'a> Parser<'a> { +impl Parser<'_> { /// ModOrContract /// = ( 'mod' | 'contract' ) identifier ( '{' Module '}' | ';' ) pub(super) fn parse_mod_or_contract( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, is_contract: bool, visibility: ItemVisibility, ) -> ItemKind { @@ -58,16 +58,16 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { - use crate::parser::{ - parser::{parse_program, tests::expect_no_errors}, - ItemKind, + use crate::{ + parse_program_with_dummy_file, + parser::{ItemKind, parser::tests::expect_no_errors}, }; #[test] fn parse_module_declaration() { // TODO: `contract foo;` is parsed correctly but we don't it's considered a module let src = "mod foo;"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -80,7 +80,7 @@ mod tests { #[test] fn parse_submodule() { let src = "mod foo { mod bar; }"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -95,7 +95,7 @@ mod tests { #[test] fn parse_contract() { let src = "contract foo {}"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/parse_many.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/parse_many.rs index be156eb16188..1c77aac7f180 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/parse_many.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/parse_many.rs @@ -42,11 +42,7 @@ impl<'a> Parser<'a> { F: FnMut(&mut Parser<'a>) -> Option, { let f = |x: &mut Parser<'a>| { - if let Some(result) = f(x) { - vec![result] - } else { - vec![] - } + if let Some(result) = f(x) { vec![result] } else { vec![] } }; self.parse_many_to_many_return_trailing_separator_if_any(items, separated_by, f) } @@ -70,7 +66,7 @@ impl<'a> Parser<'a> { } } - let start_span = self.current_token_span; + let start_location = self.current_token_location; let mut new_elements = f(self); if new_elements.is_empty() { if let Some(end) = &separated_by.until { @@ -81,7 +77,7 @@ impl<'a> Parser<'a> { if let Some(separator) = &separated_by.token { if !trailing_separator && !elements.is_empty() { - self.expected_token_separating_items(separator.clone(), items, start_span); + self.expected_token_separating_items(separator.clone(), items, start_location); } } 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 99aedc6df897..a58bd9e1bb18 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 @@ -3,13 +3,13 @@ use crate::parser::ParserErrorReason; use crate::token::{Keyword, Token}; -use noirc_errors::Span; +use noirc_errors::Location; use crate::{parser::labels::ParsingRuleLabel, token::TokenKind}; use super::Parser; -impl<'a> Parser<'a> { +impl Parser<'_> { #[cfg(test)] pub(crate) fn parse_path_or_error(&mut self) -> Path { if let Some(path) = self.parse_path() { @@ -17,11 +17,7 @@ impl<'a> Parser<'a> { } else { self.expected_label(ParsingRuleLabel::Path); - Path { - segments: Vec::new(), - kind: PathKind::Plain, - span: self.span_at_previous_token_end(), - } + Path::plain(Vec::new(), self.location_at_previous_token_end()) } } @@ -44,11 +40,7 @@ impl<'a> Parser<'a> { } else { self.expected_label(ParsingRuleLabel::Path); - Path { - segments: Vec::new(), - kind: PathKind::Plain, - span: self.span_at_previous_token_end(), - } + Path::plain(Vec::new(), self.location_at_previous_token_end()) } } @@ -65,7 +57,7 @@ impl<'a> Parser<'a> { allow_turbofish: bool, allow_trailing_double_colon: bool, ) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let kind = self.parse_path_kind(); @@ -73,7 +65,7 @@ impl<'a> Parser<'a> { kind, allow_turbofish, allow_trailing_double_colon, - start_span, + start_location, )?; if path.segments.is_empty() { if path.kind != PathKind::Plain { @@ -90,20 +82,16 @@ impl<'a> Parser<'a> { kind: PathKind, allow_turbofish: bool, allow_trailing_double_colon: bool, - start_span: Span, + start_location: Location, ) -> Option { let path = self.parse_path_after_kind( kind, allow_turbofish, allow_trailing_double_colon, - start_span, + start_location, ); - if path.segments.is_empty() && path.kind == PathKind::Plain { - None - } else { - Some(path) - } + if path.segments.is_empty() && path.kind == PathKind::Plain { None } else { Some(path) } } /// Parses a path assuming the path's kind (plain, `crate::`, `super::`, etc.) @@ -114,14 +102,14 @@ impl<'a> Parser<'a> { kind: PathKind, allow_turbofish: bool, allow_trailing_double_colon: bool, - start_span: Span, + start_location: Location, ) -> Path { let mut segments = Vec::new(); if self.token.kind() == TokenKind::Ident { loop { let ident = self.eat_ident().unwrap(); - let span = ident.span(); + let location = ident.location(); let generics = if allow_turbofish && self.at(Token::DoubleColon) @@ -133,7 +121,11 @@ impl<'a> Parser<'a> { None }; - segments.push(PathSegment { ident, generics, span }); + segments.push(PathSegment { + ident, + generics, + location: self.location_since(location), + }); if self.at(Token::DoubleColon) && matches!(self.next_token.token(), Token::Ident(..)) @@ -151,7 +143,8 @@ impl<'a> Parser<'a> { } } - Path { segments, kind, span: self.span_since(start_span) } + let location = self.location_since(start_location); + Path { segments, kind, kind_location: start_location, location } } /// PathGenerics = GenericTypeArgs @@ -165,7 +158,7 @@ impl<'a> Parser<'a> { let generics = self.parse_generic_type_args(); for (name, _typ) in &generics.named_args { - self.push_error(on_named_arg_error.clone(), name.span()); + self.push_error(on_named_arg_error.clone(), name.location()); } Some(generics.ordered_args) @@ -208,7 +201,7 @@ impl<'a> Parser<'a> { ident } else { self.expected_identifier(); - Ident::new(String::new(), self.span_at_previous_token_end()) + Ident::new(String::new(), self.location_at_previous_token_end()) }; Some(AsTraitPath { typ, trait_path, trait_generics, impl_item }) @@ -221,13 +214,13 @@ mod tests { use crate::{ ast::{Path, PathKind}, parser::{ - parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, Parser, + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, }, }; fn parse_path_no_errors(src: &str) -> Path { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let path = parser.parse_path_or_error(); expect_no_errors(&parser.errors); path @@ -294,9 +287,9 @@ mod tests { #[test] fn parses_plain_one_segment_with_trailing_colons() { let src = "foo::"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let path = parser.parse_path_or_error(); - assert_eq!(path.span.end() as usize, src.len()); + assert_eq!(path.location.span.end() as usize, src.len()); assert_eq!(parser.errors.len(), 1); assert_eq!(path.kind, PathKind::Plain); assert_eq!(path.segments.len(), 1); @@ -322,9 +315,9 @@ mod tests { #[test] fn parses_path_stops_before_trailing_double_colon() { let src = "foo::bar::"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let path = parser.parse_path_or_error(); - assert_eq!(path.span.end() as usize, src.len()); + assert_eq!(path.location.span.end() as usize, src.len()); assert_eq!(parser.errors.len(), 1); assert_eq!(path.to_string(), "foo::bar"); } @@ -332,9 +325,9 @@ mod tests { #[test] fn parses_path_with_turbofish_stops_before_trailing_double_colon() { let src = "foo::bar::<1>::"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let path = parser.parse_path_or_error(); - assert_eq!(path.span.end() as usize, src.len()); + assert_eq!(path.location.span.end() as usize, src.len()); assert_eq!(parser.errors.len(), 1); assert_eq!(path.to_string(), "foo::bar::<1>"); } @@ -346,7 +339,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let path = parser.parse_path(); assert!(path.is_none()); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/pattern.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/pattern.rs index 50779e9ccffa..61fb1572c172 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -1,14 +1,14 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{Ident, Path, Pattern}, - parser::{labels::ParsingRuleLabel, ParserErrorReason}, + parser::{ParserErrorReason, labels::ParsingRuleLabel}, token::{Keyword, Token, TokenKind}, }; use super::{ - parse_many::{separated_by_comma_until_right_brace, separated_by_comma_until_right_paren}, Parser, + parse_many::{separated_by_comma_until_right_brace, separated_by_comma_until_right_paren}, }; pub(crate) enum PatternOrSelf { @@ -22,29 +22,29 @@ pub(crate) struct SelfPattern { pub(crate) mutable: bool, } -impl<'a> Parser<'a> { +impl Parser<'_> { pub(crate) fn parse_pattern_or_error(&mut self) -> Pattern { if let Some(pattern) = self.parse_pattern() { return pattern; } self.expected_label(ParsingRuleLabel::Pattern); - Pattern::Identifier(Ident::new(String::new(), self.span_at_previous_token_end())) + Pattern::Identifier(Ident::new(String::new(), self.location_at_previous_token_end())) } /// Pattern /// = 'mut' PatternNoMut pub(crate) fn parse_pattern(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let mutable = self.eat_keyword(Keyword::Mut); - self.parse_pattern_after_modifiers(mutable, start_span) + self.parse_pattern_after_modifiers(mutable, start_location) } /// PatternOrSelf /// = Pattern /// | SelfPattern pub(crate) fn parse_pattern_or_self(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.next_is_colon() && self.eat_self() { return Some(PatternOrSelf::SelfPattern(SelfPattern { @@ -61,7 +61,7 @@ impl<'a> Parser<'a> { })); } else { return Some(PatternOrSelf::Pattern( - self.parse_pattern_after_modifiers(true, start_span)?, + self.parse_pattern_after_modifiers(true, start_location)?, )); } } @@ -77,15 +77,15 @@ impl<'a> Parser<'a> { } else { self.push_error( ParserErrorReason::RefMutCanOnlyBeUsedWithSelf, - self.current_token_span, + self.current_token_location, ); return Some(PatternOrSelf::Pattern( - self.parse_pattern_after_modifiers(true, start_span)?, + self.parse_pattern_after_modifiers(true, start_location)?, )); } } - Some(PatternOrSelf::Pattern(self.parse_pattern_after_modifiers(false, start_span)?)) + Some(PatternOrSelf::Pattern(self.parse_pattern_after_modifiers(false, start_location)?)) } fn next_is_colon(&self) -> bool { @@ -95,13 +95,13 @@ impl<'a> Parser<'a> { pub(crate) fn parse_pattern_after_modifiers( &mut self, mutable: bool, - start_span: Span, + start_location: Location, ) -> Option { let pattern = self.parse_pattern_no_mut()?; Some(if mutable { Pattern::Mutable( Box::new(pattern), - self.span_since(start_span), + self.location_since(start_location), false, // is synthesized ) } else { @@ -117,7 +117,7 @@ impl<'a> Parser<'a> { /// /// IdentifierPattern = identifier fn parse_pattern_no_mut(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if let Some(pattern) = self.parse_interned_pattern() { return Some(pattern); @@ -131,18 +131,18 @@ impl<'a> Parser<'a> { if self.at_built_in_type() { self.push_error( ParserErrorReason::ExpectedPatternButFoundType(self.token.token().clone()), - self.current_token_span, + self.current_token_location, ); } return None; }; if self.eat_left_brace() { - return Some(self.parse_struct_pattern(path, start_span)); + return Some(self.parse_struct_pattern(path, start_location)); } if !path.is_ident() { - self.push_error(ParserErrorReason::InvalidPattern, path.span); + self.push_error(ParserErrorReason::InvalidPattern, path.location); let ident = path.segments.pop().unwrap().ident; return Some(Pattern::Identifier(ident)); @@ -158,7 +158,7 @@ impl<'a> Parser<'a> { match token.into_token() { Token::InternedPattern(pattern) => { - Some(Pattern::Interned(pattern, self.previous_token_span)) + Some(Pattern::Interned(pattern, self.previous_token_location)) } _ => unreachable!(), } @@ -168,7 +168,7 @@ impl<'a> Parser<'a> { /// /// PatternList = Pattern ( ',' Pattern )* ','? fn parse_tuple_pattern(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.eat_left_paren() { return None; @@ -180,7 +180,7 @@ impl<'a> Parser<'a> { Self::parse_tuple_pattern_element, ); - Some(Pattern::Tuple(patterns, self.span_since(start_span))) + Some(Pattern::Tuple(patterns, self.location_since(start_location))) } fn parse_tuple_pattern_element(&mut self) -> Option { @@ -197,14 +197,14 @@ impl<'a> Parser<'a> { /// StructPatternFields = StructPatternField ( ',' StructPatternField )? ','? /// /// StructPatternField = identifier ( ':' Pattern )? - fn parse_struct_pattern(&mut self, path: Path, start_span: Span) -> Pattern { + fn parse_struct_pattern(&mut self, path: Path, start_location: Location) -> Pattern { let fields = self.parse_many( "struct fields", separated_by_comma_until_right_brace(), Self::parse_struct_pattern_field, ); - Pattern::Struct(path, fields, self.span_since(start_span)) + Pattern::Struct(path, fields, self.location_since(start_location)) } fn parse_struct_pattern_field(&mut self) -> Option<(Ident, Pattern)> { @@ -254,17 +254,17 @@ mod tests { use crate::{ ast::Pattern, parser::{ + Parser, ParserErrorReason, parser::tests::{ expect_no_errors, get_single_error, get_single_error_reason, get_source_with_error_span, }, - Parser, ParserErrorReason, }, token::{Keyword, Token}, }; fn parse_pattern_no_errors(src: &str) -> Pattern { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let pattern = parser.parse_pattern_or_error(); expect_no_errors(&parser.errors); pattern @@ -307,7 +307,7 @@ mod tests { #[test] fn parses_unclosed_tuple_pattern() { let src = "(foo,"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let pattern = parser.parse_pattern_or_error(); assert_eq!(parser.errors.len(), 1); let Pattern::Tuple(patterns, _) = pattern else { panic!("Expected a tuple pattern") }; @@ -317,7 +317,7 @@ mod tests { #[test] fn parses_struct_pattern_no_fields() { let src = "foo::Bar {}"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let pattern = parser.parse_pattern_or_error(); expect_no_errors(&parser.errors); let Pattern::Struct(path, patterns, _) = pattern else { @@ -353,7 +353,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let pattern = parser.parse_pattern_or_error(); let error = get_single_error(&parser.errors, span); @@ -377,7 +377,7 @@ mod tests { #[test] fn parses_unclosed_struct_pattern() { let src = "foo::Bar { x"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let pattern = parser.parse_pattern_or_error(); assert_eq!(parser.errors.len(), 1); let Pattern::Struct(path, _, _) = pattern else { panic!("Expected a struct pattern") }; @@ -391,7 +391,7 @@ mod tests { ^^^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let pattern = parser.parse_pattern(); assert!(pattern.is_none()); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement.rs index d8679da6ba88..600ddec43c96 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -1,4 +1,4 @@ -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Located, Location}; use crate::{ ast::{ @@ -6,33 +6,30 @@ use crate::{ ForLoopStatement, ForRange, Ident, InfixExpression, LValue, LetStatement, Statement, StatementKind, WhileStatement, }, - parser::{labels::ParsingRuleLabel, ParserErrorReason}, + parser::{ParserErrorReason, labels::ParsingRuleLabel}, token::{Attribute, Keyword, Token, TokenKind}, }; use super::Parser; -impl<'a> Parser<'a> { +impl Parser<'_> { pub(crate) fn parse_statement_or_error(&mut self) -> Statement { if let Some((statement, (_token, _span))) = self.parse_statement() { statement } else { self.expected_label(ParsingRuleLabel::Statement); - Statement { kind: StatementKind::Error, span: self.span_at_previous_token_end() } + Statement { + kind: StatementKind::Error, + location: self.location_at_previous_token_end(), + } } } /// Statement = Attributes StatementKind ';'? - pub(crate) fn parse_statement(&mut self) -> Option<(Statement, (Option, Span))> { + pub(crate) fn parse_statement(&mut self) -> Option<(Statement, (Option, Location))> { loop { - let span_before_doc_comments = self.current_token_span; - let doc_comments = self.parse_outer_doc_comments(); - if !doc_comments.is_empty() { - self.push_error( - ParserErrorReason::DocCommentDoesNotDocumentAnything, - span_before_doc_comments, - ); - } + // Like in Rust, we allow parsing doc comments on top of a statement but they always produce a warning. + self.warn_on_outer_doc_comments(); if !self.current_token_comments.is_empty() { self.statement_comments = Some(std::mem::take(&mut self.current_token_comments)); @@ -41,26 +38,25 @@ impl<'a> Parser<'a> { } let attributes = self.parse_attributes(); - let start_span = self.current_token_span; + let start_location = self.current_token_location; let kind = self.parse_statement_kind(attributes); - self.statement_comments = None; - let (semicolon_token, semicolon_span) = if self.at(Token::Semicolon) { + let (semicolon_token, semicolon_location) = if self.at(Token::Semicolon) { let token = self.token.clone(); self.bump(); - let span = token.to_span(); + let location = token.location(); - (Some(token.into_token()), span) + (Some(token.into_token()), location) } else { - (None, self.previous_token_span) + (None, self.previous_token_location) }; - let span = self.span_since(start_span); + let location = self.location_since(start_location); if let Some(kind) = kind { - let statement = Statement { kind, span }; - return Some((statement, (semicolon_token, semicolon_span))); + let statement = Statement { kind, location }; + return Some((statement, (semicolon_token, semicolon_location))); } self.expected_label(ParsingRuleLabel::Statement); @@ -102,14 +98,14 @@ impl<'a> Parser<'a> { /// ExpressionStatement = Expression fn parse_statement_kind( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, ) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if let Some(token) = self.eat_kind(TokenKind::InternedStatement) { match token.into_token() { Token::InternedStatement(statement) => { - return Some(StatementKind::Interned(statement)) + return Some(StatementKind::Interned(statement)); } _ => unreachable!(), } @@ -125,7 +121,7 @@ impl<'a> Parser<'a> { if self.eat_keyword(Keyword::Return) { self.parse_expression(); - self.push_error(ParserErrorReason::EarlyReturn, self.span_since(start_span)); + self.push_error(ParserErrorReason::EarlyReturn, self.location_since(start_location)); return Some(StatementKind::Error); } @@ -151,26 +147,26 @@ impl<'a> Parser<'a> { } if let Some(kind) = self.parse_if_expr() { - let span = self.span_since(start_span); - return Some(StatementKind::Expression(Expression { kind, span })); + let location = self.location_since(start_location); + return Some(StatementKind::Expression(Expression { kind, location })); } if let Some(kind) = self.parse_match_expr() { - let span = self.span_since(start_span); - return Some(StatementKind::Expression(Expression { kind, span })); + let location = self.location_since(start_location); + return Some(StatementKind::Expression(Expression { kind, location })); } if let Some(block) = self.parse_block() { return Some(StatementKind::Expression(Expression { kind: ExpressionKind::Block(block), - span: self.span_since(start_span), + location: self.location_since(start_location), })); } if let Some(token) = self.eat_kind(TokenKind::InternedLValue) { match token.into_token() { Token::InternedLValue(lvalue) => { - let lvalue = LValue::Interned(lvalue, self.span_since(start_span)); + let lvalue = LValue::Interned(lvalue, self.location_since(start_location)); self.eat_or_error(Token::Assign); let expression = self.parse_expression_or_error(); return Some(StatementKind::Assign(AssignStatement { lvalue, expression })); @@ -188,7 +184,7 @@ impl<'a> Parser<'a> { } else { self.push_error( ParserErrorReason::InvalidLeftHandSideOfAssignment, - expression.span, + expression.location, ); } } @@ -204,13 +200,13 @@ impl<'a> Parser<'a> { }; let expression = Expression::new( ExpressionKind::Infix(Box::new(infix)), - self.span_since(start_span), + self.location_since(start_location), ); return Some(StatementKind::Assign(AssignStatement { lvalue, expression })); } else { self.push_error( ParserErrorReason::InvalidLeftHandSideOfAssignment, - expression.span, + expression.location, ); } } @@ -219,7 +215,7 @@ impl<'a> Parser<'a> { } fn next_is_op_assign(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let operator = if self.next_is(Token::Assign) { match self.token.token() { Token::Plus => Some(BinaryOpKind::Add), @@ -243,7 +239,7 @@ impl<'a> Parser<'a> { if let Some(operator) = operator { self.bump(); self.bump(); - Some(Spanned::from(self.span_since(start_span), operator)) + Some(Located::from(self.location_since(start_location), operator)) } else { None } @@ -251,7 +247,7 @@ impl<'a> Parser<'a> { /// ForStatement = 'for' identifier 'in' ForRange Block fn parse_for(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.eat_keyword(Keyword::For) { return None; @@ -260,76 +256,86 @@ impl<'a> Parser<'a> { let Some(identifier) = self.eat_ident() else { self.expected_identifier(); let identifier = Ident::default(); - return Some(self.empty_for_loop(identifier, start_span)); + return Some(self.empty_for_loop(identifier, start_location)); }; if !self.eat_keyword(Keyword::In) { self.expected_token(Token::Keyword(Keyword::In)); - return Some(self.empty_for_loop(identifier, start_span)); + return Some(self.empty_for_loop(identifier, start_location)); } let range = self.parse_for_range(); - let block_start_span = self.current_token_span; + let block_start_location = self.current_token_location; let block = if let Some(block) = self.parse_block() { Expression { kind: ExpressionKind::Block(block), - span: self.span_since(block_start_span), + location: self.location_since(block_start_location), } } else { self.expected_token(Token::LeftBrace); - Expression { kind: ExpressionKind::Error, span: self.span_since(block_start_span) } + Expression { + kind: ExpressionKind::Error, + location: self.location_since(block_start_location), + } }; - Some(ForLoopStatement { identifier, range, block, span: self.span_since(start_span) }) + Some(ForLoopStatement { + identifier, + range, + block, + location: self.location_since(start_location), + }) } /// LoopStatement = 'loop' Block - fn parse_loop(&mut self) -> Option<(Expression, Span)> { - let start_span = self.current_token_span; + fn parse_loop(&mut self) -> Option<(Expression, Location)> { + let start_location = self.current_token_location; if !self.eat_keyword(Keyword::Loop) { return None; } - self.push_error(ParserErrorReason::ExperimentalFeature("loops"), start_span); - - let block_start_span = self.current_token_span; + let block_start_location = self.current_token_location; let block = if let Some(block) = self.parse_block() { Expression { kind: ExpressionKind::Block(block), - span: self.span_since(block_start_span), + location: self.location_since(block_start_location), } } else { self.expected_token(Token::LeftBrace); - Expression { kind: ExpressionKind::Error, span: self.span_since(block_start_span) } + Expression { + kind: ExpressionKind::Error, + location: self.location_since(block_start_location), + } }; - Some((block, start_span)) + Some((block, start_location)) } /// WhileStatement = 'while' ExpressionExceptConstructor Block fn parse_while(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.eat_keyword(Keyword::While) { return None; } - self.push_error(ParserErrorReason::ExperimentalFeature("while loops"), start_span); - let condition = self.parse_expression_except_constructor_or_error(); - let block_start_span = self.current_token_span; + let block_start_location = self.current_token_location; let block = if let Some(block) = self.parse_block() { Expression { kind: ExpressionKind::Block(block), - span: self.span_since(block_start_span), + location: self.location_since(block_start_location), } } else { self.expected_token(Token::LeftBrace); - Expression { kind: ExpressionKind::Error, span: self.span_since(block_start_span) } + Expression { + kind: ExpressionKind::Error, + location: self.location_since(block_start_location), + } }; - Some(WhileStatement { condition, body: block, while_keyword_span: start_span }) + Some(WhileStatement { condition, body: block, while_keyword_location: start_location }) } /// ForRange @@ -349,15 +355,15 @@ impl<'a> Parser<'a> { } } - fn empty_for_loop(&mut self, identifier: Ident, start_span: Span) -> ForLoopStatement { + fn empty_for_loop(&mut self, identifier: Ident, start_location: Location) -> ForLoopStatement { ForLoopStatement { identifier, range: ForRange::Array(Expression { kind: ExpressionKind::Error, - span: Span::default(), + location: Location::dummy(), }), - block: Expression { kind: ExpressionKind::Error, span: Span::default() }, - span: self.span_since(start_span), + block: Expression { kind: ExpressionKind::Error, location: Location::dummy() }, + location: self.location_since(start_location), } } @@ -373,9 +379,9 @@ impl<'a> Parser<'a> { /// ComptimeFor = 'comptime' ForStatement fn parse_comptime_statement( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, ) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.eat_keyword(Keyword::Comptime) { return None; @@ -384,7 +390,7 @@ impl<'a> Parser<'a> { if let Some(kind) = self.parse_comptime_statement_kind(attributes) { return Some(StatementKind::Comptime(Box::new(Statement { kind, - span: self.span_since(start_span), + location: self.location_since(start_location), }))); } @@ -399,14 +405,14 @@ impl<'a> Parser<'a> { fn parse_comptime_statement_kind( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, ) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if let Some(block) = self.parse_block() { return Some(StatementKind::Expression(Expression { kind: ExpressionKind::Block(block), - span: self.span_since(start_span), + location: self.location_since(start_location), })); } @@ -422,7 +428,10 @@ impl<'a> Parser<'a> { } /// LetStatement = 'let' pattern OptionalTypeAnnotation '=' Expression - fn parse_let_statement(&mut self, attributes: Vec<(Attribute, Span)>) -> Option { + fn parse_let_statement( + &mut self, + attributes: Vec<(Attribute, Location)>, + ) -> Option { if !self.eat_keyword(Keyword::Let) { return None; } @@ -434,7 +443,7 @@ impl<'a> Parser<'a> { self.parse_expression_or_error() } else { self.expected_token(Token::Assign); - Expression { kind: ExpressionKind::Error, span: self.current_token_span } + Expression { kind: ExpressionKind::Error, location: self.current_token_location } }; Some(LetStatement { @@ -453,16 +462,16 @@ mod tests { use crate::{ ast::{ExpressionKind, ForRange, LValue, Statement, StatementKind, UnresolvedTypeData}, parser::{ + Parser, ParserErrorReason, parser::tests::{ expect_no_errors, get_single_error, get_single_error_reason, get_source_with_error_span, }, - Parser, ParserErrorReason, }, }; fn parse_statement_no_errors(src: &str) -> Statement { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement_or_error(); expect_no_errors(&parser.errors); statement @@ -512,7 +521,9 @@ mod tests { fn parses_let_statement_with_unsafe() { let src = "// Safety: comment let x = unsafe { 1 };"; - let statement = parse_statement_no_errors(src); + let mut parser = Parser::for_str_with_dummy_file(src); + let statement = parser.parse_statement_or_error(); + assert!(parser.errors.is_empty()); let StatementKind::Let(let_statement) = statement.kind else { panic!("Expected let statement"); }; @@ -523,7 +534,7 @@ mod tests { fn parses_let_statement_with_unsafe_doc_comment() { let src = "/// Safety: doc comment let x = unsafe { 1 };"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let (statement, _) = parser.parse_statement().unwrap(); let StatementKind::Let(let_statement) = statement.kind else { panic!("Expected let statement"); @@ -531,6 +542,20 @@ mod tests { assert_eq!(let_statement.pattern.to_string(), "x"); } + #[test] + fn parses_let_statement_with_unsafe_after_some_other_comment() { + let src = "// Top comment + // Safety: comment + let x = unsafe { 1 };"; + let mut parser = Parser::for_str_with_dummy_file(src); + let statement = parser.parse_statement_or_error(); + assert!(parser.errors.is_empty()); + let StatementKind::Let(let_statement) = statement.kind else { + panic!("Expected let statement"); + }; + assert_eq!(let_statement.pattern.to_string(), "x"); + } + #[test] fn parses_comptime_block() { let src = "comptime { 1 }"; @@ -644,7 +669,7 @@ mod tests { #[test] fn parses_assignment_with_unsafe() { - let src = "// Safety: test + let src = "// Safety: test x = unsafe { 1 }"; let statement = parse_statement_no_errors(src); let StatementKind::Assign(assign) = statement.kind else { @@ -722,7 +747,7 @@ mod tests { ^^^^^^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let statement = parser.parse_statement_or_error(); assert!(matches!(statement.kind, StatementKind::Error)); let reason = get_single_error_reason(&parser.errors, span); @@ -736,7 +761,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let statement = parser.parse_statement_or_error(); assert!(matches!(statement.kind, StatementKind::Let(..))); let error = get_single_error(&parser.errors, span); @@ -746,7 +771,7 @@ mod tests { #[test] fn recovers_on_unknown_statement_followed_by_semicolon() { let src = " ] ;"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement(); assert!(statement.is_none()); assert_eq!(parser.errors.len(), 2); @@ -755,7 +780,7 @@ mod tests { #[test] fn recovers_on_unknown_statement_followed_by_right_brace() { let src = " ] }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement(); assert!(statement.is_none()); assert_eq!(parser.errors.len(), 2); @@ -764,23 +789,23 @@ mod tests { #[test] fn parses_empty_loop() { let src = "loop { }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement_or_error(); - let StatementKind::Loop(block, span) = statement.kind else { + let StatementKind::Loop(block, location) = statement.kind else { panic!("Expected loop"); }; let ExpressionKind::Block(block) = block.kind else { panic!("Expected block"); }; assert!(block.statements.is_empty()); - assert_eq!(span.start(), 0); - assert_eq!(span.end(), 4); + assert_eq!(location.span.start(), 0); + assert_eq!(location.span.end(), 4); } #[test] fn parses_loop_with_statements() { let src = "loop { 1; 2 }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement_or_error(); let StatementKind::Loop(block, _) = statement.kind else { panic!("Expected loop"); @@ -794,7 +819,7 @@ mod tests { #[test] fn parses_let_with_assert() { let src = "let _ = assert(true);"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement_or_error(); let StatementKind::Let(let_statement) = statement.kind else { panic!("Expected let"); @@ -805,7 +830,7 @@ mod tests { #[test] fn parses_empty_while() { let src = "while true { }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement_or_error(); let StatementKind::While(while_) = statement.kind else { panic!("Expected while"); @@ -814,8 +839,8 @@ mod tests { panic!("Expected block"); }; assert!(block.statements.is_empty()); - assert_eq!(while_.while_keyword_span.start(), 0); - assert_eq!(while_.while_keyword_span.end(), 5); + assert_eq!(while_.while_keyword_location.span.start(), 0); + assert_eq!(while_.while_keyword_location.span.end(), 5); assert_eq!(while_.condition.to_string(), "true"); } @@ -823,7 +848,7 @@ mod tests { #[test] fn parses_while_with_statements() { let src = "while true { 1; 2 }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement_or_error(); let StatementKind::While(while_) = statement.kind else { panic!("Expected while"); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement_or_expression_or_lvalue.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement_or_expression_or_lvalue.rs index fdc187f3fb2a..0ac1b1972c4f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement_or_expression_or_lvalue.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement_or_expression_or_lvalue.rs @@ -12,7 +12,7 @@ pub enum StatementOrExpressionOrLValue { LValue(LValue), } -impl<'a> Parser<'a> { +impl Parser<'_> { /// Parses either a statement, an expression or an LValue. Returns `StatementKind::Error` /// if none can be parsed, recording an error if so. /// @@ -20,13 +20,13 @@ impl<'a> Parser<'a> { pub(crate) fn parse_statement_or_expression_or_lvalue( &mut self, ) -> StatementOrExpressionOrLValue { - let start_span = self.current_token_span; + let start_location = self.current_token_location; // First check if it's an interned LValue if let Some(token) = self.eat_kind(TokenKind::InternedLValue) { match token.into_token() { Token::InternedLValue(lvalue) => { - let lvalue = LValue::Interned(lvalue, self.span_since(start_span)); + let lvalue = LValue::Interned(lvalue, self.location_since(start_location)); // If it is, it could be something like `lvalue = expr`: check that. if self.eat(Token::Assign) { @@ -34,7 +34,7 @@ impl<'a> Parser<'a> { let kind = StatementKind::Assign(AssignStatement { lvalue, expression }); return StatementOrExpressionOrLValue::Statement(Statement { kind, - span: self.span_since(start_span), + location: self.location_since(start_location), }); } else { return StatementOrExpressionOrLValue::LValue(lvalue); 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 b066565e6801..62f49035f722 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,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{Documented, Ident, ItemVisibility, NoirStruct, StructField, UnresolvedGenerics}, @@ -6,17 +6,17 @@ use crate::{ token::{Attribute, SecondaryAttribute, Token}, }; -use super::{parse_many::separated_by_comma_until_right_brace, Parser}; +use super::{Parser, parse_many::separated_by_comma_until_right_brace}; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Struct = 'struct' identifier Generics '{' StructField* '}' /// /// StructField = OuterDocComments identifier ':' Type pub(crate) fn parse_struct( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, visibility: ItemVisibility, - start_span: Span, + start_location: Location, ) -> NoirStruct { let attributes = self.validate_secondary_attributes(attributes); @@ -27,19 +27,19 @@ impl<'a> Parser<'a> { attributes, visibility, Vec::new(), - start_span, + start_location, ); }; let generics = self.parse_generics(); if self.eat_semicolons() { - return self.empty_struct(name, attributes, visibility, generics, start_span); + return self.empty_struct(name, attributes, visibility, generics, start_location); } if !self.eat_left_brace() { self.expected_token(Token::LeftBrace); - return self.empty_struct(name, attributes, visibility, generics, start_span); + return self.empty_struct(name, attributes, visibility, generics, start_location); } let fields = self.parse_many( @@ -54,7 +54,7 @@ impl<'a> Parser<'a> { visibility, generics, fields, - span: self.span_since(start_span), + location: self.location_since(start_location), } } @@ -65,7 +65,7 @@ impl<'a> Parser<'a> { // Loop until we find an identifier, skipping anything that's not one loop { - let doc_comments_start_span = self.current_token_span; + let doc_comments_start_location = self.current_token_location; doc_comments = self.parse_outer_doc_comments(); visibility = self.parse_item_visibility(); @@ -82,7 +82,7 @@ impl<'a> Parser<'a> { if !doc_comments.is_empty() { self.push_error( ParserErrorReason::DocCommentDoesNotDocumentAnything, - self.span_since(doc_comments_start_span), + self.location_since(doc_comments_start_location), ); } @@ -113,7 +113,7 @@ impl<'a> Parser<'a> { attributes: Vec, visibility: ItemVisibility, generics: UnresolvedGenerics, - start_span: Span, + start_location: Location, ) -> NoirStruct { NoirStruct { name, @@ -121,7 +121,7 @@ impl<'a> Parser<'a> { visibility, generics, fields: Vec::new(), - span: self.span_since(start_span), + location: self.location_since(start_location), } } } @@ -130,20 +130,18 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{IntegerBitSize, NoirStruct, Signedness, UnresolvedGeneric, UnresolvedTypeData}, + parse_program_with_dummy_file, parser::{ - parser::{ - parse_program, - tests::{ - expect_no_errors, get_single_error, get_single_error_reason, - get_source_with_error_span, - }, - }, ItemKind, ParserErrorReason, + parser::tests::{ + expect_no_errors, get_single_error, get_single_error_reason, + get_source_with_error_span, + }, }, }; fn parse_struct_no_errors(src: &str) -> NoirStruct { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -218,7 +216,7 @@ mod tests { #[test] fn parse_empty_struct_with_doc_comments() { let src = "/// Hello\nstruct Foo {}"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -232,7 +230,7 @@ mod tests { #[test] fn parse_unclosed_struct() { let src = "struct Foo {"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -249,7 +247,7 @@ mod tests { ^^^^^^^ "; let (src, span) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::NoFunctionAttributesAllowedOnType)); } @@ -261,7 +259,7 @@ mod tests { ^^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let item = &module.items[0]; 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 53df5cd00b1b..c37163ebc36c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -1,6 +1,6 @@ use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::{Located, Location}; use crate::ast::{ Documented, GenericTypeArg, GenericTypeArgs, ItemVisibility, NoirTrait, Path, Pattern, @@ -8,27 +8,28 @@ use crate::ast::{ }; use crate::{ ast::{Ident, UnresolvedTypeData}, - parser::{labels::ParsingRuleLabel, NoirTraitImpl, ParserErrorReason}, + parser::{NoirTraitImpl, ParserErrorReason, labels::ParsingRuleLabel}, token::{Attribute, Keyword, SecondaryAttribute, Token}, }; -use super::parse_many::without_separator; use super::Parser; +use super::parse_many::without_separator; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Trait = 'trait' identifier Generics ( ':' TraitBounds )? WhereClause TraitBody /// | 'trait' identifier Generics '=' TraitBounds WhereClause ';' pub(crate) fn parse_trait( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, visibility: ItemVisibility, - start_span: Span, + start_location: Location, ) -> (NoirTrait, Option) { let attributes = self.validate_secondary_attributes(attributes); let Some(name) = self.eat_ident() else { self.expected_identifier(); - let noir_trait = empty_trait(attributes, visibility, self.span_since(start_span)); + let noir_trait = + empty_trait(attributes, visibility, self.location_since(start_location)); let no_implicit_impl = None; return (noir_trait, no_implicit_impl); }; @@ -41,7 +42,7 @@ impl<'a> Parser<'a> { let bounds = self.parse_trait_bounds(); if bounds.is_empty() { - self.push_error(ParserErrorReason::EmptyTraitAlias, self.previous_token_span); + self.push_error(ParserErrorReason::EmptyTraitAlias, self.previous_token_location); } let where_clause = self.parse_where_clause(); @@ -60,17 +61,17 @@ impl<'a> Parser<'a> { (bounds, where_clause, items, is_alias) }; - let span = self.span_since(start_span); + let location = self.location_since(start_location); let noir_impl = is_alias.then(|| { - let object_type_ident = Ident::new("#T".to_string(), span); + let object_type_ident = Ident::from(Located::from(location, "#T".to_string())); let object_type_path = Path::from_ident(object_type_ident.clone()); let object_type_generic = UnresolvedGeneric::Variable(object_type_ident); let is_synthesized = true; let object_type = UnresolvedType { typ: UnresolvedTypeData::Named(object_type_path, vec![].into(), is_synthesized), - span, + location, }; let mut impl_generics = generics.clone(); @@ -85,7 +86,7 @@ impl<'a> Parser<'a> { vec![].into(), is_synthesized, ), - span, + location, }; GenericTypeArg::Ordered(generic_type) @@ -94,7 +95,7 @@ impl<'a> Parser<'a> { let r#trait = UnresolvedType { typ: UnresolvedTypeData::Named(trait_name, trait_generics, false), - span, + location, }; // bounds from trait @@ -117,7 +118,7 @@ impl<'a> Parser<'a> { generics, bounds, where_clause, - span, + location, items, attributes, visibility, @@ -205,7 +206,7 @@ impl<'a> Parser<'a> { self.parse_type_or_error() } else { self.expected_token(Token::Colon); - UnresolvedType { typ: UnresolvedTypeData::Unspecified, span: Span::default() } + UnresolvedType { typ: UnresolvedTypeData::Unspecified, location: Location::dummy() } }; let default_value = @@ -223,7 +224,10 @@ impl<'a> Parser<'a> { ); if modifiers.visibility != ItemVisibility::Private { - self.push_error(ParserErrorReason::TraitVisibilityIgnored, modifiers.visibility_span); + self.push_error( + ParserErrorReason::TraitVisibilityIgnored, + modifiers.visibility_location, + ); } if !self.eat_keyword(Keyword::Fn) { @@ -243,7 +247,7 @@ impl<'a> Parser<'a> { if let Pattern::Identifier(ident) = param.pattern { Some((ident, param.typ)) } else { - self.push_error(ParserErrorReason::InvalidPattern, param.pattern.span()); + self.push_error(ParserErrorReason::InvalidPattern, param.pattern.location()); None } }) @@ -266,14 +270,14 @@ impl<'a> Parser<'a> { fn empty_trait( attributes: Vec, visibility: ItemVisibility, - span: Span, + location: Location, ) -> NoirTrait { NoirTrait { name: Ident::default(), generics: Vec::new(), bounds: Vec::new(), where_clause: Vec::new(), - span, + location, items: Vec::new(), attributes, visibility, @@ -285,18 +289,18 @@ fn empty_trait( mod tests { use crate::{ ast::{NoirTrait, NoirTraitImpl, TraitItem, UnresolvedTypeData}, + parse_program_with_dummy_file, parser::{ + ItemKind, parser::{ - parse_program, - tests::{expect_no_errors, get_single_error, get_source_with_error_span}, ParserErrorReason, + tests::{expect_no_errors, get_single_error, get_source_with_error_span}, }, - ItemKind, }, }; fn parse_trait_opt_impl_no_errors(src: &str) -> (NoirTrait, Option) { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); let (item, impl_item) = if module.items.len() == 2 { let item = module.items.remove(0); @@ -342,7 +346,7 @@ mod tests { #[test] fn parse_empty_trait_alias() { let src = "trait Foo = ;"; - let (_module, errors) = parse_program(src); + let (_module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 2); assert_eq!(errors[1].reason(), Some(ParserErrorReason::EmptyTraitAlias).as_ref()); } @@ -401,7 +405,7 @@ mod tests { #[test] fn parse_empty_trait_alias_with_generics() { let src = "trait Foo = ;"; - let (_module, errors) = parse_program(src); + let (_module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 2); assert_eq!(errors[1].reason(), Some(ParserErrorReason::EmptyTraitAlias).as_ref()); } @@ -464,7 +468,7 @@ mod tests { #[test] fn parse_empty_trait_alias_with_where_clause() { let src = "trait Foo = where A: Z;"; - let (_module, errors) = parse_program(src); + let (_module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 2); assert_eq!(errors[1].reason(), Some(ParserErrorReason::EmptyTraitAlias).as_ref()); } @@ -534,7 +538,7 @@ mod tests { ^^^ "; let (src, span) = get_source_with_error_span(src); - let (_module, errors) = parse_program(&src); + let (_module, errors) = parse_program_with_dummy_file(&src); let error = get_single_error(&errors, span); assert!(error.to_string().contains("Visibility is ignored on a trait method")); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_alias.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_alias.rs index 52815dc37831..464e3e897c50 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_alias.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_alias.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{Ident, ItemVisibility, NoirTypeAlias, UnresolvedType, UnresolvedTypeData}, @@ -7,12 +7,12 @@ use crate::{ use super::Parser; -impl<'a> Parser<'a> { +impl Parser<'_> { /// TypeAlias = 'type' identifier Generics '=' Type ';' pub(crate) fn parse_type_alias( &mut self, visibility: ItemVisibility, - start_span: Span, + start_location: Location, ) -> NoirTypeAlias { let Some(name) = self.eat_ident() else { self.expected_identifier(); @@ -20,8 +20,8 @@ impl<'a> Parser<'a> { visibility, name: Ident::default(), generics: Vec::new(), - typ: UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }, - span: start_span, + typ: UnresolvedType { typ: UnresolvedTypeData::Error, location: Location::dummy() }, + location: start_location, }; }; @@ -30,25 +30,25 @@ impl<'a> Parser<'a> { if !self.eat_assign() { self.expected_token(Token::Assign); - let span = self.span_since(start_span); + let location = self.location_since(start_location); self.eat_semicolons(); return NoirTypeAlias { visibility, name, generics, - typ: UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }, - span, + typ: UnresolvedType { typ: UnresolvedTypeData::Error, location: Location::dummy() }, + location, }; } let typ = self.parse_type_or_error(); - let span = self.span_since(start_span); + let location = self.location_since(start_location); if !self.eat_semicolons() { self.expected_token(Token::Semicolon); } - NoirTypeAlias { visibility, name, generics, typ, span } + NoirTypeAlias { visibility, name, generics, typ, location } } } @@ -56,14 +56,12 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{NoirTypeAlias, UnresolvedTypeData}, - parser::{ - parser::{parse_program, tests::expect_no_errors}, - ItemKind, - }, + parse_program_with_dummy_file, + parser::{ItemKind, parser::tests::expect_no_errors}, }; fn parse_type_alias_no_errors(src: &str) -> NoirTypeAlias { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_expression.rs index 83b04bb157a2..f6d6dbd8a25d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -1,16 +1,16 @@ use crate::{ + BinaryTypeOperator, ast::{GenericTypeArgs, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, - parser::{labels::ParsingRuleLabel, ParserError}, + parser::{ParserError, labels::ParsingRuleLabel}, token::Token, - BinaryTypeOperator, }; use acvm::acir::{AcirField, FieldElement}; -use noirc_errors::Span; +use noirc_errors::Location; -use super::{parse_many::separated_by_comma_until_right_paren, Parser}; +use super::{Parser, parse_many::separated_by_comma_until_right_paren}; -impl<'a> Parser<'a> { +impl Parser<'_> { /// TypeExpression= AddOrSubtractTypeExpression pub(crate) fn parse_type_expression( &mut self, @@ -24,15 +24,15 @@ impl<'a> Parser<'a> { /// AddOrSubtractTypeExpression /// = MultiplyOrDivideOrModuloTypeExpression ( ( '+' | '-' ) MultiplyOrDivideOrModuloTypeExpression )* fn parse_add_or_subtract_type_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let lhs = self.parse_multiply_or_divide_or_modulo_type_expression()?; - Some(self.parse_add_or_subtract_type_expression_after_lhs(lhs, start_span)) + Some(self.parse_add_or_subtract_type_expression_after_lhs(lhs, start_location)) } fn parse_add_or_subtract_type_expression_after_lhs( &mut self, mut lhs: UnresolvedTypeExpression, - start_span: Span, + start_location: Location, ) -> UnresolvedTypeExpression { loop { let operator = if self.eat(Token::Plus) { @@ -45,12 +45,12 @@ impl<'a> Parser<'a> { match self.parse_multiply_or_divide_or_modulo_type_expression() { Some(rhs) => { - let span = self.span_since(start_span); + let location = self.location_since(start_location); lhs = UnresolvedTypeExpression::BinaryOperation( Box::new(lhs), operator, Box::new(rhs), - span, + location, ); } None => { @@ -67,15 +67,15 @@ impl<'a> Parser<'a> { fn parse_multiply_or_divide_or_modulo_type_expression( &mut self, ) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let lhs = self.parse_term_type_expression()?; - Some(self.parse_multiply_or_divide_or_modulo_type_expression_after_lhs(lhs, start_span)) + Some(self.parse_multiply_or_divide_or_modulo_type_expression_after_lhs(lhs, start_location)) } fn parse_multiply_or_divide_or_modulo_type_expression_after_lhs( &mut self, mut lhs: UnresolvedTypeExpression, - start_span: Span, + start_location: Location, ) -> UnresolvedTypeExpression { loop { let operator = if self.eat(Token::Star) { @@ -90,12 +90,12 @@ impl<'a> Parser<'a> { match self.parse_term_type_expression() { Some(rhs) => { - let span = self.span_since(start_span); + let location = self.location_since(start_location); lhs = UnresolvedTypeExpression::BinaryOperation( Box::new(lhs), operator, Box::new(rhs), - span, + location, ); } None => { @@ -112,18 +112,19 @@ impl<'a> Parser<'a> { /// = '- TermTypeExpression /// | AtomTypeExpression fn parse_term_type_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if self.eat(Token::Minus) { return match self.parse_term_type_expression() { Some(rhs) => { - let lhs = UnresolvedTypeExpression::Constant(FieldElement::zero(), start_span); + let lhs = + UnresolvedTypeExpression::Constant(FieldElement::zero(), start_location); let op = BinaryTypeOperator::Subtraction; - let span = self.span_since(start_span); + let location = self.location_since(start_location); Some(UnresolvedTypeExpression::BinaryOperation( Box::new(lhs), op, Box::new(rhs), - span, + location, )) } None => { @@ -159,8 +160,7 @@ impl<'a> Parser<'a> { /// ConstantTypeExpression = int fn parse_constant_type_expression(&mut self) -> Option { let int = self.eat_int()?; - - Some(UnresolvedTypeExpression::Constant(int, self.previous_token_span)) + Some(UnresolvedTypeExpression::Constant(int, self.previous_token_location)) } /// VariableTypeExpression = Path @@ -193,7 +193,7 @@ impl<'a> Parser<'a> { /// TypeOrTypeExpression = Type | TypeExpression pub(crate) fn parse_type_or_type_expression(&mut self) -> Option { let typ = self.parse_add_or_subtract_type_or_type_expression()?; - let span = typ.span; + let span = typ.location; // If we end up with a Variable type expression, make it a Named type (they are equivalent), // but for testing purposes and simplicity we default to types instead of type expressions. @@ -203,7 +203,7 @@ impl<'a> Parser<'a> { { UnresolvedType { typ: UnresolvedTypeData::Named(path, GenericTypeArgs::default(), false), - span, + location: span, } } else { typ @@ -212,7 +212,7 @@ impl<'a> Parser<'a> { } fn parse_add_or_subtract_type_or_type_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let lhs = self.parse_multiply_or_divide_or_modulo_type_or_type_expression()?; // If lhs is a type then no operator can follow, so we stop right away @@ -221,14 +221,14 @@ impl<'a> Parser<'a> { } let lhs = type_to_type_expr(lhs).unwrap(); - let lhs = self.parse_add_or_subtract_type_expression_after_lhs(lhs, start_span); - Some(type_expr_to_type(lhs, self.span_since(start_span))) + let lhs = self.parse_add_or_subtract_type_expression_after_lhs(lhs, start_location); + Some(type_expr_to_type(lhs, self.location_since(start_location))) } fn parse_multiply_or_divide_or_modulo_type_or_type_expression( &mut self, ) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let lhs = self.parse_term_type_or_type_expression()?; // If lhs is a type then no operator can follow, so we stop right away @@ -238,27 +238,28 @@ impl<'a> Parser<'a> { let lhs = type_to_type_expr(lhs).unwrap(); let lhs = - self.parse_multiply_or_divide_or_modulo_type_expression_after_lhs(lhs, start_span); - Some(type_expr_to_type(lhs, self.span_since(start_span))) + self.parse_multiply_or_divide_or_modulo_type_expression_after_lhs(lhs, start_location); + Some(type_expr_to_type(lhs, self.location_since(start_location))) } fn parse_term_type_or_type_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if self.eat(Token::Minus) { // If we ate '-' what follows must be a type expression, never a type return match self.parse_term_type_expression() { Some(rhs) => { - let lhs = UnresolvedTypeExpression::Constant(FieldElement::zero(), start_span); + let lhs = + UnresolvedTypeExpression::Constant(FieldElement::zero(), start_location); let op = BinaryTypeOperator::Subtraction; - let span = self.span_since(start_span); + let location = self.location_since(start_location); let type_expr = UnresolvedTypeExpression::BinaryOperation( Box::new(lhs), op, Box::new(rhs), - span, + location, ); let typ = UnresolvedTypeData::Expression(type_expr); - Some(UnresolvedType { typ, span }) + Some(UnresolvedType { typ, location }) } None => { self.push_expected_expression(); @@ -271,19 +272,19 @@ impl<'a> Parser<'a> { } fn parse_atom_type_or_type_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if let Some(path) = self.parse_path() { let generics = self.parse_generic_type_args(); let typ = UnresolvedTypeData::Named(path, generics, false); - let span = self.span_since(start_span); - return Some(UnresolvedType { typ, span }); + let location = self.location_since(start_location); + return Some(UnresolvedType { typ, location }); } if let Some(type_expr) = self.parse_constant_type_expression() { let typ = UnresolvedTypeData::Expression(type_expr); - let span = self.span_since(start_span); - return Some(UnresolvedType { typ, span }); + let location = self.location_since(start_location); + return Some(UnresolvedType { typ, location }); } if let Some(typ) = self.parse_parenthesized_type_or_type_expression() { @@ -294,7 +295,7 @@ impl<'a> Parser<'a> { } fn parse_parenthesized_type_or_type_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.eat_left_paren() { return None; @@ -303,7 +304,7 @@ impl<'a> Parser<'a> { if self.eat_right_paren() { return Some(UnresolvedType { typ: UnresolvedTypeData::Unit, - span: self.span_since(start_span), + location: self.location_since(start_location), }); } @@ -318,19 +319,19 @@ impl<'a> Parser<'a> { self.eat_or_error(Token::RightParen); return Some(UnresolvedType { typ: UnresolvedTypeData::Expression(type_expr), - span: typ.span, + location: typ.location, }); } if self.eat_right_paren() { return Some(UnresolvedType { typ: UnresolvedTypeData::Parenthesized(Box::new(typ)), - span: self.span_since(start_span), + location: self.location_since(start_location), }); } let comma_after_first_type = self.eat_comma(); - let second_type_span = self.current_token_span; + let second_type_location = self.current_token_location; let mut types = self.parse_many( "tuple items", @@ -339,14 +340,14 @@ impl<'a> Parser<'a> { ); if !types.is_empty() && !comma_after_first_type { - self.expected_token_separating_items(Token::Comma, "tuple items", second_type_span); + self.expected_token_separating_items(Token::Comma, "tuple items", second_type_location); } types.insert(0, typ); Some(UnresolvedType { typ: UnresolvedTypeData::Tuple(types), - span: self.span_since(start_span), + location: self.location_since(start_location), }) } @@ -356,7 +357,7 @@ impl<'a> Parser<'a> { Err(ParserError::expected_label( ParsingRuleLabel::TypeExpression, self.token.token().clone(), - self.current_token_span, + self.current_token_location, )) } } @@ -383,9 +384,9 @@ fn type_is_type_expr(typ: &UnresolvedType) -> bool { } } -fn type_expr_to_type(lhs: UnresolvedTypeExpression, span: Span) -> UnresolvedType { +fn type_expr_to_type(lhs: UnresolvedTypeExpression, location: Location) -> UnresolvedType { let lhs = UnresolvedTypeData::Expression(lhs); - UnresolvedType { typ: lhs, span } + UnresolvedType { typ: lhs, location } } #[cfg(test)] @@ -393,26 +394,26 @@ mod tests { use core::panic; use crate::{ + BinaryTypeOperator, ast::{UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, parser::{ + Parser, ParserErrorReason, parser::tests::{ expect_no_errors, get_single_error_reason, get_source_with_error_span, }, - Parser, ParserErrorReason, }, token::Token, - BinaryTypeOperator, }; fn parse_type_expression_no_errors(src: &str) -> UnresolvedTypeExpression { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let expr = parser.parse_type_expression().unwrap(); expect_no_errors(&parser.errors); expr } fn parse_type_or_type_expression_no_errors(src: &str) -> UnresolvedType { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let typ = parser.parse_type_or_type_expression().unwrap(); expect_no_errors(&parser.errors); typ @@ -569,7 +570,7 @@ mod tests { ^^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let typ = parser.parse_type_or_type_expression().unwrap(); @@ -594,7 +595,7 @@ mod tests { #[test] fn parses_type_or_type_expression_tuple_type_single_element() { let src = "(Field,)"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let typ = parser.parse_type_or_type_expression().unwrap(); expect_no_errors(&parser.errors); let UnresolvedTypeData::Tuple(types) = typ.typ else { 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 6b3575e19eef..bcbf57d863d6 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,29 +1,49 @@ use acvm::{AcirField, FieldElement}; use crate::{ + QuotedType, ast::{UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, - parser::{labels::ParsingRuleLabel, ParserErrorReason}, + parser::{ParserErrorReason, labels::ParsingRuleLabel}, token::{Keyword, Token, TokenKind}, - QuotedType, }; -use super::{parse_many::separated_by_comma_until_right_paren, Parser}; +use super::{Parser, parse_many::separated_by_comma_until_right_paren}; -impl<'a> Parser<'a> { +impl Parser<'_> { pub(crate) fn parse_type_or_error(&mut self) -> UnresolvedType { if let Some(typ) = self.parse_type() { typ } else { self.expected_label(ParsingRuleLabel::Type); - UnresolvedTypeData::Error.with_span(self.span_at_previous_token_end()) + UnresolvedTypeData::Error.with_location(self.location_at_previous_token_end()) + } + } + + /// Tries to parse a type. If the current token doesn't denote a type and it's not + /// one of `stop_tokens`, try to parse a type starting from the next token (and so on). + pub(crate) fn parse_type_or_error_with_recovery( + &mut self, + stop_tokens: &[Token], + ) -> UnresolvedType { + loop { + let typ = self.parse_type_or_error(); + if typ.typ != UnresolvedTypeData::Error { + return typ; + } + + if self.at_eof() || stop_tokens.contains(self.token.token()) { + return typ; + } + + self.bump(); } } pub(crate) fn parse_type(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let typ = self.parse_unresolved_type_data()?; - let span = self.span_since(start_span); - Some(UnresolvedType { typ, span }) + let location = self.location_since(start_location); + Some(UnresolvedType { typ, location }) } fn parse_unresolved_type_data(&mut self) -> Option { @@ -122,7 +142,7 @@ impl<'a> Parser<'a> { Err(err) => { self.push_error( ParserErrorReason::InvalidBitSize(err.0), - self.previous_token_span, + self.previous_token_location, ); UnresolvedTypeData::Error } @@ -139,8 +159,10 @@ impl<'a> Parser<'a> { if !self.eat_less() { self.expected_token(Token::Less); - let expr = - UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span); + let expr = UnresolvedTypeExpression::Constant( + FieldElement::zero(), + self.current_token_location, + ); return Some(UnresolvedTypeData::String(expr)); } @@ -148,7 +170,10 @@ impl<'a> Parser<'a> { Ok(expr) => expr, Err(error) => { self.errors.push(error); - UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span) + UnresolvedTypeExpression::Constant( + FieldElement::zero(), + self.current_token_location, + ) } }; @@ -164,9 +189,12 @@ impl<'a> Parser<'a> { if !self.eat_less() { self.expected_token(Token::Less); - let expr = - UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span); - let typ = UnresolvedTypeData::Error.with_span(self.span_at_previous_token_end()); + let expr = UnresolvedTypeExpression::Constant( + FieldElement::zero(), + self.current_token_location, + ); + let typ = + UnresolvedTypeData::Error.with_location(self.location_at_previous_token_end()); return Some(UnresolvedTypeData::FormatString(expr, Box::new(typ))); } @@ -174,7 +202,10 @@ impl<'a> Parser<'a> { Ok(expr) => expr, Err(error) => { self.errors.push(error); - UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span) + UnresolvedTypeExpression::Constant( + FieldElement::zero(), + self.current_token_location, + ) } }; @@ -257,7 +288,7 @@ impl<'a> Parser<'a> { self.eat_or_error(Token::RightBracket); typ } else { - UnresolvedTypeData::Unit.with_span(self.span_at_previous_token_end()) + UnresolvedTypeData::Unit.with_location(self.location_at_previous_token_end()) }; if !self.eat_left_paren() { @@ -281,7 +312,7 @@ impl<'a> Parser<'a> { self.parse_type_or_error() } else { self.expected_token(Token::Arrow); - UnresolvedTypeData::Unit.with_span(self.span_at_previous_token_end()) + UnresolvedTypeData::Unit.with_location(self.location_at_previous_token_end()) }; Some(UnresolvedTypeData::Function(args, Box::new(ret), Box::new(env), unconstrained)) @@ -289,11 +320,7 @@ impl<'a> Parser<'a> { fn parse_parameter(&mut self) -> Option { let typ = self.parse_type_or_error(); - if let UnresolvedTypeData::Error = typ.typ { - None - } else { - Some(typ) - } + if let UnresolvedTypeData::Error = typ.typ { None } else { Some(typ) } } fn parse_trait_as_type(&mut self) -> Option { @@ -422,7 +449,7 @@ impl<'a> Parser<'a> { } pub(super) fn unspecified_type_at_previous_token_end(&self) -> UnresolvedType { - UnresolvedTypeData::Unspecified.with_span(self.span_at_previous_token_end()) + UnresolvedTypeData::Unspecified.with_location(self.location_at_previous_token_end()) } } @@ -431,16 +458,16 @@ mod tests { use strum::IntoEnumIterator; use crate::{ + QuotedType, ast::{IntegerBitSize, Signedness, UnresolvedType, UnresolvedTypeData}, parser::{ + Parser, ParserErrorReason, parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, - Parser, }, - QuotedType, }; fn parse_type_no_errors(src: &str) -> UnresolvedType { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let typ = parser.parse_type_or_error(); expect_no_errors(&parser.errors); typ @@ -470,6 +497,28 @@ mod tests { )); } + #[test] + fn errors_on_invalid_bit_size() { + let src = "u31"; + let mut parser = Parser::for_str_with_dummy_file(src); + let typ = parser.parse_type_or_error(); + assert_eq!(typ.typ, UnresolvedTypeData::Error); + assert_eq!(parser.errors.len(), 1); + let error = &parser.errors[0]; + assert!(matches!(error.reason(), Some(ParserErrorReason::InvalidBitSize(..)))); + } + + #[test] + fn errors_on_i128() { + let src = "i128"; + let mut parser = Parser::for_str_with_dummy_file(src); + let typ = parser.parse_type_or_error(); + assert_eq!(typ.typ, UnresolvedTypeData::Error); + assert_eq!(parser.errors.len(), 1); + let error = &parser.errors[0]; + assert!(matches!(error.reason(), Some(ParserErrorReason::InvalidBitSize(..)))); + } + #[test] fn parses_field_type() { let src = "Field"; @@ -546,7 +595,7 @@ mod tests { #[test] fn parses_unclosed_parentheses_type() { let src = "(Field"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let typ = parser.parse_type_or_error(); assert_eq!(parser.errors.len(), 1); let UnresolvedTypeData::Parenthesized(typ) = typ.typ else { @@ -591,7 +640,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); parser.parse_type(); let error = get_single_error(&parser.errors, span); assert_eq!(error.to_string(), "Expected a ']' but found end of input"); @@ -666,7 +715,7 @@ mod tests { #[test] fn parses_function_type_with_colon_in_parameter() { let src = "fn(value: T) -> Field"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let _ = parser.parse_type_or_error(); assert!(!parser.errors.is_empty()); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/use_tree.rs index 08834383f99c..ebe058632113 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{Ident, Path, PathKind, UseTree, UseTreeKind}, @@ -6,21 +6,23 @@ use crate::{ token::{Keyword, Token}, }; -use super::{parse_many::separated_by_comma_until_right_brace, Parser}; +use super::{Parser, parse_many::separated_by_comma_until_right_brace}; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Use = 'use' PathKind PathNoTurbofish UseTree /// /// UseTree = PathNoTurbofish ( '::' '{' UseTreeList? '}' )? /// /// UseTreeList = UseTree (',' UseTree)* ','? pub(super) fn parse_use_tree(&mut self) -> UseTree { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let kind = self.parse_path_kind(); let use_tree = self.parse_use_tree_without_kind( - start_span, kind, false, // nested + start_location, + kind, + false, // nested ); if !self.eat_semicolons() { self.expected_token(Token::Semicolon); @@ -30,14 +32,15 @@ impl<'a> Parser<'a> { pub(super) fn parse_use_tree_without_kind( &mut self, - start_span: Span, + start_location: Location, kind: PathKind, nested: bool, ) -> UseTree { let prefix = self.parse_path_after_kind( - kind, false, // allow turbofish + kind, + false, // allow turbofish false, // allow trailing double colon - start_span, + start_location, ); let trailing_double_colon = if prefix.segments.is_empty() && kind != PathKind::Plain { true @@ -56,37 +59,37 @@ impl<'a> Parser<'a> { UseTree { prefix, kind: UseTreeKind::List(use_trees), - span: self.span_since(start_span), + location: self.location_since(start_location), } } else { self.expected_token(Token::LeftBrace); - self.parse_path_use_tree_end(prefix, nested, start_span) + self.parse_path_use_tree_end(prefix, nested, start_location) } } else { - self.parse_path_use_tree_end(prefix, nested, start_span) + self.parse_path_use_tree_end(prefix, nested, start_location) } } fn parse_use_tree_in_list(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; // Special case: "self" cannot be followed by anything else if self.eat_self() { return Some(UseTree { - prefix: Path { segments: Vec::new(), kind: PathKind::Plain, span: start_span }, - kind: UseTreeKind::Path(Ident::new("self".to_string(), start_span), None), - span: start_span, + prefix: Path::plain(Vec::new(), start_location), + kind: UseTreeKind::Path(Ident::new("self".to_string(), start_location), None), + location: start_location, }); } let use_tree = self.parse_use_tree_without_kind( - start_span, + start_location, PathKind::Plain, true, // nested ); // If we didn't advance at all, we are done - if start_span == self.current_token_span { + if start_location.span == self.current_token_location.span { self.expected_label(ParsingRuleLabel::UseSegment); None } else { @@ -98,7 +101,7 @@ impl<'a> Parser<'a> { &mut self, mut prefix: Path, nested: bool, - start_span: Span, + start_location: Location, ) -> UseTree { if prefix.segments.is_empty() { if nested { @@ -109,7 +112,7 @@ impl<'a> Parser<'a> { UseTree { prefix, kind: UseTreeKind::Path(Ident::default(), None), - span: self.span_since(start_span), + location: self.location_since(start_location), } } else { let ident = prefix.segments.pop().unwrap().ident; @@ -118,21 +121,21 @@ impl<'a> Parser<'a> { UseTree { prefix, kind: UseTreeKind::Path(ident, Some(alias)), - span: self.span_since(start_span), + location: self.location_since(start_location), } } else { self.expected_identifier(); UseTree { prefix, kind: UseTreeKind::Path(ident, None), - span: self.span_since(start_span), + location: self.location_since(start_location), } } } else { UseTree { prefix, kind: UseTreeKind::Path(ident, None), - span: self.span_since(start_span), + location: self.location_since(start_location), } } } @@ -143,14 +146,12 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{ItemVisibility, PathKind, UseTree, UseTreeKind}, - parser::{ - parser::{parse_program, tests::expect_no_errors}, - ItemKind, - }, + parse_program_with_dummy_file, + parser::{ItemKind, parser::tests::expect_no_errors}, }; fn parse_use_tree_no_errors(src: &str) -> (UseTree, ItemVisibility) { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -281,14 +282,14 @@ mod tests { #[test] fn errors_on_crate_in_subtree() { let src = "use foo::{crate::bar}"; - let (_, errors) = parse_program(src); + let (_, errors) = parse_program_with_dummy_file(src); assert!(!errors.is_empty()); } #[test] fn errors_on_double_colon_after_self() { let src = "use foo::{self::bar};"; - let (_, errors) = parse_program(src); + let (_, errors) = parse_program_with_dummy_file(src); assert!(!errors.is_empty()); } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/where_clause.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/where_clause.rs index 8945e6f29f5f..08adea0a20a2 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/where_clause.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -1,15 +1,15 @@ use crate::{ - ast::{GenericTypeArgs, Path, PathKind, TraitBound, UnresolvedTraitConstraint, UnresolvedType}, + ast::{GenericTypeArgs, Path, TraitBound, UnresolvedTraitConstraint, UnresolvedType}, parser::labels::ParsingRuleLabel, token::{Keyword, Token}, }; use super::{ - parse_many::{separated_by, separated_by_comma}, Parser, + parse_many::{separated_by, separated_by_comma}, }; -impl<'a> Parser<'a> { +impl Parser<'_> { /// WhereClause = 'where' WhereClauseItems? /// /// WhereClauseItems = WhereClauseItem ( ',' WhereClauseItem )* ','? @@ -70,11 +70,7 @@ impl<'a> Parser<'a> { self.expected_label(ParsingRuleLabel::TraitBound); TraitBound { - trait_path: Path { - kind: PathKind::Plain, - segments: Vec::new(), - span: self.span_at_previous_token_end(), - }, + trait_path: Path::plain(Vec::new(), self.location_at_previous_token_end()), trait_id: None, trait_generics: GenericTypeArgs::default(), } @@ -93,16 +89,16 @@ mod tests { use crate::{ ast::UnresolvedTraitConstraint, parser::{ + Parser, ParserErrorReason, parser::tests::{ expect_no_errors, get_single_error_reason, get_source_with_error_span, }, - Parser, ParserErrorReason, }, token::Token, }; fn parse_where_clause_no_errors(src: &str) -> Vec { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let constraints = parser.parse_where_clause(); expect_no_errors(&parser.errors); constraints @@ -154,7 +150,7 @@ mod tests { ^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let mut constraints = parser.parse_where_clause(); let reason = get_single_error_reason(&parser.errors, span); @@ -179,7 +175,7 @@ mod tests { #[test] fn parses_where_clause_missing_trait_bound() { let src = "where Foo: "; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); parser.parse_where_clause(); assert!(!parser.errors.is_empty()); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/signed_field.rs b/noir/noir-repo/compiler/noirc_frontend/src/signed_field.rs new file mode 100644 index 000000000000..dcddd52daa8f --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/signed_field.rs @@ -0,0 +1,175 @@ +use acvm::{AcirField, FieldElement}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct SignedField { + pub field: FieldElement, + pub is_negative: bool, +} + +impl SignedField { + pub fn new(field: FieldElement, is_negative: bool) -> Self { + Self { field, is_negative } + } + + pub fn positive(field: impl Into) -> Self { + Self { field: field.into(), is_negative: false } + } + + pub fn negative(field: impl Into) -> Self { + Self { field: field.into(), is_negative: true } + } + + /// Convert a signed integer to a SignedField, carefully handling + /// INT_MIN in the process. Note that to convert an unsigned integer + /// you can call `SignedField::positive`. + #[inline] + pub fn from_signed(value: T) -> Self + where + T: num_traits::Signed + AbsU128, + { + let negative = value.is_negative(); + let value = value.abs_u128(); + SignedField::new(value.into(), negative) + } + + /// Convert a SignedField into an unsigned integer type (up to u128), + /// returning None if the value does not fit (e.g. if it is negative). + #[inline] + pub fn try_to_unsigned>(self) -> Option { + if self.is_negative { + return None; + } + + assert!(std::mem::size_of::() <= std::mem::size_of::()); + let u128_value = self.field.try_into_u128()?; + u128_value.try_into().ok() + } + + /// Convert a SignedField into a signed integer type (up to i128), + /// returning None if the value does not fit. This function is more complex + /// for handling negative values, specifically INT_MIN which we can't cast from + /// a u128 to i128 without wrapping it. + #[inline] + pub fn try_to_signed(self) -> Option + where + T: TryFrom + TryFrom + num_traits::Signed + num_traits::Bounded + AbsU128, + u128: TryFrom, + { + let u128_value = self.field.try_into_u128()?; + + if self.is_negative { + // The positive version of the minimum value of this type. + // E.g. 128 for i8. + let positive_min = T::min_value().abs_u128(); + + // If it is the min value, we can't negate it without overflowing + // so test for it and return it directly + if u128_value == positive_min { + Some(T::min_value()) + } else { + let i128_value = -(u128_value as i128); + T::try_from(i128_value).ok() + } + } else { + T::try_from(u128_value).ok() + } + } +} + +impl std::ops::Neg for SignedField { + type Output = Self; + + fn neg(mut self) -> Self::Output { + self.is_negative = !self.is_negative; + self + } +} + +impl Ord for SignedField { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + if self.is_negative != other.is_negative { + if self.is_negative { std::cmp::Ordering::Less } else { std::cmp::Ordering::Greater } + } else if self.is_negative { + // Negative comparisons should be reversed so that -2 < -1 + other.field.cmp(&self.field) + } else { + self.field.cmp(&other.field) + } + } +} + +impl PartialOrd for SignedField { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl From for SignedField { + fn from(value: FieldElement) -> Self { + Self::new(value, false) + } +} + +impl From for FieldElement { + fn from(value: SignedField) -> Self { + if value.is_negative { -value.field } else { value.field } + } +} + +impl std::fmt::Display for SignedField { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.is_negative { + write!(f, "-")?; + } + write!(f, "{}", self.field) + } +} + +pub trait AbsU128 { + /// Necessary to handle casting to unsigned generically without overflowing on INT_MIN. + fn abs_u128(self) -> u128; +} + +macro_rules! impl_unsigned_abs_for { + ($typ:ty) => { + impl AbsU128 for $typ { + fn abs_u128(self) -> u128 { + self.unsigned_abs() as u128 + } + } + }; +} + +impl_unsigned_abs_for!(i8); +impl_unsigned_abs_for!(i16); +impl_unsigned_abs_for!(i32); +impl_unsigned_abs_for!(i64); +impl_unsigned_abs_for!(i128); + +#[cfg(test)] +mod tests { + use super::SignedField; + + #[test] + fn int_min_to_signed_field_roundtrip() { + let x = i128::MIN; + let field = SignedField::from_signed(x); + assert_eq!(field.try_to_signed(), Some(x)); + } + + #[test] + fn comparisons() { + let neg_two = SignedField::negative(2u32); + let neg_one = SignedField::negative(1u32); + let zero = SignedField::positive(0u32); + let one = SignedField::positive(1u32); + let two = SignedField::positive(2u32); + + assert!(one < two); + assert!(zero < one); + assert!(neg_one < zero); + assert!(neg_two < neg_one); + + assert!(two > neg_two); + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs index 6ff3b204f2e0..e53aa392fa96 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs @@ -3,6 +3,7 @@ mod aliases; mod arithmetic_generics; mod bound_checks; +mod enums; mod imports; mod metaprogramming; mod name_shadowing; @@ -15,22 +16,17 @@ mod visibility; // XXX: These tests repeat a lot of code // what we should do is have test cases which are passed to a test harness // A test harness will allow for more expressive and readable tests -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; +use crate::elaborator::{FrontendOptions, UnstableFeature}; use fm::FileId; use iter_extended::vecmap; -use noirc_errors::Location; +use noirc_errors::{CustomDiagnostic, Location, Span}; -use crate::ast::IntegerBitSize; -use crate::hir::comptime::InterpreterError; +use crate::hir::Context; use crate::hir::def_collector::dc_crate::CompilationError; -use crate::hir::def_collector::errors::{DefCollectorErrorKind, DuplicateType}; use crate::hir::def_map::ModuleData; -use crate::hir::resolution::errors::ResolverError; -use crate::hir::resolution::import::PathResolutionError; -use crate::hir::type_check::TypeCheckError; -use crate::hir::Context; use crate::node_interner::{NodeInterner, StmtId}; use crate::hir::def_collector::dc_crate::DefCollector; @@ -42,16 +38,16 @@ use crate::monomorphization::errors::MonomorphizationError; use crate::monomorphization::monomorphize; use crate::parser::{ItemKind, ParserErrorReason}; use crate::token::SecondaryAttribute; -use crate::{parse_program, ParsedModule}; +use crate::{ParsedModule, parse_program}; use fm::FileManager; use noirc_arena::Arena; -pub(crate) fn has_parser_error(errors: &[(CompilationError, FileId)]) -> bool { - errors.iter().any(|(e, _f)| matches!(e, CompilationError::ParseError(_))) +pub(crate) fn has_parser_error(errors: &[CompilationError]) -> bool { + errors.iter().any(|e| matches!(e, CompilationError::ParseError(_))) } -pub(crate) fn remove_experimental_warnings(errors: &mut Vec<(CompilationError, FileId)>) { - errors.retain(|(error, _)| match error { +pub(crate) fn remove_experimental_warnings(errors: &mut Vec) { + errors.retain(|error| match error { CompilationError::ParseError(error) => { !matches!(error.reason(), Some(ParserErrorReason::ExperimentalFeature(..))) } @@ -59,19 +55,29 @@ pub(crate) fn remove_experimental_warnings(errors: &mut Vec<(CompilationError, F }); } -pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec<(CompilationError, FileId)>) { - get_program_with_maybe_parser_errors( - src, false, // allow parser errors - ) +pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec) { + let allow_parser_errors = false; + get_program_with_options(src, allow_parser_errors, FrontendOptions::test_default()) +} + +pub(crate) fn get_program_using_features( + src: &str, + features: &[UnstableFeature], +) -> (ParsedModule, Context<'static, 'static>, Vec) { + let allow_parser_errors = false; + let mut options = FrontendOptions::test_default(); + options.enabled_unstable_features = features; + get_program_with_options(src, allow_parser_errors, options) } /// Compile a program. /// /// The stdlib is not available for these snippets. -pub(crate) fn get_program_with_maybe_parser_errors( +pub(crate) fn get_program_with_options( src: &str, allow_parser_errors: bool, -) -> (ParsedModule, Context, Vec<(CompilationError, FileId)>) { + options: FrontendOptions, +) -> (ParsedModule, Context<'static, 'static>, Vec) { let root = std::path::Path::new("/"); let fm = FileManager::new(root); @@ -80,8 +86,8 @@ pub(crate) fn get_program_with_maybe_parser_errors( let root_file_id = FileId::dummy(); let root_crate_id = context.crate_graph.add_crate_root(root_file_id); - let (program, parser_errors) = parse_program(src); - let mut errors = vecmap(parser_errors, |e| (e.into(), root_file_id)); + let (program, parser_errors) = parse_program(src, root_file_id); + let mut errors = vecmap(parser_errors, |e| e.into()); remove_experimental_warnings(&mut errors); if allow_parser_errors || !has_parser_error(&errors) { @@ -116,23 +122,19 @@ pub(crate) fn get_program_with_maybe_parser_errors( extern_prelude: BTreeMap::new(), }; - let debug_comptime_in_file = None; - let pedantic_solving = true; - // Now we want to populate the CrateDefMap using the DefCollector errors.extend(DefCollector::collect_crate_and_dependencies( def_map, &mut context, program.clone().into_sorted(), root_file_id, - debug_comptime_in_file, - pedantic_solving, + options, )); } (program, context, errors) } -pub(crate) fn get_program_errors(src: &str) -> Vec<(CompilationError, FileId)> { +pub(crate) fn get_program_errors(src: &str) -> Vec { get_program(src).2 } @@ -143,6 +145,161 @@ fn assert_no_errors(src: &str) { } } +/// Given a source file with annotated errors, like this +/// +/// fn main() -> pub i32 { +/// ^^^ expected i32 because of return type +/// true +/// ~~~~ bool returned here +/// } +/// +/// where: +/// - lines with "^^^" are primary errors +/// - lines with "~~~" are secondary errors +/// +/// this method will check that compiling the program without those error markers +/// will produce errors at those locations and with/ those messages. +fn check_errors(src: &str) { + let allow_parser_errors = false; + check_errors_with_options(src, allow_parser_errors, FrontendOptions::test_default()); +} + +fn check_errors_using_features(src: &str, features: &[UnstableFeature]) { + let allow_parser_errors = false; + let mut options = FrontendOptions::test_default(); + options.enabled_unstable_features = features; + check_errors_with_options(src, allow_parser_errors, options); +} + +fn check_errors_with_options(src: &str, allow_parser_errors: bool, options: FrontendOptions) { + let lines = src.lines().collect::>(); + + // Here we'll hold just the lines that are code + let mut code_lines = Vec::new(); + // Here we'll capture lines that are primary error spans, like: + // + // ^^^ error message + let mut primary_spans_with_errors: Vec<(Span, String)> = Vec::new(); + // Here we'll capture lines that are secondary error spans, like: + // + // ~~~ error message + let mut secondary_spans_with_errors: Vec<(Span, String)> = Vec::new(); + // The byte at the start of this line + let mut byte = 0; + // The length of the last line, needed to go back to the byte at the beginning of the last line + let mut last_line_length = 0; + for line in lines { + if let Some((span, message)) = + get_error_line_span_and_message(line, '^', byte, last_line_length) + { + primary_spans_with_errors.push((span, message)); + continue; + } + + if let Some((span, message)) = + get_error_line_span_and_message(line, '~', byte, last_line_length) + { + secondary_spans_with_errors.push((span, message)); + continue; + } + + code_lines.push(line); + + byte += line.len() + 1; // For '\n' + last_line_length = line.len(); + } + + let mut primary_spans_with_errors: HashMap = + primary_spans_with_errors.into_iter().collect(); + + let mut secondary_spans_with_errors: HashMap = + secondary_spans_with_errors.into_iter().collect(); + + let src = code_lines.join("\n"); + let (_, _, errors) = get_program_with_options(&src, allow_parser_errors, options); + if errors.is_empty() && !primary_spans_with_errors.is_empty() { + panic!("Expected some errors but got none"); + } + + let errors = errors.iter().map(CustomDiagnostic::from).collect::>(); + for error in &errors { + let secondary = error + .secondaries + .first() + .unwrap_or_else(|| panic!("Expected {:?} to have a secondary label", error)); + let span = secondary.location.span; + let message = &error.message; + + let Some(expected_message) = primary_spans_with_errors.remove(&span) else { + if let Some(message) = secondary_spans_with_errors.get(&span) { + panic!( + "Error at {span:?} with message {message:?} is annotated as secondary but should be primary" + ); + } else { + panic!( + "Couldn't find primary error at {span:?} with message {message:?}.\nAll errors: {errors:?}" + ); + } + }; + + assert_eq!(message, &expected_message, "Primary error at {span:?} has unexpected message"); + + for secondary in &error.secondaries { + let message = &secondary.message; + if message.is_empty() { + continue; + } + + let span = secondary.location.span; + let Some(expected_message) = secondary_spans_with_errors.remove(&span) else { + if let Some(message) = primary_spans_with_errors.get(&span) { + panic!( + "Error at {span:?} with message {message:?} is annotated as primary but should be secondary" + ); + } else { + panic!( + "Couldn't find secondary error at {span:?} with message {message:?}.\nAll errors: {errors:?}" + ); + }; + }; + + assert_eq!( + message, &expected_message, + "Secondary error at {span:?} has unexpected message" + ); + } + } + + if !primary_spans_with_errors.is_empty() { + panic!("These primary errors didn't happen: {primary_spans_with_errors:?}"); + } + + if !secondary_spans_with_errors.is_empty() { + panic!("These secondary errors didn't happen: {secondary_spans_with_errors:?}"); + } +} + +/// Helper function for `check_errors` that returns the span that +/// `^^^^` or `~~~~` occupy, together with the message that follows it. +fn get_error_line_span_and_message( + line: &str, + char: char, + byte: usize, + last_line_length: usize, +) -> Option<(Span, String)> { + if !line.trim().starts_with(char) { + return None; + } + + let chars = line.chars().collect::>(); + let first_caret = chars.iter().position(|c| *c == char).unwrap(); + let last_caret = chars.iter().rposition(|c| *c == char).unwrap(); + let start = byte - last_line_length; + let span = Span::from((start + first_caret - 1) as u32..(start + last_caret) as u32); + let error = line.trim().trim_start_matches(char).trim().to_string(); + Some((span, error)) +} + #[test] fn check_trait_implemented_for_all_t() { let src = " @@ -205,10 +362,13 @@ fn check_trait_implementation_duplicate_method() { impl Default for Foo { // Duplicate trait methods should not compile fn default(x: Field, y: Field) -> Field { + ~~~~~~~ First trait associated function found here y + 2 * x } // Duplicate trait methods should not compile fn default(x: Field, y: Field) -> Field { + ^^^^^^^ Duplicate definitions of trait associated function with name default found + ~~~~~~~ Second trait associated function found here x + 2 * y } } @@ -216,27 +376,7 @@ fn check_trait_implementation_duplicate_method() { fn main() { let _ = Foo { bar: 1, array: [2, 3] }; // silence Foo never constructed warning }"; - - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { - typ, - first_def, - second_def, - }) => { - assert_eq!(typ, &DuplicateType::TraitAssociatedFunction); - assert_eq!(first_def, "default"); - assert_eq!(second_def, "default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -251,6 +391,7 @@ fn check_trait_wrong_method_return_type() { impl Default for Foo { fn default() -> Field { + ^^^^^ Expected type Foo, found type Field 0 } } @@ -259,25 +400,7 @@ fn check_trait_wrong_method_return_type() { let _ = Foo {}; // silence Foo never constructed warning } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) => { - assert_eq!(expected_typ, "Foo"); - assert_eq!(expr_typ, "Field"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -294,6 +417,7 @@ fn check_trait_wrong_method_return_type2() { impl Default for Foo { fn default(x: Field, _y: Field) -> Field { + ^^^^^ Expected type Foo, found type Field x } } @@ -301,25 +425,32 @@ fn check_trait_wrong_method_return_type2() { fn main() { let _ = Foo { bar: 1, array: [2, 3] }; // silence Foo never constructed warning }"; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) => { - assert_eq!(expected_typ, "Foo"); - assert_eq!(expr_typ, "Field"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; + check_errors(src); +} + +#[test] +fn check_trait_wrong_method_return_type3() { + let src = " + trait Default { + fn default(x: Field, y: Field) -> Self; + } + + struct Foo { + bar: Field, + array: [Field; 2], + } + + impl Default for Foo { + fn default(_x: Field, _y: Field) { + ^ Expected type Foo, found type () + } + } + + fn main() { + let _ = Foo { bar: 1, array: [2, 3] }; // silence Foo never constructed warning } + "; + check_errors(src); } #[test] @@ -338,6 +469,8 @@ fn check_trait_missing_implementation() { } impl Default for Foo { + ^^^ Method `method2` from trait `Default` is not implemented + ~~~ Please implement method2 here fn default(x: Field, y: Field) -> Self { Self { bar: x, array: [x,y] } } @@ -346,25 +479,7 @@ fn check_trait_missing_implementation() { fn main() { } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::TraitMissingMethod { - trait_name, - method_name, - trait_impl_span: _, - }) => { - assert_eq!(trait_name, "Default"); - assert_eq!(method_name, "method2"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -375,8 +490,8 @@ fn check_trait_not_in_scope() { array: [Field; 2], } - // Default trait does not exist impl Default for Foo { + ^^^^^^^ Trait Default not found fn default(x: Field, y: Field) -> Self { Self { bar: x, array: [x,y] } } @@ -384,23 +499,8 @@ fn check_trait_not_in_scope() { fn main() { } - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::TraitNotFound { - trait_path, - }) => { - assert_eq!(trait_path.as_string(), "Default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -414,9 +514,9 @@ fn check_trait_wrong_method_name() { array: [Field; 2], } - // wrong trait name method should not compile impl Default for Foo { fn does_not_exist(x: Field, y: Field) -> Self { + ^^^^^^^^^^^^^^ Method with name `does_not_exist` is not part of trait `Default`, therefore it can't be implemented Self { bar: x, array: [x,y] } } } @@ -424,28 +524,7 @@ fn check_trait_wrong_method_name() { fn main() { let _ = Foo { bar: 1, array: [2, 3] }; // silence Foo never constructed warning }"; - let compilation_errors = get_program_errors(src); - assert!(!has_parser_error(&compilation_errors)); - assert!( - compilation_errors.len() == 1, - "Expected 1 compilation error, got: {:?}", - compilation_errors - ); - - for (err, _file_id) in compilation_errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::MethodNotInTrait { - trait_name, - impl_method, - }) => { - assert_eq!(trait_name, "Default"); - assert_eq!(impl_method, "does_not_exist"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -461,6 +540,7 @@ fn check_trait_wrong_parameter() { impl Default for Foo { fn default(x: u32) -> Self { + ^^^ Parameter #1 of method `default` must be of type Field, not u32 Foo {bar: x} } } @@ -468,27 +548,7 @@ fn check_trait_wrong_parameter() { fn main() { } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { - method_name, - expected_typ, - actual_typ, - .. - }) => { - assert_eq!(method_name, "default"); - assert_eq!(expected_typ, "Field"); - assert_eq!(actual_typ, "u32"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -505,34 +565,15 @@ fn check_trait_wrong_parameter2() { impl Default for Foo { fn default(x: Field, y: Foo) -> Self { + ^^^ Parameter #2 of method `default` must be of type Field, not Foo Self { bar: x, array: [x, y.bar] } } } fn main() { - }"; - - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { - method_name, - expected_typ, - actual_typ, - .. - }) => { - assert_eq!(method_name, "default"); - assert_eq!(expected_typ, "Field"); - assert_eq!(actual_typ, "Foo"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; } + "; + check_errors(src); } #[test] @@ -540,30 +581,14 @@ fn check_trait_wrong_parameter_type() { let src = " pub trait Default { fn default(x: Field, y: NotAType) -> Field; + ^^^^^^^^ Could not resolve 'NotAType' in path } fn main(x: Field, y: Field) { assert(y == x); - }"; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - - // This is a duplicate error in the name resolver & type checker. - // In the elaborator there is no duplicate and only 1 error is issued - assert!(errors.len() <= 2, "Expected 1 or 2 errors, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Unresolved(ident), - )) => { - assert_eq!(ident, "NotAType"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; } + "; + check_errors(src); } #[test] @@ -580,6 +605,7 @@ fn check_trait_wrong_parameters_count() { impl Default for Foo { fn default(x: Field) -> Self { + ^^^^^^^ `Default::default` expects 2 parameters, but this method has 1 Self { bar: x, array: [x, x] } } } @@ -587,28 +613,7 @@ fn check_trait_wrong_parameters_count() { fn main() { } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::MismatchTraitImplNumParameters { - actual_num_parameters, - expected_num_parameters, - trait_name, - method_name, - .. - }) => { - assert_eq!(actual_num_parameters, &1_usize); - assert_eq!(expected_num_parameters, &2_usize); - assert_eq!(method_name, "default"); - assert_eq!(trait_name, "Default"); - } - _ => { - panic!("No other errors are expected in this test case! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -619,6 +624,7 @@ fn check_trait_impl_for_non_type() { } impl Default for main { + ^^^^ expected type got function fn default(x: Field, y: Field) -> Field { x + y } @@ -626,20 +632,7 @@ fn check_trait_impl_for_non_type() { fn main() {} "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::ResolverError(ResolverError::Expected { expected, got, .. }) => { - assert_eq!(*expected, "type"); - assert_eq!(*got, "function"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -655,8 +648,8 @@ fn check_impl_struct_not_trait() { z: Field, } - // Default is a struct not a trait impl Default for Foo { + ^^^^^^^ Default is not a trait, therefore it can't be implemented fn default(x: Field, y: Field) -> Self { Self { bar: x, array: [x,y] } } @@ -666,27 +659,14 @@ fn check_impl_struct_not_trait() { let _ = Default { x: 1, z: 1 }; // silence Default never constructed warning } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::NotATrait { - not_a_trait_name, - }) => { - assert_eq!(not_a_trait_name.to_string(), "Default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] fn check_trait_duplicate_declaration() { let src = " trait Default { + ~~~~~~~ First trait definition found here fn default(x: Field, y: Field) -> Self; } @@ -701,32 +681,16 @@ fn check_trait_duplicate_declaration() { } } - trait Default { + ^^^^^^^ Duplicate definitions of trait definition with name Default found + ~~~~~~~ Second trait definition found here fn default(x: Field) -> Self; } fn main() { - }"; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { - typ, - first_def, - second_def, - }) => { - assert_eq!(typ, &DuplicateType::Trait); - assert_eq!(first_def, "Default"); - assert_eq!(second_def, "Default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; } + "; + check_errors(src); } #[test] @@ -739,29 +703,17 @@ fn check_trait_duplicate_implementation() { } impl Default for Foo { + ~~~~~~~ Previous impl defined here } impl Default for Foo { + ^^^ Impl for type `Foo` overlaps with existing impl + ~~~ Overlapping impl } fn main() { let _ = Foo { bar: 1 }; // silence Foo never constructed warning } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { - .. - }) => (), - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { - .. - }) => (), - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -776,31 +728,19 @@ fn check_trait_duplicate_implementation_with_alias() { type MyType = MyStruct; impl Default for MyStruct { + ~~~~~~~ Previous impl defined here } impl Default for MyType { + ^^^^^^ Impl for type `MyType` overlaps with existing impl + ~~~~~~ Overlapping impl } fn main() { let _ = MyStruct {}; // silence MyStruct never constructed warning } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { - .. - }) => (), - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { - .. - }) => (), - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -818,7 +758,8 @@ fn test_impl_self_within_default_def() { fn ok(self) -> Self { self } - }"; + } + "; assert_no_errors(src); } @@ -945,6 +886,7 @@ fn resolve_empty_function() { "; assert_no_errors(src); } + #[test] fn resolve_basic_function() { let src = r#" @@ -955,24 +897,18 @@ fn resolve_basic_function() { "#; assert_no_errors(src); } + #[test] fn resolve_unused_var() { let src = r#" fn main(x : Field) { let y = x + x; + ^ unused variable y + ~ unused variable assert(x == x); } "#; - - let errors = get_program_errors(src); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - // It should be regarding the unused variable - match &errors[0].0 { - CompilationError::ResolverError(ResolverError::UnusedVariable { ident }) => { - assert_eq!(&ident.0.contents, "y"); - } - _ => unreachable!("we should only have an unused var error"), - } + check_errors(src); } #[test] @@ -981,17 +917,11 @@ fn resolve_unresolved_var() { fn main(x : Field) { let y = x + x; assert(y == z); + ^ cannot find `z` in this scope + ~ not found in this scope } "#; - let errors = get_program_errors(src); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - // It should be regarding the unresolved var `z` (Maybe change to undeclared and special case) - match &errors[0].0 { - CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, span: _ }) => { - assert_eq!(name, "z"); - } - _ => unimplemented!("we should only have an unresolved variable"), - } + check_errors(src); } #[test] @@ -999,23 +929,10 @@ fn unresolved_path() { let src = " fn main(x : Field) { let _z = some::path::to::a::func(x); + ^^^^ Could not resolve 'some' in path } "; - let errors = get_program_errors(src); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (compilation_error, _file_id) in errors { - match compilation_error { - CompilationError::ResolverError(err) => { - match err { - ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { - assert_eq!(name.to_string(), "some"); - } - _ => unimplemented!("we should only have an unresolved function"), - }; - } - _ => unimplemented!(), - } - } + check_errors(src); } #[test] @@ -1034,36 +951,16 @@ fn multiple_resolution_errors() { let src = r#" fn main(x : Field) { let y = foo::bar(x); + ^^^ Could not resolve 'foo' in path let z = y + a; + ^ cannot find `a` in this scope + ~ not found in this scope + ^ unused variable z + ~ unused variable + } "#; - - let errors = get_program_errors(src); - assert!(errors.len() == 3, "Expected 3 errors, got: {:?}", errors); - - // Errors are: - // `a` is undeclared - // `z` is unused - // `foo::bar` does not exist - for (compilation_error, _file_id) in errors { - match compilation_error { - CompilationError::ResolverError(err) => { - match err { - ResolverError::UnusedVariable { ident } => { - assert_eq!(&ident.0.contents, "z"); - } - ResolverError::VariableNotDeclared { name, .. } => { - assert_eq!(name, "a"); - } - ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { - assert_eq!(name.to_string(), "foo"); - } - _ => unimplemented!(), - }; - } - _ => unimplemented!(), - } - } + check_errors(src); } #[test] @@ -1145,8 +1042,8 @@ fn resolve_basic_closure() { #[test] fn resolve_simplified_closure() { // based on bug https://github.com/noir-lang/noir/issues/1088 - - let src = r#"fn do_closure(x: Field) -> Field { + let src = r#" + fn do_closure(x: Field) -> Field { let y = x; let ret_capture = || { y @@ -1211,38 +1108,20 @@ fn resolve_fmt_strings() { let src = r#" fn main() { let string = f"this is i: {i}"; + ^ cannot find `i` in this scope + ~ not found in this scope println(string); + ^^^^^^^^^^^^^^^ Unused expression result of type fmtstr<14, ()> let new_val = 10; println(f"random_string{new_val}{new_val}"); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unused expression result of type fmtstr<31, (Field, Field)> } fn println(x : T) -> T { x } "#; - - let errors = get_program_errors(src); - assert!(errors.len() == 3, "Expected 5 errors, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::ResolverError(ResolverError::VariableNotDeclared { - name, .. - }) => { - assert_eq!(name, "i"); - } - CompilationError::TypeError(TypeCheckError::UnusedResultError { - expr_type: _, - expr_span, - }) => { - let a = src.get(expr_span.start() as usize..expr_span.end() as usize).unwrap(); - assert!( - a == "println(string)" || a == "println(f\"random_string{new_val}{new_val}\")" - ); - } - _ => unimplemented!(), - }; - } + check_errors(src); } fn monomorphize_program(src: &str) -> Result { @@ -1291,24 +1170,20 @@ fn lambda$f1(mut env$l1: (Field)) -> Field { check_rewrite(src, expected_rewrite); } -// TODO(https://github.com/noir-lang/noir/issues/6780): currently failing -// with a stack overflow #[test] -#[ignore] fn deny_cyclic_globals() { let src = r#" global A: u32 = B; + ^ This global recursively depends on itself + ^ Dependency cycle found + ~ 'A' recursively depends on itself: A -> B -> A global B: u32 = A; + ^ Variable not in scope + ~ Could not find variable fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::DependencyCycle { .. }) - )); + check_errors(src); } #[test] @@ -1316,9 +1191,11 @@ fn deny_cyclic_type_aliases() { let src = r#" type A = B; type B = A; + ^^^^^^^^^^ Dependency cycle found + ~~~~~~~~~~ 'B' recursively depends on itself: B -> A -> B fn main() {} "#; - assert_eq!(get_program_errors(src).len(), 1); + check_errors(src); } #[test] @@ -1328,9 +1205,10 @@ fn ensure_nested_type_aliases_type_check() { type B = u8; fn main() { let _a: A = 0 as u16; + ^^^^^^^^ Expected type A, found type u16 } "#; - assert_eq!(get_program_errors(src).len(), 1); + check_errors(src); } #[test] @@ -1339,7 +1217,7 @@ fn type_aliases_in_entry_point() { type Foo = u8; fn main(_x: Foo) {} "#; - assert_eq!(get_program_errors(src).len(), 0); + assert_no_errors(src); } #[test] @@ -1351,7 +1229,7 @@ fn operators_in_global_used_in_type() { let _array: [Field; COUNT] = [1, 2, 3]; } "#; - assert_eq!(get_program_errors(src).len(), 0); + assert_no_errors(src); } #[test] @@ -1361,14 +1239,18 @@ fn break_and_continue_in_constrained_fn() { for i in 0 .. 10 { if i == 2 { continue; + ^^^^^^^^^ continue is only allowed in unconstrained functions + ~~~~~~~~~ Constrained code must always have a known number of loop iterations } if i == 5 { break; + ^^^^^^ break is only allowed in unconstrained functions + ~~~~~~ Constrained code must always have a known number of loop iterations } } } "#; - assert_eq!(get_program_errors(src).len(), 2); + check_errors(src); } #[test] @@ -1376,10 +1258,12 @@ fn break_and_continue_outside_loop() { let src = r#" unconstrained fn main() { continue; + ^^^^^^^^^ continue is only allowed within loops break; + ^^^^^^ break is only allowed within loops } "#; - assert_eq!(get_program_errors(src).len(), 2); + check_errors(src); } // Regression for #2540 @@ -1395,8 +1279,7 @@ fn for_loop_over_array() { hello(array); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } // Regression for #4545 @@ -1406,51 +1289,47 @@ fn type_aliases_in_main() { type Outer = [u8; N]; fn main(_arg: Outer<1>) {} "#; - assert_eq!(get_program_errors(src).len(), 0); + assert_no_errors(src); } #[test] fn ban_mutable_globals() { - // Mutable globals are only allowed in a comptime context let src = r#" mut global FOO: Field = 0; + ^^^ Only `comptime` globals may be mutable fn main() { let _ = FOO; // silence FOO never used warning } "#; - assert_eq!(get_program_errors(src).len(), 1); + check_errors(src); } #[test] fn deny_inline_attribute_on_unconstrained() { + // TODO: improve the error location let src = r#" #[no_predicates] unconstrained pub fn foo(x: Field, y: Field) { + ^^^ misplaced #[no_predicates] attribute on unconstrained function foo. Only allowed on constrained functions + ~~~ misplaced #[no_predicates] attribute assert(x != y); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::NoPredicatesAttributeOnUnconstrained { .. }) - )); + check_errors(src); } #[test] fn deny_fold_attribute_on_unconstrained() { + // TODO: improve the error location let src = r#" #[fold] unconstrained pub fn foo(x: Field, y: Field) { + ^^^ misplaced #[fold] attribute on unconstrained function foo. Only allowed on constrained functions + ~~~ misplaced #[fold] attribute assert(x != y); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::FoldAttributeOnUnconstrained { .. }) - )); + check_errors(src); } #[test] @@ -1480,8 +1359,7 @@ fn specify_function_types_with_turbofish() { let _ = generic_func::(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -1514,8 +1392,7 @@ fn specify_method_types_with_turbofish() { let _ = foo.generic_method::(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -1543,14 +1420,10 @@ fn incorrect_turbofish_count_function_call() { fn main() { let _ = generic_func::(); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Expected 2 generics from this function, but 3 were provided } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), - )); + check_errors(src); } #[test] @@ -1581,14 +1454,10 @@ fn incorrect_turbofish_count_method_call() { fn main() { let foo: Foo = Foo { inner: 1 }; let _ = foo.generic_method::(); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Expected 1 generic from this function, but 2 were provided } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), - )); + check_errors(src); } #[test] @@ -1599,15 +1468,12 @@ fn struct_numeric_generic_in_function() { } pub fn bar() { + ^ N has a type of Foo. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`. + ~ Unsupported numeric generic type let _ = Foo { inner: 1 }; // silence Foo never constructed warning } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); + check_errors(src); } #[test] @@ -1618,19 +1484,18 @@ fn struct_numeric_generic_in_struct() { } pub struct Bar { } + ^ N has a type of Foo. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`. + ~ Unsupported numeric generic type "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType(_)), - )); + check_errors(src); } #[test] fn bool_numeric_generic() { let src = r#" pub fn read() -> Field { + ^ N has a type of bool. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`. + ~ Unsupported numeric generic type if N { 0 } else { @@ -1638,12 +1503,7 @@ fn bool_numeric_generic() { } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); + check_errors(src); } #[test] @@ -1652,49 +1512,43 @@ fn numeric_generic_binary_operation_type_mismatch() { pub fn foo() -> bool { let mut check: bool = true; check = N; + ^ Cannot assign an expression of type Field to a value of type bool check } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }), - )); + check_errors(src); } #[test] fn bool_generic_as_loop_bound() { let src = r#" - pub fn read() { // error here - let mut fields = [0; N]; // error here - for i in 0..N { // error here + pub fn read() { + ^ N has a type of bool. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`. + ~ Unsupported numeric generic type + let mut fields = [0; N]; + ^ The numeric generic is not of type `u32` + ~ expected `u32`, found `bool` + for i in 0..N { + ^ Expected type Field, found type bool fields[i] = i + 1; } assert(fields[0] == 1); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); - - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, expr_typ, .. - }) = &errors[2].0 - else { - panic!("Got an error other than a type mismatch"); - }; + check_errors(src); +} - assert_eq!(expected_typ, "Field"); - assert_eq!(expr_typ, "bool"); +#[test] +fn wrong_type_in_for_range() { + let src = r#" + pub fn foo() { + for _ in true..false { + ^^^^ The type bool cannot be used in a for loop + + } + } + "#; + check_errors(src); } #[test] @@ -1711,91 +1565,68 @@ fn numeric_generic_as_struct_field_type_fails() { pub struct Foo { a: Field, b: N, + ^ Expected type, found numeric generic + ~ not a type } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] fn normal_generic_as_array_length() { + // TODO: improve error location, should be just on N let src = r#" pub struct Foo { a: Field, b: [Field; N], + ^^^^^^^^^^ Type provided when a numeric generic was expected + ~~~~~~~~~~ the numeric generic is not of type `u32` } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] fn numeric_generic_as_param_type() { let src = r#" pub fn foo(x: I) -> I { + ^ Expected type, found numeric generic + ~ not a type + ^ Expected type, found numeric generic + ~ not a type + + let _q: I = 5; + ^ Expected type, found numeric generic + ~ not a type x } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); - - // Error from the parameter type - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // Error from the let statement annotated type - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // Error from the return type - assert!(matches!( - errors[2].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] fn numeric_generic_as_unused_param_type() { let src = r#" pub fn foo(_x: I) { } + ^ Expected type, found numeric generic + ~ not a type "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] fn numeric_generic_as_unused_trait_fn_param_type() { let src = r#" trait Foo { + ^^^ unused trait Foo + ~~~ unused trait fn foo(_x: I) { } + ^ Expected type, found numeric generic + ~ not a type } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // Foo is unused - assert!(matches!( - errors[1].0, - CompilationError::ResolverError(ResolverError::UnusedItem { .. }), - )); + check_errors(src); } #[test] @@ -1807,24 +1638,16 @@ fn numeric_generic_as_return_type() { } fn foo(x: T) -> I where T: Zeroed { + ^ Expected type, found numeric generic + ~ not a type + ^^^ unused function foo + ~~~ unused function x.zeroed() } fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - // Error from the return type - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // foo is unused - assert!(matches!( - errors[1].0, - CompilationError::ResolverError(ResolverError::UnusedItem { .. }), - )); + check_errors(src); } #[test] @@ -1836,14 +1659,11 @@ fn numeric_generic_used_in_nested_type_fails() { } pub struct Bar { inner: N + ^ Expected type, found numeric generic + ~ not a type } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] @@ -1852,17 +1672,14 @@ fn normal_generic_used_in_nested_array_length_fail() { pub struct Foo { a: Field, b: Bar, + ^ Type provided when a numeric generic was expected + ~ the numeric generic is not of type `u32` } pub struct Bar { inner: [Field; N] } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] @@ -1977,6 +1794,7 @@ fn numeric_generic_used_in_turbofish() { // allow u16 to be used as an array size #[test] fn numeric_generic_u16_array_size() { + // TODO: improve the error location let src = r#" fn len(_arr: [Field; N]) -> u32 { N @@ -1984,19 +1802,14 @@ fn numeric_generic_u16_array_size() { pub fn foo() -> u32 { let fields: [Field; N] = [0; N]; + ^ The numeric generic is not of type `u32` + ~ expected `u32`, found `u16` + ^^^^^^^^^^ The numeric generic is not of type `u32` + ~~~~~~~~~~ expected `u32`, found `u16` len(fields) } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] @@ -2010,8 +1823,7 @@ fn numeric_generic_field_larger_than_u32() { let _ = foo::(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -2034,8 +1846,7 @@ fn numeric_generic_field_arithmetic_larger_than_u32() { let _ = size(foo::()); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -2043,14 +1854,11 @@ fn cast_256_to_u8_size_checks() { let src = r#" fn main() { assert(256 as u8 == 0); + ^^^^^^^^^ Casting value of type Field to a smaller type (u8) + ~~~~~~~~~ casting untyped value (256) to a type with a maximum size (255) that's smaller than it } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::DownsizingCast { .. }), - )); + check_errors(src); } // TODO(https://github.com/noir-lang/noir/issues/6247): @@ -2062,8 +1870,7 @@ fn cast_negative_one_to_u8_size_checks() { assert((-1) as u8 != 0); } "#; - let errors = get_program_errors(src); - assert!(errors.is_empty()); + assert_no_errors(src); } #[test] @@ -2098,48 +1905,36 @@ fn normal_generic_used_when_numeric_expected_in_where_clause() { } pub fn read() -> T where T: Deserialize { + ^ Type provided when a numeric generic was expected + ~ the numeric generic is not of type `u32` T::deserialize([0, 1]) } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); + // TODO: improve the error location for the array (should be on N) let src = r#" trait Deserialize { fn deserialize(fields: [Field; N]) -> Self; } pub fn read() -> T where T: Deserialize { + ^ Type provided when a numeric generic was expected + ~ the numeric generic is not of type `u32` let mut fields: [Field; N] = [0; N]; + ^ Type provided when a numeric generic was expected + ~ the numeric generic is not of type `u32` + ^^^^^^^^^^ Type provided when a numeric generic was expected + ~~~~~~~~~~ the numeric generic is not of type `u32` for i in 0..N { + ^ cannot find `N` in this scope + ~ not found in this scope fields[i] = i as Field + 1; } T::deserialize(fields) } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 4); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[2].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // N - assert!(matches!( - errors[3].0, - CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), - )); + check_errors(src); } #[test] @@ -2153,6 +1948,8 @@ fn numeric_generics_type_kind_mismatch() { fn bar() -> u16 { foo::() + ^ The numeric generic is not of type `u32` + ~ expected `u32`, found `u16` } global M: u16 = 3; @@ -2161,12 +1958,7 @@ fn numeric_generics_type_kind_mismatch() { let _ = bar::(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] @@ -2188,6 +1980,7 @@ fn numeric_generics_value_kind_mismatch_u32_u64() { pub fn push(&mut self, elem: T) { assert(self.len < MaxLen, "push out of bounds"); + ^^^^^^^^^^^^^^^^^ Integers must have the same bit width LHS is 64, RHS is 32 self.storage[self.len] = elem; self.len += 1; } @@ -2197,20 +1990,12 @@ fn numeric_generics_value_kind_mismatch_u32_u64() { let _ = BoundedVec { storage: [1], len: 1 }; // silence never constructed warning } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::IntegerBitWidth { - bit_width_x: IntegerBitSize::SixtyFour, - bit_width_y: IntegerBitSize::ThirtyTwo, - .. - }), - )); + check_errors(src); } #[test] fn quote_code_fragments() { + // TODO: have the error also point to `contact!` as a secondary // This test ensures we can quote (and unquote/splice) code fragments // which by themselves are not valid code. They only need to be valid // by the time they are unquoted into the macro's call site. @@ -2218,6 +2003,7 @@ fn quote_code_fragments() { fn main() { comptime { concat!(quote { assert( }, quote { false); }); + ^^^^^ Assertion failed } } @@ -2225,11 +2011,7 @@ fn quote_code_fragments() { quote { $a $b } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use InterpreterError::FailingConstraint; - assert!(matches!(&errors[0].0, CompilationError::InterpreterError(FailingConstraint { .. }))); + check_errors(src); } #[test] @@ -2241,6 +2023,7 @@ fn impl_stricter_than_trait_no_trait_method_constraints() { // We want to make sure we trigger the error when override a trait method // which itself has no trait constraints. fn serialize(self) -> [Field; N]; + ~~~~~~~~~ definition of `serialize` from trait } trait ToField { @@ -2262,6 +2045,8 @@ fn impl_stricter_than_trait_no_trait_method_constraints() { impl Serialize<2> for MyType { fn serialize(self) -> [Field; 2] where T: ToField { + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `T: ToField` [ self.a.to_field(), self.b.to_field() ] } } @@ -2276,13 +2061,7 @@ fn impl_stricter_than_trait_no_trait_method_constraints() { let _ = MyType { a: 1, b: 1 }; // silence MyType never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { .. }) - )); + check_errors(src); } #[test] @@ -2295,26 +2074,18 @@ fn impl_stricter_than_trait_different_generics() { fn foo_good() where T: Default; fn foo_bad() where T: Default; + ~~~~~~~ definition of `foo_bad` from trait } impl Foo for () { fn foo_good() where A: Default {} fn foo_bad() where B: Default {} + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `B: Default` } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "B")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } + check_errors(src); } #[test] @@ -2336,14 +2107,17 @@ fn impl_stricter_than_trait_different_object_generics() { fn bar_good() where Option: MyTrait, OtherOption>: OtherTrait; fn bar_bad() where Option: MyTrait, OtherOption>: OtherTrait; + ~~~~~~~ definition of `bar_bad` from trait fn array_good() where [T; 8]: MyTrait; fn array_bad() where [T; 8]: MyTrait; + ~~~~~~~~~ definition of `array_bad` from trait fn tuple_good() where (Option, Option): MyTrait; fn tuple_bad() where (Option, Option): MyTrait; + ~~~~~~~~~ definition of `tuple_bad` from trait } impl Bar for () { @@ -2356,58 +2130,27 @@ fn impl_stricter_than_trait_different_object_generics() { where OtherOption>: OtherTrait, Option: MyTrait { } + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `Option: MyTrait` fn array_good() where [A; 8]: MyTrait { } fn array_bad() where [B; 8]: MyTrait { } + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `[B; 8]: MyTrait` fn tuple_good() where (Option, Option): MyTrait { } fn tuple_bad() where (Option, Option): MyTrait { } + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `(Option, Option): MyTrait` } fn main() { let _ = OtherOption { inner: Option { inner: 1 } }; // silence unused warnings } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "Option")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } - - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[1].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "[B; 8]")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } - - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[2].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "(Option, Option)")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } + check_errors(src); } #[test] @@ -2423,32 +2166,22 @@ fn impl_stricter_than_trait_different_trait() { trait Bar { fn bar() where Option: Default; + ~~~ definition of `bar` from trait } impl Bar for () { // Trait constraint differs due to the trait even though the constraint // types are the same. fn bar() where Option: OtherDefault {} + ^^^^^^^^^^^^ impl has stricter requirements than trait + ~~~~~~~~~~~~ impl has extra requirement `Option: OtherDefault` } fn main() { let _ = Option { inner: 1 }; // silence Option never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "Option")); - assert!(matches!(constraint_name.as_str(), "OtherDefault")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } + check_errors(src); } #[test] @@ -2458,6 +2191,7 @@ fn trait_impl_where_clause_stricter_pass() { fn good_foo() where H: OtherTrait; fn bad_foo() where H: OtherTrait; + ~~~~~~~ definition of `bad_foo` from trait } trait OtherTrait {} @@ -2470,58 +2204,35 @@ fn trait_impl_where_clause_stricter_pass() { fn good_foo() where B: OtherTrait { } fn bad_foo() where A: OtherTrait { } + ^^^^^^^^^^ impl has stricter requirements than trait + ~~~~~~~~~~ impl has extra requirement `A: OtherTrait` } fn main() { let _ = Option { inner: 1 }; // silence Option never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "A")); - assert!(matches!(constraint_name.as_str(), "OtherTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } -} + check_errors(src); +} #[test] fn impl_stricter_than_trait_different_trait_generics() { let src = r#" trait Foo { fn foo() where T: T2; + ~~~ definition of `foo` from trait } impl Foo for () { // Should be A: T2 fn foo() where A: T2 {} + ^^ impl has stricter requirements than trait + ~~ impl has extra requirement `A: T2` } trait T2 {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - constraint_generics, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "A")); - assert!(matches!(constraint_name.as_str(), "T2")); - assert!(matches!(constraint_generics.ordered[0].to_string().as_str(), "B")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } + check_errors(src); } #[test] @@ -2558,6 +2269,8 @@ fn impl_not_found_for_inner_impl() { impl MyType { fn do_thing_with_serialization_with_extra_steps(self) -> Field { process_array(serialize_thing(self)) + ^^^^^^^^^^^^^^^ No matching impl found for `T: ToField` + ~~~~~~~~~~~~~~~ No impl for `T: ToField` } } @@ -2565,13 +2278,7 @@ fn impl_not_found_for_inner_impl() { let _ = MyType { a: 1, b: 1 }; // silence MyType never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::TypeError(TypeCheckError::NoMatchingImplFound { .. }) - )); + check_errors(src); } #[test] @@ -2579,16 +2286,12 @@ fn cannot_call_unconstrained_function_outside_of_unsafe() { let src = r#" fn main() { foo(); + ^^^^^ Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block } unconstrained fn foo() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); - }; + check_errors(src); } #[test] @@ -2596,26 +2299,19 @@ fn cannot_call_unconstrained_first_class_function_outside_of_unsafe() { let src = r#" fn main() { let func = foo; - // Warning should trigger here func(); + ^^^^^^ Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block inner(func); } fn inner(x: unconstrained fn() -> ()) { - // Warning should trigger here x(); + ^^^ Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block } unconstrained fn foo() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - for error in &errors { - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &error.0 else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); - }; - } + check_errors(src); } #[test] @@ -2649,15 +2345,11 @@ fn missing_unsafe_block_when_needing_type_annotations() { impl BigNumTrait for BigNum { fn __is_zero(self) -> bool { self.__is_zero_impl() + ^^^^^^^^^^^^^^^^^^^ Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); - }; + check_errors(src); } #[test] @@ -2666,6 +2358,7 @@ fn cannot_pass_unconstrained_function_to_regular_function() { fn main() { let func = foo; expect_regular(func); + ^^^^ Converting an unconstrained fn to a non-unconstrained fn is unsafe } unconstrained fn foo() {} @@ -2673,12 +2366,7 @@ fn cannot_pass_unconstrained_function_to_regular_function() { fn expect_regular(_func: fn() -> ()) { } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { - panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); - }; + check_errors(src); } #[test] @@ -2686,24 +2374,13 @@ fn cannot_assign_unconstrained_and_regular_fn_to_variable() { let src = r#" fn main() { let _func = if true { foo } else { bar }; + ^^^ Expected type fn() -> (), found type unconstrained fn() -> () } fn foo() {} unconstrained fn bar() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0].0 else { - panic!("Expected a context error, got {:?}", errors[0].0); - }; - - if let TypeCheckError::TypeMismatch { expected_typ, expr_typ, .. } = err.as_ref() { - assert_eq!(expected_typ, "fn() -> ()"); - assert_eq!(expr_typ, "unconstrained fn() -> ()"); - } else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; + check_errors(src); } #[test] @@ -2727,18 +2404,14 @@ fn cannot_pass_unconstrained_function_to_constrained_function() { fn main() { let func = foo; expect_regular(func); + ^^^^ Converting an unconstrained fn to a non-unconstrained fn is unsafe } unconstrained fn foo() {} fn expect_regular(_func: fn() -> ()) {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { - panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); - }; + check_errors(src); } #[test] @@ -2775,24 +2448,11 @@ fn trait_impl_generics_count_mismatch() { trait Foo {} impl Foo<()> for Field {} + ^^^ Foo expects 0 generics but 1 was given - fn main() {}"#; - 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, "Foo"); - assert_eq!(*expected, 0); - assert_eq!(*found, 1); + fn main() {} + "#; + check_errors(src); } #[test] @@ -2810,28 +2470,15 @@ fn duplicate_struct_field() { let src = r#" pub struct Foo { x: i32, + ~ First struct field found here x: i32, + ^ Duplicate definitions of struct field with name x found + ~ Second struct field found here } fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { - typ: _, - first_def, - second_def, - }) = &errors[0].0 - else { - panic!("Expected a 'duplicate' 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(), 30); - assert_eq!(second_def.span().start(), 46); + check_errors(src); } #[test] @@ -2869,23 +2516,12 @@ fn incorrect_generic_count_on_struct_impl() { let src = r#" struct Foo {} impl Foo {} + ^^^ Foo expects 0 generics but 1 was given fn main() { let _ = Foo {}; // silence Foo never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { - found, expected, .. - }) = errors[0].0 - else { - panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(found, 1); - assert_eq!(expected, 0); + check_errors(src); } #[test] @@ -2893,23 +2529,12 @@ fn incorrect_generic_count_on_type_alias() { let src = r#" pub struct Foo {} pub type Bar = Foo; + ^^^ Foo expects 0 generics but 1 was given fn main() { let _ = Foo {}; // silence Foo never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { - found, expected, .. - }) = errors[0].0 - else { - panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(found, 1); - assert_eq!(expected, 0); + check_errors(src); } #[test] @@ -2966,14 +2591,18 @@ fn uses_self_type_in_trait_where_clause() { } pub trait Foo where Self: Trait { + ~~~~~ required by this bound in `Foo` fn foo(self) -> bool { self.trait_func() + ^^^^^^^^^^^^^^^^^ No method named 'trait_func' found for type 'Bar' } } struct Bar {} impl Foo for Bar { + ^^^ The trait bound `_: Trait` is not satisfied + ~~~ The trait `Trait` is not implemented for `_` } @@ -2981,22 +2610,7 @@ fn uses_self_type_in_trait_where_clause() { let _ = Bar {}; // silence Bar never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - let CompilationError::ResolverError(ResolverError::TraitNotImplemented { .. }) = &errors[0].0 - else { - panic!("Expected a trait not implemented error, got {:?}", errors[0].0); - }; - - let CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { method_name, .. }) = - &errors[1].0 - else { - panic!("Expected an unresolved method call error, got {:?}", errors[1].0); - }; - - assert_eq!(method_name, "trait_func"); + check_errors(src); } #[test] @@ -3024,16 +2638,10 @@ fn error_on_cast_over_type_variable() { fn main() { let x = "a"; let _: Field = foo(|x| x as Field, x); + ^ Expected type Field, found type str<1> } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) - )); + check_errors(src); } #[test] @@ -3116,15 +2724,9 @@ fn impl_missing_associated_type() { } impl Foo for () {} + ^^^ `Foo` is missing the associated type `Assoc` "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - &errors[0].0, - CompilationError::TypeError(TypeCheckError::MissingNamedTypeArg { .. }) - )); + check_errors(src); } #[test] @@ -3144,22 +2746,12 @@ fn as_trait_path_syntax_resolves_outside_impl() { // AsTraitPath syntax is a bit silly when associated types // are explicitly specified let _: i64 = 1 as >::Assoc; + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Expected type i64, found type i32 let _ = Bar {}; // silence Bar never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use CompilationError::TypeError; - use TypeCheckError::TypeMismatch; - let TypeError(TypeMismatch { expected_typ, expr_typ, .. }) = errors[0].0.clone() else { - panic!("Expected TypeMismatch error, found {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "i64".to_string()); - assert_eq!(expr_typ, "i32".to_string()); + check_errors(src); } #[test] @@ -3177,70 +2769,71 @@ fn as_trait_path_syntax_no_impl() { fn main() { let _: i64 = 1 as >::Assoc; + ^^^ No matching impl found for `Bar: Foo` + ~~~ No impl for `Bar: Foo` let _ = Bar {}; // silence Bar never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use CompilationError::TypeError; - assert!(matches!(&errors[0].0, TypeError(TypeCheckError::NoMatchingImplFound { .. }))); + check_errors(src); } #[test] -fn dont_infer_globals_to_u32_from_type_use() { +fn do_not_infer_globals_to_u32_from_type_use() { let src = r#" global ARRAY_LEN = 3; + ^^^^^^^^^ Globals must have a specified type + ~ Inferred type is `Field` global STR_LEN: _ = 2; + ^^^^^^^ Globals must have a specified type + ~ Inferred type is `Field` global FMT_STR_LEN = 2; + ^^^^^^^^^^^ Globals must have a specified type + ~ Inferred type is `Field` fn main() { let _a: [u32; ARRAY_LEN] = [1, 2, 3]; + ^^^^^^^^^^^^^^^^ The numeric generic is not of type `u32` + ~~~~~~~~~~~~~~~~ expected `u32`, found `Field` let _b: str = "hi"; + ^^^^^^^^^^^^ The numeric generic is not of type `u32` + ~~~~~~~~~~~~ expected `u32`, found `Field` let _c: fmtstr = f"hi"; + ^^^^^^^^^^^^^^^^^^^^^^ The numeric generic is not of type `u32` + ~~~~~~~~~~~~~~~~~~~~~~ expected `u32`, found `Field` } "#; - - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 6); - for (error, _file_id) in errors.drain(0..3) { - assert!(matches!( - error, - CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) - )); - } - for (error, _file_id) in errors { - assert!(matches!( - error, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) - )); - } + check_errors(src); } #[test] -fn dont_infer_partial_global_types() { +fn do_not_infer_partial_global_types() { let src = r#" pub global ARRAY: [Field; _] = [0; 3]; + ^^^^^ Globals must have a specified type + ~~~~~~ Inferred type is `[Field; 3]` pub global NESTED_ARRAY: [[Field; _]; 3] = [[]; 3]; + ^^^^^^^^^^^^ Globals must have a specified type + ~~~~~~~ Inferred type is `[[Field; 0]; 3]` pub global STR: str<_> = "hi"; + ^^^ Globals must have a specified type + ~~~~ Inferred type is `str<2>` + pub global NESTED_STR: [str<_>] = &["hi"]; + ^^^^^^^^^^ Globals must have a specified type + ~~~~~~~ Inferred type is `[str<2>]` pub global FORMATTED_VALUE: str<5> = "there"; pub global FMT_STR: fmtstr<_, _> = f"hi {FORMATTED_VALUE}"; - pub global TUPLE_WITH_MULTIPLE: ([str<_>], [[Field; _]; 3]) = (&["hi"], [[]; 3]); + ^^^^^^^ Globals must have a specified type + ~~~~~~~~~~~~~~~~~~~~~~~ Inferred type is `fmtstr<20, (str<5>)>` + pub global TUPLE_WITH_MULTIPLE: ([str<_>], [[Field; _]; 3]) = + ^^^^^^^^^^^^^^^^^^^ Globals must have a specified type + (&["hi"], [[]; 3]); + ~~~~~~~~~~~~~~~~~~ Inferred type is `([str<2>], [[Field; 0]; 3])` fn main() { } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 6); - for (error, _file_id) in errors { - assert!(matches!( - error, - CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) - )); - } + check_errors(src); } #[test] @@ -3256,9 +2849,7 @@ fn u32_globals_as_sizes_in_types() { let _c: fmtstr = f"hi"; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -3270,6 +2861,8 @@ fn struct_array_len() { impl Array { pub fn len(self) -> u32 { + ^^^^ unused variable self + ~~~~ unused variable N as u32 } } @@ -3281,17 +2874,11 @@ fn struct_array_len() { assert(ys.len() == 2); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnusedVariable { .. }) - )); + check_errors(src); } // TODO(https://github.com/noir-lang/noir/issues/6245): -// support u16 as an array size +// support u8 as an array size #[test] fn non_u32_as_array_length() { let src = r#" @@ -3299,15 +2886,11 @@ fn non_u32_as_array_length() { fn main() { let _a: [u32; ARRAY_LEN] = [1, 2, 3]; + ^^^^^^^^^^^^^^^^ The numeric generic is not of type `u32` + ~~~~~~~~~~~~~~~~ expected `u32`, found `u8` } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) - )); + check_errors(src); } #[test] @@ -3319,9 +2902,7 @@ fn use_non_u32_generic_in_struct() { let _: S<3> = S {}; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -3344,10 +2925,7 @@ fn use_numeric_generic_in_trait_method() { let _ = Bar{}.foo(bytes); } "#; - - let errors = get_program_errors(src); - println!("{errors:?}"); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -3373,26 +2951,17 @@ fn trait_unconstrained_methods_typechecked_correctly() { assert_eq(2.foo(), 2.identity() as Field); } "#; - - let errors = get_program_errors(src); - println!("{errors:?}"); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] fn error_if_attribute_not_in_scope() { let src = r#" #[not_in_scope] + ^^^^^^^^^^^^^^^ Attribute function `not_in_scope` is not in scope fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::AttributeFunctionNotInScope { .. }) - )); + check_errors(src); } #[test] @@ -3405,9 +2974,7 @@ fn arithmetic_generics_rounding_pass() { fn round(_x: [Field; N / M * M]) {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -3417,17 +2984,12 @@ fn arithmetic_generics_rounding_fail() { // Do not simplify N/M*M to just N // This should be 3/2*2 = 2, not 3 round::<3, 2>([1, 2, 3]); + ^^^^^^^^^ Expected type [Field; 2], found type [Field; 3] } fn round(_x: [Field; N / M * M]) {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) - )); + check_errors(src); } #[test] @@ -3445,15 +3007,10 @@ fn arithmetic_generics_rounding_fail_on_struct() { // Do not simplify N/M*M to just N // This should be 3/2*2 = 2, not 3 let _: W<3> = foo(w_3, w_2); + ^^^^^^^^^^^^^ Expected type W<3>, found type W<2> } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) - )); + check_errors(src); } #[test] @@ -3466,42 +3023,58 @@ fn unconditional_recursion_fail() { let srcs = vec![ r#" fn main() { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing main() } "#, r#" fn main() -> pub bool { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing if main() { true } else { false } } "#, r#" fn main() -> pub bool { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing if true { main() } else { main() } } "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing main() + main() } "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing 1 + main() } "#, r#" fn main() -> pub bool { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing let _ = main(); true } "#, r#" fn main(a: u64, b: u64) -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing main(a + b, main(a, b)) } "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing foo(1, main()) } fn foo(a: u64, b: u64) -> u64 { @@ -3510,12 +3083,16 @@ fn unconditional_recursion_fail() { "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing let (a, b) = (main(), main()); a + b } "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing let mut sum = 0; for i in 0 .. main() { sum += i; @@ -3526,19 +3103,7 @@ fn unconditional_recursion_fail() { ]; for src in srcs { - let errors = get_program_errors(src); - assert!( - !errors.is_empty(), - "expected 'unconditional recursion' error, got nothing; src = {src}" - ); - - for (error, _) in errors { - let CompilationError::ResolverError(ResolverError::UnconditionalRecursion { .. }) = - error - else { - panic!("Expected an 'unconditional recursion' error, got {:?}; src = {src}", error); - }; - } + check_errors(src); } } @@ -3738,117 +3303,89 @@ fn errors_with_better_message_when_trying_to_invoke_struct_field_that_is_a_funct impl Foo { fn call(self) -> bool { self.wrapped(1) + ^^^^^^^^^^^^^^^ Cannot invoke function field 'wrapped' on type 'Foo' as a method + ~~~~~~~~~~~~~~~ to call the function stored in 'wrapped', surround the field access with parentheses: '(', ')' } } fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::CannotInvokeStructFieldFunctionType { - method_name, - .. - }) = &errors[0].0 - else { - panic!("Expected a 'CannotInvokeStructFieldFunctionType' error, got {:?}", errors[0].0); - }; - - assert_eq!(method_name, "wrapped"); -} - -fn test_disallows_attribute_on_impl_method( - attr: &str, - check_error: impl FnOnce(&CompilationError), -) { - let src = format!( - " - pub struct Foo {{ }} - - impl Foo {{ - #[{attr}] - fn foo() {{ }} - }} - - fn main() {{ }} - " - ); - let errors = get_program_errors(&src); - assert_eq!(errors.len(), 1); - check_error(&errors[0].0); -} - -fn test_disallows_attribute_on_trait_impl_method( - attr: &str, - check_error: impl FnOnce(&CompilationError), -) { - let src = format!( - " - pub trait Trait {{ - fn foo() {{ }} - }} - - pub struct Foo {{ }} - - impl Trait for Foo {{ - #[{attr}] - fn foo() {{ }} - }} - - fn main() {{ }} - " - ); - let errors = get_program_errors(&src); - assert_eq!(errors.len(), 1); - check_error(&errors[0].0); + check_errors(src); } #[test] fn disallows_test_attribute_on_impl_method() { - test_disallows_attribute_on_impl_method("test", |error| { - assert!(matches!( - error, - CompilationError::DefinitionError( - DefCollectorErrorKind::TestOnAssociatedFunction { .. } - ) - )); - }); + // TODO: improve the error location + let src = " + pub struct Foo { } + + impl Foo { + #[test] + fn foo() { } + ^^^ The `#[test]` attribute is disallowed on `impl` methods + } + + fn main() { } + "; + check_errors(src); } #[test] fn disallows_test_attribute_on_trait_impl_method() { - test_disallows_attribute_on_trait_impl_method("test", |error| { - assert!(matches!( - error, - CompilationError::DefinitionError( - DefCollectorErrorKind::TestOnAssociatedFunction { .. } - ) - )); - }); + let src = " + pub trait Trait { + fn foo() { } + } + + pub struct Foo { } + + impl Trait for Foo { + #[test] + fn foo() { } + ^^^ The `#[test]` attribute is disallowed on `impl` methods + } + + fn main() { } + "; + check_errors(src); } #[test] fn disallows_export_attribute_on_impl_method() { - test_disallows_attribute_on_impl_method("export", |error| { - assert!(matches!( - error, - CompilationError::DefinitionError( - DefCollectorErrorKind::ExportOnAssociatedFunction { .. } - ) - )); - }); + // TODO: improve the error location + let src = " + pub struct Foo { } + + impl Foo { + #[export] + fn foo() { } + ^^^ The `#[export]` attribute is disallowed on `impl` methods + } + + fn main() { } + "; + check_errors(src); } #[test] fn disallows_export_attribute_on_trait_impl_method() { - test_disallows_attribute_on_trait_impl_method("export", |error| { - assert!(matches!( - error, - CompilationError::DefinitionError( - DefCollectorErrorKind::ExportOnAssociatedFunction { .. } - ) - )); - }); + // TODO: improve the error location + let src = " + pub trait Trait { + fn foo() { } + } + + pub struct Foo { } + + impl Trait for Foo { + #[export] + fn foo() { } + ^^^ The `#[export]` attribute is disallowed on `impl` methods + } + + fn main() { } + "; + check_errors(src); } #[test] @@ -3867,38 +3404,27 @@ fn disallows_underscore_on_right_hand_side() { fn main() { let _ = 1; let _x = _; + ^ in expressions, `_` can only be used on the left-hand side of an assignment + ~ `_` not allowed here } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, .. }) = - &errors[0].0 - else { - panic!("Expected a VariableNotDeclared error, got {:?}", errors[0].0); - }; - - assert_eq!(name, "_"); + check_errors(src); } #[test] fn errors_on_cyclic_globals() { let src = r#" pub comptime global A: u32 = B; + ^ This global recursively depends on itself + ^ Dependency cycle found + ~ 'A' recursively depends on itself: A -> B -> A pub comptime global B: u32 = A; + ^ Variable not in scope + ~ Could not find variable fn main() { } "#; - let errors = get_program_errors(src); - - assert!(errors.iter().any(|(error, _)| matches!( - error, - CompilationError::InterpreterError(InterpreterError::GlobalsDependencyCycle { .. }) - ))); - assert!(errors.iter().any(|(error, _)| matches!( - error, - CompilationError::ResolverError(ResolverError::DependencyCycle { .. }) - ))); + check_errors(src); } #[test] @@ -3907,18 +3433,14 @@ fn warns_on_unneeded_unsafe() { fn main() { // Safety: test unsafe { + ^^^^^^ Unnecessary `unsafe` block foo() } } fn foo() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::TypeError(TypeCheckError::UnnecessaryUnsafeBlock { .. }) - )); + check_errors(src); } #[test] @@ -3929,6 +3451,8 @@ fn warns_on_nested_unsafe() { unsafe { // Safety: test unsafe { + ^^^^^^ Unnecessary `unsafe` block + ~~~~~~ Because it's nested inside another `unsafe` block foo() } } @@ -3936,12 +3460,7 @@ fn warns_on_nested_unsafe() { unconstrained fn foo() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::TypeError(TypeCheckError::NestedUnsafeBlock { .. }) - )); + check_errors(src); } #[test] @@ -4228,28 +3747,6 @@ fn regression_7088() { assert_no_errors(src); } -#[test] -fn error_with_duplicate_enum_variant() { - let src = r#" - enum Foo { - Bar(i32), - Bar(u8), - } - - fn main() {} - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - assert!(matches!( - &errors[0].0, - CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { .. }) - )); - assert!(matches!( - &errors[1].0, - CompilationError::ResolverError(ResolverError::UnusedItem { .. }) - )); -} - #[test] fn errors_on_empty_loop_no_break() { let src = r#" @@ -4262,14 +3759,11 @@ fn errors_on_empty_loop_no_break() { unconstrained fn foo() { loop {} + ^^^^ `loop` must have at least one `break` in it + ~~~~ Infinite loops are disallowed } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::ResolverError(ResolverError::LoopWithoutBreak { .. }) - )); + check_errors(src); } #[test] @@ -4285,6 +3779,8 @@ fn errors_on_loop_without_break() { unconstrained fn foo() { let mut x = 1; loop { + ^^^^ `loop` must have at least one `break` in it + ~~~~ Infinite loops are disallowed x += 1; bar(x); } @@ -4292,12 +3788,7 @@ fn errors_on_loop_without_break() { fn bar(_: Field) {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::ResolverError(ResolverError::LoopWithoutBreak { .. }) - )); + check_errors(src); } #[test] @@ -4313,6 +3804,8 @@ fn errors_on_loop_without_break_with_nested_loop() { unconstrained fn foo() { let mut x = 1; loop { + ^^^^ `loop` must have at least one `break` in it + ~~~~ Infinite loops are disallowed x += 1; bar(x); loop { @@ -4324,12 +3817,7 @@ fn errors_on_loop_without_break_with_nested_loop() { fn bar(_: Field) {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::ResolverError(ResolverError::LoopWithoutBreak { .. }) - )); + check_errors(src); } #[test] @@ -4354,16 +3842,11 @@ fn errors_on_if_without_else_type_mismatch() { fn main() { if true { 1 + ^ Expected type Field, found type () } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0].0 else { - panic!("Expected a Context error"); - }; - assert!(matches!(**err, TypeCheckError::TypeMismatch { .. })); + check_errors(src); } #[test] @@ -4378,15 +3861,11 @@ fn errors_if_for_body_type_is_not_unit() { fn main() { for _ in 0..1 { 1 + ^ Expected type (), found type Field } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) = &errors[0].0 else { - panic!("Expected a TypeMismatch error"); - }; + check_errors(src); } #[test] @@ -4397,15 +3876,11 @@ fn errors_if_loop_body_type_is_not_unit() { if false { break; } 1 + ^ Expected type (), found type Field } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) = &errors[0].0 else { - panic!("Expected a TypeMismatch error"); - }; + check_errors(src); } #[test] @@ -4414,13 +3889,228 @@ fn errors_if_while_body_type_is_not_unit() { unconstrained fn main() { while 1 == 1 { 1 + ^ Expected type (), found type Field } } "#; + check_errors(src); +} + +#[test] +fn check_impl_duplicate_method_without_self() { + let src = " + pub struct Foo {} + + impl Foo { + fn foo() {} + ~~~ first definition found here + fn foo() {} + ^^^ duplicate definitions of foo found + ~~~ second definition found here + } + + fn main() {} + "; + check_errors(src); +} + +#[test] +fn int_min_global() { + let src = r#" + global MIN: i8 = -128; + fn main() { + let _x = MIN; + } + "#; + let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); + assert_eq!(errors.len(), 0); +} - let CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) = &errors[0].0 else { - panic!("Expected a TypeMismatch error"); - }; +#[test] +fn subtract_to_int_min() { + // This would cause an integer underflow panic before + let src = r#" + fn main() { + let _x: i8 = comptime { + let y: i8 = -127; + let z = y - 1; + z + }; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + +#[test] +fn mutate_with_reference_in_lambda() { + let src = r#" + fn main() { + let x = &mut 3; + let f = || { + *x += 2; + }; + f(); + assert(*x == 5); + } + "#; + + assert_no_errors(src); +} + +#[test] +fn mutate_with_reference_marked_mutable_in_lambda() { + let src = r#" + fn main() { + let mut x = &mut 3; + let f = || { + *x += 2; + }; + f(); + assert(*x == 5); + } + "#; + assert_no_errors(src); +} + +#[test] +fn deny_capturing_mut_variable_without_reference_in_lambda() { + let src = r#" + fn main() { + let mut x = 3; + let f = || { + x += 2; + ^ Mutable variable x captured in lambda must be a mutable reference + ~ Use '&mut' instead of 'mut' to capture a mutable variable. + }; + f(); + assert(x == 5); + } + "#; + check_errors(src); +} + +#[test] +fn deny_capturing_mut_variable_without_reference_in_nested_lambda() { + let src = r#" + fn main() { + let mut x = 3; + let f = || { + let inner = || { + x += 2; + ^ Mutable variable x captured in lambda must be a mutable reference + ~ Use '&mut' instead of 'mut' to capture a mutable variable. + }; + inner(); + }; + f(); + assert(x == 5); + } + "#; + check_errors(src); +} + +#[test] +fn allow_capturing_mut_variable_only_used_immutably() { + let src = r#" + fn main() { + let mut x = 3; + let f = || x; + let _x2 = f(); + assert(x == 3); + } + "#; + assert_no_errors(src); +} + +#[test] +fn deny_capturing_mut_var_as_param_to_function() { + let src = r#" + fn main() { + let mut x = 3; + let f = || mutate(&mut x); + ^ Mutable variable x captured in lambda must be a mutable reference + ~ Use '&mut' instead of 'mut' to capture a mutable variable. + f(); + assert(x == 3); + } + + fn mutate(x: &mut Field) { + *x = 5; + } + "#; + check_errors(src); +} + +#[test] +fn deny_capturing_mut_var_as_param_to_function_in_nested_lambda() { + let src = r#" + fn main() { + let mut x = 3; + let f = || { + let inner = || mutate(&mut x); + ^ Mutable variable x captured in lambda must be a mutable reference + ~ Use '&mut' instead of 'mut' to capture a mutable variable. + inner(); + }; + f(); + assert(x == 3); + } + + fn mutate(x: &mut Field) { + *x = 5; + } + "#; + check_errors(src); +} + +#[test] +fn deny_capturing_mut_var_as_param_to_impl_method() { + let src = r#" + struct Foo { + value: Field, + } + + impl Foo { + fn mutate(&mut self) { + self.value = 2; + } + } + + fn main() { + let mut foo = Foo { value: 1 }; + let f = || foo.mutate(); + ^^^ Mutable variable foo captured in lambda must be a mutable reference + ~~~ Use '&mut' instead of 'mut' to capture a mutable variable. + f(); + assert(foo.value == 2); + } + "#; + check_errors(src); +} + +#[test] +fn deny_attaching_mut_ref_to_immutable_object() { + let src = r#" + struct Foo { + value: Field, + } + + impl Foo { + fn mutate(&mut self) { + self.value = 2; + } + } + + fn main() { + let foo = Foo { value: 1 }; + let f = || foo.mutate(); + ^^^ Cannot mutate immutable variable `foo` + f(); + assert(foo.value == 2); + } + "#; + check_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/arithmetic_generics.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/arithmetic_generics.rs index 83de9c077ab3..bcbdbdd6211e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/arithmetic_generics.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/arithmetic_generics.rs @@ -2,11 +2,10 @@ use acvm::{AcirField, FieldElement}; -use super::get_program_errors; use crate::hir::type_check::TypeCheckError; use crate::hir_def::types::{BinaryTypeOperator, Type}; use crate::monomorphization::errors::MonomorphizationError; -use crate::tests::get_monomorphization_error; +use crate::tests::{assert_no_errors, get_monomorphization_error}; #[test] fn arithmetic_generics_canonicalization_deduplication_regression() { @@ -23,8 +22,7 @@ fn arithmetic_generics_canonicalization_deduplication_regression() { }; } "#; - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); } #[test] @@ -52,9 +50,7 @@ fn checked_casts_do_not_prevent_canonicalization() { } } "#; - let errors = get_program_errors(source); - println!("{:?}", errors); - assert_eq!(errors.len(), 0); + assert_no_errors(source); } #[test] @@ -76,9 +72,7 @@ fn arithmetic_generics_checked_cast_zeros() { bar(w) } "#; - - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); let monomorphization_error = get_monomorphization_error(source); assert!(monomorphization_error.is_some()); @@ -123,9 +117,7 @@ fn arithmetic_generics_checked_cast_indirect_zeros() { let _ = bar(w); } "#; - - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); let monomorphization_error = get_monomorphization_error(source); assert!(monomorphization_error.is_some()); @@ -166,8 +158,7 @@ fn global_numeric_generic_larger_than_u32() { let _ = foo::(); } "#; - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); } #[test] @@ -196,6 +187,5 @@ fn global_arithmetic_generic_larger_than_u32() { let _ = foo::().size(); } "#; - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/bound_checks.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/bound_checks.rs index 05669bda4111..6a084e68c7e3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/bound_checks.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/bound_checks.rs @@ -1,24 +1,14 @@ -use crate::hir::def_collector::dc_crate::CompilationError; - -use super::get_program_errors; +use crate::tests::check_errors; #[test] fn overflowing_u8() { let src = r#" fn main() { let _: u8 = 256; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `256` cannot fit into `u8` which has range `0..=255`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } + ^^^ The value `256` cannot fit into `u8` which has range `0..=255` + } + "#; + check_errors(src); } #[test] @@ -26,18 +16,10 @@ fn underflowing_u8() { let src = r#" fn main() { let _: u8 = -1; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `-1` cannot fit into `u8` which has range `0..=255`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } + ^^ The value `-1` cannot fit into `u8` which has range `0..=255` + } + "#; + check_errors(src); } #[test] @@ -45,18 +27,10 @@ fn overflowing_i8() { let src = r#" fn main() { let _: i8 = 128; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `128` cannot fit into `i8` which has range `-128..=127`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } + ^^^ The value `128` cannot fit into `i8` which has range `-128..=127` + } + "#; + check_errors(src); } #[test] @@ -64,16 +38,8 @@ fn underflowing_i8() { let src = r#" fn main() { let _: i8 = -129; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `-129` cannot fit into `i8` which has range `-128..=127`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } + ^^^^ The value `-129` cannot fit into `i8` which has range `-128..=127` + } + "#; + check_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/enums.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/enums.rs new file mode 100644 index 000000000000..6163b34003e5 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/enums.rs @@ -0,0 +1,201 @@ +use crate::{ + hir::def_collector::dc_crate::CompilationError, + parser::ParserErrorReason, + tests::{assert_no_errors, get_program_using_features}, +}; + +use super::{check_errors, check_errors_using_features}; + +#[test] +fn error_with_duplicate_enum_variant() { + let src = r#" + pub enum Foo { + Bar(i32), + ~~~ First enum variant found here + Bar(u8), + ^^^ Duplicate definitions of enum variant with name Bar found + ~~~ Second enum variant found here + } + + fn main() {} + "#; + check_errors(src); +} + +#[test] +fn errors_on_unspecified_unstable_enum() { + // Enums are experimental - this will need to be updated when they are stabilized + let src = r#" + enum Foo { Bar } + ^^^ This requires the unstable feature 'enums' which is not enabled + ~~~ Pass -Zenums to nargo to enable this feature at your own risk. + + fn main() { + let _x = Foo::Bar; + } + "#; + let no_features = &[]; + check_errors_using_features(src, no_features); +} + +#[test] +fn errors_on_unspecified_unstable_match() { + // TODO: update this test. Right now it's hard to test because the span happens in the entire + // `match` node but ideally it would be nice if it only happened in the `match` keyword. + // Enums are experimental - this will need to be updated when they are stabilized + let src = r#" + fn main() { + match 3 { + _ => (), + } + } + "#; + + let no_features = &[]; + let errors = get_program_using_features(src, no_features).2; + assert_eq!(errors.len(), 1); + + let CompilationError::ParseError(error) = &errors[0] else { + panic!("Expected a ParseError experimental feature error"); + }; + + assert!(matches!(error.reason(), Some(ParserErrorReason::ExperimentalFeature(_)))); +} + +#[test] +fn errors_on_repeated_match_variables_in_pattern() { + let src = r#" + fn main() { + match (1, 2) { + (_x, _x) => (), + ^^ Variable `_x` was already defined in the same match pattern + ~~ `_x` redefined here + ~~ `_x` was previously defined here + } + } + "#; + check_errors(src); +} + +#[test] +fn duplicate_field_in_match_struct_pattern() { + let src = r#" + fn main() { + let foo = Foo { x: 10, y: 20 }; + match foo { + Foo { x: _, x: _, y: _ } => {} + ^ duplicate field x + } + } + + struct Foo { + x: i32, + y: Field, + } + "#; + check_errors(src); +} + +#[test] +fn missing_field_in_match_struct_pattern() { + let src = r#" + fn main() { + let foo = Foo { x: 10, y: 20 }; + match foo { + Foo { x: _ } => {} + ^^^ missing field y in struct Foo + } + } + + struct Foo { + x: i32, + y: Field, + } + "#; + check_errors(src); +} + +#[test] +fn no_such_field_in_match_struct_pattern() { + let src = r#" + fn main() { + let foo = Foo { x: 10, y: 20 }; + match foo { + Foo { x: _, y: _, z: _ } => {} + ^ no such field z defined in struct Foo + } + } + + struct Foo { + x: i32, + y: Field, + } + "#; + check_errors(src); +} + +#[test] +fn match_integer_type_mismatch_in_pattern() { + let src = r#" + fn main() { + match 2 { + Foo::One(_) => (), + ^^^^^^^^ Expected type Field, found type Foo + } + } + + enum Foo { + One(i32), + } + "#; + check_errors(src); +} + +#[test] +fn match_shadow_global() { + let src = r#" + fn main() { + match 2 { + foo => assert_eq(foo, 2), + } + } + + fn foo() {} + "#; + assert_no_errors(src); +} + +#[test] +fn match_no_shadow_global() { + let src = r#" + fn main() { + match 2 { + crate::foo => (), + ^^^^^^^^^^ Expected a struct, enum, or literal pattern, but found a function + } + } + + fn foo() {} + "#; + check_errors(src); +} + +#[test] +fn constructor_arg_arity_mismatch_in_pattern() { + let src = r#" + fn main() { + match Foo::One(1) { + Foo::One(_, _) => (), + ^^^^^^^^ Expected 1 argument, but found 2 + Foo::Two(_) => (), + ^^^^^^^^ Expected 2 arguments, but found 1 + } + } + + enum Foo { + One(i32), + Two(i32, i32), + } + "#; + check_errors(src); +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/imports.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/imports.rs index 8598d8296e59..ba2dad285d6a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/imports.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/imports.rs @@ -1,9 +1,6 @@ -use crate::hir::{ - def_collector::{dc_crate::CompilationError, errors::DefCollectorErrorKind}, - resolution::{errors::ResolverError, import::PathResolutionError}, -}; +use crate::tests::check_errors; -use super::{assert_no_errors, get_program_errors}; +use super::assert_no_errors; #[test] fn use_super() { @@ -23,19 +20,11 @@ fn use_super() { #[test] fn no_super() { - let src = "use super::some_func;"; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( - PathResolutionError::NoSuper(span), - )) = &errors[0].0 - else { - panic!("Expected a 'no super' error, got {:?}", errors[0].0); - }; - - assert_eq!(span.start(), 4); - assert_eq!(span.end(), 9); + let src = " + use super::some_func; + ^^^^^ There is no super module + "; + check_errors(src); } #[test] @@ -69,18 +58,11 @@ fn warns_on_use_of_private_exported_item() { fn main() { foo::baz(); + ^^^ baz is private and not visible from the current module + ~~~ baz is private } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - &errors[0].0, - CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(..), - )) - )); + check_errors(src); } #[test] @@ -110,22 +92,15 @@ fn warns_on_re_export_of_item_with_less_visibility() { } pub use bar::baz; + ^^^ cannot re-export baz because it has less visibility than this use statement + ~~~ consider marking baz as pub } fn main() { foo::baz(); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - &errors[0].0, - CompilationError::DefinitionError( - DefCollectorErrorKind::CannotReexportItemWithLessVisibility { .. } - ) - )); + check_errors(src); } #[test] @@ -136,21 +111,10 @@ fn errors_if_using_alias_in_import() { } use foo::bar::baz; + ^^^ bar is a type alias, not a module fn main() { } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( - PathResolutionError::NotAModule { ident, kind }, - )) = &errors[0].0 - else { - panic!("Expected a 'not a module' error, got {:?}", errors[0].0); - }; - - assert_eq!(ident.to_string(), "bar"); - assert_eq!(*kind, "type alias"); + check_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/metaprogramming.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/metaprogramming.rs index b42342fa47d3..a19ef17d8353 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/metaprogramming.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/metaprogramming.rs @@ -1,17 +1,15 @@ -use noirc_errors::Spanned; +use noirc_errors::Located; use crate::{ ast::Ident, hir::{ - comptime::InterpreterError, + comptime::ComptimeError, def_collector::{ dc_crate::CompilationError, errors::{DefCollectorErrorKind, DuplicateType}, }, - resolution::errors::ResolverError, - type_check::TypeCheckError, }, - parser::ParserErrorReason, + tests::check_errors, }; use super::{assert_no_errors, get_program_errors}; @@ -23,39 +21,30 @@ fn comptime_let() { comptime let my_var = 2; assert_eq(my_var, 2); }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] fn comptime_code_rejects_dynamic_variable() { - let src = r#"fn main(x: Field) { + let src = r#" + fn main(x: Field) { comptime let my_var = (x - x) + 2; + ^ Non-comptime variable `x` referenced in comptime code + ~ Non-comptime variables can't be used in comptime code assert_eq(my_var, 2); - }"#; - let errors = get_program_errors(src); - - assert_eq!(errors.len(), 1); - match &errors[0].0 { - CompilationError::InterpreterError(InterpreterError::NonComptimeVarReferenced { - name, - .. - }) => { - assert_eq!(name, "x"); - } - _ => panic!("expected an InterpreterError"), } + "#; + check_errors(src); } #[test] fn comptime_type_in_runtime_code() { - let source = "pub fn foo(_f: FunctionDefinition) {}"; - let errors = get_program_errors(source); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::ComptimeTypeInRuntimeCode { .. }) - )); + let source = " + pub fn foo(_f: FunctionDefinition) {} + ^^^^^^^^^^^^^^^^^^ Comptime-only type `FunctionDefinition` cannot be used in runtime code + ~~~~~~~~~~~~~~~~~~ Comptime-only type used here + "; + check_errors(source); } #[test] @@ -64,6 +53,7 @@ fn macro_result_type_mismatch() { fn main() { comptime { let x = unquote!(quote { "test" }); + ^^^^^^^^^^^^^^^^^^^^^^^^^^ Expected type Field, found type str<4> let _: Field = x; } } @@ -72,13 +62,7 @@ fn macro_result_type_mismatch() { q } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) - )); + check_errors(src); } #[test] @@ -130,6 +114,8 @@ fn allows_references_to_structs_generated_by_macros() { #[test] fn errors_if_macros_inject_functions_with_name_collisions() { + // This can't be tested using `check_errors` right now because the two secondary + // errors land on the same span. let src = r#" comptime fn make_colliding_functions(_s: StructDefinition) -> Quoted { quote { @@ -150,14 +136,21 @@ fn errors_if_macros_inject_functions_with_name_collisions() { } "#; - let errors = get_program_errors(src); + let mut errors = get_program_errors(src); assert_eq!(errors.len(), 1); + + let CompilationError::ComptimeError(ComptimeError::ErrorRunningAttribute { error, .. }) = + errors.remove(0) + else { + panic!("Expected a ComptimeError, got {:?}", errors[0]); + }; + assert!(matches!( - &errors[0].0, + *error, CompilationError::DefinitionError( DefCollectorErrorKind::Duplicate { typ: DuplicateType::Function, - first_def: Ident(Spanned { contents, .. }), + first_def: Ident(Located { contents, .. }), .. }, ) if contents == "foo" @@ -194,6 +187,8 @@ fn does_not_fail_to_parse_macro_on_parser_warning() { quote { pub fn bar() { unsafe { + ^^^^^^ Unsafe block must have a safety comment above it + ~~~~~~ The comment must start with the "Safety: " word foo(); } } @@ -204,12 +199,5 @@ fn does_not_fail_to_parse_macro_on_parser_warning() { bar() } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ParseError(parser_error) = &errors[0].0 else { - panic!("Expected a ParseError, got {:?}", errors[0].0); - }; - - assert!(matches!(parser_error.reason(), Some(ParserErrorReason::MissingSafetyComment))); + check_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/references.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/references.rs index 5dbc395eb590..a11cd0eea538 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/references.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/references.rs @@ -1,9 +1,4 @@ -use crate::hir::{ - def_collector::dc_crate::CompilationError, resolution::errors::ResolverError, - type_check::TypeCheckError, -}; - -use super::get_program_errors; +use crate::tests::check_errors; #[test] fn cannot_mutate_immutable_variable() { @@ -11,21 +6,12 @@ fn cannot_mutate_immutable_variable() { fn main() { let array = [1]; mutate(&mut array); + ^^^^^ Cannot mutate immutable variable `array` } fn mutate(_: &mut [Field; 1]) {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = - &errors[0].0 - else { - panic!("Expected a CannotMutateImmutableVariable error"); - }; - - assert_eq!(name, "array"); + check_errors(src); } #[test] @@ -38,23 +24,14 @@ fn cannot_mutate_immutable_variable_on_member_access() { fn main() { let foo = Foo { x: 0 }; mutate(&mut foo.x); + ^^^^^ Cannot mutate immutable variable `foo` } fn mutate(foo: &mut Field) { *foo = 1; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = - &errors[0].0 - else { - panic!("Expected a CannotMutateImmutableVariable error"); - }; - - assert_eq!(name, "foo"); + check_errors(src); } #[test] @@ -62,23 +39,15 @@ fn does_not_crash_when_passing_mutable_undefined_variable() { let src = r#" fn main() { mutate(&mut undefined); + ^^^^^^^^^ cannot find `undefined` in this scope + ~~~~~~~~~ not found in this scope } fn mutate(foo: &mut Field) { *foo = 1; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, .. }) = - &errors[0].0 - else { - panic!("Expected a VariableNotDeclared error"); - }; - - assert_eq!(name, "undefined"); + check_errors(src); } #[test] @@ -90,6 +59,7 @@ fn constrained_reference_to_unconstrained() { // Safety: test context unsafe { mut_ref_input(x_ref, y); + ^^^^^ Cannot pass a mutable reference from a constrained runtime to an unconstrained runtime } } @@ -100,13 +70,5 @@ fn constrained_reference_to_unconstrained() { *x = y; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::ConstrainedReferenceToUnconstrained { .. }) = - &errors[0].0 - else { - panic!("Expected an error about passing a constrained reference to unconstrained"); - }; + check_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/traits.rs index 7f252b556c2c..d2f9d9a96724 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/traits.rs @@ -1,9 +1,6 @@ -use crate::hir::def_collector::dc_crate::CompilationError; -use crate::hir::def_collector::errors::DefCollectorErrorKind; -use crate::hir::resolution::errors::ResolverError; -use crate::hir::resolution::import::PathResolutionError; -use crate::hir::type_check::TypeCheckError; -use crate::tests::{get_program_errors, get_program_with_maybe_parser_errors}; +use crate::elaborator::FrontendOptions; + +use crate::tests::{check_errors, get_program_with_options}; use super::assert_no_errors; @@ -107,16 +104,12 @@ fn trait_inheritance_with_generics_4() { fn trait_inheritance_dependency_cycle() { let src = r#" trait Foo: Bar {} + ^^^ Dependency cycle found + ~~~ 'Foo' recursively depends on itself: Foo -> Bar -> Foo trait Bar: Foo {} fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::DependencyCycle { .. }) - )); + check_errors(src); } #[test] @@ -125,43 +118,31 @@ fn trait_inheritance_missing_parent_implementation() { pub trait Foo {} pub trait Bar: Foo {} + ~~~ required by this bound in `Bar` pub struct Struct {} impl Bar for Struct {} + ^^^^^^ The trait bound `Struct: Foo` is not satisfied + ~~~~~~ The trait `Foo` is not implemented for `Struct` fn main() { let _ = Struct {}; // silence Struct never constructed warning } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::TraitNotImplemented { - impl_trait, - missing_trait: the_trait, - type_missing_trait: typ, - .. - }) = &errors[0].0 - else { - panic!("Expected a TraitNotImplemented error, got {:?}", &errors[0].0); - }; - - assert_eq!(the_trait, "Foo"); - assert_eq!(typ, "Struct"); - assert_eq!(impl_trait, "Bar"); + check_errors(src); } #[test] fn errors_on_unknown_type_in_trait_where_clause() { let src = r#" pub trait Foo where T: Unknown {} + ^^^^^^^ Could not resolve 'Unknown' in path fn main() { } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); + check_errors(src); } #[test] @@ -220,6 +201,7 @@ fn does_not_error_if_impl_trait_constraint_is_satisfied_for_type_variable() { "#; assert_no_errors(src); } + #[test] fn errors_if_impl_trait_constraint_is_not_satisfied() { let src = r#" @@ -230,6 +212,7 @@ fn errors_if_impl_trait_constraint_is_not_satisfied() { pub trait Foo where T: Greeter, + ~~~~~~~ required by this bound in `Foo` { fn greet(object: U) where @@ -244,25 +227,12 @@ fn errors_if_impl_trait_constraint_is_not_satisfied() { pub struct Bar; impl Foo for Bar {} + ^^^ The trait bound `SomeGreeter: Greeter` is not satisfied + ~~~ The trait `Greeter` is not implemented for `SomeGreeter` fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::TraitNotImplemented { - impl_trait, - missing_trait: the_trait, - type_missing_trait: typ, - .. - }) = &errors[0].0 - else { - panic!("Expected a TraitNotImplemented error, got {:?}", &errors[0].0); - }; - - assert_eq!(the_trait, "Greeter"); - assert_eq!(typ, "SomeGreeter"); - assert_eq!(impl_trait, "Foo"); + check_errors(src); } #[test] @@ -441,6 +411,7 @@ fn trait_alias_polymorphic_where_clause() { fn qux(x: T) -> bool where T: Qux { x.foo().bar().baz() + ^^^^^^^^^^^^^^^^^^^ No method named 'baz' found for type 'U' } impl Foo for Field { @@ -463,35 +434,14 @@ fn trait_alias_polymorphic_where_clause() { fn main() { assert(0.foo().bar().baz() == qux(0)); + ^^^ No matching impl found for `T: Baz` + ~~~ No impl for `T: Baz` } "#; // TODO(https://github.com/noir-lang/noir/issues/6467) // assert_no_errors(src); - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - match &errors[0].0 { - CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { - method_name, .. - }) => { - assert_eq!(method_name, "baz"); - } - other => { - panic!("expected UnresolvedMethodCall, but found {:?}", other); - } - } - - match &errors[1].0 { - CompilationError::TypeError(TypeCheckError::NoMatchingImplFound(err)) => { - assert_eq!(err.constraints.len(), 2); - assert_eq!(err.constraints[0].1, "Baz"); - assert_eq!(err.constraints[1].1, "Qux<_>"); - } - other => { - panic!("expected NoMatchingImplFound, but found {:?}", other); - } - } + check_errors(src); } // TODO(https://github.com/noir-lang/noir/issues/6467): currently failing, so @@ -517,10 +467,12 @@ fn trait_alias_with_where_clause_has_equivalent_errors() { pub fn qux(x: T, _: U) -> bool where U: Qux { x.baz() + ^^^^^^^ No method named 'baz' found for type 'T' } fn main() {} "#; + check_errors(src); let alias_src = r#" trait Bar { @@ -535,37 +487,12 @@ fn trait_alias_with_where_clause_has_equivalent_errors() { pub fn qux(x: T, _: U) -> bool where U: Qux { x.baz() + ^^^^^^^ No method named 'baz' found for type 'T' } fn main() {} "#; - - let errors = get_program_errors(src); - let alias_errors = get_program_errors(alias_src); - - assert_eq!(errors.len(), 1); - assert_eq!(alias_errors.len(), 1); - - match (&errors[0].0, &alias_errors[0].0) { - ( - CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { - method_name, - object_type, - .. - }), - CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { - method_name: alias_method_name, - object_type: alias_object_type, - .. - }), - ) => { - assert_eq!(method_name, alias_method_name); - assert_eq!(object_type, alias_object_type); - } - other => { - panic!("expected UnresolvedMethodCall, but found {:?}", other); - } - } + check_errors(alias_src); } #[test] @@ -650,9 +577,9 @@ fn does_not_crash_on_as_trait_path_with_empty_path() { fn main() {} "#; - let (_, _, errors) = get_program_with_maybe_parser_errors( - src, true, // allow parser errors - ); + let allow_parser_errors = true; + let options = FrontendOptions::test_default(); + let (_, _, errors) = get_program_with_options(src, allow_parser_errors, options); assert!(!errors.is_empty()); } @@ -661,6 +588,7 @@ fn warns_if_trait_is_not_in_scope_for_function_call_and_there_is_only_one_trait_ let src = r#" fn main() { let _ = Bar::foo(); + ^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } pub struct Bar { @@ -678,17 +606,7 @@ fn warns_if_trait_is_not_in_scope_for_function_call_and_there_is_only_one_trait_ } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0].0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -790,6 +708,8 @@ fn errors_if_trait_is_not_in_scope_for_function_call_and_there_are_multiple_cand let src = r#" fn main() { let _ = Bar::foo(); + ^^^ Could not resolve 'foo' in path + ~~~ The following traits which provide `foo` are implemented but not in scope: `private_mod::Foo2`, `private_mod::Foo` } pub struct Bar { @@ -817,18 +737,7 @@ fn errors_if_trait_is_not_in_scope_for_function_call_and_there_are_multiple_cand } } "#; - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::UnresolvedWithPossibleTraitsToImport { ident, mut traits }, - )) = errors.remove(0).0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - traits.sort(); - assert_eq!(traits, vec!["private_mod::Foo", "private_mod::Foo2"]); + check_errors(src); } #[test] @@ -839,6 +748,8 @@ fn errors_if_multiple_trait_methods_are_in_scope_for_function_call() { fn main() { let _ = Bar::foo(); + ^^^ Multiple applicable items in scope + ~~~ All these trait which provide `foo` are implemented and in scope: `private_mod::Foo2`, `private_mod::Foo` } pub struct Bar { @@ -866,18 +777,7 @@ fn errors_if_multiple_trait_methods_are_in_scope_for_function_call() { } } "#; - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::MultipleTraitsInScope { ident, mut traits }, - )) = errors.remove(0).0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - traits.sort(); - assert_eq!(traits, vec!["private_mod::Foo", "private_mod::Foo2"]); + check_errors(src); } #[test] @@ -886,6 +786,7 @@ fn warns_if_trait_is_not_in_scope_for_method_call_and_there_is_only_one_trait_me fn main() { let bar = Bar { x: 42 }; let _ = bar.foo(); + ^^^^^^^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } pub struct Bar { @@ -904,17 +805,7 @@ fn warns_if_trait_is_not_in_scope_for_method_call_and_there_is_only_one_trait_me } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0].0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -952,6 +843,8 @@ fn errors_if_trait_is_not_in_scope_for_method_call_and_there_are_multiple_candid fn main() { let bar = Bar { x: 42 }; let _ = bar.foo(); + ^^^^^^^^^ Could not resolve 'foo' in path + ~~~~~~~~~ The following traits which provide `foo` are implemented but not in scope: `private_mod::Foo2`, `private_mod::Foo` } pub struct Bar { @@ -980,18 +873,7 @@ fn errors_if_trait_is_not_in_scope_for_method_call_and_there_are_multiple_candid } } "#; - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::UnresolvedWithPossibleTraitsToImport { ident, mut traits }, - )) = errors.remove(0).0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - traits.sort(); - assert_eq!(traits, vec!["private_mod::Foo", "private_mod::Foo2"]); + check_errors(src); } #[test] @@ -1003,6 +885,8 @@ fn errors_if_multiple_trait_methods_are_in_scope_for_method_call() { fn main() { let bar = Bar { x : 42 }; let _ = bar.foo(); + ^^^^^^^^^ Multiple applicable items in scope + ~~~~~~~~~ All these trait which provide `foo` are implemented and in scope: `private_mod::Foo2`, `private_mod::Foo` } pub struct Bar { @@ -1031,18 +915,7 @@ fn errors_if_multiple_trait_methods_are_in_scope_for_method_call() { } } "#; - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::MultipleTraitsInScope { ident, mut traits }, - )) = errors.remove(0).0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - traits.sort(); - assert_eq!(traits, vec!["private_mod::Foo", "private_mod::Foo2"]); + check_errors(src); } #[test] @@ -1082,28 +955,17 @@ fn type_checks_trait_default_method_and_errors() { let src = r#" pub trait Foo { fn foo(self) -> i32 { + ^^^ expected type i32, found type bool + ~~~ expected i32 because of return type let _ = self; true + ~~~~ bool returned here } } fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { - expected, - actual, - .. - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected.to_string(), "i32"); - assert_eq!(actual.to_string(), "bool"); + check_errors(src); } #[test] @@ -1145,6 +1007,7 @@ fn warns_if_trait_is_not_in_scope_for_primitive_function_call_and_there_is_only_ let src = r#" fn main() { let _ = Field::foo(); + ^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } mod private_mod { @@ -1159,17 +1022,7 @@ fn warns_if_trait_is_not_in_scope_for_primitive_function_call_and_there_is_only_ } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0].0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -1178,6 +1031,7 @@ fn warns_if_trait_is_not_in_scope_for_primitive_method_call_and_there_is_only_on fn main() { let x: Field = 1; let _ = x.foo(); + ^^^^^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } mod private_mod { @@ -1192,17 +1046,7 @@ fn warns_if_trait_is_not_in_scope_for_primitive_method_call_and_there_is_only_on } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0].0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -1211,6 +1055,7 @@ fn warns_if_trait_is_not_in_scope_for_generic_function_call_and_there_is_only_on fn main() { let x: i32 = 1; let _ = x.foo(); + ^^^^^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } mod private_mod { @@ -1225,17 +1070,7 @@ fn warns_if_trait_is_not_in_scope_for_generic_function_call_and_there_is_only_on } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0].0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -1246,25 +1081,19 @@ fn error_on_duplicate_impl_with_associated_type() { } impl Foo for i32 { + ~~~ Previous impl defined here type Bar = u32; } impl Foo for i32 { + ^^^ Impl for type `i32` overlaps with existing impl + ~~~ Overlapping impl type Bar = u8; } fn main() {} "#; - - // Expect "Impl for type `i32` overlaps with existing impl" - // and "Previous impl defined here" - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - use CompilationError::DefinitionError; - use DefCollectorErrorKind::*; - assert!(matches!(&errors[0].0, DefinitionError(OverlappingImpl { .. }))); - assert!(matches!(&errors[1].0, DefinitionError(OverlappingImplNote { .. }))); + check_errors(src); } #[test] @@ -1275,25 +1104,19 @@ fn error_on_duplicate_impl_with_associated_constant() { } impl Foo for i32 { + ~~~ Previous impl defined here let Bar = 5; } impl Foo for i32 { + ^^^ Impl for type `i32` overlaps with existing impl + ~~~ Overlapping impl let Bar = 6; } fn main() {} "#; - - // Expect "Impl for type `i32` overlaps with existing impl" - // and "Previous impl defined here" - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - use CompilationError::DefinitionError; - use DefCollectorErrorKind::*; - assert!(matches!(&errors[0].0, DefinitionError(OverlappingImpl { .. }))); - assert!(matches!(&errors[1].0, DefinitionError(OverlappingImplNote { .. }))); + check_errors(src); } // See https://github.com/noir-lang/noir/issues/6530 @@ -1337,8 +1160,7 @@ fn regression_6530() { let _: Field = foo.into(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -1385,12 +1207,41 @@ fn calls_trait_method_using_struct_name_when_multiple_impls_exist_and_errors_tur } fn main() { let _ = U60Repr::::from2([1, 2, 3]); + ^^^^^^^^^ Expected type Field, found type [Field; 3] } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) - )); + check_errors(src); +} + +#[test] +fn as_trait_path_in_expression() { + let src = r#" + fn main() { + cursed::(); + } + + fn cursed() + where T: Foo + Foo2 + { + ::bar(1); + ::bar(()); + + // Use each function with different generic arguments + ::bar(()); + } + + trait Foo { fn bar(x: U); } + trait Foo2 { fn bar(x: U); } + + pub struct S {} + + impl Foo for S { + fn bar(_x: Z) {} + } + + impl Foo2 for S { + fn bar(_x: Z) {} + } + "#; + assert_no_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/turbofish.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/turbofish.rs index 3e34ea9521b5..d1bf9002ab74 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/turbofish.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/turbofish.rs @@ -1,10 +1,6 @@ -use crate::hir::{ - def_collector::dc_crate::CompilationError, - resolution::{errors::ResolverError, import::PathResolutionError}, - type_check::TypeCheckError, -}; +use crate::tests::check_errors; -use super::{assert_no_errors, get_program_errors}; +use super::assert_no_errors; #[test] fn turbofish_numeric_generic_nested_call() { @@ -66,15 +62,10 @@ fn turbofish_in_constructor_generics_mismatch() { fn main() { let _ = Foo:: { x: 1 }; + ^^^^^^^^^^^^ struct Foo expects 1 generic but 2 were given } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::GenericCountMismatch { .. }), - )); + check_errors(src); } #[test] @@ -87,21 +78,10 @@ fn turbofish_in_constructor() { fn main() { let x: Field = 0; let _ = Foo:: { x: x }; + ^ Expected type i32, found type Field } "#; - - 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"); + check_errors(src); } #[test] @@ -122,6 +102,7 @@ fn turbofish_in_struct_pattern() { #[test] fn turbofish_in_struct_pattern_errors_if_type_mismatch() { + // TODO: maybe the error should be on the expression let src = r#" struct Foo { x: T @@ -130,17 +111,11 @@ fn turbofish_in_struct_pattern_errors_if_type_mismatch() { fn main() { let value: Field = 0; let Foo:: { x } = Foo { x: value }; + ^^^^^^^^^^^^^^^^ Cannot assign an expression of type Foo to a value of type Foo 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); - }; + check_errors(src); } #[test] @@ -153,26 +128,11 @@ fn turbofish_in_struct_pattern_generic_count_mismatch() { fn main() { let value = 0; let Foo:: { x } = Foo { x: value }; + ^^^^^^^^^^^^ struct Foo expects 1 generic but 2 were given 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); + check_errors(src); } #[test] @@ -202,19 +162,10 @@ fn errors_if_turbofish_after_module() { fn main() { moo::::foo(); + ^^^^^^^ turbofish (`::<_>`) not allowed on module `moo` } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TurbofishNotAllowedOnItem { item, .. }, - )) = &errors[0].0 - else { - panic!("Expected a turbofish not allowed on item error, got {:?}", errors[0].0); - }; - assert_eq!(item, "module `moo`"); + check_errors(src); } #[test] @@ -252,22 +203,10 @@ fn turbofish_in_type_before_call_errors() { fn main() { let _ = Foo::::new(true); + ^^^^ Expected type i32, found type bool } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "i32"); - assert_eq!(expr_typ, "bool"); + check_errors(src); } #[test] @@ -312,22 +251,10 @@ fn use_generic_type_alias_with_turbofish_in_method_call_errors() { fn main() { let _ = Bar::::new(true); + ^^^^ Expected type i32, found type bool } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "i32"); - assert_eq!(expr_typ, "bool"); + check_errors(src); } #[test] @@ -371,22 +298,10 @@ fn use_generic_type_alias_with_partial_generics_with_turbofish_in_method_call_er fn main() { let _ = Bar::::new(1, 1); + ^ Expected type bool, found type Field } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "bool"); - assert_eq!(expr_typ, "Field"); + check_errors(src); } #[test] @@ -407,22 +322,10 @@ fn use_generic_type_alias_with_partial_generics_with_turbofish_in_method_call_er fn main() { let _ = Bar::::new(true, true); + ^^^^ Expected type i32, found type bool } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "i32"); - assert_eq!(expr_typ, "bool"); + check_errors(src); } #[test] @@ -440,20 +343,8 @@ fn trait_function_with_turbofish_on_trait_gives_error() { fn main() { let _: i32 = Foo::::foo(1); + ^ Expected type bool, found type Field } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "bool"); - assert_eq!(expr_typ, "Field"); + check_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs index c38e604f2c3b..a3c501142444 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs @@ -1,9 +1,4 @@ -use crate::{ - hir::{def_collector::dc_crate::CompilationError, resolution::errors::ResolverError}, - tests::assert_no_errors, -}; - -use super::get_program_errors; +use crate::tests::{assert_no_errors, check_errors}; #[test] fn errors_on_unused_private_import() { @@ -17,6 +12,8 @@ fn errors_on_unused_private_import() { } use foo::bar; + ^^^ unused import bar + ~~~ unused import use foo::baz; use foo::Foo; @@ -27,17 +24,7 @@ fn errors_on_unused_private_import() { baz(); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "bar"); - assert_eq!(item.item_type(), "import"); + check_errors(src); } #[test] @@ -52,6 +39,8 @@ fn errors_on_unused_pub_crate_import() { } pub(crate) use foo::bar; + ^^^ unused import bar + ~~~ unused import use foo::baz; use foo::Foo; @@ -62,17 +51,7 @@ fn errors_on_unused_pub_crate_import() { baz(); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "bar"); - assert_eq!(item.item_type(), "import"); + check_errors(src); } #[test] @@ -88,51 +67,37 @@ fn errors_on_unused_function() { fn foo() { + ^^^ unused function foo + ~~~ unused function bar(); } fn bar() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "foo"); - assert_eq!(item.item_type(), "function"); + check_errors(src); } #[test] fn errors_on_unused_struct() { let src = r#" struct Foo {} + ^^^ struct `Foo` is never constructed + ~~~ struct is never constructed struct Bar {} fn main() { let _ = Bar {}; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "Foo"); - assert_eq!(item.item_type(), "struct"); + check_errors(src); } #[test] fn errors_on_unused_trait() { let src = r#" trait Foo {} + ^^^ unused trait Foo + ~~~ unused trait trait Bar {} pub struct Baz { @@ -143,17 +108,7 @@ fn errors_on_unused_trait() { fn main() { } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "Foo"); - assert_eq!(item.item_type(), "trait"); + check_errors(src); } #[test] @@ -171,44 +126,28 @@ fn silences_unused_variable_warning() { fn errors_on_unused_type_alias() { let src = r#" type Foo = Field; + ^^^ unused type alias Foo + ~~~ unused type alias type Bar = Field; pub fn bar(_: Bar) {} fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "Foo"); - assert_eq!(item.item_type(), "type alias"); + check_errors(src); } #[test] fn warns_on_unused_global() { let src = r#" global foo: u32 = 1; + ^^^ unused global foo + ~~~ unused global global bar: Field = 1; fn main() { let _ = bar; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item warning"); - }; - - assert_eq!(ident.to_string(), "foo"); - assert_eq!(item.item_type(), "global"); + check_errors(src); } #[test] @@ -262,9 +201,7 @@ fn no_warning_on_inner_struct_when_parent_is_used() { assert_eq(foos[0].a, 10); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs index 917394316cf9..ee53dcbc84ff 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs @@ -1,10 +1,4 @@ -use crate::{ - hir::{ - def_collector::{dc_crate::CompilationError, errors::DefCollectorErrorKind}, - resolution::{errors::ResolverError, import::PathResolutionError}, - }, - tests::{assert_no_errors, get_program_errors}, -}; +use crate::tests::{assert_no_errors, check_errors}; #[test] fn errors_once_on_unused_import_that_is_not_accessible() { @@ -14,39 +8,13 @@ fn errors_once_on_unused_import_that_is_not_accessible() { struct Foo {} } use moo::Foo; + ^^^ Foo is private and not visible from the current module + ~~~ Foo is private fn main() { let _ = Foo {}; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( - PathResolutionError::Private { .. } - )) - )); -} - -fn assert_type_is_more_private_than_item_error(src: &str, private_typ: &str, public_item: &str) { - let errors = get_program_errors(src); - - assert!(!errors.is_empty(), "expected visibility error, got nothing"); - for (error, _) in &errors { - let CompilationError::ResolverError(ResolverError::TypeIsMorePrivateThenItem { - typ, - item, - .. - }) = error - else { - panic!("Expected a type vs item visibility error, got {}", error); - }; - - assert_eq!(typ, private_typ); - assert_eq!(item, public_item); - } - assert_eq!(errors.len(), 1, "only expected one error"); + check_errors(src); } #[test] @@ -54,12 +22,13 @@ fn errors_if_type_alias_aliases_more_private_type() { let src = r#" struct Foo {} pub type Bar = Foo; + ^^^ Type `Foo` is more private than item `Bar` pub fn no_unused_warnings() { let _: Bar = Foo {}; } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Foo", "Bar"); + check_errors(src); } #[test] @@ -68,13 +37,14 @@ fn errors_if_type_alias_aliases_more_private_type_in_generic() { pub struct Generic { value: T } struct Foo {} pub type Bar = Generic; + ^^^^^^^^^^^^ Type `Foo` is more private than item `Bar` pub fn no_unused_warnings() { let _ = Foo {}; let _: Bar = Generic { value: Foo {} }; } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Foo", "Bar"); + check_errors(src); } #[test] @@ -84,6 +54,7 @@ fn errors_if_pub_type_alias_leaks_private_type_in_generic() { struct Bar {} pub struct Foo { pub value: T } pub type FooBar = Foo; + ^^^^^^^^ Type `Bar` is more private than item `FooBar` pub fn no_unused_warnings() { let _: FooBar = Foo { value: Bar {} }; @@ -91,7 +62,7 @@ fn errors_if_pub_type_alias_leaks_private_type_in_generic() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "FooBar"); + check_errors(src); } #[test] @@ -101,6 +72,7 @@ fn errors_if_pub_struct_field_leaks_private_type_in_generic() { struct Bar {} pub struct Foo { pub value: T } pub struct FooBar { pub value: Foo } + ^^^^^ Type `Bar` is more private than item `FooBar::value` pub fn no_unused_warnings() { let _ = FooBar { value: Foo { value: Bar {} } }; @@ -108,7 +80,7 @@ fn errors_if_pub_struct_field_leaks_private_type_in_generic() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "FooBar::value"); + check_errors(src); } #[test] @@ -118,12 +90,13 @@ fn errors_if_pub_function_leaks_private_type_in_return() { struct Bar {} pub fn bar() -> Bar { + ^^^ Type `Bar` is more private than item `bar` Bar {} } } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "bar"); + check_errors(src); } #[test] @@ -132,6 +105,7 @@ fn errors_if_pub_function_leaks_private_type_in_arg() { pub mod moo { struct Bar {} pub fn bar(_bar: Bar) {} + ^^^ Type `Bar` is more private than item `bar` pub fn no_unused_warnings() { let _ = Bar {}; @@ -139,7 +113,7 @@ fn errors_if_pub_function_leaks_private_type_in_arg() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "bar"); + check_errors(src); } #[test] @@ -172,6 +146,7 @@ fn errors_if_pub_function_on_pub_struct_returns_private() { impl Foo { pub fn bar() -> Bar { + ^^^ Type `Bar` is more private than item `bar` Bar {} } } @@ -182,7 +157,7 @@ fn errors_if_pub_function_on_pub_struct_returns_private() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "bar"); + check_errors(src); } #[test] @@ -218,6 +193,7 @@ fn errors_if_pub_trait_returns_private_struct() { pub trait Foo { fn foo() -> Bar; + ^^^ Type `Bar` is more private than item `foo` } pub fn no_unused_warnings() { @@ -226,7 +202,7 @@ fn errors_if_pub_trait_returns_private_struct() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "foo"); + check_errors(src); } #[test] @@ -262,20 +238,11 @@ fn errors_if_trying_to_access_public_function_inside_private_module() { } fn main() { foo::bar::baz() + ^^^ bar is private and not visible from the current module + ~~~ bar is private } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "bar"); + check_errors(src); } #[test] @@ -293,22 +260,13 @@ fn warns_if_calling_private_struct_method() { pub fn method(foo: moo::Foo) { foo.bar() + ^^^ bar is private and not visible from the current module + ~~~ bar is private } fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "bar"); + check_errors(src); } #[test] @@ -385,22 +343,13 @@ fn error_when_accessing_private_struct_field() { fn foo(foo: moo::Foo) -> Field { foo.x + ^ x is private and not visible from the current module + ~ x is private } fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "x"); + check_errors(src); } #[test] @@ -454,20 +403,11 @@ fn error_when_using_private_struct_field_in_constructor() { fn main() { let _ = moo::Foo { x: 1 }; + ^ x is private and not visible from the current module + ~ x is private } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "x"); + check_errors(src); } #[test] @@ -481,24 +421,15 @@ fn error_when_using_private_struct_field_in_struct_pattern() { fn foo(foo: moo::Foo) -> Field { let moo::Foo { x } = foo; + ^ x is private and not visible from the current module + ~ x is private x } fn main() { } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "x"); + check_errors(src); } #[test] @@ -563,21 +494,12 @@ fn errors_if_accessing_private_struct_member_inside_comptime_context() { comptime { let foo = Foo::new(5); let _ = foo.inner; + ^^^^^ inner is private and not visible from the current module + ~~~~~ inner is private }; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "inner"); + check_errors(src); } #[test] @@ -592,6 +514,7 @@ fn errors_if_accessing_private_struct_member_inside_function_generated_at_compti use foo::Foo; #[generate_inner_accessor] + ~~~~~~~~~~~~~~~~~~~~~~~~~~ While running this function attribute struct Bar { bar_inner: Foo, } @@ -600,6 +523,8 @@ fn errors_if_accessing_private_struct_member_inside_function_generated_at_compti quote { fn bar_get_foo_inner(x: Bar) -> Field { x.bar_inner.foo_inner + ^^^^^^^^^ foo_inner is private and not visible from the current module + ~~~~~~~~~ foo_inner is private } } } @@ -608,16 +533,5 @@ fn errors_if_accessing_private_struct_member_inside_function_generated_at_compti let _ = bar_get_foo_inner(x); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "foo_inner"); + check_errors(src); } diff --git a/noir/noir-repo/compiler/wasm/package.json b/noir/noir-repo/compiler/wasm/package.json index e4a0795f0fe2..bb965244a3ed 100644 --- a/noir/noir-repo/compiler/wasm/package.json +++ b/noir/noir-repo/compiler/wasm/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "1.0.0-beta.2", + "version": "1.0.0-beta.3", "license": "(MIT OR Apache-2.0)", "main": "dist/main.js", "types": "./dist/types/src/index.d.cts", diff --git a/noir/noir-repo/compiler/wasm/src/compile.rs b/noir/noir-repo/compiler/wasm/src/compile.rs index e823f90add58..8c0359bbced0 100644 --- a/noir/noir-repo/compiler/wasm/src/compile.rs +++ b/noir/noir-repo/compiler/wasm/src/compile.rs @@ -5,7 +5,7 @@ use js_sys::{JsString, Object}; use nargo::parse_all; use noirc_artifacts::{contract::ContractArtifact, program::ProgramArtifact}; use noirc_driver::{ - add_dep, file_manager_with_stdlib, prepare_crate, prepare_dependency, CompileOptions, + CompileOptions, add_dep, file_manager_with_stdlib, prepare_crate, prepare_dependency, }; use noirc_evaluator::errors::SsaReport; use noirc_frontend::{ @@ -13,7 +13,7 @@ use noirc_frontend::{ hir::Context, }; use serde::Deserialize; -use std::{collections::HashMap, path::Path}; +use std::{collections::BTreeMap, path::Path}; use wasm_bindgen::prelude::*; use crate::errors::{CompileError, JsCompileError}; @@ -128,16 +128,21 @@ impl JsCompileContractResult { #[derive(Deserialize, Default)] pub(crate) struct DependencyGraph { pub(crate) root_dependencies: Vec, - pub(crate) library_dependencies: HashMap>, + pub(crate) library_dependencies: BTreeMap>, } +/// This map contains the paths of all of the files in the entry-point crate and +/// the transitive dependencies of the entry-point crate. +/// +/// This is for all intents and purposes the file system that the compiler will use to resolve/compile +/// files in the crate being compiled and its dependencies. +/// +/// Using a `BTreeMap` to add files to the [FileManager] in a deterministic order, +/// which affects the `FileId` in the `Location`s in the AST on which the `hash` is based. +/// Note that we cannot expect to match the IDs assigned by the `FileManager` used by `nargo`, +/// because there the order is determined by the dependency graph as well as the file name. #[wasm_bindgen] -// This is a map containing the paths of all of the files in the entry-point crate and -// the transitive dependencies of the entry-point crate. -// -// This is for all intents and purposes the file system that the compiler will use to resolve/compile -// files in the crate being compiled and its dependencies. #[derive(Deserialize, Default)] -pub struct PathToFileSourceMap(pub(crate) HashMap); +pub struct PathToFileSourceMap(pub(crate) BTreeMap); #[wasm_bindgen] impl PathToFileSourceMap { @@ -171,7 +176,7 @@ pub fn compile_program( let compiled_program = noirc_driver::compile_main(&mut context, crate_id, &compile_options, None) .map_err(|errs| { - CompileError::with_file_diagnostics( + CompileError::with_custom_diagnostics( "Failed to compile program", errs, &context.file_manager, @@ -181,7 +186,7 @@ pub fn compile_program( let optimized_program = nargo::ops::transform_program(compiled_program, expression_width); nargo::ops::check_program(&optimized_program).map_err(|errs| { - CompileError::with_file_diagnostics( + CompileError::with_custom_diagnostics( "Compiled program is not solvable", errs, &context.file_manager, @@ -207,8 +212,8 @@ pub fn compile_contract( let compiled_contract = noirc_driver::compile_contract(&mut context, crate_id, &compile_options) - .map_err(|errs| { - CompileError::with_file_diagnostics( + .map_err(|errs: Vec| { + CompileError::with_custom_diagnostics( "Failed to compile contract", errs, &context.file_manager, @@ -231,7 +236,7 @@ fn prepare_context( ::into_serde(&JsValue::from(dependency_graph)) .map_err(|err| err.to_string())? } else { - DependencyGraph { root_dependencies: vec![], library_dependencies: HashMap::new() } + DependencyGraph { root_dependencies: vec![], library_dependencies: BTreeMap::new() } }; let fm = file_manager_with_source_map(file_source_map); @@ -272,7 +277,7 @@ pub(crate) fn file_manager_with_source_map(source_map: PathToFileSourceMap) -> F // upon some library `lib1`. Then the packages that `lib1` depend upon will be placed in the // `library_dependencies` list and the `lib1` will be placed in the `root_dependencies` list. fn process_dependency_graph(context: &mut Context, dependency_graph: DependencyGraph) { - let mut crate_names: HashMap<&CrateName, CrateId> = HashMap::new(); + let mut crate_names: BTreeMap<&CrateName, CrateId> = BTreeMap::new(); for lib in &dependency_graph.root_dependencies { let crate_id = add_noir_lib(context, lib); @@ -311,8 +316,8 @@ mod test { use crate::compile::PathToFileSourceMap; - use super::{file_manager_with_source_map, process_dependency_graph, DependencyGraph}; - use std::{collections::HashMap, path::Path}; + use super::{DependencyGraph, file_manager_with_source_map, process_dependency_graph}; + use std::{collections::BTreeMap, path::Path}; fn setup_test_context(source_map: PathToFileSourceMap) -> Context<'static, 'static> { let mut fm = file_manager_with_source_map(source_map); @@ -333,7 +338,7 @@ mod test { #[test] fn test_works_with_empty_dependency_graph() { let dependency_graph = - DependencyGraph { root_dependencies: vec![], library_dependencies: HashMap::new() }; + DependencyGraph { root_dependencies: vec![], library_dependencies: BTreeMap::new() }; let source_map = PathToFileSourceMap::default(); let mut context = setup_test_context(source_map); @@ -348,7 +353,7 @@ mod test { fn test_works_with_root_dependencies() { let dependency_graph = DependencyGraph { root_dependencies: vec![crate_name("lib1")], - library_dependencies: HashMap::new(), + library_dependencies: BTreeMap::new(), }; let source_map = PathToFileSourceMap( @@ -368,7 +373,7 @@ mod test { fn test_works_with_duplicate_root_dependencies() { let dependency_graph = DependencyGraph { root_dependencies: vec![crate_name("lib1"), crate_name("lib1")], - library_dependencies: HashMap::new(), + library_dependencies: BTreeMap::new(), }; let source_map = PathToFileSourceMap( @@ -387,7 +392,7 @@ mod test { fn test_works_with_transitive_dependencies() { let dependency_graph = DependencyGraph { root_dependencies: vec![crate_name("lib1")], - library_dependencies: HashMap::from([ + library_dependencies: BTreeMap::from([ (crate_name("lib1"), vec![crate_name("lib2")]), (crate_name("lib2"), vec![crate_name("lib3")]), ]), @@ -413,7 +418,7 @@ mod test { fn test_works_with_missing_dependencies() { let dependency_graph = DependencyGraph { root_dependencies: vec![crate_name("lib1")], - library_dependencies: HashMap::from([(crate_name("lib2"), vec![crate_name("lib3")])]), + library_dependencies: BTreeMap::from([(crate_name("lib2"), vec![crate_name("lib3")])]), }; let source_map = PathToFileSourceMap( diff --git a/noir/noir-repo/compiler/wasm/src/compile_new.rs b/noir/noir-repo/compiler/wasm/src/compile_new.rs index ac2f79147b3e..37065c8f8255 100644 --- a/noir/noir-repo/compiler/wasm/src/compile_new.rs +++ b/noir/noir-repo/compiler/wasm/src/compile_new.rs @@ -1,12 +1,12 @@ use crate::compile::{ - file_manager_with_source_map, JsCompileContractResult, JsCompileProgramResult, - PathToFileSourceMap, + JsCompileContractResult, JsCompileProgramResult, PathToFileSourceMap, + file_manager_with_source_map, }; use crate::errors::{CompileError, JsCompileError}; use acvm::acir::circuit::ExpressionWidth; use nargo::parse_all; use noirc_driver::{ - add_dep, compile_contract, compile_main, prepare_crate, prepare_dependency, CompileOptions, + CompileOptions, add_dep, compile_contract, compile_main, prepare_crate, prepare_dependency, }; use noirc_frontend::{ graph::{CrateId, CrateName}, @@ -109,7 +109,7 @@ impl CompilerContext { let compiled_program = compile_main(&mut self.context, root_crate_id, &compile_options, None) .map_err(|errs| { - CompileError::with_file_diagnostics( + CompileError::with_custom_diagnostics( "Failed to compile program", errs, &self.context.file_manager, @@ -119,7 +119,7 @@ impl CompilerContext { let optimized_program = nargo::ops::transform_program(compiled_program, expression_width); nargo::ops::check_program(&optimized_program).map_err(|errs| { - CompileError::with_file_diagnostics( + CompileError::with_custom_diagnostics( "Compiled program is not solvable", errs, &self.context.file_manager, @@ -148,7 +148,7 @@ impl CompilerContext { let compiled_contract = compile_contract(&mut self.context, root_crate_id, &compile_options) .map_err(|errs| { - CompileError::with_file_diagnostics( + CompileError::with_custom_diagnostics( "Failed to compile contract", errs, &self.context.file_manager, @@ -280,7 +280,7 @@ mod test { use noirc_driver::prepare_crate; use noirc_frontend::hir::Context; - use crate::compile::{file_manager_with_source_map, PathToFileSourceMap}; + use crate::compile::{PathToFileSourceMap, file_manager_with_source_map}; use std::path::Path; diff --git a/noir/noir-repo/compiler/wasm/src/errors.rs b/noir/noir-repo/compiler/wasm/src/errors.rs index ef56dcfc911c..47927df10562 100644 --- a/noir/noir-repo/compiler/wasm/src/errors.rs +++ b/noir/noir-repo/compiler/wasm/src/errors.rs @@ -4,7 +4,7 @@ use serde::Serialize; use wasm_bindgen::prelude::*; use fm::FileManager; -use noirc_errors::FileDiagnostic; +use noirc_errors::CustomDiagnostic; #[wasm_bindgen(typescript_custom_section)] const DIAGNOSTICS: &'static str = r#" @@ -87,8 +87,7 @@ pub struct Diagnostic { } impl Diagnostic { - fn new(file_diagnostic: &FileDiagnostic, file: String) -> Diagnostic { - let diagnostic = &file_diagnostic.diagnostic; + fn new(diagnostic: &CustomDiagnostic, file: String) -> Diagnostic { let message = diagnostic.message.clone(); let secondaries = diagnostic @@ -96,8 +95,8 @@ impl Diagnostic { .iter() .map(|label| DiagnosticLabel { message: label.message.clone(), - start: label.span.start(), - end: label.span.end(), + start: label.location.span.start(), + end: label.location.span.end(), }) .collect(); @@ -116,16 +115,16 @@ impl CompileError { CompileError { message: message.to_string(), diagnostics: vec![] } } - pub fn with_file_diagnostics( + pub fn with_custom_diagnostics( message: &str, - file_diagnostics: Vec, + custom_diagnostics: Vec, file_manager: &FileManager, ) -> CompileError { - let diagnostics: Vec<_> = file_diagnostics + let diagnostics: Vec<_> = custom_diagnostics .iter() .map(|err| { let file_path = file_manager - .path(err.file_id) + .path(err.file) .expect("File must exist to have caused diagnostics"); Diagnostic::new(err, file_path.to_str().unwrap().to_string()) }) diff --git a/noir/noir-repo/compiler/wasm/src/lib.rs b/noir/noir-repo/compiler/wasm/src/lib.rs index 6753faf20096..d24b6f4ed01a 100644 --- a/noir/noir-repo/compiler/wasm/src/lib.rs +++ b/noir/noir-repo/compiler/wasm/src/lib.rs @@ -10,8 +10,8 @@ use gloo_utils::format::JsValueSerdeExt; use noirc_driver::{GIT_COMMIT, GIT_DIRTY, NOIRC_VERSION}; use serde::{Deserialize, Serialize}; -use tracing_subscriber::prelude::*; use tracing_subscriber::EnvFilter; +use tracing_subscriber::prelude::*; use tracing_web::MakeWebConsoleWriter; mod compile; @@ -21,8 +21,8 @@ mod errors; pub use compile::{compile_contract, compile_program}; // Expose the new Context-Centric API -pub use compile_new::{compile_contract_, compile_program_, CompilerContext, CrateIDWrapper}; -use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; +pub use compile_new::{CompilerContext, CrateIDWrapper, compile_contract_, compile_program_}; +use wasm_bindgen::{JsValue, prelude::wasm_bindgen}; #[derive(Serialize, Deserialize)] pub struct BuildInfo { diff --git a/noir/noir-repo/compiler/wasm/src/types/noir_artifact.ts b/noir/noir-repo/compiler/wasm/src/types/noir_artifact.ts index b7b42186c1a1..2abdea63c516 100644 --- a/noir/noir-repo/compiler/wasm/src/types/noir_artifact.ts +++ b/noir/noir-repo/compiler/wasm/src/types/noir_artifact.ts @@ -58,6 +58,8 @@ export interface ABIVariable { export interface NoirFunctionEntry { /** The name of the function. */ name: string; + /** The hash of the circuit. */ + hash?: string; /** Whether the function is unconstrained. */ is_unconstrained: boolean; /** The custom attributes applied to the function. */ @@ -96,7 +98,7 @@ export interface ProgramArtifact { /** Version of noir used for the build. */ noir_version: string; /** The hash of the circuit. */ - hash?: number; + hash?: string; /** * The ABI of the function. */ abi: Abi; /** The bytecode of the circuit in base64. */ diff --git a/noir/noir-repo/compiler/wasm/test/compiler/shared/compile.test.ts b/noir/noir-repo/compiler/wasm/test/compiler/shared/compile.test.ts index f9e37530cbcd..8c9af58fa0ae 100644 --- a/noir/noir-repo/compiler/wasm/test/compiler/shared/compile.test.ts +++ b/noir/noir-repo/compiler/wasm/test/compiler/shared/compile.test.ts @@ -27,10 +27,11 @@ export function shouldCompileProgramIdentically( // Prepare noir-wasm artifact const noirWasmProgram = noirWasmArtifact.program; expect(noirWasmProgram).not.to.be.undefined; - const [_noirWasmDebugInfos, norWasmFileMap] = deleteProgramDebugMetadata(noirWasmProgram); + const [_noirWasmDebugInfos, noirWasmFileMap] = deleteProgramDebugMetadata(noirWasmProgram); normalizeVersion(noirWasmProgram); - // We first compare both contracts without considering debug info + // We first compare both contracts without considering debug info. + // We can't expect hashes to match `nargo` because of the different order in which dependencies are visited. delete (noirWasmProgram as Partial).hash; delete (nargoArtifact as Partial).hash; expect(nargoArtifact).to.deep.eq(noirWasmProgram); @@ -38,7 +39,7 @@ export function shouldCompileProgramIdentically( // Compare the file maps, ignoring keys, since those depend in the order in which files are visited, // which may change depending on the file manager implementation. Also ignores paths, since the base // path is reported differently between nargo and noir-wasm. - expect(getSources(nargoFileMap)).to.have.members(getSources(norWasmFileMap)); + expect(getSources(nargoFileMap)).to.have.members(getSources(noirWasmFileMap)); // Compare the debug symbol information, ignoring the actual ids used for file identifiers. // Debug symbol info looks like the following, what we need is to ignore the 'file' identifiers @@ -63,16 +64,23 @@ export function shouldCompileContractIdentically( // Prepare noir-wasm artifact const noirWasmContract = noirWasmArtifact.contract; expect(noirWasmContract).not.to.be.undefined; - const [noirWasmDebugInfos, norWasmFileMap] = deleteContractDebugMetadata(noirWasmContract); + const [noirWasmDebugInfos, noirWasmFileMap] = deleteContractDebugMetadata(noirWasmContract); normalizeVersion(noirWasmContract); // We first compare both contracts without considering debug info + // We can't expect hashes to match `nargo` because of the different order in which dependencies are visited. + nargoArtifact.functions.forEach(function (f) { + delete (f as Partial).hash; + }); + noirWasmContract.functions.forEach(function (f) { + delete (f as Partial).hash; + }); expect(nargoArtifact).to.deep.eq(noirWasmContract); // Compare the file maps, ignoring keys, since those depend in the order in which files are visited, // which may change depending on the file manager implementation. Also ignores paths, since the base // path is reported differently between nargo and noir-wasm. - expect(getSources(nargoFileMap)).to.have.members(getSources(norWasmFileMap)); + expect(getSources(nargoFileMap)).to.have.members(getSources(noirWasmFileMap)); // Compare the debug symbol information, ignoring the actual ids used for file identifiers. // Debug symbol info looks like the following, what we need is to ignore the 'file' identifiers diff --git a/noir/noir-repo/cspell.json b/noir/noir-repo/cspell.json index c7ad22afa96c..3bbbede78ccc 100644 --- a/noir/noir-repo/cspell.json +++ b/noir/noir-repo/cspell.json @@ -261,7 +261,10 @@ "whitespaces", "YOLO", "zkhash", - "zshell" + "zshell", + "flamegraph", + "flamegraphs", + "lookback" ], "ignorePaths": [ "./**/node_modules/**", diff --git a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.mdx b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.mdx index 5f6a7b08ec17..c8c7894ff911 100644 --- a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.mdx +++ b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.mdx @@ -66,7 +66,7 @@ This will compile your source code into a Noir build artifact to be stored in th ```sh bb write_vk_ultra_keccak_honk -b ./target/.json -bb contract --scheme ultra_honk +bb contract_ultra_honk ``` @@ -271,11 +271,13 @@ It would be incorrect to say that a Noir proof verification costs any gas at all ::: -## A Note on EVM chains +## Compatibility with different EVM chains -Noir proof verification requires the ecMul, ecAdd and ecPairing precompiles. Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. You can find an incomplete list of which EVM chains support these precompiles [here](https://www.evmdiff.com/features?feature=precompiles). +Barretenberg proof verification requires the `ecMul`, `ecAdd`, `ecPairing`, and `modexp` EVM precompiles. You can deploy and use the verifier contract on all EVM chains that support the precompiles. -For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: +EVM Diff provides a great table of which EVM chains support which precompiles: https://www.evmdiff.com/features?feature=precompiles + +Some EVM chains manually tested to work with the Barretenberg verifier include: - Optimism - Arbitrum @@ -289,7 +291,12 @@ For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently suppo - Linea - Moonbeam -If you test any other chains, please open a PR on this page to update the list. +Meanwhile, some EVM chains chains manually tested that failed to work with the Barretenberg verifier include: + +- zkSync ERA +- Polygon zkEVM + +Pull requests to update this section is welcome and appreciated if you have compatibility updates on existing / new chains to contribute: https://github.com/noir-lang/noir ## What's next diff --git a/noir/noir-repo/docs/docs/noir/concepts/data_types/integers.md b/noir/noir-repo/docs/docs/noir/concepts/data_types/integers.md index b8a5d4980296..ff3fafa1f905 100644 --- a/noir/noir-repo/docs/docs/noir/concepts/data_types/integers.md +++ b/noir/noir-repo/docs/docs/noir/concepts/data_types/integers.md @@ -58,54 +58,6 @@ fn main(x: i16, y: i16) { Modulo operation is defined for negative integers thanks to integer division, so that the equality `x = (x/y)*y + (x%y)` holds. -## 128 bits Unsigned Integers - -The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind: -- You cannot cast between a native integer and `U128` -- There is a higher performance cost when using `U128`, compared to a native type. - -Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. - -```rust -fn main() { - let x = U128::from_integer(23); - let y = U128::from_hex("0x7"); - let z = x + y; - assert(z.to_integer() == 30); -} -``` - -`U128` is implemented with two 64 bits limbs, representing the low and high bits, which explains the performance cost. You should expect `U128` to be twice more costly for addition and four times more costly for multiplication. -You can construct a U128 from its limbs: -```rust -fn main(x: u64, y: u64) { - let z = U128::from_u64s_be(x,y); - assert(z.hi == x as Field); - assert(z.lo == y as Field); -} -``` - -Note that the limbs are stored as Field elements in order to avoid unnecessary conversions. -Apart from this, most operations will work as usual: - -```rust -fn main(x: U128, y: U128) { - // multiplication - let c = x * y; - // addition and subtraction - let c = c - x + y; - // division - let c = x / y; - // bit operation; - let c = x & y | y; - // bit shift - let c = x << y; - // comparisons; - let c = x < y; - let c = x == y; -} -``` - ## Overflows Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: diff --git a/noir/noir-repo/docs/docs/noir/concepts/traits.md b/noir/noir-repo/docs/docs/noir/concepts/traits.md index 17cc04a97518..af5b396bfb82 100644 --- a/noir/noir-repo/docs/docs/noir/concepts/traits.md +++ b/noir/noir-repo/docs/docs/noir/concepts/traits.md @@ -153,6 +153,37 @@ fn main() { } ``` +## As Trait Syntax + +Rarely to call a method it may not be sufficient to use the general method call syntax of `obj.method(args)`. +One case where this may happen is if there are two traits in scope which both define a method with the same name. +For example: + +```rust +trait Foo { fn bar(); } +trait Foo2 { fn bar(); } + +fn example() + where T: Foo + Foo2 +{ + // How to call Foo::bar and Foo2::bar? +} +``` + +In the above example we have both `Foo` and `Foo2` which define a `bar` method. The normal way to resolve +this would be to use the static method syntax `Foo::bar(object)` but there is no object in this case and +`Self` does not appear in the type signature of `bar` at all so we would not know which impl to choose. +For these situations there is the "as trait" syntax: `::method(object, args...)` + +```rust +fn example() + where T: Foo + Foo2 +{ + ::bar(); + ::bar(); +} +``` + ## Generic Implementations You can add generics to a trait implementation by adding the generic list after the `impl` keyword: diff --git a/noir/noir-repo/docs/docs/noir/modules_packages_crates/dependencies.md b/noir/noir-repo/docs/docs/noir/modules_packages_crates/dependencies.md index 22186b225988..cb8765323929 100644 --- a/noir/noir-repo/docs/docs/noir/modules_packages_crates/dependencies.md +++ b/noir/noir-repo/docs/docs/noir/modules_packages_crates/dependencies.md @@ -77,14 +77,14 @@ use lib_a; You can also import only the specific parts of dependency that you want to use, like so: ```rust -use std::hash::sha256; +use std::hash::blake3; use std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, You can import multiple items in the same line by enclosing them in curly braces: ```rust -use std::hash::{keccak256, sha256}; +use std::hash::{blake2s, blake3}; ``` We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. diff --git a/noir/noir-repo/docs/docs/noir/standard_library/black_box_fns.md b/noir/noir-repo/docs/docs/noir/standard_library/black_box_fns.md index e9392b20a92f..ed905ecb5c29 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/black_box_fns.md +++ b/noir/noir-repo/docs/docs/noir/standard_library/black_box_fns.md @@ -23,7 +23,7 @@ Here is a list of the current black box functions: - AND - XOR - RANGE -- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) +- [Keccakf1600](./cryptographic_primitives/hashes.mdx#keccakf1600) - [Recursive proof verification](./recursion.mdx) 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. diff --git a/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx b/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx index b7518fa95c10..334873e68635 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx +++ b/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -1,8 +1,7 @@ --- title: Hash methods description: - Learn about the cryptographic primitives ready to use for any Noir project, including sha256, - blake2s and pedersen + Learn about the cryptographic primitives ready to use for any Noir project keywords: [cryptographic primitives, Noir project, sha256, blake2s, pedersen, hash] sidebar_position: 0 @@ -10,23 +9,11 @@ sidebar_position: 0 import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; -## sha256 +## sha256 compression -Given an array of bytes, returns the resulting sha256 hash. -Specify a message_size to hash only the first `message_size` bytes of the input. - -#include_code sha256 noir_stdlib/src/hash/sha256.nr rust - -example: -#include_code sha256_var test_programs/execution_success/sha256/src/main.nr rust - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::sha256::sha256_var(x, 4); -} -``` +Performs a sha256 compression on an input and initial state, returning the resulting state. +#include_code sha256_compression noir_stdlib/src/hash/mod.nr rust @@ -88,17 +75,11 @@ example: -## keccak256 +## keccakf1600 -Given an array of bytes (`u8`), returns the resulting keccak hash as an array of -32 bytes (`[u8; 32]`). Specify a message_size to hash only the first -`message_size` bytes of the input. - -#include_code keccak256 noir_stdlib/src/hash/mod.nr rust - -example: +Given an initial `[u64; 25]` state, returns the state resulting from applying a keccakf1600 permutation (`[u64; 25]`). -#include_code keccak256 test_programs/execution_success/keccak256/src/main.nr rust +#include_code keccakf1600 noir_stdlib/src/hash/mod.nr rust diff --git a/noir/noir-repo/docs/docs/noir/standard_library/traits.md b/noir/noir-repo/docs/docs/noir/standard_library/traits.md index e6f6f80ff032..ed923c0707a1 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/traits.md +++ b/noir/noir-repo/docs/docs/noir/standard_library/traits.md @@ -71,7 +71,7 @@ As a general rule of thumb, `From` may be implemented in the [situations where i - The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. - The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. - The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. -- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. +- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `u128: From<[u8; 16]>`, the methods `u128::from_le_bytes` and `u128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `u128` from the same byte array. One additional recommendation specific to Noir is: - The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. diff --git a/noir/noir-repo/docs/docs/tooling/profiler.md b/noir/noir-repo/docs/docs/tooling/profiler.md new file mode 100644 index 000000000000..c31271facbb3 --- /dev/null +++ b/noir/noir-repo/docs/docs/tooling/profiler.md @@ -0,0 +1,135 @@ +--- +title: Noir Profiler +description: Learn about the Noir Profiler, how to generate execution flamegraphs, identify bottlenecks, and visualize optimizations. +keywords: [profiling, profiler, flamegraph] +sidebar_position: 0 +--- + +`noir-profiler` is a sampling profiler designed to analyze and visualize Noir programs. It assists developers to identify bottlenecks by mapping execution data back to the original source code. + +### Installation + +`noir-profiler` comes out of the box with [noirup](../getting_started/noir_installation.md). Test that you have the profiler installed by running `noir-profiler --version`. + +### Usage + +Let's start by creating a simple Noir program. All this program aims to do is zero out an array past some dynamic index. + +```rust +fn main(ptr: pub u32, mut array: [u32; 32]) -> pub [u32; 32] { + for i in 0..32 { + if i > ptr { + array[i] = 0; + } + } + array +} +``` +You can use these values for the `Prover.toml`: +```toml +ptr = 1 +array = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] +``` + +Running `nargo info` we can get some information about the opcodes produced by this program, but it doesn't give us a lot of info on its own. Compile and execute this program normally using `nargo compile` and `nargo execute`. + +### Generating an ACIR opcode flamegraph + +The program on its own is quite high-level. Let's get a more granular look at what is happening by using `noir-profiler`. + +After compiling the program, run the following: +```sh +noir-profiler opcodes --artifact-path ./target/program.json --output ./target/ +``` +Below you can see an example flamegraph with a total 387 opcodes (using `nargo` version 1.0.0-beta.2): +![ACIR Flamegraph Unoptimized](@site/static/img/tooling/profiler/acir-flamegraph-unoptimized.png) + +You should now have a flamegraph that maps ACIR opcodes to their corresponding locations in the source code. We strongly recommend generating these graphs yourself as you follow this guide. Opening the flamegraph in a browser provides a more interactive experience, allowing you to click into and examine different regions of the graph. Simply viewing the image file won't offer the same level of insight. + +We can see that the majority of opcodes come from the write to `array[i]`. Now that we have some more information about our program's bottlenecks, let's optimize it. + +#### Transform conditional writes into reads + +We can improve our circuit's efficiency using [unconstrained functions](../noir/concepts/unconstrained.md). + +Let's replace expensive array writes with array gets with the new code below: +```rust +fn main(ptr: pub u32, array: [u32; 32]) -> pub [u32; 32] { + // Safety: Sets all elements after `ptr` in `array` to zero. + let zeroed_array = unsafe { zero_out_array(ptr, array) }; + for i in 0..32 { + if i > ptr { + assert_eq(zeroed_array[i], 0); + } else { + assert_eq(zeroed_array[i], array[i]); + } + } + zeroed_array +} + +unconstrained fn zero_out_array(ptr: u32, mut array: [u32; 32]) -> [u32; 32] { + for i in 0..32 { + if i > ptr { + array[i] = 0; + } + } + array +} +``` +We chose to instead write our array inside of the unconstrained function. Then inside of our circuit we assert on every value in the array returned from the unconstrained function. + +This new program produces the following ACIR opcodes flamegraph with a total of 284 opcodes: +![ACIR Flamegraph Optimized](@site/static/img/tooling/profiler/acir-flamegraph-optimized.png) + +In the above image we searched for the ACIR opcodes due to `i > ptr` in the source code. Trigger a search by clicking on "Search" in the top right corner of the flamegraph. In the bottom right corner of the image above, you will note that the flamegraph displays the percentage of all opcodes associated with that search. Searching for `memory::op` in the optimized flamegraph will result in no matches. This is due to no longer using a dynamic array in our circuit. By dynamic array, we are referring to using a dynamic index (values reliant upon witness inputs) when working with arrays. Most of the memory operations, have now been replaced with arithmetic operations as we are reading two arrays from known constant indices. + +### Generate a backend gates flamegraph + +Unfortunately, ACIR opcodes do not give us a full picture of where the cost of this program lies. +The `gates` command also accepts a backend binary. In the [quick start guide](../getting_started/quick_start.md#proving-backend) you can see how to get started with the [Barretenberg proving backend](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg). + +Run the following command: +```sh +noir-profiler gates --artifact-path ./target/program.json --backend-path bb --output ./target +``` +`--backend-path` accepts a path to the backend binary. In the above command we assume that you have the backend binary path saved in your PATH. If you do not, you will have to pass the binary's absolute path. + +This produces the following flamegraph with 3,737 total backend gates (using `bb` version 0.76.4): +![Gates Flamegraph Unoptimized](@site/static/img/tooling/profiler/gates-flamegraph-unoptimized.png) + +Searching for ACIR `memory::op` opcodes, they look to cause about 18.2% of the backend gates. + +You will notice that the majority of the backend gates come from the ACIR range opcodes. This is due to the way UltraHonk handles range constraints, which is the backend used in this example. UltraHonk uses lookup tables internally for its range gates. These can take up the majority of the gates for a small circuit, but whose impact becomes more meaningful in larger circuits. If our array was much larger, range gates would become a much smaller percentage of our total circuit. +Here is an example backend gates flamegraph for the same program in this guide but with an array of size 2048: +![Gates Flamegraph Unoptimized 2048](@site/static/img/tooling/profiler/gates-flamegraph-unoptimized-2048.png) +Every backend implements ACIR opcodes differently, so it is important to profile both the ACIR and the backend gates to get a full picture. + +Now let's generate a graph for our optimized circuit with an array of size 32. We get the following flamegraph that produces 3,062 total backend gates: +![Gates Flamegraph Optimized](@site/static/img/tooling/profiler/gates-flamegraph-optimized.png) + +In the optimized flamegraph, we searched for the backend gates due to `i > ptr` in the source code. The backend gates associated with this call stack were only 3.8% of the total backend gates. If we look back to the ACIR flamegraph, that same code was the cause of 43.3% ACIR opcodes. This discrepancy reiterates the earlier point about profiling both the ACIR opcodes and backend gates. + +For posterity, here is the flamegraph for the same program with a size 2048 array: +![Gates Flamegraph Optimized 2048](@site/static/img/tooling/profiler/gates-flamegraph-optimized-2048.png) + +### Generate an unconstrained execution trace flamegraph + +The profiler also enables developers to generate a flamegraph of the unconstrained execution trace. For unconstrained functions Noir compiles down to Brillig bytecode, thus we will be seeing a flamegraph of Brillig opcodes, rather than ACIR opcodes. + +Let's take our initial program and simply add an `unconstrained` modifier before main (e.g. `unconstrained fn main`). Then run the following command: +```sh +noir-profiler execution-opcodes --artifact-name ./target/program.json --prover_toml_path Prover.toml --output ./target +``` +This matches the `opcodes` command, except that now we need to accept a `Prover.toml` file to profile execution with a specific set of inputs. + +We will get the following flamegraph with 1,582 opcodes executed: +![Brillig Trace Initial Program](@site/static/img/tooling/profiler/brillig-trace-initial-32.png) + +Circuit programming (ACIR) is an entirely different execution paradigm compared to regular programming. To demonstrate this point further, let's generate an execution trace for our optimized ACIR program once we have modified `main` to be `unconstrained`. + +We then get the following flamegraph with 2,125 opcodes executed: +![Brillig Trace "Optimized"](@site/static/img/tooling/profiler/brillig-trace-opt-32.png) + +In the above graph we are searching for `new_array`, which shows up zero matches in the initial program. In the unconstrained environment, the updated program essentially just adds extra unnecessary checks. Thus, we see a longer execution trace. + +`execution-opcodes` is useful for when you are searching for bottlenecks in unconstrained code. This can be especially meaningful for optimizing witness generation. Even though unconstrained execution helps us skip proving steps, we still need to compute the relevant inputs/outputs outside of the circuit before proving. diff --git a/noir/noir-repo/docs/docs/tooling/security.md b/noir/noir-repo/docs/docs/tooling/security.md index e14481efc317..8a09d231a7da 100644 --- a/noir/noir-repo/docs/docs/tooling/security.md +++ b/noir/noir-repo/docs/docs/tooling/security.md @@ -39,7 +39,7 @@ Here, the results of `factor` are two elements of the returned array. The value This pass checks if the constraint coverage of Brillig calls is sufficient in these terms. -The check is at the moment disabled by default due to performance concerns and can be enabled by passing the `--enable-brillig-constraints-check` option to `nargo`. +The check is enabled by default and can be disabled by passing the `--skip-brillig-constraints-check` option to `nargo`. #### Lookback option diff --git a/noir/noir-repo/docs/static/img/tooling/profiler/acir-flamegraph-optimized.png b/noir/noir-repo/docs/static/img/tooling/profiler/acir-flamegraph-optimized.png new file mode 100644 index 0000000000000000000000000000000000000000..7237d7868fcce6b18de88d652b9d006ea2cd71a0 GIT binary patch literal 90387 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 z z3?5SBSxRE<7k?S^>i|(0GK+E^Hj%*F8{JzyT^2_A;ED$s!r_t9O>z)$jt>{{o++2Q z2`8~ie(%am!LAzq_9NAEB9Z)CU!D;4a}JSh7m&;LES@UOb=!(obrwmVVwZ%V zT+l6;7G{{7Zc%r4igPWy8D|=Ul;R?=XAA32)p`!tuSu%_QvlE>Y#WD<(F_9%vm1_w zB{xV&BIW}*_{vT3vZg!mA$>?UIP3FkD>8{N6QmxDqeid8FG_SAdxzAXCsc$ zKn>ZWq5Vm1M(Q6PINZ1@V9gRhnB};-w#AJ>HD%IJXGpn0AvXJ~wGi&bA2I{|L$$My zQFsytY9>ykfY;aYnbI5ONqzUDn>Ii6rL(JpsBPY7`H(TYO?2Fo;^uTkd2${#y5$bXc_n`8qKmr) z^0(G;3(xzZ`5ohLqP)Jl4`RDe%Yy;iu^w9h~95x=I#Tod}EP(!c%zYb%xPs zRCyTH>iy*9^NeQTPG~m=h(#Mw?eYp2&ZrI%dhO1ecij!OUoNlqtNfEzpiYtWj=vtq zlAQgl0Pw9+92ds!55|;jk|akD!90)$pwq3}J4T^lzJ(?6#o7E$0EdhV0Pc!i%;loM zSX;FFHf)&sM+pVl^SZf6rQVNTHIk>Sx}S90Y)g}21d(+n?>W*obk#eGkz~1KIy=N`pNTdqtigF zih;%^d4YLgk;wuvXz$cL%5?4OueN^-1gH3#*A_>NBf@-4Kz z^F=|UNhUeZ=|#CU^V}*A-fjLx%^)_JaU`3VRHX@jUt&X+`3S|Z&@1*2y$`i~F>w}m zyS%2bGYovndAP*!N0EN#vrRSjbGxXKAv@`d$JR8(L_yS!x;8BQvnHh{-y-qJDl%TT z^^=MZm3SA+Wt&hUE@(5^APJE{>wft%if5hA3U#%7(bpr@)A*Fnv9IDD_1FHb^o5^O z2Lx~e2`NOB!#l=>SSC{JR6R&@;+Z1*&9KcBEucxmc&4o>vn&okm4M#>FaU9*>$;EY{Lq_uc6DxjNc2uAye1R26eZX7^nTg)h!=Q&62AR8>t3BEo*q!ez z>190Ro|lJ(n>A_|f0!(=<}O;~vzLI(7RC`Yorc>|84Qg-()yvbjM&XXgt(ncgkh)i-oZ z0G{q9>gG>IeTK)1h&OC(Rw!XpCiPx=zcPO$mwyF&?k7!Z5fS#qKW}aKgvLKRif<1P z#I3i*hp2>-Q6J_=QPx32`{x!e=ZQu{mpm4vK!|!s?O547+=;>?{zF+Y+UE61q{k6X zTdC$5{5%vKT;CNG-CJ=@-$Cg}vX>0Wxzf{G3Dp&ZMzMalRXMR6k|&z`OyD;W^bWY3 z?D2&FBTcslBCN>GoZ!*4V|sP%d~fRt*IS{c#a>4wtQn;{h=Dvnipn#=)5aRE;&_2k zN-)2;G;!d__bU%vST@mq*!7V7pwyaNWB=^9`>tKDZ$x&=J^knwT@Z79ZJswStqG?-XkSCln$SM$#>*X#{OQ2{;k(mbZW#oxw8BU0*?=IUOs zi}?QDv#)+oCc2l&ouJKDt)I_8(l|8#R7ex$JM$-TP$UYiFXt;-nJ9XebsVcra{1nM zwXKXZYk#v#Az7`1^<%n6=E`jW^qT&Hd7}P+^9BSve?VQcjsB zWJSU`QWhb>1NR~@0Muw<;%>rRD8`ZJyfsB4JUR1hE?I8t6-DlZryGj_oR%r z%@US!Q%euQ{MIq3*4X8?yA<2pENC>WB1m#DdEx?+e(MkRX1J#Orn&0dbMTmktm~## zPA2NRm7kxeVLbi2E@qw3Xv{j^{1Nc zsG!kA>CYv^(iv&mcF{bvH4DL^FUjWXzPyu4<73Q*4Uvn|yLT@$8b)G(H>_pk zUTG{#v)!g zvBzQE@PiX$Wug@EHb2^7xNg=H(2rd54eqbhRHzI4sKkwHd4m0cbnIaCh)cWfW2id| zhY(NP$4dAu`#XR4gJ@>+D&mzf!j-Wvx&pqBsSQBm&_wlTi4VKV{hp5Fs$Zk?G@^LO|G#~rv08;D~uDX_RouA>|Z3CrkOETO9rrQ~`nl(BYIqVB&DrbsCgN7#Y z^(Vgbxu7sbvVKfO!<^qJeE&6Kht&L)uQ&-&M2F!r#<1q>?`)Ziu4wM$u5 z`$$Jv*PiCh`2blwtvq^{q-EWmjRPVi^?0x}qtXdl`aL2`xhJW7qP`wcsU|o5evZm2 z?p+Kvd{bk!Wbv0vT27a6_(hDr8fvg2b%iJ&YFuk693{OPTNshyPSSOv`q+a3Z4Yt5 zeM9DLAvgF;(yZl9n1%nCH8il7qs`}IE2vP(3=5OsifU&kW3FKTn~0t2qh(T#o$Ap+ zS26{`FG4Y<^KjS5O3oqN!jb{iD013i*;}dW-f8T7%c7I>o3v-APUWvx60^7t^zED2G@Tv(}c0J>O+1F_=RdH z&?sFhOCc*+=m!U&o;$ErR#^K!uTFrh$jjKX*Y0DflmGh+<(a}Kf37o zc|f@A+vsep<7;;{umNTt67<&#INXj#c_MHpih1F_?$G>BzpT*xsUmnW}{o!C!7mCo>X9&#%of@k!0om%p03+bq2$)0~D#q zolH4kP|t6TYwslDD-)ys9uEbw-4Iqc%hk;pb<+l0ZUs=GYtz6rQYZA{o6~f7ghiHf=|`hRs#~$~Y-Q(kj$RuXGdTdg z4;gxGJx*V@T6$^SPiEhw!QjZ}^lo9eZ*7WauP6$vKYtX0HOE^!tkdd9G96Ho&{h4Y z*JoP6raj>1?}by78c>7c3Z%jBo~4$^jf?+ke#@ESO{Wb^)o{RnXz^odHA?(i`+NS>{=gB(F_z`l=(ZNpDw#+NZw9kn${iR)8nl`8&J=6=Nr_Mr*{4}4qu>u z{n^%21knaAdZbJ^8KLjJs-~P~;bDuBH>P|o_>du%uSNap@f@EEKi#g& zD+begRWGhOKmN?cEVP}7i>tR1D2duw1)3?H(kTlrl%ggk+ukyCv^u&208HeQSltK-1!+4fhs4s&*@ZOwG=Z8)V|EJm|BNi)hu zXMvp=Me~j^<(E_4TN&-gdQo&Hxw|+Qhb;FJ5w=a8mtNn@%WF~P zYfW#2VB-^2&lUGgkI{Hupih0iC)Y+9Z1Kl)_v*nr`ZKU!h&VdUOa7K}d9Ua6&^5_> zC)fVlZ^}5aL8l`bPysc7RGviGnil7qn4k<2XnXK(YlQHh3^l};NvVq_u$0I+u#c1L zVXtWnBNgH8CSMAUMT}TLw*j9}09Y(-u~xht;+q@{_rxSh$nCCuCWiZ4&pfCDDw>IP z0t=95?%QxwsK9JC1%;b zRRd-~;UNIywlw> ztE5UZ1jaY>mWCga;9owEXxsE>^g{U*UIo7Ufq!4!zb^ECU2jzMd?7x~s3oZxr|C*@ zCNCNdjjpP|F>2|@s=`pud0MB{O%3zb64EMEsvD38^cZr=4DS`zwV}~O;|35l zj8lJc;~bSC!wzGvA1V`;g7e`!sQc4S4=zru^x zTX{KlG9Om4yHN_pH*Vf^y<(*Ov`cJw6WL)*KrxvxZaGJZtzX1>t0>v@xSH&VBBD>2 zAS8f*MF681=i)ak#9#dtSA0y(_-u%6O|a;a^H`Ot`<0-GN6myN9337;%}TqRI+F(`4cgaF6ek3;J$Jg5^X|U5=qM-IZ1;gLh3Aou=fkC?ou=mqhna z$jd>ia>0Y7Hf-%-BT^mDN*6YyX=_~?*h!`coh-xaQX_e$NnrY!+rYNA0aq ze{j!W`lGD@0q~20clCO{nyL02d1*(r0azKiUg^4#sk%@fH+FA(Qw|c+amQDllhj2G z%$`0Q9T&f+Cw-7H=@J{@`+1Xr&0+)Ag5JbUPvL-1Ho+)T92)*{&TcHT0_$S`>`Tz3 z3*0eYJ3l6$Tze{ixym-54R-~R*jl^qm_=Q#OLzKTAXU-uLxKCH0fqejGnx4M1MK^y z8D=J=+>dGS>zz_-9JGUcv+L>R64isZVpueFwc?Z;tAV}9tkCczXN*`EudV&aK8}%7=#-Pft#UylmJf2)o6H#Vcg9W-Tp>c z`TsC>)=_aS+n)~(0fGjC1R75Q!Cf0kaMwU^4KBg8ad-FN?(PKl1b26WyU*!+-+jL~ z>&?uX)qiyQkgl#%wRg#`@BSRS4HR_3Q7UB_LR>0OwajuZ~P}Jo)Q1jY(7-LLF@Db7DX_jA_PdB4bmMX_xzEh4l;FV8* zrvnUO{1cxb+&jjpPZAf8T&Jm2k${8otZYo)oi@@taLNvGcJ)YrY_Q3-RPWpBzuL#K z5N21cB*$9=;v3qeT$jjUW+jd^^~sSI8%(^Ujb32qKLBTdk>Ut{u#z26bIRHJhcc3G zGUhM2U!d}4`{RAC(@2L=nxg(-#py;5wr+e;@KX#Q-e8$(DPp2^wid(f=0=5+4cujR z!My|7OUv2rrz@YebfU47`YP=SztfsH@1nOIV^R_5`kqlV`II#Sai4(gLY=b?(3D3_ z2i+_Tkee;iI?AW(E=Jmn^;SzIj)i zirh=bf?;FcYqyTms9!j*A5%uQcw8^pR-1xtCGC_41e`lz3*{T5rBu+-J@ilrfoSy3 z()<7>hs^I!d*902$f-2*XSE+zg`E{-Cxw9G*~fTkvundTc#-=FPXnb%`zd0Sg}dY5 z^5pD)d@F@3hAKuJfH+`1>U3x~QzTyONb1(HXISQ1>|&W%2~i7sFJLvFXQZDM{SE?eq^mOYiJE!I=aZFkSSw!Mq~0cn-Wu9k`TQErv6}y z!-;3vDJ5>rT3OicHe06;^F0d^k`F+BO4Qw53#@>CfzE8L7g_H4YKA6${``4?EENd` z+SGCKqJN2@#pGf7-H+O@N#onxQJ&C=yAwv!cqqd5$bQDS1mTs-jQ&UKNM0;nV6%h^ zJJ40XNY2NNaM$N_o@@#qUAW>#A^m+B!^W&92q~om(DPbMRPxuio9Mp3QS}#>b8Kta z-uw(4a5iqoLi8PQ#Kt{F#DAb;i81TEY6c|7A2d<>@|P8)ZX|itNM}j@&YO(<^9b(n z9nsb$b$6{+#Sf9xR$o~B$%{Pf+mJhw9WmHHpcq;WXh>p{b(}19bfN}jeSCTN+wTFf z+65`o&jOoI>J_IE6WEpJV%{b!3d)Weg+Gkm?Q2>?T1PGpDCNx)S>?mu$&=F!pP=C6 zbZyAKsqZPWy_&?4MLVzOSa3Ls)n_QTvecd!NqY5BU9G%gMeDegR3WX!b@Y!^2!~7< z&5`sCpXVc$p?49-F;t5rGnLTy0hkyil*Qc^e~Y57cTryzt?n6?KLAId6c^v!HbNL) zx>CsyDO=jq+SnzZCzcV}haDlOa-UC(%hhhxZZDxL<8;1TLbiJ;L&u?nj%OF|W?VxY z`U|}su^x$C?qI5bEc^Z<+{tCma&&=He=1NHFU>oT{7?1gILi~IM*EPv6a*ka32=2F z<|b}gb6dXcNudglZ*9E)zS?e-lcMCEuFu1)O9Iij0wC(NjRBvn(v}Y%kh-l(HoP1P zT%Ns((;yVn34f_B2GTNM*}&>*cRK%;J$%ki>IYt5%v|BpZAp*ro&6}>rW)OUD;OP^ z0$gToJJ>JYnhS^d3GSV!)ZU_oz$Y_(E7**Wvqga$ZXez+?P)-RfOxx{+Mgb$J_%(lm|ZA)Cq)vdFD)k~7~ z*z&JJ7xj0+ z4aG_5g;Oqlr@IGoUI;#tp4$n+4RBLQWMC1GUAy1tek-R&z`@EcJ%4(^{d%F*!!4xqLn9j?_$i`MwiK!IXb?`d31#2*?mKFb4upEgOz3lVzo# zTsfQLIB2SA&3TXSfAxU^T8zxg=E&+%jli$ZL6Aii-s6i@KXR+Lx$b#IvFd&N7Lsa%;b zA}x4qkt%~8iNd+M(jbg|q4sITkE6W668e`D>n`C*}a z^VF(|nX1LXm)996p0Aw2S|F|48hi97P=c4i*=cu>tztyqs)0-m-{g~#-Qnd}vSpRW z?|lgR4l9nql3uARd&}(zEQIpQ9-s5si+`~I7I@Fn{vL9GGzRg3Fy;`B@Z+NH z(zS!#Qj?XO!rq!-wfpqQ6VPxV{Sx9k54HQmeCon|#2^U6^0fGo$x%DlExV|SgvTz= zORVzSq?jz7ZuoA09z?p@p=h8$d&bpVVfdJENvB#}aCMbBQtC^ET$$23LwCrD9`6OI z7_3-;A{|z@I0(`nQ?Z_#+shwNalY)L2%N1(NK(jnkH2O!8w}usTlwOoj!vZ2;0=}+ zto0>J(@NI#m}~h`_?1J*@34c}+?1)w6Yd&hLz09qqWmNeSCp1o}uQKmaz zR||w==YLp!x|m>g`ok$M0yogg&`M`j^cj;jds0a`JA}TbzO6kmA`N%4B>>#dQd4&m z1O&u|cC5O*sRzxSOguV?(bsby5ssi^S#v2~MEvE!kn0#PdVb<&sVsj~nV|QQ5FaD# zL|vr>-Q|l*Y@#Wmi{`+Fo#I~l9nLq&1{oM6H>VVl<|{$c8`jWuTF190l-pnxAf=Kd z3_LD};o?@hK~y7YLTX<}yV;&hLR@(`E)6I^c-uVS&ecumlejof9rhMu1LA*Fg`%mLB+-eN8h}tK)g^W42D#dzJ*dj_PS4-O*bmeed|b z`du>Di;zd=93)S)GF)R{+^YOh&u+cG4>L|nGLs>AH_vv)f9)6}BRCR-Cz&6Ap*BLD z4;|!)vJs<0g;rnx&q3$|GCVTnGND)`Pq8}YOw1+g8iI=+juD&A_@;jV$*XUXUhOGh z*m^u}rcMLGvkqyF0iUK*Eh&y95MwK>eJp1b?ubm~(&RM@mmWFVlSZ>mw$iv_bfk#P z-onV3&Ufqs>bAF`^E+8sNU8AHks`hOC^>}2Y3~^WPLeHebUP8o&+8U8J5#KfXFQ(E zrecsX+#hRC{UuhWU4(8T$v@>xtX)&?_uqeyt^CuYaH07W)<=lsm@DWDf zH2MiN_x%?R^K7PX5=P>^*ChWjFF|?HdD?n6p3U+Pn(~Uon(Y~Fg#6e)MTENJu;Me(}GO7^3WheKM@mvnaJ;a93{H>C;0V9>)!p&NKEskYpvP{ z@-5a!OwK73%6@&?C7r$E6?79in4lkp(Gsc$>x}#4{ZvshSFO1zu1m{qIKSV-eEdbP zC(fR$B%CxwtRM6mnL}K5D7}0>bhtr^ZD8?ca$?*4#HRs;(z^Mx#auQWh;i%8EB)O^ zJjw#g`KnJN1854-a)na9M3V5(5d_FjGIrcGsi^~H*u~BUx#;!q2e_J$5tLG@Mn-~R z_cBd4`bN?%yk(T4MN2;@|Iud_V(VYLSUle*A!N`QM5LsO{5)Sw5O`V?PkJ_h0}Pn~ zbsXk+Y`YHaWPD_Nzg!gB%t3shh!+B4Zy)_BK6>P>(+&KDpZ1Ij6k$j6u=4?wb3hKu zYzrW$z13af79Q@r*R9*N(2V5MWW&mgXprY9kY~r_fWTWaD#BZcVYXcBKE@LhQoe^^ zvYa>Pjz3S>C=@xNC{9y@p0OarU=WZ|o7m*aSI~o!Obz7Z%wq2RfMjKxhCO`U5mEtG zNe#jizG>H9zNh}!cYml^#BQuSE-KxMj6hjQNkM*`Iuno;AVU^3QqF&q$@Nut+uby{ zJjyh{ce2bQT?&~5#P}-Q(^(wUsM5}pXeO+a*EQU8Dfekj$S>CbU2u)IC%{QOb~hR?lnY z`Sp3@HU&8FF&Iy-=)5$T0y#j+l#`ftOiK{>aog|`Bcx_Qt)L6Rg^$}Mq=nm!MJ}`b zWx_vK-Zx4E<9pgA0wJ%Kvh+`ycv|CVzsru?VwC|<8W9lg7mbe~*^;u{ofk(ga_zhw z7o8azB(;-zBTk1qYZEmCk4;Gh$gu%629WF?8k`R3dAd`l&190Fy%3|(LYGtpao8=5 zmvLao!GGsJ4}?OaJO%F+b83t7YP?ZqJDZri73nZd+|^1@Nii8E8Wfa+fh26tx8+O23ydqEQ6hO4i)#zhJd*FF5O+Vc-&+We> zVYXyH-9=+hhP`cAvYNDCYRNe3`c0&>x8F~&n#Tm`{F1UQ#!YWy;Cy704xndLj=dEKl(Ww z#I^v04yAKkP{5au;$j#lew-8T!r7mh!2>*6*jD@&#L&18rr6VO^h-&l?5Qdp?TGLC zKKLnnnCAN$0ziZQhkwz$%dNlhL$);PP91uFl6b*cg_L8%KVDoq`=THhH46Ba3 zjq^_C`FwzAm|~oc?)QRVSBI}Qum)W8jYo_4Vbm3vbJ4SsqPl2mCsjEm`XNAT2_9f4AG*OiRJ0;ylht=YMAx_GqkBD_{Wp5~@g0 zhD_M!QMV03!*<||M-~>P%}#aS?WZY=kdq{yGer|?U5 z_t8dNSgFxfea0Z+oFPsfqtnc_vxzuHIjeBKV>SLIHN$ba zOGfH`YDn!^&y^~205kk^Dbtr2Q)W?b+R)*kX?WTq8W7_ENg4iq+hgULE`U6UzPt_p z(6-I}D#xb|=1rL1#63FB6sD#ZyV#lO{rLm4-jMj$41U@1i?LX7E=AonxK zM~t37v_eUlNW#B)&?rr|X`SCXIb}q8Y}iq(R&p)nL-u>&EC}j!J(fQdwnFMFX+UZN zIdaWRuOeD>dw4&&_czG$wO)^o-Ar@BZ34NW++|#iGv2mBa43Uwk0fPWh7pL^Iw?@! z_rw;HLwyHLq=&lwI2r^H4t1O!B{FR*`BSlVpyMRb9JdI|&XLd^x+1b~0P`bqNZ57X zrP3i036VHkaXOW3F*V8qch7*BW0R|9IWfm{+3zUreY-)BCIBw5_OvPk|hcI*J2uFLeChp_CYw&X)_@?O^#TD2M5}1nP3eHp>ihI3 z%+y`$mw5f$TJE)`QhT`boZjtrw!75#9i+)7nd;M)S>?aZ=nD5)(#A&Q;!-V|uaQ5N zt#i@+(b;I)|CYbb9%H`Tq-?1i13GFUPkXe_E@~XYJK8Eb@|a%lp-%595}Gv)u>Smq zwb=(pBJrXzH$Ga5uJ9#vYv;aP_}Jalqke7fbe{4FP!@~Tu}83Kz>4QIg;;AvYgmw6 zd1N>hx3`q3lcsw2EC|Z-lfl)NzhyNl*~7@+ea}!&IbKgNHH=Q!lzrCHxBk;sdEki3 z({jObk`9BAhrQ%2U{>bOho4@FEMBn&PJB-|6i8FgvyQIMP2UyK(9WfqAe$~@n6Iq1 z@*%C!eW6jX?r@cR;*&YjMem+tCB%D+*8L%R$AbSQ1b*-9_5Ja9t(A6CLB3O(?YG*Z z&)H(7JV&hXzM|?Y?;;8i@fngDzZZV@*qUP0gZ~MU8b1xDRjL+WsAzj8EA4U@4Z8~d z5VNovKpJgE-^x1N8e*DH$hw9qi$kRg!)MuBxLr-8L=+HrU)HRW-f(N=N_0A7S+)RX zr181irusE2&m^`?oLAI}w6Axmii+=#@b&~qE^<#hk@l&05uRcpQHF48RFvFeYgy!Z z@8DVe-pZ$~_}F*a(UADVO5<;hVK4oCo1ByWLwE7)j%Lv9J}nP=+go`rkc zx@a1;`%p-jRJ#vR>)I)@gq@nM_n*hG=lCIwtYQCwpr6tS$%FtarIUAY}Y;H4kY`9AG9SlshB{CNe=+YQ-BGox1o zf9_`s1{8&;xiHJZut(_%5ixY>I2Cp@f03`!#DmXF`ocfQ1ZZ>S-*e4q;n#gRxUkbM z9~Y+OH9N|2HfyEcix9f%sI27BM;WwqnHgW@tRE}Rq1#HC^msQnA$GLbp z>bUWmo*Bew=b)-%RHWPaB|E}R7r6d%+mC{2NEd~B_mW-6CsrTxt-)#BtOpuDO*1`or>os=%c1&dU zSP$mXrq!3{GsYF340o9o0+R&AlpA%fbjuoI|tc-8@d zW44@U)%QLK$nbbo8bQVnM7QE|GsHu1YCb~C{c=Il;w6I zZ#!<&cUy!I#$nlh@{8Wb#pzCbYwIRc!lk`p`Yn9kYnL+;BD=&2eVB1)^*MjZq!n{e z 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 R{SSaId1)o7A_+sk{|~h518)ET literal 0 HcmV?d00001 diff --git a/noir/noir-repo/docs/static/img/tooling/profiler/acir-flamegraph-unoptimized.png b/noir/noir-repo/docs/static/img/tooling/profiler/acir-flamegraph-unoptimized.png new file mode 100644 index 0000000000000000000000000000000000000000..d807539dcc56cf5bf6b9f894e2e47e5e2a443749 GIT binary patch literal 90830 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

4eg{j`CvW~lsetVfBLgO^ zM~T%NQblC)SP&f7gm!>+!*c$og#QI2@t2?{{v&Qw=e9Q}ygAM0>Zer)DC&CA}9tQ^oQB0Cw9rc* z;Nn2%Y`-Sp^M9_Pfc?+ENy+CA0w2ACz^Pibwaq*3&*~o2Ww6({CQ_uZ*F>Us5^fL< zz^5YfV_H)z3PWAIPhd7+|L?!J$Y1v{>IWRo7<6w$mVovI%1^#Wnez@upgca@ zYm3a4Lta3Ao$mjBVZUw@C;kRPqqvIjllmz9-T@QTv^Q^=JdkAUxzNDiYrg)GxPON) zj^{Oj@MD%cDgT_-zG)6MGx&ME>{95smT;?TR%yA$@l8C@^Jm-`)d15aD`+1?); z^%BH&p!^xoF4%G9vY`f1MgF*hYr(Ba&GyBI^{$9EoLfM3*wumpuD)`yDziAma$`Qt z^ZoH#Ov>uXSQc9)EuU`hgZSi$6R~q8dd)nr(fub$)pZN5&n;5mYPq1`u2bf#^z6t~wiX8Li3QLUEs}=>k*i0uQ{=_!m*#_EacrY^VYq3ha#0yjF zj6ah>BN+ez29J>s1xkG

5jXK#i4Cf|Q>?VY$rbkhokqzp`uJ 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 KT)B+#*Z&8&^A!vL literal 0 HcmV?d00001 diff --git a/noir/noir-repo/docs/static/img/tooling/profiler/gates-flamegraph-optimized.png b/noir/noir-repo/docs/static/img/tooling/profiler/gates-flamegraph-optimized.png new file mode 100644 index 0000000000000000000000000000000000000000..ea445d1950fcb1a60ffb3b60d6e02ba4ca9c501f GIT binary patch literal 34504 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 zⅆ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 ixbrm%;T8WPftb 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 "Open with Codespaces". It should take a few seconds to load, and you're ready to go. :::info -If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. +If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.2/noir/concepts/traits.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.2/noir/concepts/traits.md index 13818ecffac7..17cc04a97518 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.2/noir/concepts/traits.md +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.2/noir/concepts/traits.md @@ -111,6 +111,48 @@ fn foo(elements: [T], thing: U) where } ``` +## Invoking trait methods + +As seen in the previous section, the `area` method was invoked on a type `T` that had a where clause `T: Area`. + +To invoke `area` on a type that directly implements the trait `Area`, the trait must be in scope (imported): + +```rust +use geometry::Rectangle; + +fn main() { + let rectangle = Rectangle { width: 1, height: 2}; + let area = rectangle.area(); // Error: the compiler doesn't know which `area` method this is +} +``` + +The above program errors because there might be multiple traits with an `area` method, all implemented +by `Rectangle`, and it's not clear which one should be used. + +To make the above program compile, the trait must be imported: + +```rust +use geometry::Rectangle; +use geometry::Area; // Bring the Area trait into scope + +fn main() { + let rectangle = Rectangle { width: 1, height: 2}; + let area = rectangle.area(); // OK: will use `area` from `geometry::Area` +} +``` + +An error will also be produced if multiple traits with an `area` method are in scope. If both traits +are needed in a file you can use the fully-qualified path to the trait: + +```rust +use geometry::Rectangle; + +fn main() { + let rectangle = Rectangle { width: 1, height: 2}; + let area = geometry::Area::area(rectangle); +} +``` + ## Generic Implementations You can add generics to a trait implementation by adding the generic list after the `impl` keyword: @@ -325,20 +367,6 @@ let x: Field = Default::default(); let result = x + Default::default(); ``` -:::warning - -```rust -let _ = Default::default(); -``` - -If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be -arbitrarily selected. This occurs most often when the result of a trait function call with no parameters -is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type, -always refer to it via the implementation type's namespace - e.g. `MyType::default()`. -This is set to change to an error in future Noir versions. - -::: - ## Default Method Implementations A trait can also have default implementations of its methods by giving a body to the desired functions. diff --git a/noir/noir-repo/docs/docs/tooling/cspell.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/cspell.json similarity index 55% rename from noir/noir-repo/docs/docs/tooling/cspell.json rename to noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/cspell.json index e8126ba0874b..c60b0a597b10 100644 --- a/noir/noir-repo/docs/docs/tooling/cspell.json +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/cspell.json @@ -1,5 +1,5 @@ { "words": [ - "lookback" + "Cryptdoku" ] } diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-oracle.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-oracle.md new file mode 100644 index 000000000000..821e1f95c04e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-oracle.md @@ -0,0 +1,57 @@ +--- +title: Oracles +description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations. +keywords: + - Noir Programming + - Oracles + - JSON-RPC + - Foreign Call Handlers + - Constrained Functions + - Blockchain Programming +sidebar_position: 1 +--- + +If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs. + +![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg) + +A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source? + +Oracles are functions that provide this feature. + +## Use cases + +An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method. + +Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md). + +In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles. + +## Constraining oracles + +Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. + +To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have an oracle call like this: + +```rust +#[oracle(getNoun)] +unconstrained fn get_noun(address: Field) -> Field +``` + +This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract. + +In short, **Oracles don't prove anything. Your Noir program does.** + +:::danger + +If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained! + +::: + +## How to use Oracles + +On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. + +In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they match the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. + +If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-recursion.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-recursion.md new file mode 100644 index 000000000000..df8529ef4e02 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-recursion.md @@ -0,0 +1,176 @@ +--- +title: Recursive proofs +description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency. + +keywords: + [ + "Recursive Proofs", + "Zero-Knowledge Programming", + "Noir", + "EVM Blockchain", + "Smart Contracts", + "Recursion in Noir", + "Alice and Bob Guessing Game", + "Recursive Merkle Tree", + "Reusable Components", + "Optimizing Computational Resources", + "Improving Efficiency", + "Verification Key", + "Aggregation", + "Recursive zkSNARK schemes", + "PLONK", + "Proving and Verification Keys" + ] +sidebar_position: 1 +pagination_next: how_to/how-to-recursion +--- + +In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: + +```js +function factorial(n) { + if (n === 0 || n === 1) { + return 1; + } else { + return n * factorial(n - 1); + } +} +``` + +In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack: + +```md + Is `n` 1? <--------- + /\ / + / \ n = n -1 + / \ / + Yes No -------- +``` + +In Zero-Knowledge, recursion has some similarities. + +It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid. + +This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism. + +## Examples + +Let us look at some of these examples + +### Alice and Bob - Guessing game + +Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins. + +So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed. + +This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. + +As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". + +She can then generate a proof that she verified his proof, and so on. + +```md + Did you fail? <-------------------------- + / \ / + / \ n = n -1 + / \ / + Yes No / + | | / + | | / + | You win / + | / + | / +Generate proof of that / + + / + my own guess ---------------- +``` + +### Charlie - Recursive merkle tree + +Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count! + +If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such: + +```md + abcd + __________|______________ + | | + ab cd + _____|_____ ______|______ + | | | | + alice bob charlie daniel +``` + +Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes. + +### Daniel - Reusable components + +Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit. + +He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup. + +## What params do I need + +As you can see in the [recursion reference](noir/standard_library/recursion.mdx), a simple recursive proof requires: + +- The proof to verify +- The Verification Key of the circuit that generated the proof +- A hash of this verification key, as it's needed for some backends +- The public inputs for the proof + +:::info + +Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. + +So, taking the example of Alice and Bob and their guessing game: + +- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit +- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. + +We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. + +::: + +## Some architecture + +As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples: + +### Adding some logic to a proof verification + +This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections: + +- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify) +- A `guessing` section, which is basically the logic part where the actual guessing happens + +In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on. + +### Aggregating proofs + +In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere. + +To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits: + +- A `main`, non-recursive circuit with some logic +- A `recursive` circuit meant to verify two proofs in one proof + +The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day. + +### Recursively verifying different circuits + +Nothing prevents you from verifying different circuits in a recursive proof, for example: + +- A `circuit1` circuit +- A `circuit2` circuit +- A `recursive` circuit + +In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received) + +## How fast is it + +At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. + +Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. + +## How can I try it + +Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-writing-noir.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-writing-noir.md new file mode 100644 index 000000000000..b4265a14dbf4 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-writing-noir.md @@ -0,0 +1,208 @@ +--- +title: Thinking in Circuits +description: Considerations when writing Noir programs +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 proof size and proving time, so from a product point of view this should be kept as low as possible. + +Whilst writing efficient code for web apps and Solidity have some 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 + +Programs written in anything from pseudo code to C, can be translated into Noir. A Rust program written for execution can be readily ported to Noir thanks to the similarities in syntax. + +:::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: + - Conceptually 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, the `bb` binary has a `gates` option). +::: + +### Numerical types + +As mentioned earlier Noir has many familiar integer types (eg `i8`, `u64`). Ideally after bringing a program into Noir, proving/verifying of its execution just works where needed: client/server side, on an evm, or on the Aztec network. + +A program optimized for execution may leverage the binary representations of integers, reducing the number of clock cycles, and thus time of execution. +The cryptography in a proving backend makes use of a `Field` type, and leveraging this lower level type correctly can reduce gate count, and thus proof size and proving time. + +In some instances simply replacing the integer type with a `Field` could save on some range checks (and hence gates). +Note: when casting a `Field` to an integer type, the value is converted based on the integer binary representation. Eg a Field variable with a value of 260 `as u8` becomes 4 + +### `Field`s for efficiency + +`Field` types have their own underlying representation that is efficient for cryptography, which is different to binary representations efficient for CPUs. So, mathematically speaking, things like bitwise operations do not directly translate to fields. That said, the same outcome can be achieved if wanting to use the Field type as a number with lower overhead. + +For instance shift (`<<`) and or (`|`) work seamlessly with integer types (bit-packing `u8`'s into a `u16`): +``` + high as u16 << 8 | low as u16 +``` + +More efficiently with `Field` types, the equivalent is: +``` + low.assert_max_bit_size::<8>(); // ensure Field values could be represented as 8 bit numbers + high.assert_max_bit_size::<8>(); + (high * 2.pow_32(8) + low) +``` +(Note, the power of two can instead be a constant (256) or global evaluated at compile time) + +The first snippet is good for compatibility when using existing code, converting to the latter can help optimize frequently used functions. + +:::tip +Where possible, use the `Field` type for values. Writing code with smaller value types and bit-packing strategies will result in MORE gates +::: + +### 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. +::: + +### Reduce what is inside loops and conditional logic + +Putting less logic inside an `if` (`else`, etc) paths, or inside a loop, translates to less gates required to represent the program. The compiler should mostly take care of this. + +A loop duplicates the gates for each iteration of the loop, or put another way, "unrolls" the loop. Any calculations/calls that are unchanged in the loop should be calculated once before, and the result used in the loop. + +An `if` statement is "flattened" and gates created for each path even if execution uses only one path. Furthermore, there are additional operations required for each path. Sometimes this can have a multiplying effect on the operations in the `if` and `else` etc. + +:::tip +Only have essential computation inside conditional logic and loops, and calculate anything else once (before, or after, depending). +::: + +### 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-v1.0.0-beta.3/getting_started/noir_installation.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/noir_installation.md new file mode 100644 index 000000000000..05f036d4f6d6 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/noir_installation.md @@ -0,0 +1,106 @@ +--- +title: Standalone Noir Installation +description: There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains how to specify which version to install when using noirup, and using WSL for windows. +keywords: [ + Installation + Nargo + Noirup + Binaries + Compiling from Source + WSL for Windows + macOS + Linux + Nix + Direnv + Uninstalling Nargo + ] +sidebar_position: 2 +--- + +Noirup is the endorsed method for installing Nargo, streamlining the process of fetching binaries or compiling from source. It supports a range of options to cater to your specific needs, from nightly builds and specific versions to compiling from various sources. + +### Installing Noirup + +First, ensure you have `noirup` installed: + +```sh +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +### Fetching Binaries + +With `noirup`, you can easily switch between different Nargo versions, including nightly builds: + +- **Nightly Version**: Install the latest nightly build. + + ```sh + noirup --version nightly + ``` + +- **Specific Version**: Install a specific version of Nargo. + + ```sh + noirup --version + ``` + +### Compiling from Source + +`noirup` also enables compiling Nargo from various sources: + +- **From a Specific Branch**: Install from the latest commit on a branch. + + ```sh + noirup --branch + ``` + +- **From a Fork**: Install from the main branch of a fork. + + ```sh + noirup --repo + ``` + +- **From a Specific Branch in a Fork**: Install from a specific branch in a fork. + + ```sh + noirup --repo --branch + ``` + +- **From a Specific Pull Request**: Install from a specific PR. + + ```sh + noirup --pr + ``` + +- **From a Specific Commit**: Install from a specific commit. + + ```sh + noirup -C + ``` + +- **From Local Source**: Compile and install from a local directory. + + ```sh + noirup --path ./path/to/local/source + ``` + +## Installation on Windows + +The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. However, it is available by using Windows Subsystem for Linux (WSL). + +Step 1: Follow the instructions [here](https://learn.microsoft.com/en-us/windows/wsl/install) to install and run WSL. + +step 2: Follow the [Noirup instructions](#installing-noirup). + +## Setting up shell completions + +Once `nargo` is installed, you can [set up shell completions for it](setting_up_shell_completions.md). + +## Uninstalling Nargo + +If you installed Nargo with `noirup`, you can uninstall Nargo by removing the files in `~/.nargo`, `~/nargo`, and `~/noir_cache`. This ensures that all installed binaries, configurations, and cache related to Nargo are fully removed from your system. + +```bash +rm -r ~/.nargo +rm -r ~/nargo +rm -r ~/noir_cache +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/project_breakdown.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/project_breakdown.md new file mode 100644 index 000000000000..e442e377040f --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/project_breakdown.md @@ -0,0 +1,159 @@ +--- +title: Project Breakdown +description: + Learn about the anatomy of a Nargo project, including the purpose of the Prover TOML + file, and how to prove and verify your program. +keywords: + [Nargo, Nargo project, Prover.toml, proof verification, private asset transfer] +sidebar_position: 1 +--- + +This section breaks down our hello world program from the previous section. + +## Anatomy of a Nargo Project + +Upon creating a new project with `nargo new` and building the in/output files with `nargo check` +commands, you would get a minimal Nargo project of the following structure: + + - src + - Prover.toml + - Nargo.toml + +The source directory _src_ holds the source code for your Noir program. By default only a _main.nr_ +file will be generated within it. + +### Prover.toml + +_Prover.toml_ is used for specifying the input values for executing and proving the program. You can specify `toml` files with different names by using the `--prover-name` or `-p` flags, see the [Prover](#provertoml) section below. Optionally you may specify expected output values for prove-time checking as well. + +### Nargo.toml + +_Nargo.toml_ contains the environmental options of your project. It contains a "package" section and a "dependencies" section. + +Example Nargo.toml: + +```toml +[package] +name = "noir_starter" +type = "bin" +authors = ["Alice"] +compiler_version = "0.9.0" +description = "Getting started with Noir" +entry = "circuit/main.nr" +license = "MIT" + +[dependencies] +ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} +``` + +Nargo.toml for a [workspace](../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +#### Package section + +The package section defines a number of fields including: + +- `name` (**required**) - the name of the package +- `type` (**required**) - can be "bin", "lib", or "contract" to specify whether its a binary, library or Aztec contract +- `authors` (optional) - authors of the project +- `compiler_version` - specifies the version of the compiler to use. This is enforced by the compiler and follow's [Rust's versioning](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field), so a `compiler_version = 0.18.0` will enforce Nargo version 0.18.0, `compiler_version = ^0.18.0` will enforce anything above 0.18.0 but below 0.19.0, etc. For more information, see how [Rust handles these operators](https://docs.rs/semver/latest/semver/enum.Op.html) +- `description` (optional) +- `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 + +This is where you will specify any dependencies for your project. See the [Dependencies page](../noir/modules_packages_crates/dependencies.md) for more info. + +`./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or +verifier contract respectively. + +### main.nr + +The _main.nr_ file contains a `main` method, this method is the entry point into your Noir program. + +In our sample program, _main.nr_ looks like this: + +```rust +fn main(x : Field, y : Field) { + assert(x != y); +} +``` + +The parameters `x` and `y` can be seen as the API for the program and must be supplied by the prover. Since neither `x` nor `y` is marked as public, the verifier does not supply any inputs, when verifying the proof. + +The prover supplies the values for `x` and `y` in the _Prover.toml_ file. + +As for the program body, `assert` ensures that the condition to be satisfied (e.g. `x != y`) is constrained by the proof of the execution of said program (i.e. if the condition was not met, the verifier would reject the proof as an invalid proof). + +### Prover.toml + +The _Prover.toml_ file is a file which the prover uses to supply the inputs to the Noir program (both private and public). + +In our hello world program the _Prover.toml_ file looks like this: + +```toml +x = "1" +y = "2" +``` + +When the command `nargo execute` is executed, nargo will execute the Noir program using the inputs specified in `Prover.toml`, aborting if it finds that these do not satisfy the constraints defined by `main`. In this example, `x` and `y` must satisfy the inequality constraint `assert(x != y)`. + +If an output name is specified such as `nargo execute foo`, the witness generated by this execution will be written to `./target/foo.gz`. This can then be used to generate a proof of the execution. + +#### Arrays of Structs + +The following code shows how to pass an array of structs to a Noir program to generate a proof. + +```rust +// main.nr +struct Foo { + bar: Field, + baz: Field, +} + +fn main(foos: [Foo; 3]) -> pub Field { + foos[2].bar + foos[2].baz +} +``` + +Prover.toml: + +```toml +[[foos]] # foos[0] +bar = 0 +baz = 0 + +[[foos]] # foos[1] +bar = 0 +baz = 0 + +[[foos]] # foos[2] +bar = 1 +baz = 2 +``` + +#### Custom toml files + +You can specify a `toml` file with a different name to use for execution by using the `--prover-name` or `-p` flags. + +This command looks for proof inputs in the default **Prover.toml** and generates the witness and saves it at `./target/foo.gz`: + +```bash +nargo execute foo +``` + +This command looks for proof inputs in the custom **OtherProver.toml** and generates the witness and saves it at `./target/bar.gz`: + +```bash +nargo execute -p OtherProver bar +``` + +Now that you understand the concepts, you'll probably want some editor feedback while you are writing more complex code. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/quick_start.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/quick_start.md new file mode 100644 index 000000000000..7b4524f6b8e4 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/quick_start.md @@ -0,0 +1,127 @@ +--- +title: Quick Start +tags: [] +sidebar_position: 0 +--- + +## Installation + +### Noir + +The easiest way to develop with Noir is using Nargo the CLI tool. It provides you the ability to start new projects, compile, execute and test Noir programs from the terminal. + +You can use `noirup` the installation script to quickly install and update Nargo: + +```bash +curl -L https://raw.githubusercontent.com/noir-lang/noirup/refs/heads/main/install | bash +noirup +``` + +Once installed, you can [set up shell completions for the `nargo` command](setting_up_shell_completions). + +### Proving backend + +After installing Noir, we install a proving backend to work with our Noir programs. + +Proving backends provide you the abilities to generate proofs, verify proofs, generate smart contracts and more for your Noir programs. + +Different proving backends provide different tools for working with Noir programs, here we will use the [Barretenberg proving backend](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg) developed by Aztec Labs as an example. + +You can use the `bbup` installation script to quickly install and update BB, Barretenberg's CLI tool: + +You can find the full list of proving backends compatible with Noir in Awesome Noir. + +```bash +curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/refs/heads/master/barretenberg/bbup/install | bash +bbup +``` + +For the full list of proving backends compatible with Noir, visit [Awesome Noir](https://github.com/noir-lang/awesome-noir/?tab=readme-ov-file#proving-backends). + +## Nargo + +Nargo provides the ability to initiate and execute Noir projects. Let's initialize the traditional `hello_world`: + +```sh +nargo new hello_world +``` + +Two files will be created. + +- `src/main.nr` contains a simple boilerplate circuit +- `Nargo.toml` contains environmental options, such as name, author, dependencies, and others. + +Glancing at _main.nr_ , we can see that inputs in Noir are private by default, but can be labeled public using the keyword `pub`. This means that we will _assert_ that we know a value `x` which is different from `y` without revealing `x`: + +```rust +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` + +To learn more about private and public values, check the [Data Types](../noir/concepts/data_types/index.md) section. + +### Compiling and executing + +We can now use `nargo` to generate a _Prover.toml_ file, where our input values will be specified: + +```sh +cd hello_world +nargo check +``` + +Let's feed some valid values into this file: + +```toml +x = "1" +y = "2" +``` + +We're now ready to compile and execute our Noir program. By default the `nargo execute` command will do both, and generate the `witness` that we need to feed to our proving backend: + +```sh +nargo execute +``` + +The witness corresponding to this execution will then be written to the file _./target/witness-name.gz_. + +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_. + +With circuit compiled and witness generated, we're ready to prove. + +## Proving backend + +Different proving backends may provide different tools and commands to work with Noir programs. Here Barretenberg's `bb` CLI tool is used as an example: + +```sh +bb prove -b ./target/hello_world.json -w ./target/hello_world.gz -o ./target/proof +``` + +:::tip + +Naming can be confusing, specially as you pass them to the `bb` commands. If unsure, it won't hurt to delete the target folder and start fresh to make sure you're using the most recent versions of the compiled circuit and witness. + +::: + +The proof is now generated in the `target` folder. To verify it we first need to compute the verification key from the compiled circuit, and use it to verify: + +```sh +bb write_vk -b ./target/hello_world.json -o ./target/vk +bb verify -k ./target/vk -p ./target/proof +``` + +:::info + +Notice that in order to verify a proof, the verifier knows nothing but the circuit, which is compiled and used to generate the verification key. This is obviously quite important: private inputs remain private. + +As for the public inputs, you may have noticed they haven't been specified. This behavior varies with each particular backend, but barretenberg typically attaches them to the proof. You can see them by parsing and splitting it. For example if your public inputs are 32 bytes: + +```bash +head -c 32 ./target/proof | od -An -v -t x1 | tr -d $' \n' +``` + +::: + +Congratulations, you have now created and verified a proof for your very first Noir program! + +In the [next section](./project_breakdown.md), we will go into more detail on each step performed. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/setting_up_shell_completions.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/setting_up_shell_completions.md new file mode 100644 index 000000000000..0447321cbab0 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/setting_up_shell_completions.md @@ -0,0 +1,87 @@ +--- +title: Setting up shell completions +tags: [] +sidebar_position: 3 +--- + +The `nargo` binary provides a command to generate shell completions: + +```bash +nargo generate-completion-script [shell] +``` + +where `shell` must be one of `bash`, `elvish`, `fish`, `powershell`, and `zsh`. + +Below we explain how to install them in some popular shells. + +## Installing Zsh Completions + +If you have `oh-my-zsh` installed, you might already have a directory of automatically loading completion scripts — `.oh-my-zsh/completions`. +If not, first create it: + +```bash +mkdir -p ~/.oh-my-zsh/completions` +``` + +Then copy the completion script to that directory: + +```bash +nargo generate-completion-script zsh > ~/.oh-my-zsh/completions/_nargo +``` + +Without `oh-my-zsh`, you’ll need to add a path for completion scripts to your function path, and turn on completion script auto-loading. +First, add these lines to `~/.zshrc`: + +```bash +fpath=(~/.zsh/completions $fpath) +autoload -U compinit +compinit +``` + +Next, create a directory at `~/.zsh/completions`: + +```bash +mkdir -p ~/.zsh/completions +``` + +Then copy the completion script to that directory: + +```bash +nargo generate-completion-script zsh > ~/.zsh/completions/_nargo +``` + +## Installing Bash Completions + +If you have [bash-completion](https://github.com/scop/bash-completion) installed, you can just copy the completion script to the `/usr/local/etc/bash_completion.d` directory: + +```bash +nargo generate-completion-script bash > /usr/local/etc/bash_completion.d/nargo +``` + +Without `bash-completion`, you’ll need to source the completion script directly. +First create a directory such as `~/.bash_completions/`: + +```bash +mkdir ~/.bash_completions/ +``` + +Copy the completion script to that directory: + +```bash +nargo generate-completion-script bash > ~/.bash_completions/nargo.bash +``` + +Then add the following line to `~/.bash_profile` or `~/.bashrc`: + + +```bash +source ~/.bash_completions/nargo.bash +``` + +## Installing Fish Completions + +Copy the completion script to any path listed in the environment variable `$fish_completion_path`. For example, a typical location is `~/.config/fish/completions/nargo.fish`: + +```bash +nargo generate-completion-script fish > ~/.config/fish/completions/nargo.fish +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/_category_.json new file mode 100644 index 000000000000..23b560f610b8 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/_category_.json new file mode 100644 index 000000000000..cc2cbb1c2533 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Debugging", + "position": 5, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_the_repl.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_the_repl.md new file mode 100644 index 000000000000..1d64dae3f37a --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_the_repl.md @@ -0,0 +1,164 @@ +--- +title: Using the REPL Debugger +description: + Step-by-step guide on how to debug your Noir circuits with the REPL Debugger. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + REPL, + ] +sidebar_position: 1 +--- + +#### Pre-requisites + +In order to use the REPL debugger, first you need to install recent enough versions of Nargo and vscode-noir. + +## Debugging a simple circuit + +Let's debug a simple circuit: + +```rust +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` + +To start the REPL debugger, using a terminal, go to a Noir circuit's home directory. Then: + +`$ nargo debug` + +You should be seeing this in your terminal: + +``` +[main] Starting debugger +At ~/noir-examples/recursion/circuits/main/src/main.nr:1:9 + 1 -> fn main(x : Field, y : pub Field) { + 2 assert(x != y); + 3 } +> +``` + +The debugger displays the current Noir code location, and it is now waiting for us to drive it. + +Let's first take a look at the available commands. For that we'll use the `help` command. + +``` +> help +Available commands: + + opcodes display ACIR opcodes + into step into to the next opcode + next step until a new source location is reached + out step until a new source location is reached + and the current stack frame is finished + break LOCATION:OpcodeLocation add a breakpoint at an opcode location + over step until a new source location is reached + without diving into function calls + restart restart the debugging session + delete LOCATION:OpcodeLocation delete breakpoint at an opcode location + witness show witness map + witness index:u32 display a single witness from the witness map + witness index:u32 value:String update a witness with the given value + memset index:usize value:String update a memory cell with the given + value + continue continue execution until the end of the + program + vars show variable values available at this point + in execution + stacktrace display the current stack trace + memory show memory (valid when executing unconstrained code) + step step to the next ACIR opcode + +Other commands: + + help Show this help message + quit Quit repl + +``` + +Some commands operate only for unconstrained functions, such as `memory` and `memset`. If you try to use them while execution is paused at an ACIR opcode, the debugger will simply inform you that you are not executing unconstrained code: + +``` +> memory +Unconstrained VM memory not available +> +``` + +Before continuing, we can take a look at the initial witness map: + +``` +> witness +_0 = 1 +_1 = 2 +> +``` + +Cool, since `x==1`, `y==2`, and we want to check that `x != y`, our circuit should succeed. At this point we could intervene and use the witness setter command to change one of the witnesses. Let's set `y=3`, then back to 2, so we don't affect the expected result: + +``` +> witness +_0 = 1 +_1 = 2 +> witness 1 3 +_1 = 3 +> witness +_0 = 1 +_1 = 3 +> witness 1 2 +_1 = 2 +> witness +_0 = 1 +_1 = 2 +> +``` + +Now we can inspect the current state of local variables. For that we use the `vars` command. + +``` +> vars +> +``` + +We currently have no vars in context, since we are at the entry point of the program. Let's use `next` to execute until the next point in the program. + +``` +> vars +> next +At ~/noir-examples/recursion/circuits/main/src/main.nr:1:20 + 1 -> fn main(x : Field, y : pub Field) { + 2 assert(x != y); + 3 } +> vars +x:Field = 0x01 +``` + +As a result of stepping, the variable `x`, whose initial value comes from the witness map, is now in context and returned by `vars`. + +``` +> next + 1 fn main(x : Field, y : pub Field) { + 2 -> assert(x != y); + 3 } +> vars +y:Field = 0x02 +x:Field = 0x01 +``` + +Stepping again we can finally see both variables and their values. And now we can see that the next assertion should succeed. + +Let's continue to the end: + +``` +> continue +(Continuing execution...) +Finished execution +> q +[main] Circuit witness successfully solved +``` + +Upon quitting the debugger after a solved circuit, the resulting circuit witness gets saved, equivalent to what would happen if we had run the same circuit with `nargo execute`. + +We just went through the basics of debugging using Noir REPL debugger. For a comprehensive reference, check out [the reference page](../../reference/debugger/debugger_repl.md). diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_vs_code.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_vs_code.md new file mode 100644 index 000000000000..ecd64fc26536 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_vs_code.md @@ -0,0 +1,68 @@ +--- +title: Using the VS Code Debugger +description: + Step-by-step guide on how to debug your Noir circuits with the VS Code Debugger configuration and features. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + VS Code, + IDE, + ] +sidebar_position: 0 +--- + +This guide will show you how to use VS Code with the vscode-noir extension to debug a Noir project. + +#### Pre-requisites + +- Nargo +- vscode-noir +- A Noir project with a `Nargo.toml`, `Prover.toml` and at least one Noir (`.nr`) containing an entry point function (typically `main`). + +## Running the debugger + +The easiest way to start debugging is to open the file you want to debug, and press `F5`. This will cause the debugger to launch, using your `Prover.toml` file as input. + +You should see something like this: + +![Debugger launched](@site/static/img/debugger/1-started.png) + +Let's inspect the state of the program. For that, we open VS Code's _Debug pane_. Look for this icon: + +![Debug pane icon](@site/static/img/debugger/2-icon.png) + +You will now see two categories of variables: Locals and Witness Map. + +![Debug pane expanded](@site/static/img/debugger/3-debug-pane.png) + +1. **Locals**: variables of your program. At this point in execution this section is empty, but as we step through the code it will get populated by `x`, `result`, `digest`, etc. + +2. **Witness map**: these are initially populated from your project's `Prover.toml` file. In this example, they will be used to populate `x` and `result` at the beginning of the `main` function. + +Most of the time you will probably be focusing mostly on locals, as they represent the high level state of your program. + +You might be interested in inspecting the witness map in case you are trying to solve a really low level issue in the compiler or runtime itself, so this concerns mostly advanced or niche users. + +Let's step through the program, by using the debugger buttons or their corresponding keyboard shortcuts. + +![Debugger buttons](@site/static/img/debugger/4-debugger-buttons.png) + +Now we can see in the variables pane that there's values for `digest`, `result` and `x`. + +![Inspecting locals](@site/static/img/debugger/5-assert.png) + +We can also inspect the values of variables by directly hovering on them on the code. + +![Hover locals](@site/static/img/debugger/6-hover.png) + +Let's set a break point at the `keccak256` function, so we can continue execution up to the point when it's first invoked without having to go one step at a time. + +We just need to click to the right of the line number 18. Once the breakpoint appears, we can click the `continue` button or use its corresponding keyboard shortcut (`F5` by default). + +![Breakpoint](@site/static/img/debugger/7-break.png) + +Now we are debugging the `keccak256` function, notice the _Call Stack pane_ at the lower right. This lets us inspect the current call stack of our process. + +That covers most of the current debugger functionalities. Check out [the reference](../../reference/debugger/debugger_vscode.md) for more details on how to configure the debugger. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-oracles.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-oracles.md new file mode 100644 index 000000000000..845c24c98291 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-oracles.md @@ -0,0 +1,275 @@ +--- +title: How to use Oracles +description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS. +keywords: + - Noir Programming + - Oracles + - Nargo + - NoirJS + - JSON RPC Server + - Foreign Call Handlers +sidebar_position: 1 +--- + +This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that: + +- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept. +- You have a Noir program to add oracles to. You can create one using one of the starters in [awesome-noir](https://github.com/noir-lang/awesome-noir?tab=readme-ov-file#boilerplates). +- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. +- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). + +## Rundown + +This guide has 3 major steps: + +1. How to modify our Noir program to make use of oracle calls as unconstrained functions +2. How to write a JSON RPC Server to resolve these oracle calls with Nargo +3. How to use them in Nargo and how to provide a custom resolver in NoirJS + +## Step 1 - Modify your Noir program + +An oracle is defined in a Noir program by defining two methods: + +- An unconstrained method - This tells the compiler that it is executing an [unconstrained function](../noir/concepts//unconstrained.md). +- A decorated oracle method - This tells the compiler that this method is an RPC call. + +An example of an oracle that returns a `Field` would be: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(number: Field) -> Field { } + +unconstrained fn get_sqrt(number: Field) -> Field { + sqrt(number) +} +``` + +In this example, we're wrapping our oracle function in an unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); +} +``` + +In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use. + +:::danger + +As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); + assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle! +} +``` + +::: + +:::info + +Currently, oracles only work with single params or array params. For example: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt([Field; 2]) -> [Field; 2] { } +``` + +::: + +## Step 2 - Write an RPC server + +Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript. + +Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { } + +unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] { + sqrt(input) +} + +fn main(input: [Field; 2]) { + let sqrt = get_sqrt(input); + assert(sqrt[0].pow_32(2) as u64 == input[0] as u64); + assert(sqrt[1].pow_32(2) as u64 == input[1] as u64); +} + +#[test] +fn test() { + let input = [4, 16]; + main(input); +} +``` + +:::info + +Why square root? + +In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values. + +::: + +Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example): + +```js +import { JSONRPCServer } from "json-rpc-2.0"; +import express from "express"; +import bodyParser from "body-parser"; + +const app = express(); +app.use(bodyParser.json()); + +const server = new JSONRPCServer(); +app.post("/", (req, res) => { + const jsonRPCRequest = req.body; + server.receive(jsonRPCRequest).then((jsonRPCResponse) => { + if (jsonRPCResponse) { + res.json(jsonRPCResponse); + } else { + res.sendStatus(204); + } + }); +}); + +app.listen(5555); +``` + +Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: + +```js +server.addMethod("resolve_foreign_call", async (params) => { + if (params[0].function !== "getSqrt") { + throw Error("Unexpected foreign call") + }; + const values = params[0].inputs[0].map((field) => { + return `${Math.sqrt(parseInt(field, 16))}`; + }); + return { values: [values] }; +}); +``` + +If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: + +```js +export type ForeignCallSingle = string; + +export type ForeignCallArray = string[]; + +export type ForeignCallResult = { + values: (ForeignCallSingle | ForeignCallArray)[]; +}; +``` + +:::info Multidimensional Arrays + +If the Oracle function is returning an array containing other arrays, such as `[['1','2],['3','4']]`, you need to provide the values in JSON as flattened values. In the previous example, it would be `['1', '2', '3', '4']`. In the Noir program, the Oracle signature can use a nested type, the flattened values will be automatically converted to the nested type. + +::: + +## Step 3 - Usage with Nargo + +Using the [`nargo` CLI tool](../reference/nargo_commands.md), you can use oracles in the `nargo test` and `nargo execute` commands by passing a value to `--oracle-resolver`. For example: + +```bash +nargo test --oracle-resolver http://localhost:5555 +``` + +This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator. + +## Step 4 - Usage with NoirJS + +In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything. + +For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app: + +```js +const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc + +await noir.execute(inputs, foreignCallHandler) +``` + +As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. + +:::tip + +Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)? + +You don't technically have to, but then how would you run `nargo test`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server. + +::: + +In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client. + +For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code): + +```js +import { JSONRPCClient } from "json-rpc-2.0"; + +// declaring the JSONRPCClient +const client = new JSONRPCClient((jsonRPCRequest) => { +// hitting the same JSON RPC Server we coded above + return fetch("http://localhost:5555", { + method: "POST", + headers: { + "content-type": "application/json", + }, + body: JSON.stringify(jsonRPCRequest), + }).then((response) => { + if (response.status === 200) { + return response + .json() + .then((jsonRPCResponse) => client.receive(jsonRPCResponse)); + } else if (jsonRPCRequest.id !== undefined) { + return Promise.reject(new Error(response.statusText)); + } + }); +}); + +// declaring a function that takes the name of the foreign call (getSqrt) and the inputs +const foreignCallHandler = async (name, input) => { + const inputs = input[0].map((i) => i.toString("hex")) + // notice that the "inputs" parameter contains *all* the inputs + // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] + const oracleReturn = await client.request("resolve_foreign_call", [ + { + function: name, + inputs: [inputs] + }, + ]); + return [oracleReturn.values[0]]; +}; + +// the rest of your NoirJS code +const input = { input: [4, 16] }; +const { witness } = await noir.execute(input, foreignCallHandler); +``` + +:::tip + +If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem: + +```bash +yarn add cors +``` + +and use it as a middleware: + +```js +import cors from "cors"; + +const app = express(); +app.use(cors()) +``` + +::: + +## Conclusion + +Hopefully by the end of this guide, you should be able to: + +- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands. +- Provide custom foreign call handlers for NoirJS. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-recursion.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-recursion.md new file mode 100644 index 000000000000..399e4d4b38a0 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-recursion.md @@ -0,0 +1,172 @@ +--- +title: How to use recursion on NoirJS +description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `bb.js`. +keywords: + [ + "NoirJS", + "EVM blockchain", + "smart contracts", + "recursion", + "solidity verifiers", + "Barretenberg backend", + "noir_js", + "intermediate proofs", + "final proofs", + "nargo compile", + "json import", + "recursive circuit", + "recursive app" + ] +sidebar_position: 1 +--- + +This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that: + +- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). +- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) +- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.mdx), and understand how it works. + +It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. + +:::info + +As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. + +::: + +In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume the following: + +- `main`: a circuit of type `assert(x != y)`, which we want to embed in another circuit recursively. For example when proving with the `bb` tool, we can use the `--recursive` CLI option to tell the backend that it should generate proofs that are friendly for verification within another circuit. +- `recursive`: a circuit that verifies `main` + +For a full example of how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. + +## Step 1: Setup + +In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface. + +For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object. + +It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency: + +```js +const backend = new UltraPlonkBackend(circuit, { threads: 8 }, { recursive: true }) +``` + +:::tip +You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores +::: + +## Step 2: Generating the witness and the proof for `main` + +After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. + +```js +const noir = new Noir(circuit) +const { witness } = noir.execute(input) +``` + +With this witness, you are now able to generate the intermediate proof for the main circuit: + +```js +const { proof, publicInputs } = await backend.generateProof(witness) +``` + +:::warning + +Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why! + +In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof. + +With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain. + +::: + +## Step 3 - Verification and proof artifacts + +Optionally, you are able to verify the intermediate proof: + +```js +const verified = await backend.verifyProof({ proof, publicInputs }) +``` + +This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate recursive proof artifacts that will be passed to the circuit that is verifying the proof we just generated. Instead of passing the proof and verification key as a byte array, we pass them as fields which makes it cheaper to verify in a circuit: + +```js +const { proofAsFields, vkAsFields, vkHash } = await backend.generateRecursiveProofArtifacts( { publicInputs, proof }, publicInputsCount) +``` + +This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away. + +:::info + +The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. + +::: + +:::warning + +One common mistake is to forget *who* makes this call. + +In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything! + +Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit. + +::: + +## Step 4 - Recursive proof generation + +With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it: + +```js +const recursiveInputs = { + verification_key: vkAsFields, // array of length 114 + proof: proofAsFields, // array of length 93 + size of public inputs + publicInputs: [mainInput.y], // using the example above, where `y` is the only public input + key_hash: vkHash, +} + +const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! +const { proof, publicInputs } = backend.generateProof(witness) +const verified = backend.verifyProof({ proof, publicInputs }) +``` + +You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! + +:::tip + +Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example: + +```js +const circuits = { + main: mainJSON, + recursive: recursiveJSON +} +const backends = { + main: new BarretenbergBackend(circuits.main), + recursive: new BarretenbergBackend(circuits.recursive) +} +const noir_programs = { + main: new Noir(circuits.main), + recursive: new Noir(circuits.recursive) +} +``` + +This allows you to neatly call exactly the method you want without conflicting names: + +```js +// Alice runs this 👇 +const { witness: mainWitness } = await noir_programs.main.execute(input) +const proof = await backends.main.generateProof(mainWitness) + +// Bob runs this 👇 +const verified = await backends.main.verifyProof(proof) +const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateRecursiveProofArtifacts( + proof, + numPublicInputs, +); +const { witness: recursiveWitness } = await noir_programs.recursive.execute(recursiveInputs) +const recursiveProof = await backends.recursive.generateProof(recursiveWitness); +``` + +::: diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-solidity-verifier.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-solidity-verifier.mdx new file mode 100644 index 000000000000..c8c7894ff911 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-solidity-verifier.mdx @@ -0,0 +1,305 @@ +--- +title: Generate a Solidity Verifier +description: + Learn how to run the verifier as a smart contract on the blockchain. Compile a Solidity verifier + contract for your Noir program and deploy it on any EVM blockchain acting as a verifier smart + contract. Read more to find out +keywords: + [ + solidity verifier, + smart contract, + blockchain, + compiler, + plonk_vk.sol, + EVM blockchain, + verifying Noir programs, + proving backend, + Barretenberg, + ] +sidebar_position: 0 +pagination_next: tutorials/noirjs_app +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Noir is universal. The witness and the compiled program can be fed into a proving backend such as Aztec's [Barretenberg](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg), which can then generate a verifier contract for deployment on blockchains. + +This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. + +Although not strictly in the domain of Noir itself, this guide shows how to generate a Solidity Verifier with Barretenberg and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: + +- You will be using Barretenberg as your proving backend +- You will be using an EVM blockchain to verify your proof +- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network +- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/quick_start.md) with Nargo and the example Hello Noir circuit +- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. + +## Rundown + +Generating a Solidity Verifier with Barretenberg contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: + +1. How to generate a solidity smart contract +2. How to compile the smart contract in the RemixIDE +3. How to deploy it to a testnet + +:::info[Which proving system to use?] + +Barretenberg currently provides two provers: `UltraPlonk` and `UltraHonk`. In a nutshell, `UltraHonk` is faster and uses less RAM, but its verifier contract is much more expensive. `UltraPlonk` is optimized for on-chain verification, but proving is more expensive. + +In any case, we provide instructions for both. Choose your poison ☠️ + +::: + +## Step 1 - Generate a contract + +This is by far the most straightforward step. Just run: + +```sh +nargo compile +``` + +This will compile your source code into a Noir build artifact to be stored in the `./target` directory. From here on, it's Barretenberg's work. You can generate the smart contract using the commands: + + + + +```sh +bb write_vk_ultra_keccak_honk -b ./target/.json +bb contract_ultra_honk +``` + + + + +```sh +bb write_vk -b ./target/.json +bb contract +``` + + + + +replacing `` with the name of your Noir project. A `Verifier.sol` contract is now in the target folder and can be deployed to any EVM blockchain acting as a verifier smart contract. + +You can find more information about `bb` and the default Noir proving backend on [this page](../getting_started/quick_start.md#proving-backend). + + +## Step 2 - Compiling + +We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open +
Remix and create a blank workspace. + +![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) + +We will create a new file to contain the contract Nargo generated, and copy-paste its content. + +:::warning + +You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. + +::: + +To compile our the verifier, we can navigate to the compilation tab: + +![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) + +Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely tell you the contract is too big to deploy on mainnet, or complain about a stack too deep: + +![Contract code too big](@site/static/img/how-tos/solidity_verifier_6.png) +![Stack too deep](@site/static/img/how-tos/solidity_verifier_8.png) + +To avoid this, you can just use some optimization. Open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. + +![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) + +## Step 3 - Deploying + +At this point we should have a compiled contract ready to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. + +Looking closely, we will notice that our "Solidity Verifier" is composed on multiple contracts working together. Remix will take care of the dependencies for us so we can simply deploy the Verifier contract by selecting it and hitting "deploy": + + + + +![Deploying HonkVerifier](@site/static/img/how-tos/solidity_verifier_7.png) + + + + +![Deploying PlonkVerifier](@site/static/img/how-tos/solidity_verifier_9.png) + + + + +A contract will show up in the "Deployed Contracts" section. + +## Step 4 - Verifying + +To verify a proof using the Solidity verifier contract, we call the `verify` function: + +```solidity +function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) +``` + +First generate a proof with `bb`. We need a `Prover.toml` file for our inputs. Run: + +```bash +nargo check +``` + +This will generate a `Prover.toml` you can fill with the values you want to prove. We can now execute the circuit with `nargo` and then use the proving backend to prove: + + + + +```bash +nargo execute +bb prove_ultra_keccak_honk -b ./target/.json -w ./target/ -o ./target/proof +``` + +:::tip[Public inputs] +Barretenberg attaches the public inputs to the proof, which in this case it's not very useful. If you're up for some JS, `bb.js` has [a method for it](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/ts/src/proof/index.ts), but in the CLI you can use this ugly snippet: + +```bash +cat ./target/proof | od -An -v -t x1 | tr -d $' \n' | sed 's/^.\{8\}//' | (read hex; echo "${hex:0:192}${hex:256}") +``` + +Beautiful. This assumes a circuit with one public input (32 bytes, for Barretenberg). For more inputs, you can just increment `hex:256` with 32 more bytes for each public input. + +::: + + + + +```bash +nargo execute +bb prove -b ./target/.json -w ./target/ -o ./target/proof +``` + + +:::tip[Public inputs] +Barretenberg attaches the public inputs to the proof, which in this case it's not very useful. If you're up for some JS, `bb.js` has [a method for it](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/ts/src/proof/index.ts), but in the CLI you can use this ugly snippet: + +```bash +tail -c +33 ./target/proof | od -An -v -t x1 | tr -d $' \n' +``` + +Beautiful. This assumes a circuit with one public input (32 bytes, for Barretenberg). For more inputs, you can just add 32 more bytes for each public input to the `tail` command. + +::: + + + + +Remix expects that the public inputs will be split into an array of `bytes32` values so `HEX_PUBLIC_INPUTS` needs to be split up into 32 byte chunks which are prefixed with `0x` accordingly. + +A programmatic example of how the `verify` function is called can be seen in the example zk voting application [here](https://github.com/noir-lang/noir-examples/blob/33e598c257e2402ea3a6b68dd4c5ad492bce1b0a/foundry-voting/src/zkVote.sol#L35): + +```solidity +function castVote(bytes calldata proof, uint proposalId, uint vote, bytes32 nullifierHash) public returns (bool) { + // ... + bytes32[] memory publicInputs = new bytes32[](4); + publicInputs[0] = merkleRoot; + publicInputs[1] = bytes32(proposalId); + publicInputs[2] = bytes32(vote); + publicInputs[3] = nullifierHash; + require(verifier.verify(proof, publicInputs), "Invalid proof"); +``` + +:::info[Return Values] + +A circuit doesn't have the concept of a return value. Return values are just syntactic sugar in Noir. + +Under the hood, the return value is passed as an input to the circuit and is checked at the end of the circuit program. + +For example, if you have Noir program like this: + +```rust +fn main( + // Public inputs + pubkey_x: pub Field, + pubkey_y: pub Field, + // Private inputs + priv_key: Field, +) -> pub Field +``` + +the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. + +Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. + +In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return]`. + +::: + +:::tip[Structs] + +You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. + +For example, consider the following program: + +```rust +struct Type1 { + val1: Field, + val2: Field, +} + +struct Nested { + t1: Type1, + is_true: bool, +} + +fn main(x: pub Field, nested: pub Nested, y: pub Field) { + //... +} +``` + +The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` + +::: + +The other function you can call is our entrypoint `verify` function, as defined above. + +:::tip + +It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. + +This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. + +It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). + +::: + +## Compatibility with different EVM chains + +Barretenberg proof verification requires the `ecMul`, `ecAdd`, `ecPairing`, and `modexp` EVM precompiles. You can deploy and use the verifier contract on all EVM chains that support the precompiles. + +EVM Diff provides a great table of which EVM chains support which precompiles: https://www.evmdiff.com/features?feature=precompiles + +Some EVM chains manually tested to work with the Barretenberg verifier include: + +- Optimism +- Arbitrum +- Polygon PoS +- Scroll +- Celo +- BSC +- Blast L2 +- Avalanche C-Chain +- Mode +- Linea +- Moonbeam + +Meanwhile, some EVM chains chains manually tested that failed to work with the Barretenberg verifier include: + +- zkSync ERA +- Polygon zkEVM + +Pull requests to update this section is welcome and appreciated if you have compatibility updates on existing / new chains to contribute: https://github.com/noir-lang/noir + +## What's next + +Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks. You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. + +You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/merkle-proof.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/merkle-proof.mdx new file mode 100644 index 000000000000..0a128adb2de6 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/merkle-proof.mdx @@ -0,0 +1,48 @@ +--- +title: Prove Merkle Tree Membership +description: + Learn how to use merkle membership proof in Noir to prove that a given leaf is a member of a + merkle tree with a specified root, at a given index. +keywords: + [merkle proof, merkle membership proof, Noir, rust, hash function, Pedersen, sha256, merkle tree] +sidebar_position: 4 +--- + +Let's walk through an example of a merkle membership proof in Noir that proves that a given leaf is +in a merkle tree. + +```rust + +fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { + let leaf = std::hash::hash_to_field(message.as_slice()); + let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); + assert(merkle_root == root); +} + +``` + +The message is hashed using `hash_to_field`. The specific hash function that is being used is chosen +by the backend. The only requirement is that this hash function can heuristically be used as a +random oracle. If only collision resistance is needed, then one can call `std::hash::pedersen_hash` +instead. + +```rust +let leaf = std::hash::hash_to_field(message.as_slice()); +``` + +The leaf is then passed to a compute_merkle_root function with the root, index and hashpath. The returned root can then be asserted to be the same as the provided root. + +```rust +let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); +assert (merkle_root == root); +``` + +> **Note:** It is possible to re-implement the merkle tree implementation without standard library. +> However, for most usecases, it is enough. In general, the standard library will always opt to be +> as conservative as possible, while striking a balance with efficiency. + +An example, the merkle membership proof, only requires a hash function that has collision +resistance, hence a hash function like Pedersen is allowed, which in most cases is more efficient +than the even more conservative sha256. + +[View an example on the starter repo](https://github.com/noir-lang/noir-examples/blob/3ea09545cabfa464124ec2f3ea8e60c608abe6df/stealthdrop/circuits/src/main.nr#L20) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/using-devcontainers.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/using-devcontainers.mdx new file mode 100644 index 000000000000..dcd30d898015 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/using-devcontainers.mdx @@ -0,0 +1,91 @@ +--- +title: Developer Containers and Codespaces +description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally." +keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"] +sidebar_position: 1 +--- + +Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser. + +## What's a devcontainer after all? + +A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box. + +There are many advantages to this: + +- It's platform and architecture agnostic +- You don't need to have an IDE installed, or Nargo, or use a terminal at all +- It's safer for using on a public machine or public network + +One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use. +Enter Codespaces. + +## Codespaces + +If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof? + +Nothing! Except perhaps the 30-40$ per hour it will cost you. + +The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick. + +Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool: + +- You can start coding Noir in less than a minute +- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be +- It makes it easy to share work with your frens +- It's fully reusable, you can stop and restart whenever you need to + +:::info + +Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine. + +::: + +## Tell me it's _actually_ easy + +It is! + +Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide. + + + +8 simple steps: + +#### 1. Create a new repository on GitHub. + +#### 2. Click "Start coding with Codespaces". This will use the default image. + +#### 3. Create a folder called `.devcontainer` in the root of your repository. + +#### 4. Create a file called `devcontainer.json` in the same folder, and paste the following code: + +```json +{ + "name": "Noir on Codespaces", + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "ghcr.io/noir-lang/features/noir:latest": { + "version": "1.0.0-beta.2" + } + } +} +``` +#### 6. Commit and push your changes + +This will pull the new image and build it, so it could take a minute or so + +#### 8. Done! +Just wait for the build to finish, and there's your easy Noir environment. Some examples of how to use it can be found in the [awesome-noir](https://github.com/noir-lang/awesome-noir?tab=readme-ov-file#boilerplates) repository. + +## How do I use it? + +Using the codespace is obviously much easier than setting it up. +Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go. + +:::info + +If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. +Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/index.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/index.mdx new file mode 100644 index 000000000000..5c116a73b3fe --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/index.mdx @@ -0,0 +1,76 @@ +--- +title: Noir Lang +hide_title: true +description: + Learn about the public alpha release of Noir, a domain specific language heavily influenced by Rust that compiles to + an intermediate language which can be compiled to an arithmetic circuit or a rank-1 constraint system. +keywords: + [Noir, + Domain Specific Language, + Rust, + Intermediate Language, + Arithmetic Circuit, + Rank-1 Constraint System, + Ethereum Developers, + Protocol Developers, + Blockchain Developers, + Proving System, + Smart Contract Language] +sidebar_position: 0 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import ThemedImage from '@theme/ThemedImage'; +import useBaseUrl from '@docusaurus/useBaseUrl'; + + + +Noir is an open-source Domain-Specific Language for safe and seamless construction of privacy-preserving Zero-Knowledge programs, requiring no previous knowledge on the underlying mathematics or cryptography. + +ZK programs are programs that can generate short proofs of statements without revealing all inputs to the statements. You can read more about Zero-Knowledge Proofs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). + +## What's new about Noir? + +Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. + +:::info + +Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. + +However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. + +::: + +## Who is Noir for? + +Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: + + + + Noir Logo + + Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. + + + Soliditry Verifier Example + Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) + + + Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. + + + + +## Libraries + +Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. +The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. +Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/migration_notes.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/migration_notes.md new file mode 100644 index 000000000000..6bd740024e5b --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/migration_notes.md @@ -0,0 +1,105 @@ +--- +title: Migration notes +description: Read about migration notes from previous versions, which could solve problems while updating +keywords: [Noir, notes, migration, updating, upgrading] +--- + +Noir is in full-speed development. Things break fast, wild, and often. This page attempts to leave some notes on errors you might encounter when upgrading and how to resolve them until proper patches are built. + +### `backend encountered an error: libc++.so.1` + +Depending on your OS, you may encounter the following error when running `nargo prove` for the first time: + +```text +The backend encountered an error: "/home/codespace/.nargo/backends/acvm-backend-barretenberg/backend_binary: error while loading shared libraries: libc++.so.1: cannot open shared object file: No such file or directory\n" +``` + +Install the `libc++-dev` library with: + +```bash +sudo apt install libc++-dev +``` + +## ≥0.19 + +### Enforcing `compiler_version` + +From this version on, the compiler will check for the `compiler_version` field in `Nargo.toml`, and will error if it doesn't match the current Nargo version in use. + +To update, please make sure this field in `Nargo.toml` matches the output of `nargo --version`. + +## ≥0.14 + +The index of the [for loops](noir/concepts/control_flow.md#loops) is now of type `u64` instead of `Field`. An example refactor would be: + +```rust +for i in 0..10 { + let i = i as Field; +} +``` + +## ≥v0.11.0 and Nargo backend + +From this version onwards, Nargo starts managing backends through the `nargo backend` command. Upgrading to the versions per usual steps might lead to: + +### `backend encountered an error` + +This is likely due to the existing locally installed version of proving backend (e.g. barretenberg) is incompatible with the version of Nargo in use. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo prove +``` + +with your Noir program. + +This will trigger the download and installation of the latest version of barretenberg compatible with your Nargo in use. + +### `backend encountered an error: illegal instruction` + +On certain Intel-based systems, an `illegal instruction` error may arise due to incompatibility of barretenberg with certain CPU instructions. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo backend install acvm-backend-barretenberg https://github.com/noir-lang/barretenberg-js-binary/raw/master/run-bb.tar.gz +``` + +This downloads and installs a specific bb.js based version of barretenberg binary from GitHub. + +The gzipped file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), where we need to gzip it as the Nargo currently expect the backend to be zipped up. + +Then run: + +``` +DESIRED_BINARY_VERSION=0.8.1 nargo info +``` + +This overrides the bb native binary with a bb.js node application instead, which should be compatible with most if not all hardware. This does come with the drawback of being generally slower than native binary. + +0.8.1 indicates bb.js version 0.8.1, so if you change that it will update to a different version or the default version in the script if none was supplied. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/_category_.json new file mode 100644 index 000000000000..7da08f8a8c5d --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Concepts", + "position": 0, + "collapsible": true, + "collapsed": true +} \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/assert.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/assert.md new file mode 100644 index 000000000000..c7bc42fa3228 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/assert.md @@ -0,0 +1,79 @@ +--- +title: Assert Function +description: + Learn about the `assert` and `static_assert` functions in Noir, which can be used to explicitly + constrain the predicate or comparison expression that follows to be true, and what happens if + the expression is false at runtime or compile-time, respectively. +keywords: [Noir programming language, assert statement, predicate expression, comparison expression] +sidebar_position: 4 +--- + +Noir includes a special `assert` function which will explicitly constrain the predicate/comparison +expression that follows to be true. If this expression is false at runtime, the program will fail to +be proven. As of v1.0.0-beta.2, assert statements are expressions and can be used in value contexts. + +Example: + +```rust +fn main(x : Field, y : Field) { + assert(x == y); +} +``` + +> Assertions only work for predicate operations, such as `==`. If there's any ambiguity on the operation, the program will fail to compile. For example, it is unclear if `assert(x + y)` would check for `x + y == 0` or simply would return `true`. + +You can optionally provide a message to be logged when the assertion fails: + +```rust +assert(x == y, "x and y are not equal"); +``` + +Aside string literals, the optional message can be a format string or any other type supported as input for Noir's [print](../standard_library/logging.md) functions. This feature lets you incorporate runtime variables into your failed assertion logs: + +```rust +assert(x == y, f"Expected x == y, but got {x} == {y}"); +``` + +Using a variable as an assertion message directly: + +```rust +struct myStruct { + myField: Field +} + +let s = myStruct { myField: y }; +assert(s.myField == x, s); +``` + +There is also a special `static_assert` function that behaves like `assert`, +but that runs at compile-time. + +```rust +fn main(xs: [Field; 3]) { + let x = 2 + 2; + let y = 4; + static_assert(x == y, "expected 2 + 2 to equal 4"); + + // This passes since the length of `xs` is known at compile-time + static_assert(xs.len() == 3, "expected the input to have 3 elements"); +} +``` + +This function fails when passed a dynamic (run-time) argument: + +```rust +fn main(x : Field, y : Field) { + // this fails because `x` is not known at compile-time + static_assert(x == 2, "expected x to be known at compile-time and equal to 2"); + + let mut example_slice = &[]; + if y == 4 { + example_slice = example_slice.push_back(0); + } + + // This fails because the length of `example_slice` is not known at + // compile-time + let error_message = "expected an empty slice, known at compile-time"; + static_assert(example_slice.len() == 0, error_message); +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comments.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comments.md new file mode 100644 index 000000000000..b51a85f5c949 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comments.md @@ -0,0 +1,33 @@ +--- +title: Comments +description: + Learn how to write comments in Noir programming language. A comment is a line of code that is + ignored by the compiler, but it can be read by programmers. Single-line and multi-line comments + are supported in Noir. +keywords: [Noir programming language, comments, single-line comments, multi-line comments] +sidebar_position: 10 +--- + +A comment is a line in your codebase which the compiler ignores, however it can be read by +programmers. + +Here is a single line comment: + +```rust +// This is a comment and is ignored +``` + +`//` is used to tell the compiler to ignore the rest of the line. + +Noir also supports multi-line block comments. Start a block comment with `/*` and end the block with `*/`. + +Noir does not natively support doc comments. You may be able to use [Rust doc comments](https://doc.rust-lang.org/reference/comments.html) in your code to leverage some Rust documentation build tools with Noir code. + +```rust +/* + This is a block comment describing a complex function. +*/ +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comptime.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comptime.md new file mode 100644 index 000000000000..4cdd2094a162 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comptime.md @@ -0,0 +1,565 @@ +--- +title: Compile-time Code & Metaprogramming +description: Learn how to use metaprogramming in Noir to create macros or derive your own traits +keywords: [Noir, comptime, compile-time, metaprogramming, macros, quote, unquote] +sidebar_position: 15 +--- + +## Overview + +Metaprogramming in Noir is comprised of three parts: +1. `comptime` code +2. Quoting and unquoting +3. The metaprogramming API in `std::meta` + +Each of these are explained in more detail in the next sections but the wide picture is that +`comptime` allows us to write code which runs at compile-time. In this `comptime` code we +can quote and unquote snippets of the program, manipulate them, and insert them in other +parts of the program. Comptime functions which do this are said to be macros. Additionally, +there's a compile-time API of built-in types and functions provided by the compiler which allows +for greater analysis and modification of programs. + +--- + +## Comptime + +`comptime` is a new keyword in Noir which marks an item as executing or existing at compile-time. It can be used in several ways: + +- `comptime fn` to define functions which execute exclusively during compile-time. +- `comptime global` to define a global variable which is evaluated at compile-time. + - Unlike runtime globals, `comptime global`s can be mutable. +- `comptime { ... }` to execute a block of statements during compile-time. +- `comptime let` to define a variable whose value is evaluated at compile-time. +- `comptime for` to run a for loop at compile-time. Syntax sugar for `comptime { for .. }`. + +### Scoping + +Note that while in a `comptime` context, any runtime variables _local to the current function_ are never visible. + +### Evaluating + +Evaluation rules of `comptime` follows the normal unconstrained evaluation rules for other Noir code. There are a few things to note though: + +- Certain built-in functions may not be available, although more may be added over time. +- Evaluation order of `comptime {}` blocks within global items is currently unspecified. For example, given the following two functions we can't guarantee +which `println` will execute first. The ordering of the two printouts will be arbitrary, but should be stable across multiple compilations with the same `nargo` version as long as the program is also unchanged. + +```rust +fn one() { + comptime { println("one"); } +} + +fn two() { + comptime { println("two"); } +} +``` + +- Since evaluation order is unspecified, care should be taken when using mutable globals so that they do not rely on a particular ordering. +For example, using globals to generate unique ids should be fine but relying on certain ids always being produced (especially after edits to the program) should be avoided. +- Although the ordering of comptime code is usually unspecified, there are cases where it is: + - Dependencies of a crate will always be evaluated before the dependent crate. + - Any attributes on a function will be run before the function body is resolved. This is to allow the attribute to modify the function if necessary. Note that if the + function itself was called at compile-time previously, it will already be resolved and cannot be modified. To prevent accidentally calling functions you wish to modify + at compile-time, it may be helpful to sort your `comptime` annotation functions into a different submodule crate along with any dependencies they require. + - Unlike raw `comptime {}` blocks, attributes on top-level items in the program do have a set evaluation order. Attributes within a module are evaluated top-down, and attributes + in different modules are evaluated submodule-first. Sibling modules to the same parent module are evaluated in order of the module declarations (`mod foo; mod bar;`) in their + parent module. + +### Lowering + +When a `comptime` value is used in runtime code it must be lowered into a runtime value. This means replacing the expression with the literal that it evaluated to. For example, the code: + +```rust +struct Foo { array: [Field; 2], len: u32 } + +fn main() { + println(comptime { + let mut foo = std::mem::zeroed::(); + foo.array[0] = 4; + foo.len = 1; + foo + }); +} +``` + +will be converted to the following after `comptime` expressions are evaluated: + +```rust +struct Foo { array: [Field; 2], len: u32 } + +fn main() { + println(Foo { array: [4, 0], len: 1 }); +} +``` + +Not all types of values can be lowered. For example, references, `Type`s, and `TypeDefinition`s (among other types) cannot be lowered at all. + +```rust +fn main() { + // There's nothing we could inline here to create a Type value at runtime + // let _ = get_type!(); +} + +comptime fn get_type() -> Type { ... } +``` + +Values of certain types may also change type when they are lowered. For example, a comptime format string will already be +formatted, and thus lowers into a runtime string instead: + +```rust +fn main() { + let foo = comptime { + let i = 2; + f"i = {i}" + }; + assert_eq(foo, "i = 2"); +} +``` + +--- + +## (Quasi) Quote + +Macros in Noir are `comptime` functions which return code as a value which is inserted into the call site when it is lowered there. +A code value in this case is of type `Quoted` and can be created by a `quote { ... }` expression. +More specifically, the code value `quote` creates is a token stream - a representation of source code as a series of words, numbers, string literals, or operators. +For example, the expression `quote { Hi "there reader"! }` would quote three tokens: the word "hi", the string "there reader", and an exclamation mark. +You'll note that snippets that would otherwise be invalid syntax can still be quoted. + +When a `Quoted` value is used in runtime code, it is lowered into a `quote { ... }` expression. Since this expression is only valid +in compile-time code however, we'd get an error if we tried this. Instead, we can use macro insertion to insert each token into the +program at that point, and parse it as an expression. To do this, we have to add a `!` after the function name returning the `Quoted` value. +If the value was created locally and there is no function returning it, `std::meta::unquote!(_)` can be used instead. +Calling such a function at compile-time without `!` will just return the `Quoted` value to be further manipulated. For example: + +```rust title="quote-example" showLineNumbers +comptime fn quote_one() -> Quoted { + quote { 1 } + } + + #[test] + fn returning_versus_macro_insertion() { + comptime { + // let _a: Quoted = quote { 1 }; + let _a: Quoted = quote_one(); + + // let _b: Field = 1; + let _b: Field = quote_one!(); + + // Since integers default to fields, if we + // want a different type we have to explicitly cast + // let _c: i32 = 1 as i32; + let _c: i32 = quote_one!() as i32; + } + } +``` +> Source code: noir_stdlib/src/meta/mod.nr#L131-L151 + + +For those familiar with quoting from other languages (primarily lisps), Noir's `quote` is actually a _quasiquote_. +This means we can escape the quoting by using the unquote operator to splice values in the middle of quoted code. + +In addition to curly braces, you can also use square braces for the quote operator: + +```rust +comptime { + let q1 = quote { 1 }; + let q2 = quote [ 2 ]; + assert_eq(q1, q2); + + // Square braces can be used to quote mismatched curly braces if needed + let _ = quote[}]; +} +``` + +--- + +## Unquote + +The unquote operator `$` is usable within a `quote` expression. +It takes a variable as an argument, evaluates the variable, and splices the resulting value into the quoted token stream at that point. For example, + +```rust +comptime { + let x = 1 + 2; + let y = quote { $x + 4 }; +} +``` + +The value of `y` above will be the token stream containing `3`, `+`, and `4`. We can also use this to combine `Quoted` values into larger token streams: + +```rust +comptime { + let x = quote { 1 + 2 }; + let y = quote { $x + 4 }; +} +``` + +The value of `y` above is now the token stream containing five tokens: `1 + 2 + 4`. + +Note that to unquote something, a variable name _must_ follow the `$` operator in a token stream. +If it is an expression (even a parenthesized one), it will do nothing. Most likely a parse error will be given when the macro is later unquoted. + +Unquoting can also be avoided by escaping the `$` with a backslash: + +```rust +comptime { + let x = quote { 1 + 2 }; + + // y contains the four tokens: `$x + 4` + let y = quote { \$x + 4 }; +} +``` + +### Combining Tokens + +Note that `Quoted` is internally a series of separate tokens, and that all unquoting does is combine these token vectors. +This means that code which appears to append like a string actually appends like a vector internally: + +```rust +comptime { + let x = 3; + let q = quote { foo$x }; // This is [foo, 3], not [foo3] + + // Spaces are ignored in general, they're never part of a token + assert_eq(q, quote { foo 3 }); +} +``` + +If you do want string semantics, you can use format strings then convert back to a `Quoted` value with `.quoted_contents()`. +Note that formatting a quoted value with multiple tokens will always insert a space between each token. If this is +undesired, you'll need to only operate on quoted values containing a single token. To do this, you can iterate +over each token of a larger quoted value with `.tokens()`: + +```rust title="concatenate-example" showLineNumbers +comptime fn concatenate(q1: Quoted, q2: Quoted) -> Quoted { + assert(q1.tokens().len() <= 1); + assert(q2.tokens().len() <= 1); + + f"{q1}{q2}".quoted_contents() + } + + // The CtString type is also useful for a compile-time string of unbounded size + // so that you can append to it in a loop. + comptime fn double_spaced(q: Quoted) -> CtString { + let mut result = "".as_ctstring(); + + for token in q.tokens() { + if result != "".as_ctstring() { + result = result.append_str(" "); + } + result = result.append_fmtstr(f"{token}"); + } + + result + } + + #[test] + fn concatenate_test() { + comptime { + let result = concatenate(quote {foo}, quote {bar}); + assert_eq(result, quote {foobar}); + + let result = double_spaced(quote {foo bar 3}).as_quoted_str!(); + assert_eq(result, "foo bar 3"); + } + } +``` +> Source code: noir_stdlib/src/meta/mod.nr#L266-L299 + + +--- + +## Attributes + +Attributes provide a way to run a `comptime` function on an item in the program. +When you use an attribute, the function with the same name will be called with that item as an argument: + +```rust +#[my_struct_attribute] +struct Foo {} + +comptime fn my_struct_attribute(s: StructDefinition) { + println("Called my_struct_attribute!"); +} + +#[my_function_attribute] +fn foo() {} + +comptime fn my_function_attribute(f: FunctionDefinition) { + println("Called my_function_attribute!"); +} +``` + +Anything returned from one of these functions will be inserted at top-level along with the original item. +Note that expressions are not valid at top-level so you'll get an error trying to return `3` or similar just as if you tried to write a program containing `3; struct Foo {}`. +You can insert other top-level items such as trait impls, structs, or functions this way though. +For example, this is the mechanism used to insert additional trait implementations into the program when deriving a trait impl from a struct: + +```rust title="derive-field-count-example" showLineNumbers +trait FieldCount { + fn field_count() -> u32; + } + + #[derive_field_count] + struct Bar { + x: Field, + y: [Field; 2], + } + + comptime fn derive_field_count(s: StructDefinition) -> Quoted { + let typ = s.as_type(); + let field_count = s.fields_as_written().len(); + quote { + impl FieldCount for $typ { + fn field_count() -> u32 { + $field_count + } + } + } + } +``` +> Source code: noir_stdlib/src/meta/mod.nr#L153-L175 + + +### Calling annotations with additional arguments + +Arguments may optionally be given to attributes. +When this is done, these additional arguments are passed to the attribute function after the item argument. + +```rust title="annotation-arguments-example" showLineNumbers +#[assert_field_is_type(quote { i32 }.as_type())] + struct MyStruct { + my_field: i32, + } + + comptime fn assert_field_is_type(s: StructDefinition, typ: Type) { + // Assert the first field in `s` has type `typ` + let fields = s.fields([]); + assert_eq(fields[0].1, typ); + } +``` +> Source code: noir_stdlib/src/meta/mod.nr#L177-L188 + + +We can also take any number of arguments by adding the `varargs` attribute: + +```rust title="annotation-varargs-example" showLineNumbers +#[assert_three_args(1, 2, 3)] + struct MyOtherStruct { + my_other_field: u32, + } + + #[varargs] + comptime fn assert_three_args(_s: StructDefinition, args: [Field]) { + assert_eq(args.len(), 3); + } +``` +> Source code: noir_stdlib/src/meta/mod.nr#L190-L200 + + +### Attribute Evaluation Order + +Unlike the evaluation order of stray `comptime {}` blocks within functions, attributes have a well-defined evaluation +order. Within a module, attributes are evaluated top to bottom. Between modules, attributes in child modules are evaluated +first. Attributes in sibling modules are resolved following the `mod foo; mod bar;` declaration order within their parent +modules. + +```rust +mod foo; // attributes in foo are run first +mod bar; // followed by attributes in bar + +// followed by any attributes in the parent module +#[derive(Eq)] +struct Baz {} +``` + +Note that because of this evaluation order, you may get an error trying to derive a trait for a struct whose fields +have not yet had the trait derived already: + +```rust +// Error! `Bar` field of `Foo` does not (yet) implement Eq! +#[derive(Eq)] +struct Foo { + bar: Bar +} + +#[derive(Eq)] +struct Bar {} +``` + +In this case, the issue can be resolved by rearranging the structs. + +--- + +## Comptime API + +Although `comptime`, `quote`, and unquoting provide a flexible base for writing macros, +Noir's true metaprogramming ability comes from being able to interact with the compiler through a compile-time API. +This API can be accessed through built-in functions in `std::meta` as well as on methods of several `comptime` types. + +The following is an incomplete list of some `comptime` types along with some useful methods on them. You can see more in the standard library [Metaprogramming section](../standard_library/meta). + +- `Quoted`: A token stream +- `Type`: The type of a Noir type + - `fn implements(self, constraint: TraitConstraint) -> bool` + - Returns true if `self` implements the given trait constraint +- `Expr`: A syntactically valid expression. Can be used to recur on a program's parse tree to inspect how it is structured. + - Methods: + - `fn as_function_call(self) -> Option<(Expr, [Expr])>` + - If this is a function call expression, return `(function, arguments)` + - `fn as_block(self) -> Option<[Expr]>` + - If this is a block, return each statement in the block +- `FunctionDefinition`: A function definition + - Methods: + - `fn parameters(self) -> [(Quoted, Type)]` + - Returns a slice of `(name, type)` pairs for each parameter +- `StructDefinition`: A struct definition + - Methods: + - `fn as_type(self) -> Type` + - Returns this `StructDefinition` as a `Type`. Any generics are kept as-is + - `fn generics(self) -> [Quoted]` + - Return the name of each generic on this struct + - `fn fields(self) -> [(Quoted, Type)]` + - Return the name and type of each field +- `TraitConstraint`: A trait constraint such as `From` +- `TypedExpr`: A type-checked expression. +- `UnresolvedType`: A syntactic notation that refers to a Noir type that hasn't been resolved yet + +There are many more functions available by exploring the `std::meta` module and its submodules. +Using these methods is the key to writing powerful metaprogramming libraries. + +### `#[use_callers_scope]` + +Since certain functions such as `Quoted::as_type`, `Expression::as_type`, or `Quoted::as_trait_constraint` will attempt +to resolve their contents in a particular scope - it can be useful to change the scope they resolve in. By default +these functions will resolve in the current function's scope which is usually the attribute function they are called in. +If you're working on a library however, this may be a completely different module or crate to the item you're trying to +use the attribute on. If you want to be able to use `Quoted::as_type` to refer to types local to the caller's scope for +example, you can annotate your attribute function with `#[use_callers_scope]`. This will ensure your attribute, and any +closures it uses, can refer to anything in the caller's scope. `#[use_callers_scope]` also works recursively. So if both +your attribute function and a helper function it calls use it, then they can both refer to the same original caller. + +--- + +## Example: Derive + +Using all of the above, we can write a `derive` macro that behaves similarly to Rust's but is not built into the language. +From the user's perspective it will look like this: + +```rust +// Example usage +#[derive(Default, Eq, Ord)] +struct MyStruct { my_field: u32 } +``` + +To implement `derive` we'll have to create a `comptime` function that accepts +a variable amount of traits. + +```rust title="derive_example" showLineNumbers +// These are needed for the unconstrained hashmap we're using to store derive functions +use crate::collections::umap::UHashMap; +use crate::hash::BuildHasherDefault; +use crate::hash::poseidon2::Poseidon2Hasher; + +// A derive function is one that given a struct definition can +// create us a quoted trait impl from it. +pub type DeriveFunction = fn(StructDefinition) -> Quoted; + +// We'll keep a global HANDLERS map to keep track of the derive handler for each trait +comptime mut global HANDLERS: UHashMap> = + UHashMap::default(); + +// Given a struct and a slice of traits to derive, create trait impls for each. +// This function is as simple as iterating over the slice, checking if we have a trait +// handler registered for the given trait, calling it, and appending the result. +#[varargs] +pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted { + let mut result = quote {}; + + for trait_to_derive in traits { + let handler = HANDLERS.get(trait_to_derive); + 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 +} +``` +> Source code: noir_stdlib/src/meta/mod.nr#L33-L66 + + +Registering a derive function could be done as follows: + +```rust title="derive_via" showLineNumbers +// To register a handler for a trait, just add it to our handlers map +pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { + HANDLERS.insert(t, f); +} +``` +> Source code: noir_stdlib/src/meta/mod.nr#L68-L75 + + +```rust title="big-derive-usage-example" showLineNumbers +// Finally, to register a handler we call the above function as an annotation + // with our handler function. + #[derive_via(derive_do_nothing)] + trait DoNothing { + fn do_nothing(self); + } + + comptime fn derive_do_nothing(s: StructDefinition) -> Quoted { + // This is simplified since we don't handle generics or where clauses! + // In a real example we'd likely also need to introduce each of + // `s.generics()` as well as a trait constraint for each generic + // to ensure they also implement the trait. + let typ = s.as_type(); + quote { + impl DoNothing for $typ { + fn do_nothing(self) { + // Traits can't tell us what to do + println("something"); + } + } + } + } + + // Since `DoNothing` is a simple trait which: + // 1. Only has one method + // 2. Does not have any generics on the trait itself + // We can use `std::meta::make_trait_impl` to help us out. + // This helper function will generate our impl for us along with any + // necessary where clauses and still provides a flexible interface + // for us to work on each field on the struct. + comptime fn derive_do_nothing_alt(s: StructDefinition) -> Quoted { + let trait_name = quote { DoNothing }; + let method_signature = quote { fn do_nothing(self) }; + + // Call `do_nothing` recursively on each field in the struct + let for_each_field = |field_name| quote { self.$field_name.do_nothing(); }; + + // Some traits like Eq want to join each field expression with something like `&`. + // We don't need that here + let join_fields_with = quote {}; + + // The body function is a spot to insert any extra setup/teardown needed. + // We'll insert our println here. Since we recur on each field, we should see + // one println for the struct itself, followed by a println for every field (recursively). + let body = |body| quote { + println("something"); + $body + }; + crate::meta::make_trait_impl( + s, + trait_name, + method_signature, + for_each_field, + join_fields_with, + body, + ) + } +``` +> Source code: noir_stdlib/src/meta/mod.nr#L202-L260 + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/control_flow.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/control_flow.md new file mode 100644 index 000000000000..57816c38c575 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/control_flow.md @@ -0,0 +1,111 @@ +--- +title: Control Flow +description: + Learn how to use loops and if expressions in the Noir programming language. Discover the syntax + and examples for for loops and if-else statements. +keywords: [Noir programming language, loops, for loop, if-else statements, Rust syntax] +sidebar_position: 2 +--- + +## If Expressions + +Noir supports `if-else` statements. The syntax is most similar to Rust's where it is not required +for the statement's conditional to be surrounded by parentheses. + +```rust +let a = 0; +let mut x: u32 = 0; + +if a == 0 { + if a != 0 { + x = 6; + } else { + x = 2; + } +} else { + x = 5; + assert(x == 5); +} +assert(x == 2); +``` + +## For loops + +`for` loops allow you to repeat a block of code multiple times. + +The following block of code between the braces is run 10 times. + +```rust +for i in 0..10 { + // do something +} +``` + +Alternatively, `start..=end` can be used for a range that is inclusive on both ends. + +The index for loops is of type `u64`. + +### Break and Continue + +In unconstrained code, `break` and `continue` are also allowed in `for` and `loop` loops. These are only allowed +in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations +a loop may have. `break` and `continue` can be used like so: + +```rust +for i in 0 .. 10 { + println("Iteration start") + + if i == 2 { + continue; + } + + if i == 5 { + break; + } + + println(i); +} +println("Loop end") +``` + +When used, `break` will end the current loop early and jump to the statement after the for loop. In the example +above, the `break` will stop the loop and jump to the `println("Loop end")`. + +`continue` will stop the current iteration of the loop, and jump to the start of the next iteration. In the example +above, `continue` will jump to `println("Iteration start")` when used. Note that the loop continues as normal after this. +The iteration variable `i` is still increased by one as normal when `continue` is used. + +`break` and `continue` cannot currently be used to jump out of more than a single loop at a time. + +## Loops + +In unconstrained code, `loop` is allowed for loops that end with a `break`. +A `loop` must contain at least one `break` statement that is reachable during execution. +This is only allowed in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations +a loop may have. + +```rust +let mut i = 10; +loop { + println(i); + i -= 1; + + if i == 0 { + break; + } +} +``` + +## While loops + +In unconstrained code, `while` is allowed for loops that end when a given condition is met. +This is only allowed in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations +a loop may have. + +```rust +let mut i = 0 +while i < 10 { + println(i); + i += 2; +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_bus.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_bus.mdx new file mode 100644 index 000000000000..e55e58622ce8 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_bus.mdx @@ -0,0 +1,23 @@ +--- +title: Data Bus +sidebar_position: 13 +--- +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +The data bus is an optimization that the backend can use to make recursion more efficient. +In order to use it, you must define some inputs of the program entry points (usually the `main()` +function) with the `call_data` modifier, and the return values with the `return_data` modifier. +These modifiers are incompatible with `pub` and `mut` modifiers. + +## Example + +```rust +fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { + let a = z[x]; + a+y +} +``` + +As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/_category_.json new file mode 100644 index 000000000000..5d694210bbf3 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/arrays.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/arrays.md new file mode 100644 index 000000000000..289145a8c4d1 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/arrays.md @@ -0,0 +1,276 @@ +--- +title: Arrays +description: + Dive into the Array data type in Noir. Grasp its methods, practical examples, and best practices for efficiently using Arrays in your Noir code. +keywords: + [ + noir, + array type, + methods, + examples, + indexing, + ] +sidebar_position: 4 +--- + +An array is one way of grouping together values into one compound type. Array types can be inferred +or explicitly specified via the syntax `[; ]`: + +```rust +fn main(x : Field, y : Field) { + let my_arr = [x, y]; + let your_arr: [Field; 2] = [x, y]; +} +``` + +Here, both `my_arr` and `your_arr` are instantiated as an array containing two `Field` elements. + +Array elements can be accessed using indexing: + +```rust +fn main() { + let a = [1, 2, 3, 4, 5]; + + let first = a[0]; + let second = a[1]; +} +``` + +All elements in an array must be of the same type (i.e. homogeneous). That is, an array cannot group +a `Field` value and a `u8` value together for example. + +You can write mutable arrays, like: + +```rust +fn main() { + let mut arr = [1, 2, 3, 4, 5]; + assert(arr[0] == 1); + + arr[0] = 42; + assert(arr[0] == 42); +} +``` + +You can instantiate a new array of a fixed size with the same value repeated for each element. The following example instantiates an array of length 32 where each element is of type Field and has the value 0. + +```rust +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: + +```rust +let array: [Field; 32] = [0; 32]; +let sl = array.as_slice() +``` + +You can define multidimensional arrays: + +```rust +let array : [[Field; 2]; 2]; +let element = array[0][0]; +``` + +However, multidimensional slices are not supported. For example, the following code will error at compile time: + +```rust +let slice : [[Field]] = &[]; +``` + +## Types + +You can create arrays of primitive types or structs. There is not yet support for nested arrays +(arrays of arrays) or arrays of structs that contain arrays. + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for arrays. +Each of these functions are located within the generic impl `impl [T; N] {`. +So anywhere `self` appears, it refers to the variable `self: [T; N]`. + +### len + +Returns the length of an array + +```rust +fn len(self) -> Field +``` + +example + +```rust +fn main() { + let array = [42, 42]; + assert(array.len() == 2); +} +``` + +### sort + +Returns a new sorted array. The original array remains untouched. Notice that this function will +only work for arrays of fields or integers, not for any arbitrary type. This is because the sorting +logic it uses internally is optimized specifically for these values. If you need a sort function to +sort any type, you should use the function `sort_via` described below. + +```rust +fn sort(self) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32]; + let sorted = arr.sort(); + assert(sorted == [32, 42]); +} +``` + +### sort_via + +Sorts the array with a custom comparison function. The ordering function must return true if the first argument should be sorted to be before the second argument or is equal to the second argument. + +Using this method with an operator like `<` that does not return `true` for equal values will result in an assertion failure for arrays with equal elements. + +```rust +fn sort_via(self, ordering: fn(T, T) -> bool) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32] + let sorted_ascending = arr.sort_via(|a, b| a <= b); + assert(sorted_ascending == [32, 42]); // verifies + + let sorted_descending = arr.sort_via(|a, b| a >= b); + assert(sorted_descending == [32, 42]); // does not verify +} +``` + +### map + +Applies a function to each element of the array, returning a new array containing the mapped elements. + +```rust +fn map(self, f: fn(T) -> U) -> [U; N] +``` + +example + +```rust +let a = [1, 2, 3]; +let b = a.map(|a| a * 2); // b is now [2, 4, 6] +``` + +### fold + +Applies a function to each element of the array, returning the final accumulated value. The first +parameter is the initial value. + +```rust +fn fold(self, mut accumulator: U, f: fn(U, T) -> U) -> U +``` + +This is a left fold, so the given function will be applied to the accumulator and first element of +the array, then the second, and so on. For a given call the expected result would be equivalent to: + +```rust +let a1 = [1]; +let a2 = [1, 2]; +let a3 = [1, 2, 3]; + +let f = |a, b| a - b; +a1.fold(10, f) //=> f(10, 1) +a2.fold(10, f) //=> f(f(10, 1), 2) +a3.fold(10, f) //=> f(f(f(10, 1), 2), 3) +``` + +example: + +```rust + +fn main() { + let arr = [2, 2, 2, 2, 2]; + let folded = arr.fold(0, |a, b| a + b); + assert(folded == 10); +} + +``` + +### reduce + +Same as fold, but uses the first element as the starting element. + +Requires `self` to be non-empty. + +```rust +fn reduce(self, f: fn(T, T) -> T) -> T +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let reduced = arr.reduce(|a, b| a + b); + assert(reduced == 10); +} +``` + +### all + +Returns true if all the elements satisfy the given predicate + +```rust +fn all(self, predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let all = arr.all(|a| a == 2); + assert(all); +} +``` + +### any + +Returns true if any of the elements satisfy the given predicate + +```rust +fn any(self, predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 5]; + let any = arr.any(|a| a == 5); + assert(any); +} +``` + +### as_str_unchecked + +Converts a byte array of type `[u8; N]` to a string. Note that this performs no UTF-8 validation - +the given array is interpreted as-is as a string. + +```rust +impl [u8; N] { + pub fn as_str_unchecked(self) -> str +} +``` + +example: + +```rust +fn main() { + let hi = [104, 105].as_str_unchecked(); + assert_eq(hi, "hi"); +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/booleans.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/booleans.md new file mode 100644 index 000000000000..2507af710e71 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/booleans.md @@ -0,0 +1,28 @@ +--- +title: Booleans +description: + Delve into the Boolean data type in Noir. Understand its methods, practical examples, and best practices for using Booleans in your Noir programs. +keywords: + [ + noir, + boolean type, + methods, + examples, + logical operations, + ] +sidebar_position: 2 +--- + + +The `bool` type in Noir has two possible values: `true` and `false`: + +```rust +fn main() { + let t = true; + let f: bool = false; +} +``` + +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. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/fields.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/fields.md new file mode 100644 index 000000000000..7f424516ab95 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/fields.md @@ -0,0 +1,260 @@ +--- +title: Fields +description: + Dive deep into the Field data type in Noir. Understand its methods, practical examples, and best practices to effectively use Fields in your Noir programs. +keywords: + [ + noir, + field type, + methods, + examples, + best practices, + ] +sidebar_position: 0 +--- + +The field type corresponds to the native field type of the proving backend. + +The size of a Noir field depends on the elliptic curve's finite field for the proving backend +adopted. For example, a field would be a 254-bit integer when paired with the default backend that +spans the Grumpkin curve. + +Fields support integer arithmetic and are often used as the default numeric type in Noir: + +```rust +fn main(x : Field, y : Field) { + let z = x + y; +} +``` + +`x`, `y` and `z` are all private fields in this example. Using the `let` keyword we defined a new +private value `z` constrained to be equal to `x + y`. + +If proving efficiency is of priority, fields should be used as a default for solving problems. +Smaller integer types (e.g. `u64`) incur extra range constraints. + +## Methods + +After declaring a Field, you can use these common methods on it: + +### to_le_bits + +Transforms the field into an array of bits, Little Endian. + +```rust title="to_le_bits" showLineNumbers +pub fn to_le_bits(self: Self) -> [u1; N] { +``` +> Source code: noir_stdlib/src/field/mod.nr#L60-L62 + + +example: + +```rust title="to_le_bits_example" showLineNumbers +fn test_to_le_bits() { + let field = 2; + let bits: [u1; 8] = field.to_le_bits(); + assert_eq(bits, [0, 1, 0, 0, 0, 0, 0, 0]); + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L356-L362 + + + +### to_be_bits + +Transforms the field into an array of bits, Big Endian. + +```rust title="to_be_bits" showLineNumbers +pub fn to_be_bits(self: Self) -> [u1; N] { +``` +> Source code: noir_stdlib/src/field/mod.nr#L92-L94 + + +example: + +```rust title="to_be_bits_example" showLineNumbers +fn test_to_be_bits() { + let field = 2; + let bits: [u1; 8] = field.to_be_bits(); + assert_eq(bits, [0, 0, 0, 0, 0, 0, 1, 0]); + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L347-L353 + + + +### to_le_bytes + +Transforms into an array of bytes, Little Endian + +```rust title="to_le_bytes" showLineNumbers +pub fn to_le_bytes(self: Self) -> [u8; N] { +``` +> Source code: noir_stdlib/src/field/mod.nr#L124-L126 + + +example: + +```rust title="to_le_bytes_example" showLineNumbers +fn test_to_le_bytes() { + let field = 2; + let bytes: [u8; 8] = field.to_le_bytes(); + assert_eq(bytes, [2, 0, 0, 0, 0, 0, 0, 0]); + assert_eq(Field::from_le_bytes::<8>(bytes), field); + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L375-L382 + + +### to_be_bytes + +Transforms into an array of bytes, Big Endian + +```rust title="to_be_bytes" showLineNumbers +pub fn to_be_bytes(self: Self) -> [u8; N] { +``` +> Source code: noir_stdlib/src/field/mod.nr#L161-L163 + + +example: + +```rust title="to_be_bytes_example" showLineNumbers +fn test_to_be_bytes() { + let field = 2; + let bytes: [u8; 8] = field.to_be_bytes(); + assert_eq(bytes, [0, 0, 0, 0, 0, 0, 0, 2]); + assert_eq(Field::from_be_bytes::<8>(bytes), field); + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L365-L372 + + + +### to_le_radix + +Decomposes into an array over the specified base, Little Endian + +```rust title="to_le_radix" showLineNumbers +pub fn to_le_radix(self: Self, radix: u32) -> [u8; N] { + // Brillig does not need an immediate radix + if !crate::runtime::is_unconstrained() { + static_assert(1 < radix, "radix must be greater than 1"); + static_assert(radix <= 256, "radix must be less than or equal to 256"); + static_assert(radix & (radix - 1) == 0, "radix must be a power of 2"); + } + self.__to_le_radix(radix) + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L189-L199 + + + +example: + +```rust title="to_le_radix_example" showLineNumbers +fn test_to_le_radix() { + // 259, in base 256, little endian, is [3, 1]. + // i.e. 3 * 256^0 + 1 * 256^1 + let field = 259; + + // The radix (in this example, 256) must be a power of 2. + // The length of the returned byte array can be specified to be + // >= the amount of space needed. + let bytes: [u8; 8] = field.to_le_radix(256); + assert_eq(bytes, [3, 1, 0, 0, 0, 0, 0, 0]); + assert_eq(Field::from_le_bytes::<8>(bytes), field); + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L401-L414 + + + +### to_be_radix + +Decomposes into an array over the specified base, Big Endian + +```rust title="to_be_radix" showLineNumbers +pub fn to_be_radix(self: Self, radix: u32) -> [u8; N] { + // Brillig does not need an immediate radix + if !crate::runtime::is_unconstrained() { + crate::assert_constant(radix); + } + self.__to_be_radix(radix) + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L201-L209 + + +example: + +```rust title="to_be_radix_example" showLineNumbers +fn test_to_be_radix() { + // 259, in base 256, big endian, is [1, 3]. + // i.e. 3 * 256^0 + 1 * 256^1 + let field = 259; + + // The radix (in this example, 256) must be a power of 2. + // The length of the returned byte array can be specified to be + // >= the amount of space needed. + let bytes: [u8; 8] = field.to_be_radix(256); + assert_eq(bytes, [0, 0, 0, 0, 0, 0, 1, 3]); + assert_eq(Field::from_be_bytes::<8>(bytes), field); + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L385-L398 + + + +### pow_32 + +Returns the value to the power of the specified exponent + +```rust +fn pow_32(self, exponent: Field) -> Field +``` + +example: + +```rust +fn main() { + let field = 2 + let pow = field.pow_32(4); + assert(pow == 16); +} +``` + +### assert_max_bit_size + +Adds a constraint to specify that the field can be represented with `bit_size` number of bits + +```rust title="assert_max_bit_size" showLineNumbers +pub fn assert_max_bit_size(self) { +``` +> Source code: noir_stdlib/src/field/mod.nr#L10-L12 + + +example: + +```rust +fn main() { + let field = 2 + field.assert_max_bit_size::<32>(); +} +``` + +### sgn0 + +Parity of (prime) Field element, i.e. sgn0(x mod p) = 0 if x ∈ \{0, ..., p-1\} is even, otherwise sgn0(x mod p) = 1. + +```rust +fn sgn0(self) -> u1 +``` + + +### lt + +Returns true if the field is less than the other field + +```rust +pub fn lt(self, another: Field) -> bool +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/function_types.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/function_types.md new file mode 100644 index 000000000000..f6121af17e24 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/function_types.md @@ -0,0 +1,26 @@ +--- +title: Function types +sidebar_position: 10 +--- + +Noir supports higher-order functions. The syntax for a function type is as follows: + +```rust +fn(arg1_type, arg2_type, ...) -> return_type +``` + +Example: + +```rust +fn assert_returns_100(f: fn() -> Field) { // f takes no args and returns a Field + assert(f() == 100); +} + +fn main() { + assert_returns_100(|| 100); // ok + assert_returns_100(|| 150); // fails +} +``` + +A function type also has an optional capture environment - this is necessary to support closures. +See [Lambdas](../lambdas.md) for more details. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/index.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/index.md new file mode 100644 index 000000000000..0f2db2b2d753 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/index.md @@ -0,0 +1,126 @@ +--- +title: Data Types +description: + Get a clear understanding of the two categories of Noir data types - primitive types and compound + types. Learn about their characteristics, differences, and how to use them in your Noir + programming. +keywords: + [ + noir, + data types, + primitive types, + compound types, + private types, + public types, + ] +--- + +Every value in Noir has a type, which determines which operations are valid for it. + +All values in Noir are fundamentally composed of `Field` elements. For a more approachable +developing experience, abstractions are added on top to introduce different data types in Noir. + +Noir has two category of data types: primitive types (e.g. `Field`, integers, `bool`) and compound +types that group primitive types (e.g. arrays, tuples, structs). Each value can either be private or +public. + +## Private & Public Types + +A **private value** is known only to the Prover, while a **public value** is known by both the +Prover and Verifier. Mark values as `private` when the value should only be known to the prover. All +primitive types (including individual fields of compound types) in Noir are private by default, and +can be marked public when certain values are intended to be revealed to the Verifier. + +> **Note:** For public values defined in Noir programs paired with smart contract verifiers, once +> the proofs are verified on-chain the values can be considered known to everyone that has access to +> that blockchain. + +Public data types are treated no differently to private types apart from the fact that their values +will be revealed in proofs generated. Simply changing the value of a public type will not change the +circuit (where the same goes for changing values of private types as well). + +_Private values_ are also referred to as _witnesses_ sometimes. + +> **Note:** The terms private and public when applied to a type (e.g. `pub Field`) have a different +> meaning than when applied to a function (e.g. `pub fn foo() {}`). +> +> The former is a visibility modifier for the Prover to interpret if a value should be made known to +> the Verifier, while the latter is a visibility modifier for the compiler to interpret if a +> function should be made accessible to external Noir programs like in other languages. + +### pub Modifier + +All data types in Noir are private by default. Types are explicitly declared as public using the +`pub` modifier: + +```rust +fn main(x : Field, y : pub Field) -> pub Field { + x + y +} +``` + +In this example, `x` is **private** while `y` and `x + y` (the return value) are **public**. Note +that visibility is handled **per variable**, so it is perfectly valid to have one input that is +private and another that is public. + +> **Note:** Public types can only be declared through parameters on `main`. + +## Type Aliases + +A type alias is a new name for an existing type. Type aliases are declared with the keyword `type`: + +```rust +type Id = u8; + +fn main() { + let id: Id = 1; + let zero: u8 = 0; + assert(zero + 1 == id); +} +``` + +Type aliases can also be used with [generics](../generics.md): + +```rust +type Id = Size; + +fn main() { + let id: Id = 1; + let zero: u32 = 0; + assert(zero + 1 == id); +} +``` + +Type aliases can even refer to other aliases. An error will be issued if they form a cycle: + +```rust +// Ok! +type A = B; +type B = Field; + +type Bad1 = Bad2; + +// error: Dependency cycle found +type Bad2 = Bad1; +// ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2 +``` + +By default, like functions, type aliases are private to the module they exist in. You can use `pub` +to make the type alias public or `pub(crate)` to make it public to just its crate: + +```rust +// This type alias is now public +pub type Id = u8; +``` + +## Wildcard Type +Noir can usually infer the type of the variable from the context, so specifying the type of a variable is only required when it cannot be inferred. However, specifying a complex type can be tedious, especially when it has multiple generic arguments. Often some of the generic types can be inferred from the context, and Noir only needs a hint to properly infer the other types. We can partially specify a variable's type by using `_` as a marker, indicating where we still want the compiler to infer the type. + +```rust +let a: [_; 4] = foo(b); +``` + + +### BigInt + +You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/integers.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/integers.md new file mode 100644 index 000000000000..b8a5d4980296 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/integers.md @@ -0,0 +1,178 @@ +--- +title: Integers +description: Explore the Integer data type in Noir. Learn about its methods, see real-world examples, and grasp how to efficiently use Integers in your Noir code. +keywords: [noir, integer types, methods, examples, arithmetic] +sidebar_position: 1 +--- + +An integer type is a range constrained field type. +The Noir frontend supports both unsigned and signed integer types. +The allowed sizes are 1, 8, 16, 32 and 64 bits. + +:::info + +When an integer is defined in Noir without a specific type, it will default to `Field`. + +The one exception is for loop indices which default to `u64` since comparisons on `Field`s are not possible. + +::: + +## Unsigned Integers + +An unsigned integer type is specified first with the letter `u` (indicating its unsigned nature) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: u8 = 1; + let y: u8 = 1; + let z = x + y; + assert (z == 2); +} +``` + +The bit size determines the maximum value the integer type can store. For example, a `u8` variable can store a value in the range of 0 to 255 (i.e. $\\2^{8}-1\\$). + +## Signed Integers + +A signed integer type is specified first with the letter `i` (which stands for integer) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: i8 = -1; + let y: i8 = -1; + let z = x + y; + assert (z == -2); +} +``` + +The bit size determines the maximum and minimum range of value the integer type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$). + + +```rust +fn main(x: i16, y: i16) { + // modulo + let c = x % y; + let c = x % -13; +} +``` + +Modulo operation is defined for negative integers thanks to integer division, so that the equality `x = (x/y)*y + (x%y)` holds. + +## 128 bits Unsigned Integers + +The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind: +- You cannot cast between a native integer and `U128` +- There is a higher performance cost when using `U128`, compared to a native type. + +Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. + +```rust +fn main() { + let x = U128::from_integer(23); + let y = U128::from_hex("0x7"); + let z = x + y; + assert(z.to_integer() == 30); +} +``` + +`U128` is implemented with two 64 bits limbs, representing the low and high bits, which explains the performance cost. You should expect `U128` to be twice more costly for addition and four times more costly for multiplication. +You can construct a U128 from its limbs: +```rust +fn main(x: u64, y: u64) { + let z = U128::from_u64s_be(x,y); + assert(z.hi == x as Field); + assert(z.lo == y as Field); +} +``` + +Note that the limbs are stored as Field elements in order to avoid unnecessary conversions. +Apart from this, most operations will work as usual: + +```rust +fn main(x: U128, y: U128) { + // multiplication + let c = x * y; + // addition and subtraction + let c = c - x + y; + // division + let c = x / y; + // bit operation; + let c = x & y | y; + // bit shift + let c = x << y; + // comparisons; + let c = x < y; + let c = x == y; +} +``` + +## Overflows + +Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: + +```rust +fn main(x: u8, y: u8) -> pub u8 { + let z = x + y; + z +} +``` + +With: + +```toml +x = "255" +y = "1" +``` + +Would result in: + +``` +$ nargo execute +error: Assertion failed: 'attempt to add with overflow' +┌─ ~/src/main.nr:9:13 +│ +│ let z = x + y; +│ ----- +│ += Call stack: + ... +``` + +A similar error would happen with signed integers: + +```rust +fn main() -> i8 { + let x: i8 = -118; + let y: i8 = -11; + let z = x + y; + z +} +``` + +Note that if a computation ends up being unused the compiler might remove it and it won't end up producing an overflow: + +```rust +fn main() { + // "255 + 1" would overflow, but `z` is unused so no computation happens + let z: u8 = 255 + 1; +} +``` + +### Wrapping methods + +Although integer overflow is expected to error, some use-cases rely on wrapping. For these use-cases, the standard library provides `wrapping` variants of certain common operations: + +```rust +fn wrapping_add(x: T, y: T) -> T; +fn wrapping_sub(x: T, y: T) -> T; +fn wrapping_mul(x: T, y: T) -> T; +``` + +Example of how it is used: + +```rust + +fn main(x: u8, y: u8) -> pub u8 { + std::wrapping_add(x, y) +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/references.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/references.md new file mode 100644 index 000000000000..a5293d11cfb9 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/references.md @@ -0,0 +1,23 @@ +--- +title: References +sidebar_position: 9 +--- + +Noir supports first-class references. References are a bit like pointers: they point to a specific address that can be followed to access the data stored at that address. You can use Rust-like syntax to use pointers in Noir: the `&` operator references the variable, the `*` operator dereferences it. + +Example: + +```rust +fn main() { + let mut x = 2; + + // you can reference x as &mut and pass it to multiplyBy2 + multiplyBy2(&mut x); +} + +// you can access &mut here +fn multiplyBy2(x: &mut Field) { + // and dereference it with * + *x = *x * 2; +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/slices.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/slices.mdx new file mode 100644 index 000000000000..e8091c62dd86 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/slices.mdx @@ -0,0 +1,358 @@ +--- +title: Slices +description: Explore the Slice data type in Noir. Understand its methods, see real-world examples, and learn how to effectively use Slices in your Noir programs. +keywords: [noir, slice type, methods, examples, subarrays] +sidebar_position: 5 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A slice is a dynamically-sized view into a sequence of elements. They can be resized at runtime, but because they don't own the data, they cannot be returned from a circuit. You can treat slices as arrays without a constrained size. + +```rust +fn main() -> pub u32 { + let mut slice: [Field] = &[0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +To write a slice literal, use a preceding ampersand as in: `&[0; 2]` or +`&[1, 2, 3]`. + +It is important to note that slices are not references to arrays. In Noir, +`&[..]` is more similar to an immutable, growable vector. + +View the corresponding test file [here][test-file]. + +[test-file]: https://github.com/noir-lang/noir/blob/f387ec1475129732f72ba294877efdf6857135ac/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for slices: + +### push_back + +Pushes a new element to the end of the slice, returning a new slice with a length one greater than the original unmodified slice. + +```rust +fn push_back(_self: [T], _elem: T) -> [T] +``` + +example: + +```rust +fn main() -> pub Field { + let mut slice: [Field] = &[0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +View the corresponding test file [here][test-file]. + +### push_front + +Returns a new slice with the specified element inserted at index 0. The existing elements indexes are incremented by 1. + +```rust +fn push_front(_self: Self, _elem: T) -> Self +``` + +Example: + +```rust +let mut new_slice: [Field] = &[]; +new_slice = new_slice.push_front(20); +assert(new_slice[0] == 20); // returns true +``` + +View the corresponding test file [here][test-file]. + +### pop_front + +Returns a tuple of two items, the first element of the slice and the rest of the slice. + +```rust +fn pop_front(_self: Self) -> (T, Self) +``` + +Example: + +```rust +let (first_elem, rest_of_slice) = slice.pop_front(); +``` + +View the corresponding test file [here][test-file]. + +### pop_back + +Returns a tuple of two items, the beginning of the slice with the last element omitted and the last element. + +```rust +fn pop_back(_self: Self) -> (Self, T) +``` + +Example: + +```rust +let (popped_slice, last_elem) = slice.pop_back(); +``` + +View the corresponding test file [here][test-file]. + +### append + +Loops over a slice and adds it to the end of another. + +```rust +fn append(mut self, other: Self) -> Self +``` + +Example: + +```rust +let append = &[1, 2].append(&[3, 4, 5]); +``` + +### insert + +Inserts an element at a specified index and shifts all following elements by 1. + +```rust +fn insert(_self: Self, _index: Field, _elem: T) -> Self +``` + +Example: + +```rust +new_slice = rest_of_slice.insert(2, 100); +assert(new_slice[2] == 100); +``` + +View the corresponding test file [here][test-file]. + +### remove + +Remove an element at a specified index, shifting all elements after it to the left, returning the altered slice and the removed element. + +```rust +fn remove(_self: Self, _index: Field) -> (Self, T) +``` + +Example: + +```rust +let (remove_slice, removed_elem) = slice.remove(3); +``` + +### len + +Returns the length of a slice + +```rust +fn len(self) -> Field +``` + +Example: + +```rust +fn main() { + let slice = &[42, 42]; + assert(slice.len() == 2); +} +``` + +### as_array + +Converts this slice into an array. + +Make sure to specify the size of the resulting array. +Panics if the resulting array length is different than the slice's length. + +```rust +fn as_array(self) -> [T; N] +``` + +Example: + +```rust +fn main() { + let slice = &[5, 6]; + + // Always specify the length of the resulting array! + let array: [Field; 2] = slice.as_array(); + + assert(array[0] == slice[0]); + assert(array[1] == slice[1]); +} +``` + +### map + +Applies a function to each element of the slice, returning a new slice containing the mapped elements. + +```rust +fn map(self, f: fn[Env](T) -> U) -> [U] +``` + +example + +```rust +let a = &[1, 2, 3]; +let b = a.map(|a| a * 2); // b is now &[2, 4, 6] +``` + +### fold + +Applies a function to each element of the slice, returning the final accumulated value. The first +parameter is the initial value. + +```rust +fn fold(self, mut accumulator: U, f: fn[Env](U, T) -> U) -> U +``` + +This is a left fold, so the given function will be applied to the accumulator and first element of +the slice, then the second, and so on. For a given call the expected result would be equivalent to: + +```rust +let a1 = &[1]; +let a2 = &[1, 2]; +let a3 = &[1, 2, 3]; + +let f = |a, b| a - b; +a1.fold(10, f) //=> f(10, 1) +a2.fold(10, f) //=> f(f(10, 1), 2) +a3.fold(10, f) //=> f(f(f(10, 1), 2), 3) +``` + +example: + +```rust + +fn main() { + let slice = &[2, 2, 2, 2, 2]; + let folded = slice.fold(0, |a, b| a + b); + assert(folded == 10); +} + +``` + +### reduce + +Same as fold, but uses the first element as the starting element. + +```rust +fn reduce(self, f: fn[Env](T, T) -> T) -> T +``` + +example: + +```rust +fn main() { + let slice = &[2, 2, 2, 2, 2]; + let reduced = slice.reduce(|a, b| a + b); + assert(reduced == 10); +} +``` + +### filter + +Returns a new slice containing only elements for which the given predicate returns true. + +```rust +fn filter(self, f: fn[Env](T) -> bool) -> Self +``` + +example: + +```rust +fn main() { + let slice = &[1, 2, 3, 4, 5]; + let odds = slice.filter(|x| x % 2 == 1); + assert_eq(odds, &[1, 3, 5]); +} +``` + +### join + +Flatten each element in the slice into one value, separated by `separator`. + +Note that although slices implement `Append`, `join` cannot be used on slice +elements since nested slices are prohibited. + +```rust +fn join(self, separator: T) -> T where T: Append +``` + +example: + +```rust +struct Accumulator { + total: Field, +} + +// "Append" two accumulators by adding them +impl Append for Accumulator { + fn empty() -> Self { + Self { total: 0 } + } + + fn append(self, other: Self) -> Self { + Self { total: self.total + other.total } + } +} + +fn main() { + let slice = &[1, 2, 3, 4, 5].map(|total| Accumulator { total }); + + let result = slice.join(Accumulator::empty()); + assert_eq(result, Accumulator { total: 15 }); + + // We can use a non-empty separator to insert additional elements to sum: + let separator = Accumulator { total: 10 }; + let result = slice.join(separator); + assert_eq(result, Accumulator { total: 55 }); +} +``` + +### all + +Returns true if all the elements satisfy the given predicate + +```rust +fn all(self, predicate: fn[Env](T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let slice = &[2, 2, 2, 2, 2]; + let all = slice.all(|a| a == 2); + assert(all); +} +``` + +### any + +Returns true if any of the elements satisfy the given predicate + +```rust +fn any(self, predicate: fn[Env](T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let slice = &[2, 2, 2, 2, 5]; + let any = slice.any(|a| a == 5); + assert(any); +} + +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/strings.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/strings.md new file mode 100644 index 000000000000..b2257e8bdbbb --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/strings.md @@ -0,0 +1,114 @@ +--- +title: Strings +description: + Discover the String data type in Noir. Learn about its methods, see real-world examples, and understand how to effectively manipulate and use Strings in Noir. +keywords: + [ + noir, + string type, + methods, + examples, + concatenation, + ] +sidebar_position: 3 +--- + + +The string type is a fixed length value defined with `str`. + +You can use strings in `assert()` functions or print them with +`println()`. See more about [Logging](../../standard_library/logging.md). + +```rust + +fn main(message : pub str<11>, hex_as_string : str<4>) { + println(message); + assert(message == "hello world"); + assert(hex_as_string == "0x41"); +} +``` + +You can convert a `str` to a byte array by calling `as_bytes()` +or a vector by calling `as_bytes_vec()`. + +```rust +fn main() { + let message = "hello world"; + let message_bytes = message.as_bytes(); + let mut message_vec = message.as_bytes_vec(); + assert(message_bytes.len() == 11); + assert(message_bytes[0] == 104); + assert(message_bytes[0] == message_vec.get(0)); +} +``` + +## Escape characters + +You can use escape characters for your strings: + +| Escape Sequence | Description | +|-----------------|-----------------| +| `\r` | Carriage Return | +| `\n` | Newline | +| `\t` | Tab | +| `\0` | Null Character | +| `\"` | Double Quote | +| `\\` | Backslash | + +Example: + +```rust +let s = "Hello \"world" // prints "Hello "world" +let s = "hey \tyou"; // prints "hey you" +``` + +## Raw strings + +A raw string begins with the letter `r` and is optionally delimited by a number of hashes `#`. + +Escape characters are *not* processed within raw strings. All contents are interpreted literally. + +Example: + +```rust +let s = r"Hello world"; +let s = r#"Simon says "hello world""#; + +// Any number of hashes may be used (>= 1) as long as the string also terminates with the same number of hashes +let s = r#####"One "#, Two "##, Three "###, Four "####, Five will end the string."#####; +``` + +## Format strings + +A format string begins with the letter `f` and allows inserting the value of local and global variables in it. + +Example: + +```rust +let four = 2 + 2; +let s = f"Two plus two is: {four}"; +println(s); +``` + +The output of the above program is: + +```text +Two plus two is: 4 +``` + +To insert the value of a local or global variable, put it inside `{...}` in the string. + +If you need to write the `{` or `}` characters, use `{{` and `}}` respectively: + +```rust +let four = 2 + 2; + +// Prints "This is not expanded: {four}" +println(f"This is not expanded: {{four}}"); +``` + +More complex expressions are not allowed inside `{...}`: + +```rust +let s = f"Two plus two is: {2 + 2}" // Error: invalid format string +``` \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/structs.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/structs.md new file mode 100644 index 000000000000..4b0a8001226d --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/structs.md @@ -0,0 +1,96 @@ +--- +title: Structs +description: + Explore the Struct data type in Noir. Learn about its methods, see real-world examples, and grasp how to effectively define and use Structs in your Noir programs. +keywords: + [ + noir, + struct type, + methods, + examples, + data structures, + ] +sidebar_position: 8 +--- + +A struct also allows for grouping multiple values of different types. Unlike tuples, we can also +name each field. + +> **Note:** The usage of _field_ here refers to each element of the struct and is unrelated to the +> field type of Noir. + +Defining a struct requires giving it a name and listing each field within as `: ` pairs: + +```rust +struct Animal { + hands: Field, + legs: Field, + eyes: u8, +} +``` + +An instance of a struct can then be created with actual values in `: ` pairs in any +order. Struct fields are accessible using their given names: + +```rust +fn main() { + let legs = 4; + + let dog = Animal { + eyes: 2, + hands: 0, + legs, + }; + + let zero = dog.hands; +} +``` + +Structs can also be destructured in a pattern, binding each field to a new variable: + +```rust +fn main() { + let Animal { hands, legs: feet, eyes } = get_octopus(); + + let ten = hands + feet + eyes as Field; +} + +fn get_octopus() -> Animal { + let octopus = Animal { + hands: 0, + legs: 8, + eyes: 2, + }; + + octopus +} +``` + +The new variables can be bound with names different from the original struct field names, as +showcased in the `legs --> feet` binding in the example above. + +### Visibility + +By default, like functions, structs are private to the module they exist in. You can use `pub` +to make the struct public or `pub(crate)` to make it public to just its crate: + +```rust +// This struct is now public +pub struct Animal { + hands: Field, + legs: Field, + eyes: u8, +} +``` + +The same applies to struct fields: by default they are private to the module they exist in, +but they can be made `pub` or `pub(crate)`: + +```rust +// This struct is now public +pub struct Animal { + hands: Field, // private to its module + pub(crate) legs: Field, // accessible from the entire crate + pub eyes: u8, // accessible from anywhere +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/tuples.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/tuples.md new file mode 100644 index 000000000000..2ec5c9c41135 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/tuples.md @@ -0,0 +1,48 @@ +--- +title: Tuples +description: + Dive into the Tuple data type in Noir. Understand its methods, practical examples, and best practices for efficiently using Tuples in your Noir code. +keywords: + [ + noir, + tuple type, + methods, + examples, + multi-value containers, + ] +sidebar_position: 7 +--- + +A tuple collects multiple values like an array, but with the added ability to collect values of +different types: + +```rust +fn main() { + let tup: (u8, u64, Field) = (255, 500, 1000); +} +``` + +One way to access tuple elements is via destructuring using pattern matching: + +```rust +fn main() { + let tup = (1, 2); + + let (one, two) = tup; + + let three = one + two; +} +``` + +Another way to access tuple elements is via direct member access, using a period (`.`) followed by +the index of the element we want to access. Index `0` corresponds to the first tuple element, `1` to +the second and so on: + +```rust +fn main() { + let tup = (5, 6, 7, 8); + + let five = tup.0; + let eight = tup.3; +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/functions.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/functions.md new file mode 100644 index 000000000000..f656cdfd97a1 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/functions.md @@ -0,0 +1,226 @@ +--- +title: Functions +description: + Learn how to declare functions and methods in Noir, a programming language with Rust semantics. + This guide covers parameter declaration, return types, call expressions, and more. +keywords: [Noir, Rust, functions, methods, parameter declaration, return types, call expressions] +sidebar_position: 1 +--- + +Functions in Noir follow the same semantics of Rust, though Noir does not support early returns. + +To declare a function the `fn` keyword is used. + +```rust +fn foo() {} +``` + +By default, functions are visible only within the package they are defined. To make them visible outside of that package (for example, as part of a [library](../modules_packages_crates/crates_and_packages.md#libraries)), you should mark them as `pub`: + +```rust +pub fn foo() {} +``` + +You can also restrict the visibility of the function to only the crate it was defined in, by specifying `pub(crate)`: + +```rust +pub(crate) fn foo() {} //foo can only be called within its crate +``` + +All parameters in a function must have a type and all types are known at compile time. The parameter +is pre-pended with a colon and the parameter type. Multiple parameters are separated using a comma. + +```rust +fn foo(x : Field, y : Field){} +``` + +The return type of a function can be stated by using the `->` arrow notation. The function below +states that the foo function must return a `Field`. If the function returns no value, then the arrow +is omitted. + +```rust +fn foo(x : Field, y : Field) -> Field { + x + y +} +``` + +Note that a `return` keyword is unneeded in this case - the last expression in a function's body is +returned. + +## Main function + +If you're writing a binary, the `main` function is the starting point of your program. You can pass all types of expressions to it, as long as they have a fixed size at compile time: + +```rust +fn main(x : Field) // this is fine: passing a Field +fn main(x : [Field; 2]) // this is also fine: passing a Field with known size at compile-time +fn main(x : (Field, bool)) // 👌: passing a (Field, bool) tuple means size 2 +fn main(x : str<5>) // this is fine, as long as you pass a string of size 5 + +fn main(x : Vec) // can't compile, has variable size +fn main(x : [Field]) // can't compile, has variable size +fn main(....// i think you got it by now +``` + +Keep in mind [tests](../../tooling/testing.md) don't differentiate between `main` and any other function. The following snippet passes tests, but won't compile or prove: + +```rust +fn main(x : [Field]) { + assert(x[0] == 1); +} + +#[test] +fn test_one() { + main(&[1, 2]); +} +``` + +```bash +$ nargo test +[testing] Running 1 test functions +[testing] Testing test_one... ok +[testing] All tests passed + +$ nargo check +The application panicked (crashed). +Message: Cannot have variable sized arrays as a parameter to main +``` + +## Call Expressions + +Calling a function in Noir is executed by using the function name and passing in the necessary +arguments. + +Below we show how to call the `foo` function from the `main` function using a call expression: + +```rust +fn main(x : Field, y : Field) { + let z = foo(x); +} + +fn foo(x : Field) -> Field { + x + x +} +``` + +## Methods + +You can define methods in Noir on any struct type in scope. + +```rust +struct MyStruct { + foo: Field, + bar: Field, +} + +impl MyStruct { + fn new(foo: Field) -> MyStruct { + MyStruct { + foo, + bar: 2, + } + } + + fn sum(self) -> Field { + self.foo + self.bar + } +} + +fn main() { + let s = MyStruct::new(40); + assert(s.sum() == 42); +} +``` + +Methods are just syntactic sugar for functions, so if we wanted to we could also call `sum` as +follows: + +```rust +assert(MyStruct::sum(s) == 42); +``` + +It is also possible to specialize which method is chosen depending on the [generic](./generics.md) type that is used. In this example, the `foo` function returns different values depending on its type: + +```rust +struct Foo {} + +impl Foo { + fn foo(self) -> Field { 1 } +} + +impl Foo { + fn foo(self) -> Field { 2 } +} + +fn main() { + let f1: Foo = Foo{}; + let f2: Foo = Foo{}; + assert(f1.foo() + f2.foo() == 3); +} +``` + +Also note that impls with the same method name defined in them cannot overlap. For example, if we already have `foo` defined for `Foo` and `Foo` like we do above, we cannot also define `foo` in an `impl Foo` since it would be ambiguous which version of `foo` to choose. + +```rust +// Including this impl in the same project as the above snippet would +// cause an overlapping impls error +impl Foo { + fn foo(self) -> Field { 3 } +} +``` + +## Lambdas + +Lambdas are anonymous functions. They follow the syntax of Rust - `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +See [Lambdas](./lambdas.md) for more details. + +## Attributes + +Attributes are metadata that can be applied to a function, using the following syntax: `#[attribute(value)]`. + +Supported attributes include: + +- **builtin**: the function is implemented by the compiler, for efficiency purposes. +- **deprecated**: mark the function as _deprecated_. Calling the function will generate a warning: `warning: use of deprecated function` +- **field**: Used to enable conditional compilation of code depending on the field size. See below for more details +- **oracle**: mark the function as _oracle_; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. +- **test**: mark the function as unit tests. See [Tests](../../tooling/testing.md) for more details + +### Field Attribute + +The field attribute defines which field the function is compatible for. The function is conditionally compiled, under the condition that the field attribute matches the Noir native field. +The field can be defined implicitly, by using the name of the elliptic curve usually associated to it - for instance bn254, bls12_381 - or explicitly by using the field (prime) order, in decimal or hexadecimal form. +As a result, it is possible to define multiple versions of a function with each version specialized for a different field attribute. This can be useful when a function requires different parameters depending on the underlying elliptic curve. + +Example: we define the function `foo()` three times below. Once for the default Noir bn254 curve, once for the field $\mathbb F_{23}$, which will normally never be used by Noir, and once again for the bls12_381 curve. + +```rust +#[field(bn254)] +fn foo() -> u32 { + 1 +} + +#[field(23)] +fn foo() -> u32 { + 2 +} + +// This commented code would not compile as foo would be defined twice because it is the same field as bn254 +// #[field(21888242871839275222246405745257275088548364400416034343698204186575808495617)] +// fn foo() -> u32 { +// 2 +// } + +#[field(bls12_381)] +fn foo() -> u32 { + 3 +} +``` + +If the field name is not known to Noir, it will discard the function. Field names are case insensitive. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/generics.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/generics.md new file mode 100644 index 000000000000..7b3906b682e8 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/generics.md @@ -0,0 +1,262 @@ +--- +title: Generics +description: Learn how to use Generics in Noir +keywords: [Noir, Rust, generics, functions, structs] +sidebar_position: 7 +--- + +Generics allow you to use the same functions with multiple different concrete data types. You can +read more about the concept of generics in the Rust documentation +[here](https://doc.rust-lang.org/book/ch10-01-syntax.html). + +Here is a trivial example showing the identity function that supports any type. In Rust, it is +common to refer to the most general type as `T`. We follow the same convention in Noir. + +```rust +fn id(x: T) -> T { + x +} +``` + +## Numeric Generics + +If we want to be generic over array lengths (which are type-level integers), we can use numeric +generics. Using these looks similar to using regular generics, but introducing them into scope +requires declaring them with `let MyGenericName: IntegerType`. This can be done anywhere a normal +generic is declared. Instead of types, these generics resolve to integers at compile-time. +Here's an example of a struct that is generic over the size of the array it contains internally: + +```rust +struct BigInt { + limbs: [u32; N], +} + +impl BigInt { + // `N` is in scope of all methods in the impl + fn first(first: BigInt, second: BigInt) -> Self { + assert(first.limbs != second.limbs); + first + } + + fn second(first: BigInt, second: Self) -> Self { + assert(first.limbs != second.limbs); + second + } +} +``` + +## In Structs + +Generics are useful for specifying types in structs. For example, we can specify that a field in a +struct will be of a certain generic type. In this case `value` is of type `T`. + +```rust +struct RepeatedValue { + value: T, + count: Field, +} + +impl RepeatedValue { + fn print(self) { + for _i in 0 .. self.count { + println(self.value); + } + } +} + +fn main() { + let repeated = RepeatedValue { value: "Hello!", count: 2 }; + repeated.print(); +} +``` + +The `print` function will print `Hello!` an arbitrary number of times, twice in this case. + +## Calling functions on generic parameters + +Since a generic type `T` can represent any type, how can we call functions on the underlying type? +In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" + +This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over +any type `T` that implements the `Eq` trait for equality: + +```rust +fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool + where T: Eq +{ + if (array1.len() == 0) | (array2.len() == 0) { + true + } else { + array1[0] == array2[0] + } +} + +fn main() { + assert(first_element_is_equal([1, 2, 3], [1, 5, 6])); + + // We can use first_element_is_equal for arrays of any type + // as long as we have an Eq impl for the types we pass in + let array = [MyStruct::new(), MyStruct::new()]; + assert(first_element_is_equal(array, array)); +} + +struct MyStruct { + foo: Field +} + +impl MyStruct { + fn new() -> Self { + MyStruct { foo: 0 } + } +} + +impl Eq for MyStruct { + fn eq(self, other: MyStruct) -> bool { + self.foo == other.foo + } +} +``` + +You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). + +## Manually Specifying Generics with the Turbofish Operator + +There are times when the compiler cannot reasonably infer what type should be used for a generic, or when the developer themselves may want to manually distinguish generic type parameters. This is where the `::<>` turbofish operator comes into play. + +The `::<>` operator can follow a variable or path and can be used to manually specify generic arguments within the angle brackets. +The name "turbofish" comes from that `::<>` looks like a little fish. + +Examples: +```rust +fn main() { + let mut slice = []; + slice = slice.push_back(1); + slice = slice.push_back(2); + // Without turbofish a type annotation would be needed on the left hand side + let array = slice.as_array::<2>(); +} +``` + + +```rust +trait MyTrait { + fn ten() -> Self; +} + +impl MyTrait for Field { + fn ten() -> Self { 10 } +} + +struct Foo { + inner: T +} + +impl Foo { + fn generic_method(_self: Self) -> U where U: MyTrait { + U::ten() + } +} + +fn example() { + let foo: Foo = Foo { inner: 1 }; + // Using a type other than `Field` here (e.g. u32) would fail as + // there is no matching impl for `u32: MyTrait`. + // + // Substituting the `10` on the left hand side of this assert + // with `10 as u32` would fail with a type mismatch as we + // are expecting a `Field` from the right hand side. + assert(10 == foo.generic_method::()); +} +``` + +## Arithmetic Generics + +In addition to numeric generics, Noir also allows a limited form of arithmetic on generics. +When you have a numeric generic such as `N`, you can use the following operators on it in a +type position: `+`, `-`, `*`, `/`, and `%`. + +Note that type checking arithmetic generics is a best effort guess from the compiler and there +are many cases of types that are equal that the compiler may not see as such. For example, +we know that `T * (N + M)` should be equal to `T*N + T*M` but the compiler does not currently +apply the distributive law and thus sees these as different types. + +Even with this limitation though, the compiler can handle common cases decently well: + +```rust +trait Serialize { + fn serialize(self) -> [Field; N]; +} + +impl Serialize<1> for Field { + fn serialize(self) -> [Field; 1] { + [self] + } +} + +impl Serialize for [T; N] + where T: Serialize { .. } + +impl Serialize for (T, U) + where T: Serialize, U: Serialize { .. } + +fn main() { + let data = (1, [2, 3, 4]); + assert_eq(data.serialize().len(), 4); +} +``` + +Note that if there is any over or underflow the types will fail to unify: + +```rust title="underflow-example" showLineNumbers +fn pop(array: [Field; N]) -> [Field; N - 1] { + let mut result: [Field; N - 1] = std::mem::zeroed(); + for i in 0..N - 1 { + result[i] = array[i]; + } + result +} + +fn main() { + // error: Could not determine array length `(0 - 1)` + pop([]); +} +``` +> Source code: test_programs/compile_failure/arithmetic_generics_underflow/src/main.nr#L1-L14 + + +This also applies if there is underflow in an intermediate calculation: + +```rust title="intermediate-underflow-example" showLineNumbers +fn main() { + // From main it looks like there's nothing sketchy going on + seems_fine([]); +} + +// Since `seems_fine` says it can receive and return any length N +fn seems_fine(array: [Field; N]) -> [Field; N] { + // But inside `seems_fine` we pop from the array which + // requires the length to be greater than zero. + + // error: Could not determine array length `(0 - 1)` + push_zero(pop(array)) +} + +fn pop(array: [Field; N]) -> [Field; N - 1] { + let mut result: [Field; N - 1] = std::mem::zeroed(); + for i in 0..N - 1 { + result[i] = array[i]; + } + result +} + +fn push_zero(array: [Field; N]) -> [Field; N + 1] { + let mut result: [Field; N + 1] = std::mem::zeroed(); + for i in 0..N { + result[i] = array[i]; + } + // index N is already zeroed + result +} +``` +> Source code: test_programs/compile_failure/arithmetic_generics_intermediate_underflow/src/main.nr#L1-L32 + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/globals.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/globals.md new file mode 100644 index 000000000000..c64b6c53746e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/globals.md @@ -0,0 +1,82 @@ +--- +title: Global Variables +description: + Learn about global variables in Noir. Discover how + to declare, modify, and use them in your programs. +keywords: [noir programming language, globals, global variables, constants] +sidebar_position: 8 +--- + +## Globals + + +Noir supports global variables. The global's type must be specified by the user: + +```rust +global N: Field = 5; + +global TUPLE: (Field, Field) = (3, 2); + +fn main() { + assert(N == 5); + assert(N == TUPLE.0 + TUPLE.1); +} +``` + +:::info + +Globals can be defined as any expression, so long as they don't depend on themselves - otherwise there would be a dependency cycle! For example: + +```rust +global T: u32 = foo(T); // dependency error +``` + +::: + + +If they are initialized to a literal integer, globals can be used to specify an array's length: + +```rust +global N: u32 = 2; + +fn main(y : [Field; N]) { + assert(y[0] == y[1]) +} +``` + +A global from another module can be imported or referenced externally like any other name: + +```rust +global N: Field = 20; + +fn main() { + assert(my_submodule::N != N); +} + +mod my_submodule { + global N: Field = 10; +} +``` + +When a global is used, Noir replaces the name with its definition on each occurrence. +This means globals defined using function calls will repeat the call each time they're used: + +```rust +global RESULT: [Field; 100] = foo(); + +fn foo() -> [Field; 100] { ... } +``` + +This is usually fine since Noir will generally optimize any function call that does not +refer to a program input into a constant. It should be kept in mind however, if the called +function performs side-effects like `println`, as these will still occur on each use. + +### Visibility + +By default, like functions, globals are private to the module they exist in. You can use `pub` +to make the global public or `pub(crate)` to make it public to just its crate: + +```rust +// This global is now public +pub global N: u32 = 5; +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/lambdas.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/lambdas.md new file mode 100644 index 000000000000..be3c7e0b5caa --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/lambdas.md @@ -0,0 +1,81 @@ +--- +title: Lambdas +description: Learn how to use anonymous functions in Noir programming language. +keywords: [Noir programming language, lambda, closure, function, anonymous function] +sidebar_position: 9 +--- + +## Introduction + +Lambdas are anonymous functions. The syntax is `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +A block can be used as the body of a lambda, allowing you to declare local variables inside it: + +```rust +let cool = || { + let x = 100; + let y = 100; + x + y +} + +assert(cool() == 200); +``` + +## Closures + +Inside the body of a lambda, you can use variables defined in the enclosing function. Such lambdas are called **closures**. In this example `x` is defined inside `main` and is accessed from within the lambda: + +```rust +fn main() { + let x = 100; + let closure = || x + 150; + assert(closure() == 250); +} +``` + +## Passing closures to higher-order functions + +It may catch you by surprise that the following code fails to compile: + +```rust +fn foo(f: fn () -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // error :( +} +``` + +The reason is that the closure's capture environment affects its type - we have a closure that captures two Fields and `foo` +expects a regular function as an argument - those are incompatible. +:::note + +Variables contained within the `||` are the closure's parameters, and the expression that follows it is the closure's body. The capture environment is comprised of any variables used in the closure's body that are not parameters. + +E.g. in |x| x + y, y would be a captured variable, but x would not be, since it is a parameter of the closure. + +::: +The syntax for the type of a closure is `fn[env](args) -> ret_type`, where `env` is the capture environment of the closure - +in this example that's `(Field, Field)`. + +The best solution in our case is to make `foo` generic over the environment type of its parameter, so that it can be called +with closures with any environment, as well as with regular functions: + +```rust +fn foo(f: fn[Env]() -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // compiles fine + assert(foo(|| 60) == 60); // compiles fine +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/mutability.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/mutability.md new file mode 100644 index 000000000000..fdeef6a87c53 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/mutability.md @@ -0,0 +1,121 @@ +--- +title: Mutability +description: + Learn about mutable variables in Noir. Discover how + to declare, modify, and use them in your programs. +keywords: [noir programming language, mutability in noir, mutable variables] +sidebar_position: 8 +--- + +Variables in noir can be declared mutable via the `mut` keyword. Mutable variables can be reassigned +to via an assignment expression. + +```rust +let x = 2; +x = 3; // error: x must be mutable to be assigned to + +let mut y = 3; +let y = 4; // OK +``` + +The `mut` modifier can also apply to patterns: + +```rust +let (a, mut b) = (1, 2); +a = 11; // error: a must be mutable to be assigned to +b = 12; // OK + +let mut (c, d) = (3, 4); +c = 13; // OK +d = 14; // OK + +// etc. +let MyStruct { x: mut y } = MyStruct { x: a }; +// y is now in scope +``` + +Note that mutability in noir is local and everything is passed by value, so if a called function +mutates its parameters then the parent function will keep the old value of the parameters. + +```rust +fn main() -> pub Field { + let x = 3; + helper(x); + x // x is still 3 +} + +fn helper(mut x: i32) { + x = 4; +} +``` + +## Non-local mutability + +Non-local mutability can be achieved through the mutable reference type `&mut T`: + +```rust +fn set_to_zero(x: &mut Field) { + *x = 0; +} + +fn main() { + let mut y = 42; + set_to_zero(&mut y); + assert(*y == 0); +} +``` + +When creating a mutable reference, the original variable being referred to (`y` in this +example) must also be mutable. Since mutable references are a reference type, they must +be explicitly dereferenced via `*` to retrieve the underlying value. Note that this yields +a copy of the value, so mutating this copy will not change the original value behind the +reference: + +```rust +fn main() { + let mut x = 1; + let x_ref = &mut x; + + let mut y = *x_ref; + let y_ref = &mut y; + + x = 2; + *x_ref = 3; + + y = 4; + *y_ref = 5; + + assert(x == 3); + assert(*x_ref == 3); + assert(y == 5); + assert(*y_ref == 5); +} +``` + +Note that types in Noir are actually deeply immutable so the copy that occurs when +dereferencing is only a conceptual copy - no additional constraints will occur. + +Mutable references can also be stored within structs. Note that there is also +no lifetime parameter on these unlike rust. This is because the allocated memory +always lasts the entire program - as if it were an array of one element. + +```rust +struct Foo { + x: &mut Field +} + +impl Foo { + fn incr(mut self) { + *self.x += 1; + } +} + +fn main() { + let foo = Foo { x: &mut 0 }; + foo.incr(); + assert(*foo.x == 1); +} +``` + +In general, you should avoid non-local & shared mutability unless it is needed. Sticking +to only local mutability will improve readability and potentially improve compiler optimizations as well. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/ops.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/ops.md new file mode 100644 index 000000000000..c35c36c38a90 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/ops.md @@ -0,0 +1,98 @@ +--- +title: Logical Operations +description: + Learn about the supported arithmetic and logical operations in the Noir programming language. + Discover how to perform operations on private input types, integers, and booleans. +keywords: + [ + Noir programming language, + supported operations, + arithmetic operations, + logical operations, + predicate operators, + bitwise operations, + short-circuiting, + backend, + ] +sidebar_position: 3 +--- + +# Operations + +## Table of Supported Operations + +| Operation | Description | Requirements | +| :-------- | :------------------------------------------------------------: | -------------------------------------: | +| + | Adds two private input types together | Types must be private input | +| - | Subtracts two private input types together | Types must be private input | +| \* | Multiplies two private input types together | Types must be private input | +| / | Divides two private input types together | Types must be private input | +| ^ | XOR two private input types together | Types must be integer | +| & | AND two private input types together | Types must be integer | +| \| | OR two private input types together | Types must be integer | +| \<\< | Left shift an integer by another integer amount | Types must be integer, shift must be u8 | +| >> | Right shift an integer by another integer amount | Types must be integer, shift must be u8 | +| ! | Bitwise not of a value | Type must be integer or boolean | +| \< | returns a bool if one value is less than the other | Upper bound must have a known bit size | +| \<= | returns a bool if one value is less than or equal to the other | Upper bound must have a known bit size | +| > | returns a bool if one value is more than the other | Upper bound must have a known bit size | +| >= | returns a bool if one value is more than or equal to the other | Upper bound must have a known bit size | +| == | returns a bool if one value is equal to the other | Both types must not be constants | +| != | returns a bool if one value is not equal to the other | Both types must not be constants | + +### Predicate Operators + +`<,<=, !=, == , >, >=` are known as predicate/comparison operations because they compare two values. +This differs from the operations such as `+` where the operands are used in _computation_. + +### Bitwise Operations Example + +```rust +fn main(x : Field) { + let y = x as u32; + let z = y & y; +} +``` + +`z` is implicitly constrained to be the result of `y & y`. The `&` operand is used to denote bitwise +`&`. + +> `x & x` would not compile as `x` is a `Field` and not an integer type. + +### Logical Operators + +Noir has no support for the logical operators `||` and `&&`. This is because encoding the +short-circuiting that these operators require can be inefficient for Noir's backend. Instead you can +use the bitwise operators `|` and `&` which operate identically for booleans, just without the +short-circuiting. + +```rust +let my_val = 5; + +let mut flag = 1; +if (my_val > 6) | (my_val == 0) { + flag = 0; +} +assert(flag == 1); + +if (my_val != 10) & (my_val < 50) { + flag = 0; +} +assert(flag == 0); +``` + +### Shorthand operators + +Noir shorthand operators for most of the above operators, namely `+=, -=, *=, /=, %=, &=, |=, ^=, <<=`, and `>>=`. These allow for more concise syntax. For example: + +```rust +let mut i = 0; +i = i + 1; +``` + +could be written as: + +```rust +let mut i = 0; +i += 1; +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/oracles.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/oracles.mdx new file mode 100644 index 000000000000..77a2ac1550ac --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/oracles.mdx @@ -0,0 +1,29 @@ +--- +title: Oracles +description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide. +keywords: + - Noir + - Oracles + - RPC Calls + - Unconstrained Functions + - Programming + - Blockchain +sidebar_position: 6 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation. + +Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md) + +You can declare an Oracle through the `#[oracle()]` flag. Example: + +```rust +#[oracle(get_number_sequence)] +unconstrained fn get_number_sequence(_size: Field) -> [Field] {} +``` + +The timeout for when using an external RPC oracle resolver can be set with the `NARGO_FOREIGN_CALL_TIMEOUT` environment variable. This timeout is in units of milliseconds. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/shadowing.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/shadowing.md new file mode 100644 index 000000000000..5ce6130d2011 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/shadowing.md @@ -0,0 +1,44 @@ +--- +title: Shadowing +sidebar_position: 12 +--- + +Noir allows for inheriting variables' values and re-declaring them with the same name similar to Rust, known as shadowing. + +For example, the following function is valid in Noir: + +```rust +fn main() { + let x = 5; + + { + let x = x * 2; + assert (x == 10); + } + + assert (x == 5); +} +``` + +In this example, a variable x is first defined with the value 5. + +The local scope that follows shadows the original x, i.e. creates a local mutable x based on the value of the original x. It is given a value of 2 times the original x. + +When we return to the main scope, x once again refers to just the original x, which stays at the value of 5. + +## Temporal mutability + +One way that shadowing is useful, in addition to ergonomics across scopes, is for temporarily mutating variables. + +```rust +fn main() { + let age = 30; + // age = age + 5; // Would error as `age` is immutable by default. + + let mut age = age + 5; // Temporarily mutates `age` with a new value. + + let age = age; // Locks `age`'s mutability again. + + assert (age == 35); +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/traits.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/traits.md new file mode 100644 index 000000000000..17cc04a97518 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/traits.md @@ -0,0 +1,611 @@ +--- +title: Traits +description: + Traits in Noir can be used to abstract out a common interface for functions across + several data types. +keywords: [noir programming language, traits, interfaces, generic, protocol] +sidebar_position: 14 +--- + +## Overview + +Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines +the interface of several methods contained within the trait. Types can then implement this trait by providing +implementations for these methods. For example in the program: + +```rust +struct Rectangle { + width: Field, + height: Field, +} + +impl Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +fn log_area(r: Rectangle) { + println(r.area()); +} +``` + +We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this +function to work on `Triangle`s as well?: + +```rust +struct Triangle { + width: Field, + height: Field, +} + +impl Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can +introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`: + +```rust +trait Area { + fn area(self) -> Field; +} + +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing +impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined +by the `Area` trait. + +```rust +impl Area for Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +impl Area for Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Now we have a working program that is generic over any type of Shape that is used! Others can even use this program +as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types. + +## Where Clauses + +As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements +a trait, we can add a where clause to the generic function. + +```rust +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+` +operator. Similarly, we can have multiple trait constraints by separating each with a comma: + +```rust +fn foo(elements: [T], thing: U) where + T: Default + Add + Eq, + U: Bar, +{ + let mut sum = T::default(); + + for element in elements { + sum += element; + } + + if sum == T::default() { + thing.bar(); + } +} +``` + +## Invoking trait methods + +As seen in the previous section, the `area` method was invoked on a type `T` that had a where clause `T: Area`. + +To invoke `area` on a type that directly implements the trait `Area`, the trait must be in scope (imported): + +```rust +use geometry::Rectangle; + +fn main() { + let rectangle = Rectangle { width: 1, height: 2}; + let area = rectangle.area(); // Error: the compiler doesn't know which `area` method this is +} +``` + +The above program errors because there might be multiple traits with an `area` method, all implemented +by `Rectangle`, and it's not clear which one should be used. + +To make the above program compile, the trait must be imported: + +```rust +use geometry::Rectangle; +use geometry::Area; // Bring the Area trait into scope + +fn main() { + let rectangle = Rectangle { width: 1, height: 2}; + let area = rectangle.area(); // OK: will use `area` from `geometry::Area` +} +``` + +An error will also be produced if multiple traits with an `area` method are in scope. If both traits +are needed in a file you can use the fully-qualified path to the trait: + +```rust +use geometry::Rectangle; + +fn main() { + let rectangle = Rectangle { width: 1, height: 2}; + let area = geometry::Area::area(rectangle); +} +``` + +## Generic Implementations + +You can add generics to a trait implementation by adding the generic list after the `impl` keyword: + +```rust +trait Second { + fn second(self) -> Field; +} + +impl Second for (T, Field) { + fn second(self) -> Field { + self.1 + } +} +``` + +You can also implement a trait for every type this way: + +```rust +trait Debug { + fn debug(self); +} + +impl Debug for T { + fn debug(self) { + println(self); + } +} + +fn main() { + 1.debug(); +} +``` + +### Generic Trait Implementations With Where Clauses + +Where clauses can be placed on trait implementations themselves to restrict generics in a similar way. +For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar` +will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types. +For example, here is the implementation for array equality: + +```rust +impl Eq for [T; let N: u32] where T: Eq { + // Test if two arrays have the same elements. + // Because both arrays must have length N, we know their lengths already match. + fn eq(self, other: Self) -> bool { + let mut result = true; + + for i in 0 .. self.len() { + // The T: Eq constraint is needed to call == on the array elements here + result &= self[i] == other[i]; + } + + result + } +} +``` + +Where clauses can also be placed on struct implementations. +For example, here is a method utilizing a generic type that implements the equality trait. + +```rust +struct Foo { + a: u32, + b: T, +} + +impl Foo where T: Eq { + fn eq(self, other: Self) -> bool { + (self.a == other.a) & self.b.eq(other.b) + } +} +``` + +## Generic Traits + +Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in +scope of every item within the trait. + +```rust +trait Into { + // Convert `self` to type `T` + fn into(self) -> T; +} +``` + +When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime +when referencing a generic trait (e.g. in a `where` clause). + +```rust +struct MyStruct { + array: [Field; 2], +} + +impl Into<[Field; 2]> for MyStruct { + fn into(self) -> [Field; 2] { + self.array + } +} + +fn as_array(x: T) -> [Field; 2] + where T: Into<[Field; 2]> +{ + x.into() +} + +fn main() { + let array = [1, 2]; + let my_struct = MyStruct { array }; + + assert_eq(as_array(my_struct), array); +} +``` + +### Associated Types and Constants + +Traits also support associated types and constraints which can be thought of as additional generics that are referred to by name. + +Here's an example of a trait with an associated type `Foo` and a constant `Bar`: + +```rust +trait MyTrait { + type Foo; + + let Bar: u32; +} +``` + +Now when we're implementing `MyTrait` we also have to provide values for `Foo` and `Bar`: + +```rust +impl MyTrait for Field { + type Foo = i32; + + let Bar: u32 = 11; +} +``` + +Since associated constants can also be used in a type position, its values are limited to only other +expression kinds allowed in numeric generics. + +When writing a trait constraint, you can specify all associated types and constants explicitly if +you wish: + +```rust +fn foo(x: T) where T: MyTrait { + ... +} +``` + +Or you can also elide them since there should only be one `Foo` and `Bar` for a given implementation +of `MyTrait` for a type: + +```rust +fn foo(x: T) where T: MyTrait { + ... +} +``` + +If you elide associated types, you can still refer to them via the type as trait syntax ``: + +```rust +fn foo(x: T) where + T: MyTrait, + ::Foo: Default + Eq +{ + let foo_value: ::Foo = Default::default(); + assert_eq(foo_value, foo_value); +} +``` + +## Trait Methods With No `self` + +A trait can contain any number of methods, each of which have access to the `Self` type which represents each type +that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used. +For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type +but doesn't need to take any parameters: + +```rust +trait Default { + fn default() -> Self; +} +``` + +Implementing this trait can be done similarly to any other trait: + +```rust +impl Default for Field { + fn default() -> Field { + 0 + } +} + +struct MyType {} + +impl Default for MyType { + fn default() -> Field { + MyType {} + } +} +``` + +However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`. +Instead, we'll need to refer to the function directly. This can be done either by referring to the +specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later +case, type inference determines the impl that is selected. + +```rust +let my_struct = MyStruct::default(); + +let x: Field = Default::default(); +let result = x + Default::default(); +``` + +## Default Method Implementations + +A trait can also have default implementations of its methods by giving a body to the desired functions. +Note that this body must be valid for all types that may implement the trait. As a result, the only +valid operations on `self` will be operations valid for any type or other operations on the trait itself. + +```rust +trait Numeric { + fn add(self, other: Self) -> Self; + + // Default implementation of double is (self + self) + fn double(self) -> Self { + self.add(self) + } +} +``` + +When implementing a trait with default functions, a type may choose to implement only the required functions: + +```rust +impl Numeric for Field { + fn add(self, other: Field) -> Field { + self + other + } +} +``` + +Or it may implement the optional methods as well: + +```rust +impl Numeric for u32 { + fn add(self, other: u32) -> u32 { + self + other + } + + fn double(self) -> u32 { + self * 2 + } +} +``` + +## Impl Specialization + +When implementing traits for a generic type it is possible to implement the trait for only a certain combination +of generics. This can be either as an optimization or because those specific generics are required to implement the trait. + +```rust +trait Sub { + fn sub(self, other: Self) -> Self; +} + +struct NonZero { + value: T, +} + +impl Sub for NonZero { + fn sub(self, other: Self) -> Self { + let value = self.value - other.value; + assert(value != 0); + NonZero { value } + } +} +``` + +## Overlapping Implementations + +Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous. +This means if a trait `Foo` is already implemented +by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other +type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create +any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given +method call. + +```rust +trait Trait {} + +// Previous impl defined here +impl Trait for (A, B) {} + +// error: Impl for type `(Field, Field)` overlaps with existing impl +impl Trait for (Field, Field) {} +``` + +## Trait Coherence + +Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create +impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same +program. + +The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared +in the crate the impl is in. + +In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does +not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate. +While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its +own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the +library your choices are to either submit a patch to the library or use the newtype pattern. + +### The Newtype Pattern + +The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type +that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create +impls for any trait we need on it. + +```rust +struct Wrapper { + foo: some_library::Foo, +} + +impl Default for Wrapper { + fn default() -> Wrapper { + Wrapper { + foo: some_library::Foo::new(), + } + } +} +``` + +Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated +to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and +unwrapping of values when converting to and from the `Wrapper` and `Foo` types. + +### Trait Inheritance + +Sometimes, you might need one trait to use another trait’s functionality (like "inheritance" in some other languages). In this case, you can specify this relationship by listing any child traits after the parent trait's name and a colon. Now, whenever the parent trait is implemented it will require the child traits to be implemented as well. A parent trait is also called a "super trait." + +```rust +trait Person { + fn name(self) -> String; +} + +// Person is a supertrait of Student. +// Implementing Student requires you to also impl Person. +trait Student: Person { + fn university(self) -> String; +} + +trait Programmer { + fn fav_language(self) -> String; +} + +// CompSciStudent (computer science student) is a subtrait of both Programmer +// and Student. Implementing CompSciStudent requires you to impl both supertraits. +trait CompSciStudent: Programmer + Student { + fn git_username(self) -> String; +} +``` + +### Trait Aliases + +Similar to the proposed Rust feature for [trait aliases](https://github.com/rust-lang/rust/blob/4d215e2426d52ca8d1af166d5f6b5e172afbff67/src/doc/unstable-book/src/language-features/trait-alias.md), +Noir supports aliasing one or more traits and using those aliases wherever +traits would normally be used. + +```rust +trait Foo { + fn foo(self) -> Self; +} + +trait Bar { + fn bar(self) -> Self; +} + +// Equivalent to: +// trait Baz: Foo + Bar {} +// +// impl Baz for T where T: Foo + Bar {} +trait Baz = Foo + Bar; + +// We can use `Baz` to refer to `Foo + Bar` +fn baz(x: T) -> T where T: Baz { + x.foo().bar() +} +``` + +#### Generic Trait Aliases + +Trait aliases can also be generic by placing the generic arguments after the +trait name. These generics are in scope of every item within the trait alias. + +```rust +trait Foo { + fn foo(self) -> Self; +} + +trait Bar { + fn bar(self) -> T; +} + +// Equivalent to: +// trait Baz: Foo + Bar {} +// +// impl Baz for U where U: Foo + Bar {} +trait Baz = Foo + Bar; +``` + +#### Trait Alias Where Clauses + +Trait aliases support where clauses to add trait constraints to any of their +generic arguments, e.g. ensuring `T: Baz` for a trait alias `Qux`. + +```rust +trait Foo { + fn foo(self) -> Self; +} + +trait Bar { + fn bar(self) -> T; +} + +trait Baz { + fn baz(self) -> bool; +} + +// Equivalent to: +// trait Qux: Foo + Bar where T: Baz {} +// +// impl Qux for U where +// U: Foo + Bar, +// T: Baz, +// {} +trait Qux = Foo + Bar where T: Baz; +``` + +Note that while trait aliases support where clauses, +the equivalent traits can fail due to [#6467](https://github.com/noir-lang/noir/issues/6467) + +### Visibility + +By default, like functions, traits and trait aliases are private to the module +they exist in. You can use `pub` to make the trait public or `pub(crate)` to make +it public to just its crate: + +```rust +// This trait is now public +pub trait Trait {} + +// This trait alias is now public +pub trait Baz = Foo + Bar; +``` + +Trait methods have the same visibility as the trait they are in. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/unconstrained.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/unconstrained.md new file mode 100644 index 000000000000..467253bed0d2 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/unconstrained.md @@ -0,0 +1,104 @@ +--- +title: Unconstrained Functions +description: "Learn about what unconstrained functions in Noir are, how to use them and when you'd want to." + +keywords: [Noir programming language, unconstrained, open] +sidebar_position: 5 +--- + +Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic computation. + +## Why? + +Zero-knowledge (ZK) domain-specific languages (DSL) enable developers to generate ZK proofs from their programs by compiling code down to the constraints of an NP complete language (such as R1CS or PLONKish languages). However, the hard bounds of a constraint system can be very limiting to the functionality of a ZK DSL. + +Enabling a circuit language to perform unconstrained execution is a powerful tool. Said another way, unconstrained execution lets developers generate witnesses from code that does not generate any constraints. Being able to execute logic outside of a circuit is critical for both circuit performance and constructing proofs on information that is external to a circuit. + +Fetching information from somewhere external to a circuit can also be used to enable developers to improve circuit efficiency. + +A ZK DSL does not just prove computation, but proves that some computation was handled correctly. Thus, it is necessary that when we switch from performing some operation directly inside of a circuit to inside of an unconstrained environment that the appropriate constraints are still laid down elsewhere in the circuit. + +## Example + +An in depth example might help drive the point home. This example comes from the excellent [post](https://discord.com/channels/1113924620781883405/1124022445054111926/1128747641853972590) by Tom in the Noir Discord. + +Let's look at how we can optimize a function to turn a `u72` into an array of `u8`s. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u72 & 0xff) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 91 +Backend circuit size: 3619 +``` + +A lot of the operations in this function are optimized away by the compiler (all the bit-shifts turn into divisions by constants). However we can save a bunch of gates by casting to u8 a bit earlier. This automatically truncates the bit-shifted value to fit in a u8 which allows us to remove the AND against 0xff. This saves us ~480 gates in total. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 75 +Backend circuit size: 3143 +``` + +Those are some nice savings already but we can do better. This code is all constrained so we're proving every step of calculating out using num, but we don't actually care about how we calculate this, just that it's correct. This is where brillig comes in. + +It turns out that truncating a u72 into a u8 is hard to do inside a snark, each time we do as u8 we lay down 4 ACIR opcodes which get converted into multiple gates. It's actually much easier to calculate num from out than the other way around. All we need to do is multiply each element of out by a constant and add them all together, both relatively easy operations inside a snark. + +We can then run `u72_to_u8` as unconstrained brillig code in order to calculate out, then use that result in our constrained function and assert that if we were to do the reverse calculation we'd get back num. This looks a little like the below: + +```rust +fn main(num: u72) -> pub [u8; 8] { + // Safety: 'out' is properly constrained below in 'assert(num == reconstructed_num);' + let out = unsafe { u72_to_u8(num) }; + + let mut reconstructed_num: u72 = 0; + for i in 0..8 { + reconstructed_num += (out[i] as u72 << (56 - (8 * i))); + } + assert(num == reconstructed_num); + out +} + +unconstrained fn u72_to_u8(num: u72) -> [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8))) as u8; + } + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 78 +Backend circuit size: 2902 +``` + +This ends up taking off another ~250 gates from our circuit! We've ended up with more ACIR opcodes than before but they're easier for the backend to prove (resulting in fewer gates). + +Note that in order to invoke unconstrained functions we need to wrap them in an `unsafe` block, +to make it clear that the call is unconstrained. +Furthermore, a warning is emitted unless the `unsafe` block is commented with a `// Safety: ...` comment explaining why it is fine to call the unconstrained function. Note that either the `unsafe` block can be commented this way or the statement it exists in (like in the `let` example above). + +Generally we want to use brillig whenever there's something that's easy to verify but hard to compute within the circuit. For example, if you wanted to calculate a square root of a number it'll be a much better idea to calculate this in brillig and then assert that if you square the result you get back your number. + +## Break and Continue + +In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow.md#break-and-continue) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/_category_.json new file mode 100644 index 000000000000..1debcfe76753 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Modules, Packages and Crates", + "position": 2, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/crates_and_packages.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/crates_and_packages.md new file mode 100644 index 000000000000..8881130be634 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/crates_and_packages.md @@ -0,0 +1,43 @@ +--- +title: Crates and Packages +description: Learn how to use Crates and Packages in your Noir project +keywords: [Nargo, dependencies, package management, crates, package] +sidebar_position: 0 +--- + +## Crates + +A crate is the smallest amount of code that the Noir compiler considers at a time. +Crates can contain modules, and the modules may be defined in other files that get compiled with the crate, as we’ll see in the coming sections. + +### Crate Types + +A Noir crate can come in several forms: binaries, libraries or contracts. + +#### Binaries + +_Binary crates_ are programs which you can compile to an ACIR circuit which you can then create proofs against. Each must have a function called `main` that defines the ACIR circuit which is to be proved. + +#### Libraries + +_Library crates_ don't have a `main` function and they don't compile down to ACIR. Instead they define functionality intended to be shared with multiple projects, and eventually included in a binary crate. + +#### Contracts + +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts). + +### Crate Root + +Every crate has a root, which is the source file that the compiler starts, this is also known as the root module. The Noir compiler does not enforce any conditions on the name of the file which is the crate root, however if you are compiling via Nargo the crate root must be called `lib.nr` or `main.nr` for library or binary crates respectively. + +## Packages + +A Nargo _package_ is a collection of one or more crates that provides a set of functionality. A package must include a Nargo.toml file. + +A package _must_ contain either a library or a binary crate, but not both. + +### Differences from Cargo Packages + +One notable difference between Rust's Cargo and Noir's Nargo is that while Cargo allows a package to contain an unlimited number of binary crates and a single library crate, Nargo currently only allows a package to contain a single crate. + +In future this restriction may be lifted to allow a Nargo package to contain both a binary and library crate or multiple binary crates. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/dependencies.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/dependencies.md new file mode 100644 index 000000000000..22186b225988 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/dependencies.md @@ -0,0 +1,122 @@ +--- +title: Dependencies +description: + Learn how to specify and manage dependencies in Nargo, allowing you to upload packages to GitHub + and use them easily in your project. +keywords: [Nargo, dependencies, GitHub, package management, versioning] +sidebar_position: 1 +--- + +Nargo allows you to upload packages to GitHub and use them as dependencies. + +## Specifying a dependency + +Specifying a dependency requires a tag to a specific commit and the git url to the url containing +the package. + +Currently, there are no requirements on the tag contents. If requirements are added, it would follow +semver 2.0 guidelines. + +> Note: Without a `tag` , there would be no versioning and dependencies would change each time you +> compile your project. + +For example, to add the [ecrecover-noir library](https://github.com/colinnielsen/ecrecover-noir) to your project, add it to `Nargo.toml`: + +```toml +# Nargo.toml + +[dependencies] +ecrecover = {tag = "v0.8.0", git = "https://github.com/colinnielsen/ecrecover-noir"} +``` + +If the module is in a subdirectory, you can define a subdirectory in your git repository, for example: + +```toml +# Nargo.toml + +[dependencies] +easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "noir-contracts/contracts/easy_private_token_contract"} +``` + +## Specifying a local dependency + +You can also specify dependencies that are local to your machine. + +For example, this file structure has a library and binary crate + +```tree +├── binary_crate +│   ├── Nargo.toml +│   └── src +│   └── main.nr +└── lib_a + ├── Nargo.toml + └── src + └── lib.nr +``` + +Inside of the binary crate, you can specify: + +```toml +# Nargo.toml + +[dependencies] +lib_a = { path = "../lib_a" } +``` + +## Importing dependencies + +You can import a dependency to a Noir file using the following syntax. For example, to import the +ecrecover-noir library and local lib_a referenced above: + +```rust +use ecrecover; +use lib_a; +``` + +You can also import only the specific parts of dependency that you want to use, like so: + +```rust +use std::hash::sha256; +use std::scalar_mul::fixed_base_embedded_curve; +``` + +Lastly, You can import multiple items in the same line by enclosing them in curly braces: + +```rust +use std::hash::{keccak256, sha256}; +``` + +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +## Dependencies of Dependencies + +Note that when you import a dependency, you also get access to all of the dependencies of that package. + +For example, the [phy_vector](https://github.com/resurgencelabs/phy_vector) library imports an [fraction](https://github.com/resurgencelabs/fraction) library. If you're importing the phy_vector library, then you can access the functions in fractions library like so: + +```rust +use phy_vector; + +fn main(x : Field, y : pub Field) { + //... + let f = phy_vector::fraction::toFraction(true, 2, 1); + //... +} +``` + +## Available Libraries + +Noir does not currently have an official package manager. You can find a list of available Noir libraries in the [awesome-noir repo here](https://github.com/noir-lang/awesome-noir#libraries). + +Some libraries that are available today include: + +- [Standard Library](https://github.com/noir-lang/noir/tree/master/noir_stdlib) - the Noir Standard Library +- [Ethereum Storage Proof Verification](https://github.com/aragonzkresearch/noir-trie-proofs) - a library that contains the primitives necessary for RLP decoding (in the form of look-up table construction) and Ethereum state and storage proof verification (or verification of any trie proof involving 32-byte long keys) +- [BigInt](https://github.com/shuklaayush/noir-bigint) - a library that provides a custom BigUint56 data type, allowing for computations on large unsigned integers +- [ECrecover](https://github.com/colinnielsen/ecrecover-noir/tree/main) - a library to verify an ECDSA signature and return the source Ethereum address +- [Sparse Merkle Tree Verifier](https://github.com/vocdoni/smtverifier-noir/tree/main) - a library for verification of sparse Merkle trees +- [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir +- [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/modules.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/modules.md new file mode 100644 index 000000000000..14aa1f0579ac --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/modules.md @@ -0,0 +1,221 @@ +--- +title: Modules +description: + Learn how to organize your files using modules in Noir, following the same convention as Rust's + module system. Examples included. +keywords: [Noir, Rust, modules, organizing files, sub-modules] +sidebar_position: 2 +--- + +Noir's module system follows the same convention as the _newer_ version of Rust's module system. + +## Purpose of Modules + +Modules are used to organize files. Without modules all of your code would need to live in a single +file. In Noir, the compiler does not automatically scan all of your files to detect modules. This +must be done explicitly by the developer. + +## Examples + +### Importing a module in the crate root + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::hello_world(); +} +``` + +Filename : `src/foo.nr` + +```rust +fn from_foo() {} +``` + +In the above snippet, the crate root is the `src/main.nr` file. The compiler sees the module +declaration `mod foo` which prompts it to look for a foo.nr file. + +Visually this module hierarchy looks like the following : + +``` +crate + ├── main + │ + └── foo + └── from_foo + +``` + +The module filename may also be the name of the module as a directory with the contents in a +file named `mod.nr` within that directory. The above example can alternatively be expressed like this: + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::hello_world(); +} +``` + +Filename : `src/foo/mod.nr` + +```rust +fn from_foo() {} +``` + +Note that it's an error to have both files `src/foo.nr` and `src/foo/mod.nr` in the filesystem. + +### Importing a module throughout the tree + +All modules are accessible from the `crate::` namespace. + +``` +crate + ├── bar + ├── foo + └── main + +``` + +In the above snippet, if `bar` would like to use functions in `foo`, it can do so by `use crate::foo::function_name`. + +### Sub-modules + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::from_foo(); +} +``` + +Filename : `src/foo.nr` + +```rust +mod bar; +fn from_foo() {} +``` + +Filename : `src/foo/bar.nr` + +```rust +fn from_bar() {} +``` + +In the above snippet, we have added an extra module to the module tree; `bar`. `bar` is a submodule +of `foo` hence we declare bar in `foo.nr` with `mod bar`. Since `foo` is not the crate root, the +compiler looks for the file associated with the `bar` module in `src/foo/bar.nr` + +Visually the module hierarchy looks as follows: + +``` +crate + ├── main + │ + └── foo + ├── from_foo + └── bar + └── from_bar +``` + +Similar to importing a module in the crate root, modules can be placed in a `mod.nr` file, like this: + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::from_foo(); +} +``` + +Filename : `src/foo/mod.nr` + +```rust +mod bar; +fn from_foo() {} +``` + +Filename : `src/foo/bar/mod.nr` + +```rust +fn from_bar() {} +``` + +### Referencing a parent module + +Given a submodule, you can refer to its parent module using the `super` keyword. + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::from_foo(); +} +``` + +Filename : `src/foo.nr` + +```rust +mod bar; + +fn from_foo() {} +``` + +Filename : `src/foo/bar.nr` + +```rust +// Same as bar::from_foo +use super::from_foo; + +fn from_bar() { + from_foo(); // invokes super::from_foo(), which is bar::from_foo() + super::from_foo(); // also invokes bar::from_foo() +} +``` + +### `use` visibility + +`use` declarations are private to the containing module, by default. However, like functions, +they can be marked as `pub` or `pub(crate)`. Such a use declaration serves to _re-export_ a name. +A public `use` declaration can therefore redirect some public name to a different target definition: +even a definition with a private canonical path, inside a different module. + +An example of re-exporting: + +```rust +mod some_module { + pub use foo::{bar, baz}; + mod foo { + pub fn bar() {} + pub fn baz() {} + } +} + +fn main() { + some_module::bar(); + some_module::baz(); +} +``` + +In this example, the module `some_module` re-exports two public names defined in `foo`. + +### Visibility + +By default, like functions, modules are private to the module (or crate) they exist in. You can use `pub` +to make the module public or `pub(crate)` to make it public to just its crate: + +```rust +// This module is now public and can be seen by other crates. +pub mod foo; +``` \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/workspaces.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/workspaces.md new file mode 100644 index 000000000000..05213be0222e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/workspaces.md @@ -0,0 +1,46 @@ +--- +title: Workspaces +sidebar_position: 3 +--- + +Workspaces are a feature of nargo that allow you to manage multiple related Noir packages in a single repository. A workspace is essentially a group of related projects that share common build output directories and configurations. + +Each Noir project (with its own Nargo.toml file) can be thought of as a package. Each package is expected to contain exactly one "named circuit", being the "name" defined in Nargo.toml with the program logic defined in `./src/main.nr`. + +For a project with the following structure: + +```tree +├── crates +│ ├── a +│ │ ├── Nargo.toml +│ │ └── Prover.toml +│ │ └── src +│ │ └── main.nr +│ └── b +│ ├── Nargo.toml +│ └── Prover.toml +│ └── src +│ └── main.nr +│ +└── Nargo.toml +``` + +You can define a workspace in Nargo.toml like so: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +`members` indicates which packages are included in the workspace. As such, all member packages of a workspace will be processed when the `--workspace` flag is used with various commands or if a `default-member` is not specified. The `--package` option can be used to limit +the scope of some commands to a specific member of the workspace; otherwise these commands run on the package nearest on the path to the +current directory where `nargo` was invoked. + +`default-member` indicates which package various commands process by default. + +Libraries can be defined in a workspace. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +Please note that nesting regular packages is not supported: certain commands work on the workspace level and will use the topmost Nargo.toml file they can find on the path; unless this is a workspace configuration with `members`, the command might run on some unintended package. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/_category_.json new file mode 100644 index 000000000000..af04c0933fdb --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Standard Library", + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/black_box_fns.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/black_box_fns.md new file mode 100644 index 000000000000..e9392b20a92f --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/black_box_fns.md @@ -0,0 +1,31 @@ +--- +title: Black Box Functions +description: Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. +keywords: [noir, black box functions] +--- + +Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. This makes certain zk-snark unfriendly computations cheaper than if they were implemented in Noir. + +The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not. + +## Function list + +Here is a list of the current black box functions: + +- [AES128](./cryptographic_primitives/ciphers.mdx#aes128) +- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) +- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) +- [Blake3](./cryptographic_primitives/hashes.mdx#blake3) +- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) +- [Embedded curve operations (MSM, addition, ...)](./cryptographic_primitives/embedded_curve_ops.mdx) +- AND +- XOR +- RANGE +- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) +- [Recursive proof verification](./recursion.mdx) + +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. + +You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/bn254.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/bn254.md new file mode 100644 index 000000000000..3294f005dbb4 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/bn254.md @@ -0,0 +1,46 @@ +--- +title: Bn254 Field Library +--- + +Noir provides a module in standard library with some optimized functions for bn254 Fr in `std::field::bn254`. + +## decompose + +```rust +fn decompose(x: Field) -> (Field, Field) {} +``` + +Decomposes a single field into two fields, low and high. The low field contains the lower 16 bytes of the input field and the high field contains the upper 16 bytes of the input field. Both field results are range checked to 128 bits. + + +## assert_gt + +```rust +fn assert_gt(a: Field, b: Field) {} +``` + +Asserts that a > b. This will generate less constraints than using `assert(gt(a, b))`. + +## assert_lt + +```rust +fn assert_lt(a: Field, b: Field) {} +``` + +Asserts that a < b. This will generate less constraints than using `assert(lt(a, b))`. + +## gt + +```rust +fn gt(a: Field, b: Field) -> bool {} +``` + +Returns true if a > b. + +## lt + +```rust +fn lt(a: Field, b: Field) -> bool {} +``` + +Returns true if a < b. \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/boundedvec.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/boundedvec.md new file mode 100644 index 000000000000..ba75e45564ab --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/boundedvec.md @@ -0,0 +1,479 @@ +--- +title: Bounded Vectors +keywords: [noir, vector, bounded vector, slice] +sidebar_position: 1 +--- + +A `BoundedVec` is a growable storage similar to a `Vec` except that it +is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented +via slices and thus is not subject to the same restrictions slices are (notably, nested +slices - and thus nested vectors as well - are disallowed). + +Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by +pushing an additional element is also more efficient - the length only needs to be increased +by one. + +For these reasons `BoundedVec` should generally be preferred over `Vec` when there +is a reasonable maximum bound that can be placed on the vector. + +Example: + +```rust +let mut vector: BoundedVec = BoundedVec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +assert(vector.max_len() == 10); +``` + +## Methods + +### new + +```rust +pub fn new() -> Self +``` + +Creates a new, empty vector of length zero. + +Since this container is backed by an array internally, it still needs an initial value +to give each element. To resolve this, each element is zeroed internally. This value +is guaranteed to be inaccessible unless `get_unchecked` is used. + +Example: + +```rust +let empty_vector: BoundedVec = BoundedVec::new(); +assert(empty_vector.len() == 0); +``` + +Note that whenever calling `new` the maximum length of the vector should always be specified +via a type signature: + +```rust title="new_example" showLineNumbers +fn good() -> BoundedVec { + // Ok! MaxLen is specified with a type annotation + let v1: BoundedVec = BoundedVec::new(); + let v2 = BoundedVec::new(); + + // Ok! MaxLen is known from the type of `good`'s return value + v2 +} + +fn bad() { + // Error: Type annotation needed + // The compiler can't infer `MaxLen` from this code. + let mut v3 = BoundedVec::new(); + v3.push(5); +} +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L11-L27 + + +This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions +but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a constraint failure at runtime when the vec is pushed to. + +### get + +```rust +pub fn get(self, index: u64) -> T { +``` + +Retrieves an element from the vector at the given index, starting from zero. + +If the given index is equal to or greater than the length of the vector, this +will issue a constraint failure. + +Example: + +```rust +fn foo(v: BoundedVec) { + let first = v.get(0); + let last = v.get(v.len() - 1); + assert(first != last); +} +``` + +### get_unchecked + +```rust +pub fn get_unchecked(self, index: u64) -> T { +``` + +Retrieves an element from the vector at the given index, starting from zero, without +performing a bounds check. + +Since this function does not perform a bounds check on length before accessing the element, +it is unsafe! Use at your own risk! + +Example: + +```rust title="get_unchecked_example" showLineNumbers +fn sum_of_first_three(v: BoundedVec) -> u32 { + // Always ensure the length is larger than the largest + // index passed to get_unchecked + assert(v.len() > 2); + let first = v.get_unchecked(0); + let second = v.get_unchecked(1); + let third = v.get_unchecked(2); + first + second + third +} +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L54-L64 + + +### set + +```rust +pub fn set(&mut self: Self, index: u64, value: T) { +``` + +Writes an element to the vector at the given index, starting from zero. + +If the given index is equal to or greater than the length of the vector, this will issue a constraint failure. + +Example: + +```rust +fn foo(v: BoundedVec) { + let first = v.get(0); + assert(first != 42); + v.set(0, 42); + let new_first = v.get(0); + assert(new_first == 42); +} +``` + +### set_unchecked + +```rust +pub fn set_unchecked(&mut self: Self, index: u64, value: T) -> T { +``` + +Writes an element to the vector at the given index, starting from zero, without performing a bounds check. + +Since this function does not perform a bounds check on length before accessing the element, it is unsafe! Use at your own risk! + +Example: + +```rust title="set_unchecked_example" showLineNumbers +fn set_unchecked_example() { + let mut vec: BoundedVec = BoundedVec::new(); + vec.extend_from_array([1, 2]); + + // Here we're safely writing within the valid range of `vec` + // `vec` now has the value [42, 2] + vec.set_unchecked(0, 42); + + // We can then safely read this value back out of `vec`. + // Notice that we use the checked version of `get` which would prevent reading unsafe values. + assert_eq(vec.get(0), 42); + + // We've now written past the end of `vec`. + // As this index is still within the maximum potential length of `v`, + // it won't cause a constraint failure. + vec.set_unchecked(2, 42); + println(vec); + + // This will write past the end of the maximum potential length of `vec`, + // it will then trigger a constraint failure. + vec.set_unchecked(5, 42); + println(vec); +} +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L67-L91 + + + +### push + +```rust +pub fn push(&mut self, elem: T) { +``` + +Pushes an element to the end of the vector. This increases the length +of the vector by one. + +Panics if the new length of the vector will be greater than the max length. + +Example: + +```rust title="bounded-vec-push-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + v.push(1); + v.push(2); + + // Panics with failed assertion "push out of bounds" + v.push(3); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L95-L103 + + +### pop + +```rust +pub fn pop(&mut self) -> T +``` + +Pops the element at the end of the vector. This will decrease the length +of the vector by one. + +Panics if the vector is empty. + +Example: + +```rust title="bounded-vec-pop-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + v.push(1); + v.push(2); + + let two = v.pop(); + let one = v.pop(); + + assert(two == 2); + assert(one == 1); + // error: cannot pop from an empty vector + // let _ = v.pop(); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L108-L120 + + +### len + +```rust +pub fn len(self) -> u64 { +``` + +Returns the current length of this vector + +Example: + +```rust title="bounded-vec-len-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + assert(v.len() == 0); + + v.push(100); + assert(v.len() == 1); + + v.push(200); + v.push(300); + v.push(400); + assert(v.len() == 4); + + let _ = v.pop(); + let _ = v.pop(); + assert(v.len() == 2); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L125-L140 + + +### max_len + +```rust +pub fn max_len(_self: BoundedVec) -> u64 { +``` + +Returns the maximum length of this vector. This is always +equal to the `MaxLen` parameter this vector was initialized with. + +Example: + +```rust title="bounded-vec-max-len-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + assert(v.max_len() == 5); + v.push(10); + assert(v.max_len() == 5); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L145-L151 + + +### storage + +```rust +pub fn storage(self) -> [T; MaxLen] { +``` + +Returns the internal array within this vector. +Since arrays in Noir are immutable, mutating the returned storage array will not mutate +the storage held internally by this vector. + +Note that uninitialized elements may be zeroed out! + +Example: + +```rust title="bounded-vec-storage-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + assert(v.storage() == [0, 0, 0, 0, 0]); + + v.push(57); + assert(v.storage() == [57, 0, 0, 0, 0]); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L156-L163 + + +### extend_from_array + +```rust +pub fn extend_from_array(&mut self, array: [T; Len]) +``` + +Pushes each element from the given array to this vector. + +Panics if pushing each element would cause the length of this vector +to exceed the maximum length. + +Example: + +```rust title="bounded-vec-extend-from-array-example" showLineNumbers +let mut vec: BoundedVec = BoundedVec::new(); + vec.extend_from_array([2, 4]); + + assert(vec.len == 2); + assert(vec.get(0) == 2); + assert(vec.get(1) == 4); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L168-L175 + + +### extend_from_bounded_vec + +```rust +pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) +``` + +Pushes each element from the other vector to this vector. The length of +the other vector is left unchanged. + +Panics if pushing each element would cause the length of this vector +to exceed the maximum length. + +Example: + +```rust title="bounded-vec-extend-from-bounded-vec-example" showLineNumbers +let mut v1: BoundedVec = BoundedVec::new(); + let mut v2: BoundedVec = BoundedVec::new(); + + v2.extend_from_array([1, 2, 3]); + v1.extend_from_bounded_vec(v2); + + assert(v1.storage() == [1, 2, 3, 0, 0]); + assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L180-L189 + + +### from_array + +```rust +pub fn from_array(array: [T; Len]) -> Self +``` + +Creates a new vector, populating it with values derived from an array input. +The maximum length of the vector is determined based on the type signature. + +Example: +```rust +let bounded_vec: BoundedVec = BoundedVec::from_array([1, 2, 3]) +``` + +### from_parts + +```rust +pub fn from_parts(mut array: [T; MaxLen], len: u32) -> Self +``` + +Creates a new BoundedVec from the given array and length. +The given length must be less than or equal to the length of the array. + +This function will zero out any elements at or past index `len` of `array`. +This incurs an extra runtime cost of O(MaxLen). If you are sure your array is +zeroed after that index, you can use `from_parts_unchecked` to remove the extra loop. + +Example: + +```rust title="from-parts" showLineNumbers +let vec: BoundedVec = BoundedVec::from_parts([1, 2, 3, 0], 3); + assert_eq(vec.len(), 3); + + // Any elements past the given length are zeroed out, so these + // two BoundedVecs will be completely equal + let vec1: BoundedVec = BoundedVec::from_parts([1, 2, 3, 1], 3); + let vec2: BoundedVec = BoundedVec::from_parts([1, 2, 3, 2], 3); + assert_eq(vec1, vec2); +``` +> Source code: noir_stdlib/src/collections/bounded_vec.nr#L693-L702 + + +### from_parts_unchecked + +```rust +pub fn from_parts_unchecked(array: [T; MaxLen], len: u32) -> Self +``` + +Creates a new BoundedVec from the given array and length. +The given length must be less than or equal to the length of the array. + +This function is unsafe because it expects all elements past the `len` index +of `array` to be zeroed, but does not check for this internally. Use `from_parts` +for a safe version of this function which does zero out any indices past the +given length. Invalidating this assumption can notably cause `BoundedVec::eq` +to give incorrect results since it will check even elements past `len`. + +Example: + +```rust title="from-parts-unchecked" showLineNumbers +let vec: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 0], 3); + assert_eq(vec.len(), 3); + + // invalid use! + let vec1: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 1], 3); + let vec2: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 2], 3); + + // both vecs have length 3 so we'd expect them to be equal, but this + // fails because elements past the length are still checked in eq + assert(vec1 != vec2); +``` +> Source code: noir_stdlib/src/collections/bounded_vec.nr#L707-L718 + + +### map + +```rust +pub fn map(self, f: fn[Env](T) -> U) -> BoundedVec +``` + +Creates a new vector of equal size by calling a closure on each element in this vector. + +Example: + +```rust title="bounded-vec-map-example" showLineNumbers +let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]); + let result = vec.map(|value| value * 2); +``` +> Source code: noir_stdlib/src/collections/bounded_vec.nr#L580-L583 + + +### any + +```rust +pub fn any(self, predicate: fn[Env](T) -> bool) -> bool +``` + +Returns true if the given predicate returns true for any element +in this vector. + +Example: + +```rust title="bounded-vec-any-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + v.extend_from_array([2, 4, 6]); + + let all_even = !v.any(|elem: u32| elem % 2 != 0); + assert(all_even); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L256-L262 + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/hashmap.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/hashmap.md new file mode 100644 index 000000000000..810baad16ba0 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/hashmap.md @@ -0,0 +1,587 @@ +--- +title: HashMap +keywords: [noir, map, hash, hashmap] +sidebar_position: 1 +--- + +`HashMap` is used to efficiently store and look up key-value pairs. + +`HashMap` is a bounded type which can store anywhere from zero to `MaxLen` total elements. +Note that due to hash collisions, the actual maximum number of elements stored by any particular +hashmap is likely lower than `MaxLen`. This is true even with cryptographic hash functions since +every hash value will be performed modulo `MaxLen`. + +Example: + +```rust +// Create a mapping from Fields to u32s with a maximum length of 12 +// using a poseidon2 hasher +use std::hash::poseidon2::Poseidon2Hasher; +let mut map: HashMap> = HashMap::default(); + +map.insert(1, 2); +map.insert(3, 4); + +let two = map.get(1).unwrap(); +``` + +## Methods + +### default + +```rust title="default" showLineNumbers +impl Default for HashMap +where + B: BuildHasher + Default, + H: Hasher + Default, +{ + /// Constructs an empty HashMap. + /// + /// Example: + /// + /// ```noir + /// let hashmap: HashMap> = HashMap::default(); + /// assert(hashmap.is_empty()); + /// ``` + fn default() -> Self { +``` +> Source code: noir_stdlib/src/collections/map.nr#L688-L703 + + +Creates a fresh, empty HashMap. + +When using this function, always make sure to specify the maximum size of the hash map. + +This is the same `default` from the `Default` implementation given further below. It is +repeated here for convenience since it is the recommended way to create a hashmap. + +Example: + +```rust title="default_example" showLineNumbers +let hashmap: HashMap> = HashMap::default(); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L208-L211 + + +Because `HashMap` has so many generic arguments that are likely to be the same throughout +your program, it may be helpful to create a type alias: + +```rust title="type_alias" showLineNumbers +type MyMap = HashMap>; +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L202-L204 + + +### with_hasher + +```rust title="with_hasher" showLineNumbers +pub fn with_hasher(_build_hasher: B) -> Self + where + B: BuildHasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L103-L108 + + +Creates a hashmap with an existing `BuildHasher`. This can be used to ensure multiple +hashmaps are created with the same hasher instance. + +Example: + +```rust title="with_hasher_example" showLineNumbers +let my_hasher: BuildHasherDefault = Default::default(); + let hashmap: HashMap> = + HashMap::with_hasher(my_hasher); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L212-L217 + + +### get + +```rust title="get" showLineNumbers +pub fn get(self, key: K) -> Option + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L472-L479 + + +Retrieves a value from the hashmap, returning `Option::none()` if it was not found. + +Example: + +```rust title="get_example" showLineNumbers +fn get_example(map: HashMap>) { + let x = map.get(12); + + if x.is_some() { + assert(x.unwrap() == 42); + } +} +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L297-L305 + + +### insert + +```rust title="insert" showLineNumbers +pub fn insert(&mut self, key: K, value: V) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L514-L521 + + +Inserts a new key-value pair into the map. If the key was already in the map, its +previous value will be overridden with the newly provided one. + +Example: + +```rust title="insert_example" showLineNumbers +let mut map: HashMap> = HashMap::default(); + map.insert(12, 42); + assert(map.len() == 1); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L218-L222 + + +### remove + +```rust title="remove" showLineNumbers +pub fn remove(&mut self, key: K) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L570-L577 + + +Removes the given key-value pair from the map. If the key was not already present +in the map, this does nothing. + +Example: + +```rust title="remove_example" showLineNumbers +map.remove(12); + assert(map.is_empty()); + + // If a key was not present in the map, remove does nothing + map.remove(12); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L225-L232 + + +### is_empty + +```rust title="is_empty" showLineNumbers +pub fn is_empty(self) -> bool { +``` +> Source code: noir_stdlib/src/collections/map.nr#L167-L169 + + +True if the length of the hash map is empty. + +Example: + +```rust title="is_empty_example" showLineNumbers +assert(map.is_empty()); + + map.insert(1, 2); + assert(!map.is_empty()); + + map.remove(1); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L233-L241 + + +### len + +```rust title="len" showLineNumbers +pub fn len(self) -> u32 { +``` +> Source code: noir_stdlib/src/collections/map.nr#L431-L433 + + +Returns the current length of this hash map. + +Example: + +```rust title="len_example" showLineNumbers +// This is equivalent to checking map.is_empty() + assert(map.len() == 0); + + map.insert(1, 2); + map.insert(3, 4); + map.insert(5, 6); + assert(map.len() == 3); + + // 3 was already present as a key in the hash map, so the length is unchanged + map.insert(3, 7); + assert(map.len() == 3); + + map.remove(1); + assert(map.len() == 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L242-L257 + + +### capacity + +```rust title="capacity" showLineNumbers +pub fn capacity(_self: Self) -> u32 { +``` +> Source code: noir_stdlib/src/collections/map.nr#L453-L455 + + +Returns the maximum capacity of this hashmap. This is always equal to the capacity +specified in the hashmap's type. + +Unlike hashmaps in general purpose programming languages, hashmaps in Noir have a +static capacity that does not increase as the map grows larger. Thus, this capacity +is also the maximum possible element count that can be inserted into the hashmap. +Due to hash collisions (modulo the hashmap length), it is likely the actual maximum +element count will be lower than the full capacity. + +Example: + +```rust title="capacity_example" showLineNumbers +let empty_map: HashMap> = + HashMap::default(); + assert(empty_map.len() == 0); + assert(empty_map.capacity() == 42); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L258-L263 + + +### clear + +```rust title="clear" showLineNumbers +pub fn clear(&mut self) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L123-L125 + + +Clears the hashmap, removing all key-value pairs from it. + +Example: + +```rust title="clear_example" showLineNumbers +assert(!map.is_empty()); + map.clear(); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L264-L268 + + +### contains_key + +```rust title="contains_key" showLineNumbers +pub fn contains_key(self, key: K) -> bool + where + K: Hash + Eq, + B: BuildHasher, + H: Hasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L143-L150 + + +True if the hashmap contains the given key. Unlike `get`, this will not also return +the value associated with the key. + +Example: + +```rust title="contains_key_example" showLineNumbers +if map.contains_key(7) { + let value = map.get(7); + assert(value.is_some()); + } else { + println("No value for key 7!"); + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L269-L276 + + +### entries + +```rust title="entries" showLineNumbers +pub fn entries(self) -> BoundedVec<(K, V), N> { +``` +> Source code: noir_stdlib/src/collections/map.nr#L191-L193 + + +Returns a vector of each key-value pair present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="entries_example" showLineNumbers +let entries = map.entries(); + + // The length of a hashmap may not be compile-time known, so we + // need to loop over its capacity instead + for i in 0..map.capacity() { + if i < entries.len() { + let (key, value) = entries.get(i); + println(f"{key} -> {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L308-L319 + + +### keys + +```rust title="keys" showLineNumbers +pub fn keys(self) -> BoundedVec { +``` +> Source code: noir_stdlib/src/collections/map.nr#L230-L232 + + +Returns a vector of each key present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="keys_example" showLineNumbers +let keys = map.keys(); + + for i in 0..keys.max_len() { + if i < keys.len() { + let key = keys.get_unchecked(i); + let value = map.get(key).unwrap_unchecked(); + println(f"{key} -> {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L320-L330 + + +### values + +```rust title="values" showLineNumbers +pub fn values(self) -> BoundedVec { +``` +> Source code: noir_stdlib/src/collections/map.nr#L267-L269 + + +Returns a vector of each value present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="values_example" showLineNumbers +let values = map.values(); + + for i in 0..values.max_len() { + if i < values.len() { + let value = values.get_unchecked(i); + println(f"Found value {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L331-L340 + + +### iter_mut + +```rust title="iter_mut" showLineNumbers +pub fn iter_mut(&mut self, f: fn(K, V) -> (K, V)) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L304-L311 + + +Iterates through each key-value pair of the HashMap, setting each key-value pair to the +result returned from the given function. + +Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated +through. If this is not desired, use `iter_values_mut` if only values need to be mutated, +or `entries` if neither keys nor values need to be mutated. + +The iteration order is left unspecified. As a result, if two keys are mutated to become +equal, which of the two values that will be present for the key in the resulting map is also unspecified. + +Example: + +```rust title="iter_mut_example" showLineNumbers +// Add 1 to each key in the map, and double the value associated with that key. + map.iter_mut(|k, v| (k + 1, v * 2)); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L344-L347 + + +### iter_keys_mut + +```rust title="iter_keys_mut" showLineNumbers +pub fn iter_keys_mut(&mut self, f: fn(K) -> K) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L342-L349 + + +Iterates through the HashMap, mutating each key to the result returned from +the given function. + +Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated +through. If only iteration is desired and the keys are not intended to be mutated, +prefer using `entries` instead. + +The iteration order is left unspecified. As a result, if two keys are mutated to become +equal, which of the two values that will be present for the key in the resulting map is also unspecified. + +Example: + +```rust title="iter_keys_mut_example" showLineNumbers +// Double each key, leaving the value associated with that key untouched + map.iter_keys_mut(|k| k * 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L348-L351 + + +### iter_values_mut + +```rust title="iter_values_mut" showLineNumbers +pub fn iter_values_mut(&mut self, f: fn(V) -> V) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L374-L376 + + +Iterates through the HashMap, applying the given function to each value and mutating the +value to equal the result. This function is more efficient than `iter_mut` and `iter_keys_mut` +because the keys are untouched and the underlying hashmap thus does not need to be reordered. + +Example: + +```rust title="iter_values_mut_example" showLineNumbers +// Halve each value + map.iter_values_mut(|v| v / 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L352-L355 + + +### retain + +```rust title="retain" showLineNumbers +pub fn retain(&mut self, f: fn(K, V) -> bool) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L395-L397 + + +Retains only the key-value pairs for which the given function returns true. +Any key-value pairs for which the function returns false will be removed from the map. + +Example: + +```rust title="retain_example" showLineNumbers +map.retain(|k, v| (k != 0) & (v != 0)); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L280-L282 + + +## Trait Implementations + +### default + +```rust title="default" showLineNumbers +impl Default for HashMap +where + B: BuildHasher + Default, + H: Hasher + Default, +{ + /// Constructs an empty HashMap. + /// + /// Example: + /// + /// ```noir + /// let hashmap: HashMap> = HashMap::default(); + /// assert(hashmap.is_empty()); + /// ``` + fn default() -> Self { +``` +> Source code: noir_stdlib/src/collections/map.nr#L688-L703 + + +Constructs an empty HashMap. + +Example: + +```rust title="default_example" showLineNumbers +let hashmap: HashMap> = HashMap::default(); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L208-L211 + + +### eq + +```rust title="eq" showLineNumbers +impl Eq for HashMap +where + K: Eq + Hash, + V: Eq, + B: BuildHasher, + H: Hasher, +{ + /// Checks if two HashMaps are equal. + /// + /// Example: + /// + /// ```noir + /// let mut map1: HashMap> = HashMap::default(); + /// let mut map2: HashMap> = HashMap::default(); + /// + /// map1.insert(1, 2); + /// map1.insert(3, 4); + /// + /// map2.insert(3, 4); + /// map2.insert(1, 2); + /// + /// assert(map1 == map2); + /// ``` + fn eq(self, other: HashMap) -> bool { +``` +> Source code: noir_stdlib/src/collections/map.nr#L636-L661 + + +Checks if two HashMaps are equal. + +Example: + +```rust title="eq_example" showLineNumbers +let mut map1: HashMap> = HashMap::default(); + let mut map2: HashMap> = HashMap::default(); + + map1.insert(1, 2); + map1.insert(3, 4); + + map2.insert(3, 4); + map2.insert(1, 2); + + assert(map1 == map2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L283-L294 + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/index.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/index.md new file mode 100644 index 000000000000..ea84c6d5c21e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/index.md @@ -0,0 +1,5 @@ +--- +title: Containers +description: Container types provided by Noir's standard library for storing and retrieving data +keywords: [containers, data types, vec, hashmap] +--- diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/vec.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/vec.mdx new file mode 100644 index 000000000000..475011922f81 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/vec.mdx @@ -0,0 +1,170 @@ +--- +title: Vectors +description: Delve into the Vec data type in Noir. Learn about its methods, practical examples, and best practices for using Vectors in your Noir code. +keywords: [noir, vector type, methods, examples, dynamic arrays] +sidebar_position: 6 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A vector is a collection type similar to Rust's `Vec` type. In Noir, it is a convenient way to use slices as mutable arrays. + +Example: + +```rust +let mut vector: Vec = Vec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +``` + +## Methods + +### new + +Creates a new, empty vector. + +```rust +pub fn new() -> Self +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` + +### from_slice + +Creates a vector containing each element from a given slice. Mutations to the resulting vector will not affect the original slice. + +```rust +pub fn from_slice(slice: [T]) -> Self +``` + +Example: + +```rust +let slice: [Field] = &[1, 2, 3]; +let vector_from_slice = Vec::from_slice(slice); +assert(vector_from_slice.len() == 3); +``` + +### len + +Returns the number of elements in the vector. + +```rust +pub fn len(self) -> Field +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` + +### get + +Retrieves an element from the vector at a given index. Panics if the index points beyond the vector's end. + +```rust +pub fn get(self, index: Field) -> T +``` + +Example: + +```rust +let vector: Vec = Vec::from_slice(&[10, 20, 30]); +assert(vector.get(1) == 20); +``` + +### set + +```rust +pub fn set(&mut self: Self, index: u64, value: T) { +``` + +Writes an element to the vector at the given index, starting from zero. + +Panics if the index points beyond the vector's end. + +Example: + +```rust +let vector: Vec = Vec::from_slice(&[10, 20, 30]); +assert(vector.get(1) == 20); +vector.set(1, 42); +assert(vector.get(1) == 42); +``` + +### push + +Adds a new element to the vector's end, returning a new vector with a length one greater than the original unmodified vector. + +```rust +pub fn push(&mut self, elem: T) +``` + +Example: + +```rust +let mut vector: Vec = Vec::new(); +vector.push(10); +assert(vector.len() == 1); +``` + +### pop + +Removes an element from the vector's end, returning a new vector with a length one less than the original vector, along with the removed element. Panics if the vector's length is zero. + +```rust +pub fn pop(&mut self) -> T +``` + +Example: + +```rust +let mut vector = Vec::from_slice(&[10, 20]); +let popped_elem = vector.pop(); +assert(popped_elem == 20); +assert(vector.len() == 1); +``` + +### insert + +Inserts an element at a specified index, shifting subsequent elements to the right. + +```rust +pub fn insert(&mut self, index: Field, elem: T) +``` + +Example: + +```rust +let mut vector = Vec::from_slice(&[10, 30]); +vector.insert(1, 20); +assert(vector.get(1) == 20); +``` + +### remove + +Removes an element at a specified index, shifting subsequent elements to the left, and returns the removed element. + +```rust +pub fn remove(&mut self, index: Field) -> T +``` + +Example: + +```rust +let mut vector = Vec::from_slice(&[10, 20, 30]); +let removed_elem = vector.remove(1); +assert(removed_elem == 20); +assert(vector.len() == 2); +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/_category_.json new file mode 100644 index 000000000000..5d694210bbf3 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ciphers.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ciphers.mdx new file mode 100644 index 000000000000..9f82890a92a2 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ciphers.mdx @@ -0,0 +1,36 @@ +--- +title: Ciphers +description: + Learn about the implemented ciphers ready to use for any Noir project +keywords: + [ciphers, Noir project, aes128, encrypt] +sidebar_position: 0 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; + +## aes128 + +Given a plaintext as an array of bytes, returns the corresponding aes128 ciphertext (CBC mode). Input padding is automatically performed using PKCS#7, so that the output length is `input.len() + (16 - input.len() % 16)`. + +```rust title="aes128" showLineNumbers +pub fn aes128_encrypt( + input: [u8; N], + iv: [u8; 16], + key: [u8; 16], +) -> [u8; N + 16 - N % 16] {} +``` +> Source code: noir_stdlib/src/aes128.nr#L2-L8 + + +```rust +fn main() { + let input: [u8; 4] = [0, 12, 3, 15] // Random bytes, will be padded to 16 bytes. + let iv: [u8; 16] = [0; 16]; // Initialisation vector + let key: [u8; 16] = [0; 16] // AES key + let ciphertext = std::aes128::aes128_encrypt(inputs.as_bytes(), iv.as_bytes(), key.as_bytes()); // In this case, the output length will be 16 bytes. +} +``` + + + \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx new file mode 100644 index 000000000000..8d96027b42cb --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx @@ -0,0 +1,98 @@ +--- +title: ECDSA Signature Verification +description: Learn about the cryptographic primitives regarding ECDSA over the secp256k1 and secp256r1 curves +keywords: [cryptographic primitives, Noir project, ecdsa, secp256k1, secp256r1, signatures] +sidebar_position: 3 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; + +Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. + +## ecdsa_secp256k1::verify_signature + +Verifier for ECDSA Secp256k1 signatures. +See ecdsa_secp256k1::verify_signature_slice for a version that accepts slices directly. + +```rust title="ecdsa_secp256k1" showLineNumbers +pub fn verify_signature( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N], +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 + + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + + + +## ecdsa_secp256k1::verify_signature_slice + +Verifier for ECDSA Secp256k1 signatures where the message is a slice. + +```rust title="ecdsa_secp256k1_slice" showLineNumbers +pub fn verify_signature_slice( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8], +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L13-L20 + + + + +## ecdsa_secp256r1::verify_signature + +Verifier for ECDSA Secp256r1 signatures. +See ecdsa_secp256r1::verify_signature_slice for a version that accepts slices directly. + +```rust title="ecdsa_secp256r1" showLineNumbers +pub fn verify_signature( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N], +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 + + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256r1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + + + +## ecdsa_secp256r1::verify_signature + +Verifier for ECDSA Secp256r1 signatures where the message is a slice. + +```rust title="ecdsa_secp256r1_slice" showLineNumbers +pub fn verify_signature_slice( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8], +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L13-L20 + + + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx new file mode 100644 index 000000000000..482a36932b94 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx @@ -0,0 +1,95 @@ +--- +title: Scalar multiplication +description: See how you can perform scalar multiplication in Noir +keywords: [cryptographic primitives, Noir project, scalar multiplication] +sidebar_position: 1 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; + +The following functions perform operations over the embedded curve whose coordinates are defined by the configured noir field. +For the BN254 scalar field, this is BabyJubJub or Grumpkin. + +:::note +Suffixes `_low` and `_high` denote low and high limbs of a scalar. +::: + +## embedded_curve_ops::multi_scalar_mul + +Performs multi scalar multiplication over the embedded curve. +The function accepts arbitrary amount of point-scalar pairs on the input, it multiplies the individual pairs over +the curve and returns a sum of the resulting points. + +Points represented as x and y coordinates [x1, y1, x2, y2, ...], scalars as low and high limbs [low1, high1, low2, high2, ...]. + +```rust title="multi_scalar_mul" showLineNumbers +pub fn multi_scalar_mul( + points: [EmbeddedCurvePoint; N], + scalars: [EmbeddedCurveScalar; N], +) -> EmbeddedCurvePoint +``` +> Source code: noir_stdlib/src/embedded_curve_ops.nr#L103-L108 + + +example + +```rust +fn main(point_x: Field, point_y: Field, scalar_low: Field, scalar_high: Field) { + let point = std::embedded_curve_ops::multi_scalar_mul([point_x, point_y], [scalar_low, scalar_high]); + println(point); +} +``` + +## embedded_curve_ops::fixed_base_scalar_mul + +Performs fixed base scalar multiplication over the embedded curve (multiplies input scalar with a generator point). +The function accepts a single scalar on the input represented as 2 fields. + +```rust title="fixed_base_scalar_mul" showLineNumbers +pub fn fixed_base_scalar_mul(scalar: EmbeddedCurveScalar) -> EmbeddedCurvePoint +``` +> Source code: noir_stdlib/src/embedded_curve_ops.nr#L120-L122 + + +example + +```rust +fn main(scalar_low: Field, scalar_high: Field) { + let point = std::embedded_curve_ops::fixed_base_scalar_mul(scalar_low, scalar_high); + println(point); +} +``` + +## embedded_curve_ops::embedded_curve_add + +Adds two points on the embedded curve. +This function takes two `EmbeddedCurvePoint` structures as parameters, representing points on the curve, and returns a new `EmbeddedCurvePoint` structure that represents their sum. + +### Parameters: +- `point1` (`EmbeddedCurvePoint`): The first point to add. +- `point2` (`EmbeddedCurvePoint`): The second point to add. + +### Returns: +- `EmbeddedCurvePoint`: The resulting point after the addition of `point1` and `point2`. + +```rust title="embedded_curve_add" showLineNumbers +pub fn embedded_curve_add( + point1: EmbeddedCurvePoint, + point2: EmbeddedCurvePoint, +) -> EmbeddedCurvePoint { +``` +> Source code: noir_stdlib/src/embedded_curve_ops.nr#L136-L141 + + +example + +```rust +fn main() { + let point1 = EmbeddedCurvePoint { x: 1, y: 2 }; + let point2 = EmbeddedCurvePoint { x: 3, y: 4 }; + let result = std::embedded_curve_ops::embedded_curve_add(point1, point2); + println!("Resulting Point: ({}, {})", result.x, result.y); +} +``` + + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/hashes.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/hashes.mdx new file mode 100644 index 000000000000..039ca6b3d494 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -0,0 +1,228 @@ +--- +title: Hash methods +description: + Learn about the cryptographic primitives ready to use for any Noir project, including sha256, + blake2s and pedersen +keywords: + [cryptographic primitives, Noir project, sha256, blake2s, pedersen, hash] +sidebar_position: 0 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; + +## sha256 + +Given an array of bytes, returns the resulting sha256 hash. +Specify a message_size to hash only the first `message_size` bytes of the input. + +```rust title="sha256" showLineNumbers +#[deprecated("sha256 is being deprecated from the stdlib, use https://github.com/noir-lang/sha256 instead")] +pub fn sha256(input: [u8; N]) -> HASH +``` +> Source code: noir_stdlib/src/hash/sha256.nr#L46-L49 + + +example: +```rust title="sha256_var" showLineNumbers +let digest = std::hash::sha256_var([x as u8], 1); +``` +> Source code: test_programs/execution_success/sha256/src/main.nr#L15-L17 + + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::sha256::sha256_var(x, 4); +} +``` + + + + +## blake2s + +Given an array of bytes, returns an array with the Blake2 hash + +```rust title="blake2s" showLineNumbers +pub fn blake2s(input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash/mod.nr#L18-L20 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake2s(x); +} +``` + + + +## blake3 + +Given an array of bytes, returns an array with the Blake3 hash + +```rust title="blake3" showLineNumbers +pub fn blake3(input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash/mod.nr#L24-L26 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake3(x); +} +``` + + + +## pedersen_hash + +Given an array of Fields, returns the Pedersen hash. + +```rust title="pedersen_hash" showLineNumbers +pub fn pedersen_hash(input: [Field; N]) -> Field +``` +> Source code: noir_stdlib/src/hash/mod.nr#L49-L51 + + +example: + +```rust title="pedersen-hash" showLineNumbers +fn main(x: Field, y: Field, expected_hash: Field) { + let hash = std::hash::pedersen_hash([x, y]); + assert_eq(hash, expected_hash); +} +``` +> Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L6 + + + + +## pedersen_commitment + +Given an array of Fields, returns the Pedersen commitment. + +```rust title="pedersen_commitment" showLineNumbers +pub fn pedersen_commitment(input: [Field; N]) -> EmbeddedCurvePoint { +``` +> Source code: noir_stdlib/src/hash/mod.nr#L29-L31 + + +example: + +```rust title="pedersen-commitment" showLineNumbers +fn main(x: Field, y: Field, expected_commitment: std::embedded_curve_ops::EmbeddedCurvePoint) { + let commitment = std::hash::pedersen_commitment([x, y]); + assert_eq(commitment.x, expected_commitment.x); + assert_eq(commitment.y, expected_commitment.y); +} +``` +> Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L7 + + + + +## keccak256 + +Given an array of bytes (`u8`), returns the resulting keccak hash as an array of +32 bytes (`[u8; 32]`). Specify a message_size to hash only the first +`message_size` bytes of the input. + +```rust title="keccak256" showLineNumbers +pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash/mod.nr#L117-L119 + + +example: + +```rust title="keccak256" showLineNumbers +fn main(x: Field, result: [u8; 32]) { + // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field + // The padding is taken care of by the program + let digest = std::hash::keccak256([x as u8], 1); + assert(digest == result); + + //#1399: variable message size + let message_size = 4; + let hash_a = std::hash::keccak256([1, 2, 3, 4], message_size); + let hash_b = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); + + assert(hash_a == hash_b); + + let message_size_big = 8; + let hash_c = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); + + assert(hash_a != hash_c); +} +``` +> Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L20 + + + + +## poseidon + +Given an array of Fields, returns a new Field with the Poseidon Hash. Mind that you need to specify +how many inputs are there to your Poseidon function. + +```rust +// example for hash_1, hash_2 accepts an array of length 2, etc +fn hash_1(input: [Field; 1]) -> Field +``` + +example: + +```rust title="poseidon" showLineNumbers +use std::hash::poseidon; + +fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field) { + let hash1 = poseidon::bn254::hash_2(x1); + assert(hash1 == y1); + + let hash2 = poseidon::bn254::hash_4(x2); + assert(hash2 == y2); +} +``` +> Source code: test_programs/execution_success/poseidon_bn254_hash/src/main.nr#L1-L11 + + +## poseidon 2 + +Given an array of Fields, returns a new Field with the Poseidon2 Hash. Contrary to the Poseidon +function, there is only one hash and you can specify a message_size to hash only the first +`message_size` bytes of the input, + +```rust +// example for hashing the first three elements of the input +Poseidon2::hash(input, 3); +``` + +example: + +```rust title="poseidon2" showLineNumbers +use std::hash::poseidon2; + +fn main(inputs: [Field; 4], expected_hash: Field) { + let hash = poseidon2::Poseidon2::hash(inputs, inputs.len()); + assert_eq(hash, expected_hash); +} +``` +> Source code: test_programs/execution_success/poseidon2/src/main.nr#L1-L8 + + +## hash_to_field + +```rust +fn hash_to_field(_input : [Field]) -> Field {} +``` + +Calculates the `blake2s` hash of the inputs and returns the hash modulo the field modulus to return +a value which can be represented as a `Field`. + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/index.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/index.md new file mode 100644 index 000000000000..650f30165d56 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/index.md @@ -0,0 +1,14 @@ +--- +title: Cryptographic Primitives +description: + Learn about the cryptographic primitives ready to use for any Noir project +keywords: + [ + cryptographic primitives, + Noir project, + ] +--- + +The Noir team is progressively adding new cryptographic primitives to the standard library. Reach out for news or if you would be interested in adding more of these calculations in Noir. + +Some methods are available thanks to the Aztec backend, not being performed using Noir. When using other backends, these methods may or may not be supplied. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/fmtstr.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/fmtstr.md new file mode 100644 index 000000000000..19809d60261d --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/fmtstr.md @@ -0,0 +1,17 @@ +--- +title: fmtstr +--- + +`fmtstr` is the type resulting from using format string (`f"..."`). + +## Methods + +### quoted_contents + +```rust title="quoted_contents" showLineNumbers +pub comptime fn quoted_contents(self) -> Quoted {} +``` +> Source code: noir_stdlib/src/meta/format_string.nr#L3-L5 + + +Returns the format string contents (that is, without the leading and trailing double quotes) as a `Quoted` value. \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/is_unconstrained.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/is_unconstrained.md new file mode 100644 index 000000000000..34b0698552b7 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/is_unconstrained.md @@ -0,0 +1,69 @@ +--- +title: Is Unconstrained Function +description: + The is_unconstrained function returns whether the context at that point of the program is unconstrained or not. +keywords: + [ + unconstrained + ] +--- + +It's very common for functions in circuits to take unconstrained hints of an expensive computation and then verify it. This is done by running the hint in an unconstrained context and then verifying the result in a constrained context. + +When a function is marked as unconstrained, any subsequent functions that it calls will also be run in an unconstrained context. However, if we are implementing a library function, other users might call it within an unconstrained context or a constrained one. Generally, in an unconstrained context we prefer just computing the result instead of taking a hint of it and verifying it, since that'd mean doing the same computation twice: + +```rust + +fn my_expensive_computation(){ + ... +} + +unconstrained fn my_expensive_computation_hint(){ + my_expensive_computation() +} + +pub fn external_interface(){ + my_expensive_computation_hint(); + // verify my_expensive_computation: If external_interface is called from unconstrained, this is redundant + ... +} + +``` + +In order to improve the performance in an unconstrained context you can use the function at `std::runtime::is_unconstrained() -> bool`: + + +```rust +use dep::std::runtime::is_unconstrained; + +fn my_expensive_computation(){ + ... +} + +unconstrained fn my_expensive_computation_hint(){ + my_expensive_computation() +} + +pub fn external_interface(){ + if is_unconstrained() { + my_expensive_computation(); + } else { + my_expensive_computation_hint(); + // verify my_expensive_computation + ... + } +} + +``` + +The is_unconstrained result is resolved at compile time, so in unconstrained contexts the compiler removes the else branch, and in constrained contexts the compiler removes the if branch, reducing the amount of compute necessary to run external_interface. + +Note that using `is_unconstrained` in a `comptime` context will also return `true`: + +``` +fn main() { + comptime { + assert(is_unconstrained()); + } +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/logging.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/logging.md new file mode 100644 index 000000000000..db75ef9f86fa --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/logging.md @@ -0,0 +1,78 @@ +--- +title: Logging +description: + Learn how to use the println statement for debugging in Noir with this tutorial. Understand the + basics of logging in Noir and how to implement it in your code. +keywords: + [ + noir logging, + println statement, + print statement, + debugging in noir, + noir std library, + logging tutorial, + basic logging in noir, + noir logging implementation, + noir debugging techniques, + rust, + ] +--- + +The standard library provides two familiar statements you can use: `println` and `print`. Despite being a limited implementation of rust's `println!` and `print!` macros, these constructs can be useful for debugging. + +You can print the output of both statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are print statements in your tests). + +It is recommended to use `nargo execute` if you want to debug failing constraints with `println` or `print` statements. This is due to every input in a test being a constant rather than a witness, so we issue an error during compilation while we only print during execution (which comes after compilation). Neither `println`, nor `print` are callable for failed constraints caught at compile time. + +Both `print` and `println` are generic functions which can work on integers, fields, strings, and even structs or expressions. Note however, that slices are currently unsupported. For example: + +```rust +struct Person { + age: Field, + height: Field, +} + +fn main(age: Field, height: Field) { + let person = Person { + age: age, + height: height, + }; + println(person); + println(age + height); + println("Hello world!"); +} +``` + +You can print different types in the same statement (including strings) with a type called `fmtstr`. It can be specified in the same way as a normal string, just prepended with an "f" character: + +```rust + let fmt_str = f"i: {i}, j: {j}"; + println(fmt_str); + + let s = myStruct { y: x, x: y }; + println(s); + + println(f"i: {i}, s: {s}"); + + println(x); + println([x, y]); + + let foo = fooStruct { my_struct: s, foo: 15 }; + println(f"s: {s}, foo: {foo}"); + + println(15); // prints 0x0f, implicit Field + println(-1 as u8); // prints 255 + println(-1 as i8); // prints -1 +``` + +Examples shown above are interchangeable between the two `print` statements: + +```rust +let person = Person { age : age, height : height }; + +println(person); +print(person); + +println("Hello world!"); // Prints with a newline at the end of the input +print("Hello world!"); // Prints the input and keeps cursor on the same line +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/mem.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/mem.md new file mode 100644 index 000000000000..1e9102b32dc3 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/mem.md @@ -0,0 +1,82 @@ +--- +title: Memory Module +description: + This module contains functions which manipulate memory in a low-level way +keywords: + [ + mem, memory, zeroed, transmute, checked_transmute + ] +--- + +# `std::mem::zeroed` + +```rust +fn zeroed() -> T +``` + +Returns a zeroed value of any type. +This function is generally unsafe to use as the zeroed bit pattern is not guaranteed to be valid for all types. +It can however, be useful in cases when the value is guaranteed not to be used such as in a BoundedVec library implementing a growable vector, up to a certain length, backed by an array. +The array can be initialized with zeroed values which are guaranteed to be inaccessible until the vector is pushed to. +Similarly, enumerations in noir can be implemented using this method by providing zeroed values for the unused variants. + +This function currently supports the following types: + +- Field +- Bool +- Uint +- Array +- Slice +- String +- Tuple +- Functions + +Using it on other types could result in unexpected behavior. + +# `std::mem::checked_transmute` + +```rust +fn checked_transmute(value: T) -> U +``` + +Transmutes a value of one type into the same value but with a new type `U`. + +This function is safe to use since both types are asserted to be equal later during compilation after the concrete values for generic types become known. +This function is useful for cases where the compiler may fail a type check that is expected to pass where +a user knows the two types to be equal. For example, when using arithmetic generics there are cases the compiler +does not see as equal, such as `[Field; N*(A + B)]` and `[Field; N*A + N*B]`, which users may know to be equal. +In these cases, `checked_transmute` can be used to cast the value to the desired type while also preserving safety +by checking this equality once `N`, `A`, `B` are fully resolved. + +Note that since this safety check is performed after type checking rather than during, no error is issued if the function +containing `checked_transmute` is never called. + +# `std::mem::array_refcount` + +```rust +fn array_refcount(array: [T; N]) -> u32 {} +``` + +Returns the internal reference count of an array value in unconstrained code. + +Arrays only have reference count in unconstrained code - using this anywhere +else will return zero. + +This function is mostly intended for debugging compiler optimizations but can also be used +to find where array copies may be happening in unconstrained code by placing it before array +mutations. + +# `std::mem::slice_refcount` + +```rust +fn slice_refcount(slice: [T]) -> u32 {} +``` + +Returns the internal reference count of a slice value in unconstrained code. + +Slices only have reference count in unconstrained code - using this anywhere +else will return zero. + +This function is mostly intended for debugging compiler optimizations but can also be used +to find where slice copies may be happening in unconstrained code by placing it before slice +mutations. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/merkle_trees.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/merkle_trees.md new file mode 100644 index 000000000000..6a9ebf72ada0 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/merkle_trees.md @@ -0,0 +1,58 @@ +--- +title: Merkle Trees +description: Learn about Merkle Trees in Noir with this tutorial. Explore the basics of computing a merkle root using a proof, with examples. +keywords: + [ + Merkle trees in Noir, + Noir programming language, + check membership, + computing root from leaf, + Noir Merkle tree implementation, + Merkle tree tutorial, + Merkle tree code examples, + Noir libraries, + pedersen hash., + ] +--- + +## compute_merkle_root + +Returns the root of the tree from the provided leaf and its hash path, using a [Pedersen hash](./cryptographic_primitives/hashes.mdx#pedersen_hash). + +```rust +fn compute_merkle_root(leaf : Field, index : Field, hash_path: [Field]) -> Field +``` + +example: + +```rust +/** + // these values are for this example only + index = "0" + priv_key = "0x000000000000000000000000000000000000000000000000000000616c696365" + secret = "0x1929ea3ab8d9106a899386883d9428f8256cfedb3c4f6b66bf4aa4d28a79988f" + note_hash_path = [ + "0x1e61bdae0f027b1b2159e1f9d3f8d00fa668a952dddd822fda80dc745d6f65cc", + "0x0e4223f3925f98934393c74975142bd73079ab0621f4ee133cee050a3c194f1a", + "0x2fd7bb412155bf8693a3bd2a3e7581a679c95c68a052f835dddca85fa1569a40" + ] + */ +fn main(index: Field, priv_key: Field, secret: Field, note_hash_path: [Field; 3]) { + + let pubkey = std::scalar_mul::fixed_base_embedded_curve(priv_key); + let pubkey_x = pubkey[0]; + let pubkey_y = pubkey[1]; + let note_commitment = std::hash::pedersen(&[pubkey_x, pubkey_y, secret]); + + let root = std::merkle::compute_merkle_root(note_commitment[0], index, note_hash_path.as_slice()); + println(root); +} +``` + +To check merkle tree membership: + +1. Include a merkle root as a program input. +2. Compute the merkle root of a given leaf, index and hash path. +3. Assert the merkle roots are equal. + +For more info about merkle trees, see the Wikipedia [page](https://en.wikipedia.org/wiki/Merkle_tree). diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/ctstring.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/ctstring.md new file mode 100644 index 000000000000..fe33486487ef --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/ctstring.md @@ -0,0 +1,100 @@ +--- +title: CtString +--- + +`std::meta::ctstring` contains methods on the built-in `CtString` type which is +a compile-time, dynamically-sized string type. Compared to `str` and `fmtstr`, +`CtString` is useful because its size does not need to be specified in its type. This +can be used for formatting items at compile-time or general string handling in `comptime` +code. + +Since `fmtstr`s can be converted into `CtString`s, you can make use of their formatting +abilities in CtStrings by formatting in `fmtstr`s then converting the result to a CtString +afterward. + +## Traits + +### AsCtString + +```rust title="as-ctstring" showLineNumbers +pub trait AsCtString { + comptime fn as_ctstring(self) -> CtString; +} +``` +> Source code: noir_stdlib/src/meta/ctstring.nr#L44-L48 + + +Converts an object into a compile-time string. + +Implementations: + +```rust +impl AsCtString for str { ... } +impl AsCtString for fmtstr { ... } +``` + +## Methods + +### new + +```rust title="new" showLineNumbers +pub comptime fn new() -> Self { +``` +> Source code: noir_stdlib/src/meta/ctstring.nr#L4-L6 + + +Creates an empty `CtString`. + +### append_str + +```rust title="append_str" showLineNumbers +pub comptime fn append_str(self, s: str) -> Self { +``` +> Source code: noir_stdlib/src/meta/ctstring.nr#L12-L14 + + +Returns a new CtString with the given str appended onto the end. + +### append_fmtstr + +```rust title="append_fmtstr" showLineNumbers +pub comptime fn append_fmtstr(self, s: fmtstr) -> Self { +``` +> Source code: noir_stdlib/src/meta/ctstring.nr#L18-L20 + + +Returns a new CtString with the given fmtstr appended onto the end. + +### as_quoted_str + +```rust title="as_quoted_str" showLineNumbers +pub comptime fn as_quoted_str(self) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/ctstring.nr#L27-L29 + + +Returns a quoted string literal from this string's contents. + +There is no direct conversion from a `CtString` to a `str` since +the size would not be known. To get around this, this function can +be used in combination with macro insertion (`!`) to insert this string +literal at this function's call site. + +Example: + +```rust title="as_quoted_str_example" showLineNumbers +let my_ctstring = "foo bar".as_ctstring(); + let my_str = my_ctstring.as_quoted_str!(); + + assert_eq(crate::meta::type_of(my_str), quote { str<7> }.as_type()); +``` +> Source code: noir_stdlib/src/meta/ctstring.nr#L95-L100 + + +## Trait Implementations + +```rust +impl Eq for CtString +impl Hash for CtString +impl Append for CtString +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/expr.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/expr.md new file mode 100644 index 000000000000..b6d395c6700c --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/expr.md @@ -0,0 +1,380 @@ +--- +title: Expr +--- + +`std::meta::expr` contains methods on the built-in `Expr` type for quoted, syntactically valid expressions. + +## Methods + +### as_array + +```rust title="as_array" showLineNumbers +pub comptime fn as_array(self) -> Option<[Expr]> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L10-L12 + + +If this expression is an array, this returns a slice of each element in the array. + +### as_assert + +```rust title="as_assert" showLineNumbers +pub comptime fn as_assert(self) -> Option<(Expr, Option)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L16-L18 + + +If this expression is an assert, this returns the assert expression and the optional message. + +### as_assert_eq + +```rust title="as_assert_eq" showLineNumbers +pub comptime fn as_assert_eq(self) -> Option<(Expr, Expr, Option)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L23-L25 + + +If this expression is an assert_eq, this returns the left-hand-side and right-hand-side +expressions, together with the optional message. + +### as_assign + +```rust title="as_assign" showLineNumbers +pub comptime fn as_assign(self) -> Option<(Expr, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L30-L32 + + +If this expression is an assignment, this returns a tuple with the left hand side +and right hand side in order. + +### as_binary_op + +```rust title="as_binary_op" showLineNumbers +pub comptime fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L37-L39 + + +If this expression is a binary operator operation ` `, +return the left-hand side, operator, and the right-hand side of the operation. + +### as_block + +```rust title="as_block" showLineNumbers +pub comptime fn as_block(self) -> Option<[Expr]> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L44-L46 + + +If this expression is a block `{ stmt1; stmt2; ...; stmtN }`, return +a slice containing each statement. + +### as_bool + +```rust title="as_bool" showLineNumbers +pub comptime fn as_bool(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L50-L52 + + +If this expression is a boolean literal, return that literal. + +### as_cast + +```rust title="as_cast" showLineNumbers +#[builtin(expr_as_cast)] + pub comptime fn as_cast(self) -> Option<(Expr, UnresolvedType)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L56-L59 + + +If this expression is a cast expression (`expr as type`), returns the casted +expression and the type to cast to. + +### as_comptime + +```rust title="as_comptime" showLineNumbers +pub comptime fn as_comptime(self) -> Option<[Expr]> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L64-L66 + + +If this expression is a `comptime { stmt1; stmt2; ...; stmtN }` block, +return each statement in the block. + +### as_constructor + +```rust title="as_constructor" showLineNumbers +pub comptime fn as_constructor(self) -> Option<(UnresolvedType, [(Quoted, Expr)])> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L71-L73 + + +If this expression is a constructor `Type { field1: expr1, ..., fieldN: exprN }`, +return the type and the fields. + +### as_for + +```rust title="as_for" showLineNumbers +pub comptime fn as_for(self) -> Option<(Quoted, Expr, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L78-L80 + + +If this expression is a for statement over a single expression, return the identifier, +the expression and the for loop body. + +### as_for_range + +```rust title="as_for" showLineNumbers +pub comptime fn as_for(self) -> Option<(Quoted, Expr, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L78-L80 + + +If this expression is a for statement over a range, return the identifier, +the range start, the range end and the for loop body. + +### as_function_call + +```rust title="as_function_call" showLineNumbers +pub comptime fn as_function_call(self) -> Option<(Expr, [Expr])> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L92-L94 + + +If this expression is a function call `foo(arg1, ..., argN)`, return +the function and a slice of each argument. + +### as_if + +```rust title="as_if" showLineNumbers +pub comptime fn as_if(self) -> Option<(Expr, Expr, Option)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L100-L102 + + +If this expression is an `if condition { then_branch } else { else_branch }`, +return the condition, then branch, and else branch. If there is no else branch, +`None` is returned for that branch instead. + +### as_index + +```rust title="as_index" showLineNumbers +pub comptime fn as_index(self) -> Option<(Expr, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L107-L109 + + +If this expression is an index into an array `array[index]`, return the +array and the index. + +### as_integer + +```rust title="as_integer" showLineNumbers +pub comptime fn as_integer(self) -> Option<(Field, bool)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L114-L116 + + +If this expression is an integer literal, return the integer as a field +as well as whether the integer is negative (true) or not (false). + +### as_lambda + +```rust title="as_lambda" showLineNumbers +pub comptime fn as_lambda( + self, + ) -> Option<([(Expr, Option)], Option, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L120-L124 + + +If this expression is a lambda, returns the parameters, return type and body. + +### as_let + +```rust title="as_let" showLineNumbers +pub comptime fn as_let(self) -> Option<(Expr, Option, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L129-L131 + + +If this expression is a let statement, returns the let pattern as an `Expr`, +the optional type annotation, and the assigned expression. + +### as_member_access + +```rust title="as_member_access" showLineNumbers +pub comptime fn as_member_access(self) -> Option<(Expr, Quoted)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L136-L138 + + +If this expression is a member access `foo.bar`, return the struct/tuple +expression and the field. The field will be represented as a quoted value. + +### as_method_call + +```rust title="as_method_call" showLineNumbers +pub comptime fn as_method_call(self) -> Option<(Expr, Quoted, [UnresolvedType], [Expr])> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L143-L145 + + +If this expression is a method call `foo.bar::(arg1, ..., argN)`, return +the receiver, method name, a slice of each generic argument, and a slice of each argument. + +### as_repeated_element_array + +```rust title="as_repeated_element_array" showLineNumbers +pub comptime fn as_repeated_element_array(self) -> Option<(Expr, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L150-L152 + + +If this expression is a repeated element array `[elem; length]`, return +the repeated element and the length expressions. + +### as_repeated_element_slice + +```rust title="as_repeated_element_slice" showLineNumbers +pub comptime fn as_repeated_element_slice(self) -> Option<(Expr, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L157-L159 + + +If this expression is a repeated element slice `[elem; length]`, return +the repeated element and the length expressions. + +### as_slice + +```rust title="as_slice" showLineNumbers +pub comptime fn as_slice(self) -> Option<[Expr]> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L164-L166 + + +If this expression is a slice literal `&[elem1, ..., elemN]`, +return each element of the slice. + +### as_tuple + +```rust title="as_tuple" showLineNumbers +pub comptime fn as_tuple(self) -> Option<[Expr]> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L171-L173 + + +If this expression is a tuple `(field1, ..., fieldN)`, +return each element of the tuple. + +### as_unary_op + +```rust title="as_unary_op" showLineNumbers +pub comptime fn as_unary_op(self) -> Option<(UnaryOp, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L178-L180 + + +If this expression is a unary operation ` `, +return the unary operator as well as the right-hand side expression. + +### as_unsafe + +```rust title="as_unsafe" showLineNumbers +pub comptime fn as_unsafe(self) -> Option<[Expr]> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L185-L187 + + +If this expression is an `unsafe { stmt1; ...; stmtN }` block, +return each statement inside in a slice. + +### has_semicolon + +```rust title="has_semicolon" showLineNumbers +pub comptime fn has_semicolon(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L206-L208 + + +`true` if this expression is trailed by a semicolon. E.g. + +``` +comptime { + let expr1 = quote { 1 + 2 }.as_expr().unwrap(); + let expr2 = quote { 1 + 2; }.as_expr().unwrap(); + + assert(expr1.as_binary_op().is_some()); + assert(expr2.as_binary_op().is_some()); + + assert(!expr1.has_semicolon()); + assert(expr2.has_semicolon()); +} +``` + +### is_break + +```rust title="is_break" showLineNumbers +pub comptime fn is_break(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L212-L214 + + +`true` if this expression is `break`. + +### is_continue + +```rust title="is_continue" showLineNumbers +pub comptime fn is_continue(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L218-L220 + + +`true` if this expression is `continue`. + +### modify + +```rust title="modify" showLineNumbers +pub comptime fn modify(self, f: fn[Env](Expr) -> Option) -> Expr { +``` +> Source code: noir_stdlib/src/meta/expr.nr#L229-L231 + + +Applies a mapping function to this expression and to all of its sub-expressions. +`f` will be applied to each sub-expression first, then applied to the expression itself. + +This happens recursively for every expression within `self`. + +For example, calling `modify` on `(&[1], &[2, 3])` with an `f` that returns `Option::some` +for expressions that are integers, doubling them, would return `(&[2], &[4, 6])`. + +### quoted + +```rust title="quoted" showLineNumbers +pub comptime fn quoted(self) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/expr.nr#L266-L268 + + +Returns this expression as a `Quoted` value. It's the same as `quote { $self }`. + +### resolve + +```rust title="resolve" showLineNumbers +pub comptime fn resolve(self, in_function: Option) -> TypedExpr {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L282-L284 + + +Resolves and type-checks this expression and returns the result as a `TypedExpr`. + +The `in_function` argument specifies where the expression is resolved: +- If it's `none`, the expression is resolved in the function where `resolve` was called +- If it's `some`, the expression is resolved in the given function + +If any names used by this expression are not in scope or if there are any type errors, +this will give compiler errors as if the expression was written directly into +the current `comptime` function. \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/function_def.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/function_def.md new file mode 100644 index 000000000000..7c9615e6ab55 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/function_def.md @@ -0,0 +1,181 @@ +--- +title: FunctionDefinition +--- + +`std::meta::function_def` contains methods on the built-in `FunctionDefinition` type representing +a function definition in the source program. + +## Methods + +### add_attribute + +```rust title="add_attribute" showLineNumbers +pub comptime fn add_attribute(self, attribute: str) {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L3-L5 + + +Adds an attribute to the function. This is only valid +on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +### as_typed_expr + +```rust title="as_typed_expr" showLineNumbers +pub comptime fn as_typed_expr(self) -> TypedExpr {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L8-L10 + + +Returns this function as a `TypedExpr`, which can be unquoted. For example: + +```rust +let typed_expr = some_function.as_typed_expr(); +let _ = quote { $typed_expr(1, 2, 3); }; +``` + +### body + +```rust title="body" showLineNumbers +pub comptime fn body(self) -> Expr {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L13-L15 + + +Returns the body of the function as an expression. This is only valid +on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +### has_named_attribute + +```rust title="has_named_attribute" showLineNumbers +pub comptime fn has_named_attribute(self, name: str) -> bool {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L18-L20 + + +Returns true if this function has a custom attribute with the given name. + +### is_unconstrained + +```rust title="is_unconstrained" showLineNumbers +pub comptime fn is_unconstrained(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L23-L25 + + +Returns true if this function is unconstrained. + +### module + +```rust title="module" showLineNumbers +pub comptime fn module(self) -> Module {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L28-L30 + + +Returns the module where the function is defined. + +### name + +```rust title="name" showLineNumbers +pub comptime fn name(self) -> Quoted {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L33-L35 + + +Returns the name of the function. + +### parameters + +```rust title="parameters" showLineNumbers +pub comptime fn parameters(self) -> [(Quoted, Type)] {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L38-L40 + + +Returns each parameter of the function as a tuple of (parameter pattern, parameter type). + +### return_type + +```rust title="return_type" showLineNumbers +pub comptime fn return_type(self) -> Type {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L43-L45 + + +The return type of the function. + +### set_body + +```rust title="set_body" showLineNumbers +pub comptime fn set_body(self, body: Expr) {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L48-L50 + + +Mutate the function body to a new expression. This is only valid +on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +### set_parameters + +```rust title="set_parameters" showLineNumbers +pub comptime fn set_parameters(self, parameters: [(Quoted, Type)]) {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L53-L55 + + +Mutates the function's parameters to a new set of parameters. This is only valid +on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +Expects a slice of (parameter pattern, parameter type) for each parameter. Requires +each parameter pattern to be a syntactically valid parameter. + +### set_return_type + +```rust title="set_return_type" showLineNumbers +pub comptime fn set_return_type(self, return_type: Type) {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L58-L60 + + +Mutates the function's return type to a new type. This is only valid +on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +### set_return_public + +```rust title="set_return_public" showLineNumbers +pub comptime fn set_return_public(self, public: bool) {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L63-L65 + + +Mutates the function's return visibility to public (if `true` is given) or private (if `false` is given). +This is only valid on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +### set_unconstrained + +```rust title="set_unconstrained" showLineNumbers +pub comptime fn set_unconstrained(self, value: bool) {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L71-L73 + + +Mutates the function to be unconstrained (if `true` is given) or not (if `false` is given). +This is only valid on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +## Trait Implementations + +```rust +impl Eq for FunctionDefinition +impl Hash for FunctionDefinition +``` + +Note that each function is assigned a unique ID internally and this is what is used for +equality and hashing. So even functions with identical signatures and bodies may not +be equal in this sense if they were originally different items in the source program. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/index.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/index.md new file mode 100644 index 000000000000..1d94d4a0374e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/index.md @@ -0,0 +1,224 @@ +--- +title: Metaprogramming +description: Noir's Metaprogramming API +keywords: [metaprogramming, comptime, macros, macro, quote, unquote] +--- + +`std::meta` is the entry point for Noir's metaprogramming API. This consists of `comptime` functions +and types used for inspecting and modifying Noir programs. + +## Functions + +### type_of + +```rust title="type_of" showLineNumbers +pub comptime fn type_of(x: T) -> Type {} +``` +> Source code: noir_stdlib/src/meta/mod.nr#L29-L31 + + +Returns the type of a variable at compile-time. + +Example: +```rust +comptime { + let x: i32 = 1; + let x_type: Type = std::meta::type_of(x); + + assert_eq(x_type, quote { i32 }.as_type()); +} +``` + +### unquote + +```rust title="unquote" showLineNumbers +pub comptime fn unquote(code: Quoted) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/mod.nr#L21-L23 + + +Unquotes the passed-in token stream where this function was called. + +Example: +```rust +comptime { + let code = quote { 1 + 2 }; + + // let x = 1 + 2; + let x = unquote!(code); +} +``` + +### derive + +```rust title="derive" showLineNumbers +#[varargs] +pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/mod.nr#L50-L53 + + +Attribute placed on struct definitions. + +Creates a trait impl for each trait passed in as an argument. +To do this, the trait must have a derive handler registered +with `derive_via` beforehand. The traits in the stdlib that +can be derived this way are `Eq`, `Ord`, `Default`, and `Hash`. + +Example: +```rust +#[derive(Eq, Default)] +struct Foo { + x: i32, + y: T, +} + +fn main() { + let foo1 = Foo::default(); + let foo2 = Foo { x: 0, y: &[0] }; + assert_eq(foo1, foo2); +} +``` + +### derive_via + +```rust title="derive_via_signature" showLineNumbers +pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { +``` +> Source code: noir_stdlib/src/meta/mod.nr#L70-L72 + + +Attribute placed on trait definitions. + +Registers a function to create impls for the given trait +when the trait is used in a `derive` call. Users may use +this to register their own functions to enable their traits +to be derived by `derive`. + +Because this function requires a function as an argument which +should produce a trait impl for any given struct, users may find +it helpful to use a function like `std::meta::make_trait_impl` to +help creating these impls. + +Example: +```rust +#[derive_via(derive_do_nothing)] +trait DoNothing { + fn do_nothing(self); +} + +comptime fn derive_do_nothing(s: StructDefinition) -> Quoted { + let typ = s.as_type(); + quote { + impl DoNothing for $typ { + fn do_nothing(self) { + println("Nothing"); + } + } + } +} +``` + +As another example, `derive_eq` in the stdlib is used to derive the `Eq` +trait for any struct. It makes use of `make_trait_impl` to do this: + +```rust title="derive_eq" showLineNumbers +comptime fn derive_eq(s: StructDefinition) -> Quoted { + let signature = quote { fn eq(_self: Self, _other: Self) -> bool }; + let for_each_field = |name| quote { (_self.$name == _other.$name) }; + let body = |fields| { + if s.fields_as_written().len() == 0 { + quote { true } + } else { + fields + } + }; + crate::meta::make_trait_impl( + s, + quote { Eq }, + signature, + for_each_field, + quote { & }, + body, + ) +} +``` +> Source code: noir_stdlib/src/cmp.nr#L10-L30 + + +### make_trait_impl + +```rust title="make_trait_impl" showLineNumbers +pub comptime fn make_trait_impl( + s: StructDefinition, + trait_name: Quoted, + function_signature: Quoted, + for_each_field: fn[Env1](Quoted) -> Quoted, + join_fields_with: Quoted, + body: fn[Env2](Quoted) -> Quoted, +) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/mod.nr#L89-L98 + + +A helper function to more easily create trait impls while deriving traits. + +Note that this function only works for traits which: +1. Have only one method +2. Have no generics on the trait itself. + - E.g. Using this on a trait such as `trait Foo { ... }` will result in the + generated impl incorrectly missing the `T` generic. + +If your trait fits these criteria then `make_trait_impl` is likely the easiest +way to write your derive handler. The arguments are as follows: + +- `s`: The struct to make the impl for +- `trait_name`: The name of the trait to derive. E.g. `quote { Eq }`. +- `function_signature`: The signature of the trait method to derive. E.g. `fn eq(self, other: Self) -> bool`. +- `for_each_field`: An operation to be performed on each field. E.g. `|name| quote { (self.$name == other.$name) }`. +- `join_fields_with`: A separator to join each result of `for_each_field` with. + E.g. `quote { & }`. You can also use an empty `quote {}` for no separator. +- `body`: The result of the field operations is passed into this function for any final processing. + This is the place to insert any setup/teardown code the trait requires. If the trait doesn't require + any such code, you can return the body as-is: `|body| body`. + +Example deriving `Hash`: + +```rust title="derive_hash" showLineNumbers +comptime fn derive_hash(s: StructDefinition) -> Quoted { + let name = quote { Hash }; + let signature = quote { fn hash(_self: Self, _state: &mut H) where H: std::hash::Hasher }; + let for_each_field = |name| quote { _self.$name.hash(_state); }; + crate::meta::make_trait_impl( + s, + name, + signature, + for_each_field, + quote {}, + |fields| fields, + ) +} +``` +> Source code: noir_stdlib/src/hash/mod.nr#L138-L152 + + +Example deriving `Ord`: + +```rust title="derive_ord" showLineNumbers +comptime fn derive_ord(s: StructDefinition) -> Quoted { + let signature = quote { fn cmp(_self: Self, _other: Self) -> std::cmp::Ordering }; + let for_each_field = |name| quote { + if result == std::cmp::Ordering::equal() { + result = _self.$name.cmp(_other.$name); + } + }; + let body = |fields| quote { + let mut result = std::cmp::Ordering::equal(); + $fields + result + }; + crate::meta::make_trait_impl(s, quote { Ord }, signature, for_each_field, quote {}, body) +} +``` +> Source code: noir_stdlib/src/cmp.nr#L221-L236 + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/module.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/module.md new file mode 100644 index 000000000000..f47231972b7f --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/module.md @@ -0,0 +1,82 @@ +--- +title: Module +--- + +`std::meta::module` contains methods on the built-in `Module` type which represents a module in the source program. +Note that this type represents a module generally, it isn't limited to only `mod my_submodule { ... }` +declarations in the source program. + +## Methods + +### add_item + +```rust title="add_item" showLineNumbers +pub comptime fn add_item(self, item: Quoted) {} +``` +> Source code: noir_stdlib/src/meta/module.nr#L3-L5 + + +Adds a top-level item (a function, a struct, a global, etc.) to the module. +Adding multiple items in one go is also valid if the `Quoted` value has multiple items in it. +Note that the items are type-checked as if they are inside the module they are being added to. + +### functions + +```rust title="functions" showLineNumbers +pub comptime fn functions(self) -> [FunctionDefinition] {} +``` +> Source code: noir_stdlib/src/meta/module.nr#L18-L20 + + +Returns each function defined in the module. + +### has_named_attribute + +```rust title="has_named_attribute" showLineNumbers +pub comptime fn has_named_attribute(self, name: str) -> bool {} +``` +> Source code: noir_stdlib/src/meta/module.nr#L8-L10 + + +Returns true if this module has a custom attribute with the given name. + +### is_contract + +```rust title="is_contract" showLineNumbers +pub comptime fn is_contract(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/module.nr#L13-L15 + + +`true` if this module is a contract module (was declared via `contract foo { ... }`). + +### name + +```rust title="name" showLineNumbers +pub comptime fn name(self) -> Quoted {} +``` +> Source code: noir_stdlib/src/meta/module.nr#L28-L30 + + +Returns the name of the module. + +### structs + +```rust title="structs" showLineNumbers +pub comptime fn structs(self) -> [StructDefinition] {} +``` +> Source code: noir_stdlib/src/meta/module.nr#L23-L25 + + +Returns each struct defined in the module. + +## Trait Implementations + +```rust +impl Eq for Module +impl Hash for Module +``` + +Note that each module is assigned a unique ID internally and this is what is used for +equality and hashing. So even modules with identical names and contents may not +be equal in this sense if they were originally different items in the source program. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/op.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/op.md new file mode 100644 index 000000000000..5e3632890aee --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/op.md @@ -0,0 +1,244 @@ +--- +title: UnaryOp and BinaryOp +--- + +`std::meta::op` contains the `UnaryOp` and `BinaryOp` types as well as methods on them. +These types are used to represent a unary or binary operator respectively in Noir source code. + +## Types + +### UnaryOp + +Represents a unary operator. One of `-`, `!`, `&mut`, or `*`. + +### Methods + +#### is_minus + +```rust title="is_minus" showLineNumbers +pub fn is_minus(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L26-L28 + + +Returns `true` if this operator is `-`. + +#### is_not + +```rust title="is_not" showLineNumbers +pub fn is_not(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L32-L34 + + +`true` if this operator is `!` + +#### is_mutable_reference + +```rust title="is_mutable_reference" showLineNumbers +pub fn is_mutable_reference(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L38-L40 + + +`true` if this operator is `&mut` + +#### is_dereference + +```rust title="is_dereference" showLineNumbers +pub fn is_dereference(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L44-L46 + + +`true` if this operator is `*` + +#### quoted + +```rust title="unary_quoted" showLineNumbers +pub comptime fn quoted(self) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/op.nr#L50-L52 + + +Returns this operator as a `Quoted` value. + +### Trait Implementations + +```rust +impl Eq for UnaryOp +impl Hash for UnaryOp +``` + +### BinaryOp + +Represents a binary operator. One of `+`, `-`, `*`, `/`, `%`, `==`, `!=`, `<`, `<=`, `>`, `>=`, `&`, `|`, `^`, `>>`, or `<<`. + +### Methods + +#### is_add + +```rust title="is_add" showLineNumbers +pub fn is_add(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L88-L90 + + +`true` if this operator is `+` + +#### is_subtract + +```rust title="is_subtract" showLineNumbers +pub fn is_subtract(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L94-L96 + + +`true` if this operator is `-` + +#### is_multiply + +```rust title="is_multiply" showLineNumbers +pub fn is_multiply(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L100-L102 + + +`true` if this operator is `*` + +#### is_divide + +```rust title="is_divide" showLineNumbers +pub fn is_divide(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L106-L108 + + +`true` if this operator is `/` + +#### is_modulo + +```rust title="is_modulo" showLineNumbers +pub fn is_modulo(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L178-L180 + + +`true` if this operator is `%` + +#### is_equal + +```rust title="is_equal" showLineNumbers +pub fn is_equal(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L112-L114 + + +`true` if this operator is `==` + +#### is_not_equal + +```rust title="is_not_equal" showLineNumbers +pub fn is_not_equal(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L118-L120 + + +`true` if this operator is `!=` + +#### is_less_than + +```rust title="is_less_than" showLineNumbers +pub fn is_less_than(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L124-L126 + + +`true` if this operator is `<` + +#### is_less_than_or_equal + +```rust title="is_less_than_or_equal" showLineNumbers +pub fn is_less_than_or_equal(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L130-L132 + + +`true` if this operator is `<=` + +#### is_greater_than + +```rust title="is_greater_than" showLineNumbers +pub fn is_greater_than(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L136-L138 + + +`true` if this operator is `>` + +#### is_greater_than_or_equal + +```rust title="is_greater_than_or_equal" showLineNumbers +pub fn is_greater_than_or_equal(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L142-L144 + + +`true` if this operator is `>=` + +#### is_and + +```rust title="is_and" showLineNumbers +pub fn is_and(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L148-L150 + + +`true` if this operator is `&` + +#### is_or + +```rust title="is_or" showLineNumbers +pub fn is_or(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L154-L156 + + +`true` if this operator is `|` + +#### is_shift_right + +```rust title="is_shift_right" showLineNumbers +pub fn is_shift_right(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L166-L168 + + +`true` if this operator is `>>` + +#### is_shift_left + +```rust title="is_shift_left" showLineNumbers +pub fn is_shift_left(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L172-L174 + + +`true` if this operator is `<<` + +#### quoted + +```rust title="binary_quoted" showLineNumbers +pub comptime fn quoted(self) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/op.nr#L184-L186 + + +Returns this operator as a `Quoted` value. + +### Trait Implementations + +```rust +impl Eq for BinaryOp +impl Hash for BinaryOp +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/quoted.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/quoted.md new file mode 100644 index 000000000000..1914f71f4e7d --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/quoted.md @@ -0,0 +1,140 @@ +--- +title: Quoted +--- + +`std::meta::quoted` contains methods on the built-in `Quoted` type which represents +quoted token streams and is the result of the `quote { ... }` expression. + +## Methods + +### as_expr + +```rust title="as_expr" showLineNumbers +pub comptime fn as_expr(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/quoted.nr#L6-L8 + + +Parses the quoted token stream as an expression. Returns `Option::none()` if +the expression failed to parse. + +Example: + +```rust title="as_expr_example" showLineNumbers +#[test] + fn test_expr_as_function_call() { + comptime { + let expr = quote { foo(42) }.as_expr().unwrap(); + let (_function, args) = expr.as_function_call().unwrap(); + assert_eq(args.len(), 1); + assert_eq(args[0].as_integer().unwrap(), (42, false)); + } + } +``` +> Source code: test_programs/noir_test_success/comptime_expr/src/main.nr#L336-L346 + + +### as_module + +```rust title="as_module" showLineNumbers +pub comptime fn as_module(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/quoted.nr#L11-L13 + + +Interprets this token stream as a module path leading to the name of a module. +Returns `Option::none()` if the module isn't found or this token stream cannot be parsed as a path. + +Example: + +```rust title="as_module_example" showLineNumbers +mod baz { + pub mod qux {} +} + +#[test] +fn as_module_test() { + comptime { + let my_mod = quote { baz::qux }.as_module().unwrap(); + assert_eq(my_mod.name(), quote { qux }); + } +} +``` +> Source code: test_programs/compile_success_empty/comptime_module/src/main.nr#L115-L127 + + +### as_trait_constraint + +```rust title="as_trait_constraint" showLineNumbers +pub comptime fn as_trait_constraint(self) -> TraitConstraint {} +``` +> Source code: noir_stdlib/src/meta/quoted.nr#L16-L18 + + +Interprets this token stream as a trait constraint (without an object type). +Note that this function panics instead of returning `Option::none()` if the token +stream does not parse and resolve to a valid trait constraint. + +Example: + +```rust title="implements_example" showLineNumbers +pub fn function_with_where(_x: T) +where + T: SomeTrait, +{ + comptime { + let t = quote { T }.as_type(); + let some_trait_i32 = quote { SomeTrait }.as_trait_constraint(); + assert(t.implements(some_trait_i32)); + + assert(t.get_trait_impl(some_trait_i32).is_none()); + } +} +``` +> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L160-L173 + + +### as_type + +```rust title="as_type" showLineNumbers +pub comptime fn as_type(self) -> Type {} +``` +> Source code: noir_stdlib/src/meta/quoted.nr#L21-L23 + + +Interprets this token stream as a resolved type. Panics if the token +stream doesn't parse to a type or if the type isn't a valid type in scope. + +```rust title="implements_example" showLineNumbers +pub fn function_with_where(_x: T) +where + T: SomeTrait, +{ + comptime { + let t = quote { T }.as_type(); + let some_trait_i32 = quote { SomeTrait }.as_trait_constraint(); + assert(t.implements(some_trait_i32)); + + assert(t.get_trait_impl(some_trait_i32).is_none()); + } +} +``` +> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L160-L173 + + +### tokens + +```rust title="tokens" showLineNumbers +pub comptime fn tokens(self) -> [Quoted] {} +``` +> Source code: noir_stdlib/src/meta/quoted.nr#L26-L28 + + +Returns a slice of the individual tokens that form this token stream. + +## Trait Implementations + +```rust +impl Eq for Quoted +impl Hash for Quoted +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/struct_def.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/struct_def.md new file mode 100644 index 000000000000..5aa557871683 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/struct_def.md @@ -0,0 +1,199 @@ +--- +title: StructDefinition +--- + +`std::meta::struct_def` contains methods on the built-in `StructDefinition` type. +This type corresponds to `struct Name { field1: Type1, ... }` items in the source program. + +## Methods + +### add_attribute + +```rust title="add_attribute" showLineNumbers +pub comptime fn add_attribute(self, attribute: str) {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L5-L7 + + +Adds an attribute to the struct. + +### add_generic + +```rust title="add_generic" showLineNumbers +pub comptime fn add_generic(self, generic_name: str) -> Type {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L10-L12 + + +Adds an generic to the struct. Returns the new generic type. +Errors if the given generic name isn't a single identifier or if +the struct already has a generic with the same name. + +This method should be used carefully, if there is existing code referring +to the struct type it may be checked before this function is called and +see the struct with the original number of generics. This method should +thus be preferred to use on code generated from other macros and structs +that are not used in function signatures. + +Example: + +```rust title="add-generic-example" showLineNumbers +comptime fn add_generic(s: StructDefinition) { + assert_eq(s.generics().len(), 0); + let new_generic = s.add_generic("T"); + + let generics = s.generics(); + assert_eq(generics.len(), 1); + let (typ, numeric) = generics[0]; + assert_eq(typ, new_generic); + assert(numeric.is_none()); + } +``` +> Source code: test_programs/compile_success_empty/comptime_struct_definition/src/main.nr#L35-L46 + + +### as_type + +```rust title="as_type" showLineNumbers +pub comptime fn as_type(self) -> Type {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L17-L19 + + +Returns this struct as a type in the source program. If this struct has +any generics, the generics are also included as-is. + +### generics + +```rust title="generics" showLineNumbers +pub comptime fn generics(self) -> [(Type, Option)] {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L29-L31 + + +Returns each generic on this struct. Each generic is represented as a tuple containing the type, +and an optional containing the numeric type if it's a numeric generic. + +Example: + +``` +#[example] +struct Foo { + bar: [T; K], + baz: Baz, +} + +comptime fn example(foo: StructDefinition) { + assert_eq(foo.generics().len(), 3); + + // Fails because `T` isn't in scope + // let t = quote { T }.as_type(); + // assert_eq(foo.generics()[0].0, t); + assert(foo.generics()[0].1.is_none()); + + // Last generic is numeric, so we have the numeric type available to us + assert(foo.generics()[2].1.is_some()); +} +``` + +### fields + +```rust title="fields" showLineNumbers +pub comptime fn fields(self, generic_args: [Type]) -> [(Quoted, Type)] {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L37-L39 + + +Returns (name, type) pairs of each field in this struct. +Any generic types used in each field type is automatically substituted with the +provided generic arguments. + +### fields_as_written + +```rust title="fields_as_written" showLineNumbers +pub comptime fn fields_as_written(self) -> [(Quoted, Type)] {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L46-L48 + + +Returns (name, type) pairs of each field in this struct. Each type is as-is +with any generic arguments unchanged. Unless the field types are not needed, +users should generally prefer to use `StructDefinition::fields` over this +function if possible. + +### has_named_attribute + +```rust title="has_named_attribute" showLineNumbers +pub comptime fn has_named_attribute(self, name: str) -> bool {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L22-L24 + + +Returns true if this struct has a custom attribute with the given name. + +### module + +```rust title="module" showLineNumbers +pub comptime fn module(self) -> Module {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L51-L53 + + +Returns the module where the struct is defined. + +### name + +```rust title="name" showLineNumbers +pub comptime fn name(self) -> Quoted {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L56-L58 + + +Returns the name of this struct + +Note that the returned quoted value will be just the struct name, it will +not be the full path to the struct, nor will it include any generics. + +### set_fields + +```rust title="set_fields" showLineNumbers +pub comptime fn set_fields(self, new_fields: [(Quoted, Type)]) {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L65-L67 + + +Sets the fields of this struct to the given fields list where each element +is a pair of the field's name and the field's type. Expects each field name +to be a single identifier. Note that this will override any previous fields +on this struct. If those should be preserved, use `.fields()` to retrieve the +current fields on the struct type and append the new fields from there. + +Example: + +```rust +// Change this struct to: +// struct Foo { +// a: u32, +// b: i8, +// } +#[mangle_fields] +struct Foo { x: Field } + +comptime fn mangle_fields(s: StructDefinition) { + s.set_fields(&[ + (quote { a }, quote { u32 }.as_type()), + (quote { b }, quote { i8 }.as_type()), + ]); +} +``` + +## Trait Implementations + +```rust +impl Eq for StructDefinition +impl Hash for StructDefinition +``` + +Note that each struct is assigned a unique ID internally and this is what is used for +equality and hashing. So even structs with identical generics and fields may not +be equal in this sense if they were originally different items in the source program. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_constraint.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_constraint.md new file mode 100644 index 000000000000..3106f732b5a6 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_constraint.md @@ -0,0 +1,17 @@ +--- +title: TraitConstraint +--- + +`std::meta::trait_constraint` contains methods on the built-in `TraitConstraint` type which represents +a trait constraint that can be used to search for a trait implementation. This is similar +syntactically to just the trait itself, but can also contain generic arguments. E.g. `Eq`, `Default`, +`BuildHasher`. + +This type currently has no public methods but it can be used alongside `Type` in `implements` or `get_trait_impl`. + +## Trait Implementations + +```rust +impl Eq for TraitConstraint +impl Hash for TraitConstraint +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_def.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_def.md new file mode 100644 index 000000000000..e661d3af7f1a --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_def.md @@ -0,0 +1,26 @@ +--- +title: TraitDefinition +--- + +`std::meta::trait_def` contains methods on the built-in `TraitDefinition` type. This type +represents trait definitions such as `trait Foo { .. }` at the top-level of a program. + +## Methods + +### as_trait_constraint + +```rust title="as_trait_constraint" showLineNumbers +pub comptime fn as_trait_constraint(_self: Self) -> TraitConstraint {} +``` +> Source code: noir_stdlib/src/meta/trait_def.nr#L6-L8 + + +Converts this trait into a trait constraint. If there are any generics on this +trait, they will be kept as-is without instantiating or replacing them. + +## Trait Implementations + +```rust +impl Eq for TraitDefinition +impl Hash for TraitDefinition +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_impl.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_impl.md new file mode 100644 index 000000000000..355213507b41 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_impl.md @@ -0,0 +1,62 @@ +--- +title: TraitImpl +--- + +`std::meta::trait_impl` contains methods on the built-in `TraitImpl` type which represents a trait +implementation such as `impl Foo for Bar { ... }`. + +## Methods + +### trait_generic_args + +```rust title="trait_generic_args" showLineNumbers +pub comptime fn trait_generic_args(self) -> [Type] {} +``` +> Source code: noir_stdlib/src/meta/trait_impl.nr#L3-L5 + + +Returns any generic arguments on the trait of this trait implementation, if any. + +```rs +impl Foo for Bar { ... } + +comptime { + let bar_type = quote { Bar }.as_type(); + let foo = quote { Foo }.as_trait_constraint(); + + let my_impl: TraitImpl = bar_type.get_trait_impl(foo).unwrap(); + + let generics = my_impl.trait_generic_args(); + assert_eq(generics.len(), 2); + + assert_eq(generics[0].0, quote { i32 }.as_type()); + assert(generics[0].1.is_none()); + assert_eq(generics[1].0, quote { Field }.as_type()); + assert(generics[1].1.is_none()); +} +``` + +### methods + +```rust title="methods" showLineNumbers +pub comptime fn methods(self) -> [FunctionDefinition] {} +``` +> Source code: noir_stdlib/src/meta/trait_impl.nr#L8-L10 + + +Returns each method in this trait impl. + +Example: + +```rs +comptime { + let i32_type = quote { i32 }.as_type(); + let eq = quote { Eq }.as_trait_constraint(); + + let impl_eq_for_i32: TraitImpl = i32_type.get_trait_impl(eq).unwrap(); + let methods = impl_eq_for_i32.methods(); + + assert_eq(methods.len(), 1); + assert_eq(methods[0].name(), quote { eq }); +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typ.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typ.md new file mode 100644 index 000000000000..3a85a739a4c8 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typ.md @@ -0,0 +1,264 @@ +--- +title: Type +--- + +`std::meta::typ` contains methods on the built-in `Type` type used for representing +a type in the source program. + +## Functions + +```rust title="fresh_type_variable" showLineNumbers +pub comptime fn fresh_type_variable() -> Type {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L57-L59 + + +Creates and returns an unbound type variable. This is a special kind of type internal +to type checking which will type check with any other type. When it is type checked +against another type it will also be set to that type. For example, if `a` is a type +variable and we have the type equality `(a, i32) = (u8, i32)`, the compiler will set +`a` equal to `u8`. + +Unbound type variables will often be rendered as `_` while printing them. Bound type +variables will appear as the type they are bound to. + +This can be used in conjunction with functions which internally perform type checks +such as `Type::implements` or `Type::get_trait_impl` to potentially grab some of the types used. + +Note that calling `Type::implements` or `Type::get_trait_impl` on a type variable will always +fail. + +Example: + +```rust title="serialize-setup" showLineNumbers +trait Serialize {} + +impl Serialize<1> for Field {} + +impl Serialize for [T; N] +where + T: Serialize, +{} + +impl Serialize for (T, U) +where + T: Serialize, + U: Serialize, +{} +``` +> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L14-L29 + +```rust title="fresh-type-variable-example" showLineNumbers +let typevar1 = std::meta::typ::fresh_type_variable(); + let constraint = quote { Serialize<$typevar1> }.as_trait_constraint(); + let field_type = quote { Field }.as_type(); + + // Search for a trait impl (binding typevar1 to 1 when the impl is found): + assert(field_type.implements(constraint)); + + // typevar1 should be bound to the "1" generic now: + assert_eq(typevar1.as_constant().unwrap(), 1); + + // If we want to do the same with a different type, we need to + // create a new type variable now that `typevar1` is bound + let typevar2 = std::meta::typ::fresh_type_variable(); + let constraint = quote { Serialize<$typevar2> }.as_trait_constraint(); + let array_type = quote { [(Field, Field); 5] }.as_type(); + assert(array_type.implements(constraint)); + + // Now typevar2 should be bound to the serialized pair size 2 times the array length 5 + assert_eq(typevar2.as_constant().unwrap(), 10); +``` +> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L129-L149 + + +## Methods + +### as_array + +```rust title="as_array" showLineNumbers +pub comptime fn as_array(self) -> Option<(Type, Type)> {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L76-L78 + + +If this type is an array, return a pair of (element type, size type). + +Example: + +```rust +comptime { + let array_type = quote { [Field; 3] }.as_type(); + let (field_type, three_type) = array_type.as_array().unwrap(); + + assert(field_type.is_field()); + assert_eq(three_type.as_constant().unwrap(), 3); +} +``` + +### as_constant + +```rust title="as_constant" showLineNumbers +pub comptime fn as_constant(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L83-L85 + + +If this type is a constant integer (such as the `3` in the array type `[Field; 3]`), +return the numeric constant. + +### as_integer + +```rust title="as_integer" showLineNumbers +pub comptime fn as_integer(self) -> Option<(bool, u8)> {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L90-L92 + + +If this is an integer type, return a boolean which is `true` +if the type is signed, as well as the number of bits of this integer type. + +### as_mutable_reference + +```rust title="as_mutable_reference" showLineNumbers +comptime fn as_mutable_reference(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L96-L98 + + +If this is a mutable reference type `&mut T`, returns the mutable type `T`. + +### as_slice + +```rust title="as_slice" showLineNumbers +pub comptime fn as_slice(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L102-L104 + + +If this is a slice type, return the element type of the slice. + +### as_str + +```rust title="as_str" showLineNumbers +pub comptime fn as_str(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L108-L110 + + +If this is a `str` type, returns the length `N` as a type. + +### as_struct + +```rust title="as_struct" showLineNumbers +pub comptime fn as_struct(self) -> Option<(StructDefinition, [Type])> {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L114-L116 + + +If this is a struct type, returns the struct in addition to +any generic arguments on this type. + +### as_tuple + +```rust title="as_tuple" showLineNumbers +pub comptime fn as_tuple(self) -> Option<[Type]> {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L120-L122 + + +If this is a tuple type, returns each element type of the tuple. + +### get_trait_impl + +```rust title="get_trait_impl" showLineNumbers +pub comptime fn get_trait_impl(self, constraint: TraitConstraint) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L143-L145 + + +Retrieves the trait implementation that implements the given +trait constraint for this type. If the trait constraint is not +found, `None` is returned. Note that since the concrete trait implementation +for a trait constraint specified in a `where` clause is unknown, +this function will return `None` in these cases. If you only want to know +whether a type implements a trait, use `implements` instead. + +Example: + +```rust +comptime { + let field_type = quote { Field }.as_type(); + let default = quote { Default }.as_trait_constraint(); + + let the_impl: TraitImpl = field_type.get_trait_impl(default).unwrap(); + assert(the_impl.methods().len(), 1); +} +``` + +### implements + +```rust title="implements" showLineNumbers +pub comptime fn implements(self, constraint: TraitConstraint) -> bool {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L166-L168 + + +`true` if this type implements the given trait. Note that unlike +`get_trait_impl` this will also return true for any `where` constraints +in scope. + +Example: + +```rust +fn foo() where T: Default { + comptime { + let field_type = quote { Field }.as_type(); + let default = quote { Default }.as_trait_constraint(); + assert(field_type.implements(default)); + + let t = quote { T }.as_type(); + assert(t.implements(default)); + } +} +``` + +### is_bool + +```rust title="is_bool" showLineNumbers +pub comptime fn is_bool(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L172-L174 + + +`true` if this type is `bool`. + +### is_field + +```rust title="is_field" showLineNumbers +pub comptime fn is_field(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L178-L180 + + +`true` if this type is `Field`. + +### is_unit + +```rust title="is_unit" showLineNumbers +comptime fn is_unit(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L184-L186 + + +`true` if this type is the unit `()` type. + +## Trait Implementations + +```rust +impl Eq for Type +impl Hash for Type +``` +Note that this is syntactic equality, this is not the same as whether two types will type check +to be the same type. Unless type inference or generics are being used however, users should not +typically have to worry about this distinction unless `std::meta::typ::fresh_type_variable` is used. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typed_expr.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typed_expr.md new file mode 100644 index 000000000000..0db7dbfef610 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typed_expr.md @@ -0,0 +1,27 @@ +--- +title: TypedExpr +--- + +`std::meta::typed_expr` contains methods on the built-in `TypedExpr` type for resolved and type-checked expressions. + +## Methods + +### get_type + +```rust title="as_function_definition" showLineNumbers +pub comptime fn as_function_definition(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typed_expr.nr#L7-L9 + + +If this expression refers to a function definitions, returns it. Otherwise returns `Option::none()`. + +### get_type + +```rust title="get_type" showLineNumbers +pub comptime fn get_type(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typed_expr.nr#L13-L15 + + +Returns the type of the expression, or `Option::none()` if there were errors when the expression was previously resolved. \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/unresolved_type.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/unresolved_type.md new file mode 100644 index 000000000000..2826ec5ec0f0 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/unresolved_type.md @@ -0,0 +1,57 @@ +--- +title: UnresolvedType +--- + +`std::meta::unresolved_type` contains methods on the built-in `UnresolvedType` type for the syntax of types. + +## Methods + +### as_mutable_reference + +```rust title="as_mutable_reference" showLineNumbers +comptime fn as_mutable_reference(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/unresolved_type.nr#L8-L10 + + +If this is a mutable reference type `&mut T`, returns the mutable type `T`. + +### as_slice + +```rust title="as_slice" showLineNumbers +comptime fn as_slice(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/unresolved_type.nr#L14-L16 + + +If this is a slice `&[T]`, returns the element type `T`. + +### is_bool + +```rust title="is_bool" showLineNumbers +comptime fn is_bool(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/unresolved_type.nr#L20-L22 + + +Returns `true` if this type is `bool`. + +### is_field + +```rust title="is_field" showLineNumbers +pub comptime fn is_field(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/unresolved_type.nr#L26-L28 + + +Returns true if this type refers to the Field type. + +### is_unit + +```rust title="is_unit" showLineNumbers +comptime fn is_unit(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/unresolved_type.nr#L32-L34 + + +Returns true if this type is the unit `()` type. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/options.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/options.md new file mode 100644 index 000000000000..a1bd4e1de5fd --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/options.md @@ -0,0 +1,101 @@ +--- +title: Option Type +--- + +The `Option` type is a way to express that a value might be present (`Some(T))` or absent (`None`). It's a safer way to handle potential absence of values, compared to using nulls in many other languages. + +```rust +struct Option { + None, + Some(T), +} +``` + +The `Option` type, already imported into your Noir program, can be used directly: + +```rust +fn main() { + let none = Option::none(); + let some = Option::some(3); +} +``` + +See [this test](https://github.com/noir-lang/noir/blob/5cbfb9c4a06c8865c98ff2b594464b037d821a5c/crates/nargo_cli/tests/test_data/option/src/main.nr) for a more comprehensive set of examples of each of the methods described below. + +## Methods + +### none + +Constructs a none value. + +### some + +Constructs a some wrapper around a given value. + +### is_none + +Returns true if the Option is None. + +### is_some + +Returns true of the Option is Some. + +### unwrap + +Asserts `self.is_some()` and returns the wrapped value. + +### unwrap_unchecked + +Returns the inner value without asserting `self.is_some()`. This method can be useful within an if condition when we already know that `option.is_some()`. If the option is None, there is no guarantee what value will be returned, only that it will be of type T for an `Option`. + +### unwrap_or + +Returns the wrapped value if `self.is_some()`. Otherwise, returns the given default value. + +### unwrap_or_else + +Returns the wrapped value if `self.is_some()`. Otherwise, calls the given function to return a default value. + +### expect + +Asserts `self.is_some()` with a provided custom message and returns the contained `Some` value. The custom message is expected to be a format string. + +### map + +If self is `Some(x)`, this returns `Some(f(x))`. Otherwise, this returns `None`. + +### map_or + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns the given default value. + +### map_or_else + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns `default()`. + +### and + +Returns None if self is None. Otherwise, this returns `other`. + +### and_then + +If self is None, this returns None. Otherwise, this calls the given function with the Some value contained within self, and returns the result of that call. In some languages this function is called `flat_map` or `bind`. + +### or + +If self is Some, return self. Otherwise, return `other`. + +### or_else + +If self is Some, return self. Otherwise, return `default()`. + +### xor + +If only one of the two Options is Some, return that option. Otherwise, if both options are Some or both are None, None is returned. + +### filter + +Returns `Some(x)` if self is `Some(x)` and `predicate(x)` is true. Otherwise, this returns `None`. + +### flatten + +Flattens an `Option>` into a `Option`. This returns `None` if the outer Option is None. Otherwise, this returns the inner Option. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/recursion.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/recursion.mdx new file mode 100644 index 000000000000..fcb362780606 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/recursion.mdx @@ -0,0 +1,67 @@ +--- +title: Recursive Proofs +description: Learn about how to write recursive proofs in Noir. +keywords: [recursion, recursive proofs, verification_key, verify_proof] +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; + +Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. + +Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) + +## Verifying Recursive Proofs + +```rust +#[foreign(recursive_aggregation)] +pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} +``` + + + +## Example usage + +```rust + +fn main( + verification_key : [Field; 114], + proof : [Field; 93], + public_inputs : [Field; 1], + key_hash : Field, + proof_b : [Field; 93], +) { + std::verify_proof( + verification_key, + proof, + public_inputs, + key_hash + ); + + std::verify_proof( + verification_key, + proof_b, + public_inputs, + key_hash + ); +} +``` + +You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). + +## Parameters + +### `verification_key` + +The verification key for the zk program that is being verified. + +### `proof` + +The proof for the zk program that is being verified. + +### `public_inputs` + +These represent the public inputs of the proof we are verifying. + +### `key_hash` + +A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/traits.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/traits.md new file mode 100644 index 000000000000..9f447ad82a83 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/traits.md @@ -0,0 +1,655 @@ +--- +title: Traits +description: Noir's stdlib provides a few commonly used traits. +keywords: [traits, trait, interface, protocol, default, add, eq] +--- + +## `std::default` + +### `std::default::Default` + +```rust title="default-trait" showLineNumbers +pub trait Default { + fn default() -> Self; +} +``` +> Source code: noir_stdlib/src/default.nr#L4-L8 + + +Constructs a default value of a type. + +Implementations: +```rust +impl Default for Field { .. } + +impl Default for i8 { .. } +impl Default for i16 { .. } +impl Default for i32 { .. } +impl Default for i64 { .. } + +impl Default for u8 { .. } +impl Default for u16 { .. } +impl Default for u32 { .. } +impl Default for u64 { .. } + +impl Default for () { .. } +impl Default for bool { .. } + +impl Default for [T; N] + where T: Default { .. } + +impl Default for [T] { .. } + +impl Default for (A, B) + where A: Default, B: Default { .. } + +impl Default for (A, B, C) + where A: Default, B: Default, C: Default { .. } + +impl Default for (A, B, C, D) + where A: Default, B: Default, C: Default, D: Default { .. } + +impl Default for (A, B, C, D, E) + where A: Default, B: Default, C: Default, D: Default, E: Default { .. } +``` + +For primitive integer types, the return value of `default` is `0`. Container +types such as arrays are filled with default values of their element type, +except slices whose length is unknown and thus defaulted to zero. + +--- + +## `std::convert` + +### `std::convert::From` + +```rust title="from-trait" showLineNumbers +pub trait From { + fn from(input: T) -> Self; +} +``` +> Source code: noir_stdlib/src/convert.nr#L1-L5 + + +The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented. + +The Noir standard library provides a number of implementations of `From` between primitive types. +```rust title="from-impls" showLineNumbers +// Unsigned integers + +impl From for u32 { + fn from(value: u8) -> u32 { + value as u32 + } +} + +impl From for u64 { + fn from(value: u8) -> u64 { + value as u64 + } +} +impl From for u64 { + fn from(value: u32) -> u64 { + value as u64 + } +} + +impl From for u128 { + fn from(value: u8) -> u128 { + value as u128 + } +} +impl From for u128 { + fn from(value: u32) -> u128 { + value as u128 + } +} +impl From for u128 { + fn from(value: u64) -> u128 { + value as u128 + } +} + +impl From for Field { + fn from(value: u8) -> Field { + value as Field + } +} +impl From for Field { + fn from(value: u32) -> Field { + value as Field + } +} +impl From for Field { + fn from(value: u64) -> Field { + value as Field + } +} + +impl From for Field { + fn from(value: u128) -> Field { + value as Field + } +} + +// Signed integers + +impl From for i32 { + fn from(value: i8) -> i32 { + value as i32 + } +} + +impl From for i64 { + fn from(value: i8) -> i64 { + value as i64 + } +} +impl From for i64 { + fn from(value: i32) -> i64 { + value as i64 + } +} + +// Booleans +impl From for u8 { + fn from(value: bool) -> u8 { + value as u8 + } +} +impl From for u32 { + fn from(value: bool) -> u32 { + value as u32 + } +} +impl From for u64 { + fn from(value: bool) -> u64 { + value as u64 + } +} +impl From for i8 { + fn from(value: bool) -> i8 { + value as i8 + } +} +impl From for i32 { + fn from(value: bool) -> i32 { + value as i32 + } +} +impl From for i64 { + fn from(value: bool) -> i64 { + value as i64 + } +} +impl From for Field { + fn from(value: bool) -> Field { + value as Field + } +} +``` +> Source code: noir_stdlib/src/convert.nr#L28-L141 + + +#### When to implement `From` + +As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from): + +- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. +- The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. +- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. +- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. + +One additional recommendation specific to Noir is: +- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. + +### `std::convert::Into` + +The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`. + +For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation. + +```rust title="into-trait" showLineNumbers +pub trait Into { + fn into(self) -> T; +} + +impl Into for U +where + T: From, +{ + fn into(self) -> T { + T::from(self) + } +} +``` +> Source code: noir_stdlib/src/convert.nr#L13-L26 + + +`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. + +--- + +## `std::cmp` + +### `std::cmp::Eq` + +```rust title="eq-trait" showLineNumbers +pub trait Eq { + fn eq(self, other: Self) -> bool; +} +``` +> Source code: noir_stdlib/src/cmp.nr#L4-L8 + + +Returns `true` if `self` is equal to `other`. Implementing this trait on a type +allows the type to be used with `==` and `!=`. + +Implementations: +```rust +impl Eq for Field { .. } + +impl Eq for i8 { .. } +impl Eq for i16 { .. } +impl Eq for i32 { .. } +impl Eq for i64 { .. } + +impl Eq for u8 { .. } +impl Eq for u16 { .. } +impl Eq for u32 { .. } +impl Eq for u64 { .. } + +impl Eq for () { .. } +impl Eq for bool { .. } + +impl Eq for [T; N] + where T: Eq { .. } + +impl Eq for [T] + where T: Eq { .. } + +impl Eq for (A, B) + where A: Eq, B: Eq { .. } + +impl Eq for (A, B, C) + where A: Eq, B: Eq, C: Eq { .. } + +impl Eq for (A, B, C, D) + where A: Eq, B: Eq, C: Eq, D: Eq { .. } + +impl Eq for (A, B, C, D, E) + where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } +``` + +### `std::cmp::Ord` + +```rust title="ord-trait" showLineNumbers +pub trait Ord { + fn cmp(self, other: Self) -> Ordering; +} +``` +> Source code: noir_stdlib/src/cmp.nr#L215-L219 + + +`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, +`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. +Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be +used on values of the type. + +`std::cmp` also provides `max` and `min` functions for any type which implements the `Ord` trait. + +Implementations: + +```rust +impl Ord for u8 { .. } +impl Ord for u16 { .. } +impl Ord for u32 { .. } +impl Ord for u64 { .. } + +impl Ord for i8 { .. } +impl Ord for i16 { .. } +impl Ord for i32 { .. } + +impl Ord for i64 { .. } + +impl Ord for () { .. } +impl Ord for bool { .. } + +impl Ord for [T; N] + where T: Ord { .. } + +impl Ord for [T] + where T: Ord { .. } + +impl Ord for (A, B) + where A: Ord, B: Ord { .. } + +impl Ord for (A, B, C) + where A: Ord, B: Ord, C: Ord { .. } + +impl Ord for (A, B, C, D) + where A: Ord, B: Ord, C: Ord, D: Ord { .. } + +impl Ord for (A, B, C, D, E) + where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } +``` + +--- + +## `std::ops` + +### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` + +These traits abstract over addition, subtraction, multiplication, and division respectively. +Implementing these traits for a given type will also allow that type to be used with the corresponding operator +for that trait (`+` for Add, etc) in addition to the normal method names. + +```rust title="add-trait" showLineNumbers +pub trait Add { + fn add(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L1-L5 + +```rust title="sub-trait" showLineNumbers +pub trait Sub { + fn sub(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L65-L69 + +```rust title="mul-trait" showLineNumbers +pub trait Mul { + fn mul(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L129-L133 + +```rust title="div-trait" showLineNumbers +pub trait Div { + fn div(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L193-L197 + + +The implementations block below is given for the `Add` trait, but the same types that implement +`Add` also implement `Sub`, `Mul`, and `Div`. + +Implementations: +```rust +impl Add for Field { .. } + +impl Add for i8 { .. } +impl Add for i16 { .. } +impl Add for i32 { .. } +impl Add for i64 { .. } + +impl Add for u8 { .. } +impl Add for u16 { .. } +impl Add for u32 { .. } +impl Add for u64 { .. } +``` + +### `std::ops::Rem` + +```rust title="rem-trait" showLineNumbers +pub trait Rem { + fn rem(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L257-L261 + + +`Rem::rem(a, b)` is the remainder function returning the result of what is +left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator +to be used with the implementation type. + +Unlike other numeric traits, `Rem` is not implemented for `Field`. + +Implementations: +```rust +impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } +impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } +impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } +impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } + +impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } +impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } +impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } +impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } +``` + +### `std::ops::Neg` + +```rust title="neg-trait" showLineNumbers +pub trait Neg { + fn neg(self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L315-L319 + + +`Neg::neg` is equivalent to the unary negation operator `-`. + +Implementations: +```rust title="neg-trait-impls" showLineNumbers +impl Neg for Field { + fn neg(self) -> Field { + -self + } +} + +impl Neg for i8 { + fn neg(self) -> i8 { + -self + } +} +impl Neg for i16 { + fn neg(self) -> i16 { + -self + } +} +impl Neg for i32 { + fn neg(self) -> i32 { + -self + } +} +impl Neg for i64 { + fn neg(self) -> i64 { + -self + } +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L321-L348 + + +### `std::ops::Not` + +```rust title="not-trait" showLineNumbers +pub trait Not { + fn not(self: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L1-L5 + + +`Not::not` is equivalent to the unary bitwise NOT operator `!`. + +Implementations: +```rust title="not-trait-impls" showLineNumbers +impl Not for bool { + fn not(self) -> bool { + !self + } +} + +impl Not for u128 { + fn not(self) -> u128 { + !self + } +} +impl Not for u64 { + fn not(self) -> u64 { + !self + } +} +impl Not for u32 { + fn not(self) -> u32 { + !self + } +} +impl Not for u16 { + fn not(self) -> u16 { + !self + } +} +impl Not for u8 { + fn not(self) -> u8 { + !self + } +} +impl Not for u1 { + fn not(self) -> u1 { + !self + } +} + +impl Not for i8 { + fn not(self) -> i8 { + !self + } +} +impl Not for i16 { + fn not(self) -> i16 { + !self + } +} +impl Not for i32 { + fn not(self) -> i32 { + !self + } +} +impl Not for i64 { + fn not(self) -> i64 { + !self + } +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L7-L65 + + +### `std::ops::{ BitOr, BitAnd, BitXor }` + +```rust title="bitor-trait" showLineNumbers +pub trait BitOr { + fn bitor(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L67-L71 + +```rust title="bitand-trait" showLineNumbers +pub trait BitAnd { + fn bitand(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L131-L135 + +```rust title="bitxor-trait" showLineNumbers +pub trait BitXor { + fn bitxor(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L195-L199 + + +Traits for the bitwise operations `|`, `&`, and `^`. + +Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively +to be used with the type. + +The implementations block below is given for the `BitOr` trait, but the same types that implement +`BitOr` also implement `BitAnd` and `BitXor`. + +Implementations: +```rust +impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } + +impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } +impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } +impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } +impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } + +impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } +impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } +impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } +impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } +``` + +### `std::ops::{ Shl, Shr }` + +```rust title="shl-trait" showLineNumbers +pub trait Shl { + fn shl(self, other: u8) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L259-L263 + +```rust title="shr-trait" showLineNumbers +pub trait Shr { + fn shr(self, other: u8) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L317-L321 + + +Traits for a bit shift left and bit shift right. + +Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. +Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. + +Note that bit shifting is not currently implemented for signed types. + +The implementations block below is given for the `Shl` trait, but the same types that implement +`Shl` also implement `Shr`. + +Implementations: +```rust +impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } +impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } +impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } +impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } +``` + +--- + +## `std::append` + +### `std::append::Append` + +`Append` can abstract over types that can be appended to - usually container types: + +```rust title="append-trait" showLineNumbers +pub trait Append { + fn empty() -> Self; + fn append(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/append.nr#L9-L14 + + +`Append` requires two methods: + +- `empty`: Constructs an empty value of `Self`. +- `append`: Append two values together, returning the result. + +Additionally, it is expected that for any implementation: + +- `T::empty().append(x) == x` +- `x.append(T::empty()) == x` + +Implementations: +```rust +impl Append for [T] +impl Append for Quoted +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/.nojekyll b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/.nojekyll new file mode 100644 index 000000000000..e2ac6616addc --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/classes/Noir.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/classes/Noir.md new file mode 100644 index 000000000000..ead255bc5047 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/classes/Noir.md @@ -0,0 +1,52 @@ +# Noir + +## Constructors + +### new Noir(circuit) + +```ts +new Noir(circuit): Noir +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `circuit` | `CompiledCircuit` | + +#### Returns + +[`Noir`](Noir.md) + +## Methods + +### execute() + +```ts +execute(inputs, foreignCallHandler?): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | `InputMap` | +| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Allows to execute a circuit to get its witness and return value. + +#### Example + +```typescript +async execute(inputs) +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/and.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/and.md new file mode 100644 index 000000000000..c783283e3965 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/and.md @@ -0,0 +1,22 @@ +# and() + +```ts +and(lhs, rhs): string +``` + +Performs a bitwise AND operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/blake2s256.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/blake2s256.md new file mode 100644 index 000000000000..7882d0da8d50 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/blake2s256.md @@ -0,0 +1,21 @@ +# blake2s256() + +```ts +blake2s256(inputs): Uint8Array +``` + +Calculates the Blake2s256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md new file mode 100644 index 000000000000..5e3cd53e9d36 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256k1\_verify() + +```ts +ecdsa_secp256k1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256k1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md new file mode 100644 index 000000000000..0b20ff689575 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256r1\_verify() + +```ts +ecdsa_secp256r1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256r1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/xor.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/xor.md new file mode 100644 index 000000000000..8d762b895d30 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/xor.md @@ -0,0 +1,22 @@ +# xor() + +```ts +xor(lhs, rhs): string +``` + +Performs a bitwise XOR operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/index.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/index.md new file mode 100644 index 000000000000..4de7a6969910 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/index.md @@ -0,0 +1,47 @@ +# noir_js + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [Noir](classes/Noir.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [ErrorWithPayload](type-aliases/ErrorWithPayload.md) | - | +| [ForeignCallHandler](type-aliases/ForeignCallHandler.md) | A callback which performs an foreign call and returns the response. | +| [ForeignCallInput](type-aliases/ForeignCallInput.md) | - | +| [ForeignCallOutput](type-aliases/ForeignCallOutput.md) | - | +| [WitnessMap](type-aliases/WitnessMap.md) | - | + +### Functions + +| Function | Description | +| :------ | :------ | +| [and](functions/and.md) | Performs a bitwise AND operation between `lhs` and `rhs` | +| [blake2s256](functions/blake2s256.md) | Calculates the Blake2s256 hash of the input bytes | +| [ecdsa\_secp256k1\_verify](functions/ecdsa_secp256k1_verify.md) | Verifies a ECDSA signature over the secp256k1 curve. | +| [ecdsa\_secp256r1\_verify](functions/ecdsa_secp256r1_verify.md) | Verifies a ECDSA signature over the secp256r1 curve. | +| [xor](functions/xor.md) | Performs a bitwise XOR operation between `lhs` and `rhs` | + +## References + +### CompiledCircuit + +Renames and re-exports [InputMap](index.md#inputmap) + +## Variables + +### InputMap + +```ts +InputMap: any; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md new file mode 100644 index 000000000000..e8c2f4aef3d5 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md @@ -0,0 +1,15 @@ +# ErrorWithPayload + +```ts +type ErrorWithPayload: ExecutionError & object; +``` + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `decodedAssertionPayload` | `any` | - | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md new file mode 100644 index 000000000000..812b8b164818 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md @@ -0,0 +1,24 @@ +# ForeignCallHandler + +```ts +type ForeignCallHandler: (name, inputs) => Promise; +``` + +A callback which performs an foreign call and returns the response. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | The identifier for the type of foreign call being performed. | +| `inputs` | [`ForeignCallInput`](ForeignCallInput.md)[] | An array of hex encoded inputs to the foreign call. | + +## Returns + +`Promise`\<[`ForeignCallOutput`](ForeignCallOutput.md)[]\> + +outputs - An array of hex encoded outputs containing the results of the foreign call. + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md new file mode 100644 index 000000000000..dd95809186a2 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md @@ -0,0 +1,9 @@ +# ForeignCallInput + +```ts +type ForeignCallInput: string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md new file mode 100644 index 000000000000..b71fb78a9469 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md @@ -0,0 +1,9 @@ +# ForeignCallOutput + +```ts +type ForeignCallOutput: string | string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/WitnessMap.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/WitnessMap.md new file mode 100644 index 000000000000..258c46f9d0c9 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/WitnessMap.md @@ -0,0 +1,9 @@ +# WitnessMap + +```ts +type WitnessMap: Map; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/typedoc-sidebar.cjs b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/typedoc-sidebar.cjs new file mode 100644 index 000000000000..4796b5abaa86 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ErrorWithPayload","label":"ErrorWithPayload"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_js/functions/and","label":"and"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/xor","label":"xor"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/.nojekyll b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/.nojekyll new file mode 100644 index 000000000000..e2ac6616addc --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile.md new file mode 100644 index 000000000000..6faf763b37f7 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile.md @@ -0,0 +1,51 @@ +# compile() + +```ts +compile( + fileManager, + projectPath?, + logFn?, +debugLogFn?): Promise +``` + +Compiles a Noir project + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `fileManager` | `FileManager` | The file manager to use | +| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | +| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | +| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | + +## Returns + +`Promise`\<[`ProgramCompilationArtifacts`](../index.md#programcompilationartifacts)\> + +## Example + +```typescript +// Node.js + +import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager(myProjectPath); +const myCompiledCode = await compile_program(fm); +``` + +```typescript +// Browser + +import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager('/'); +for (const path of files) { + await fm.writeFile(path, await getFileAsStream(path)); +} +const myCompiledCode = await compile_program(fm); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile_contract.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile_contract.md new file mode 100644 index 000000000000..7d0b39a43ef8 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile_contract.md @@ -0,0 +1,51 @@ +# compile\_contract() + +```ts +compile_contract( + fileManager, + projectPath?, + logFn?, +debugLogFn?): Promise +``` + +Compiles a Noir project + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `fileManager` | `FileManager` | The file manager to use | +| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | +| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | +| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | + +## Returns + +`Promise`\<[`ContractCompilationArtifacts`](../index.md#contractcompilationartifacts)\> + +## Example + +```typescript +// Node.js + +import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager(myProjectPath); +const myCompiledCode = await compile_contract(fm); +``` + +```typescript +// Browser + +import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager('/'); +for (const path of files) { + await fm.writeFile(path, await getFileAsStream(path)); +} +const myCompiledCode = await compile_contract(fm); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/createFileManager.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/createFileManager.md new file mode 100644 index 000000000000..7e65c1d69c7e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/createFileManager.md @@ -0,0 +1,21 @@ +# createFileManager() + +```ts +createFileManager(dataDir): FileManager +``` + +Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `dataDir` | `string` | root of the file system | + +## Returns + +`FileManager` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md new file mode 100644 index 000000000000..fcea92753412 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md @@ -0,0 +1,21 @@ +# inflateDebugSymbols() + +```ts +inflateDebugSymbols(debugSymbols): any +``` + +Decompresses and decodes the debug symbols + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `debugSymbols` | `string` | The base64 encoded debug symbols | + +## Returns + +`any` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/index.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/index.md new file mode 100644 index 000000000000..b6e0f9d1bc0e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/index.md @@ -0,0 +1,49 @@ +# noir_wasm + +## Exports + +### Functions + +| Function | Description | +| :------ | :------ | +| [compile](functions/compile.md) | Compiles a Noir project | +| [compile\_contract](functions/compile_contract.md) | Compiles a Noir project | +| [createFileManager](functions/createFileManager.md) | Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) | +| [inflateDebugSymbols](functions/inflateDebugSymbols.md) | Decompresses and decodes the debug symbols | + +## References + +### compile\_program + +Renames and re-exports [compile](functions/compile.md) + +## Interfaces + +### ContractCompilationArtifacts + +The compilation artifacts of a given contract. + +#### Properties + +| Property | Type | Description | +| :------ | :------ | :------ | +| `contract` | `ContractArtifact` | The compiled contract. | +| `warnings` | `unknown`[] | Compilation warnings. | + +*** + +### ProgramCompilationArtifacts + +The compilation artifacts of a given program. + +#### Properties + +| Property | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | not part of the compilation output, injected later | +| `program` | `ProgramArtifact` | The compiled contract. | +| `warnings` | `unknown`[] | Compilation warnings. | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs new file mode 100644 index 000000000000..e0870710349c --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"doc","id":"reference/NoirJS/noir_wasm/index","label":"API"},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile","label":"compile"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile_contract","label":"compile_contract"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/createFileManager","label":"createFileManager"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/inflateDebugSymbols","label":"inflateDebugSymbols"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/_category_.json new file mode 100644 index 000000000000..5b6a20a609af --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 4, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/_category_.json new file mode 100644 index 000000000000..27869205ad3c --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Debugger", + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_known_limitations.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_known_limitations.md new file mode 100644 index 000000000000..936d416ac4bc --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_known_limitations.md @@ -0,0 +1,59 @@ +--- +title: Known limitations +description: + An overview of known limitations of the current version of the Noir debugger +keywords: + [ + Nargo, + Noir Debugger, + VS Code, + ] +sidebar_position: 2 +--- + +# Debugger Known Limitations + +There are currently some limits to what the debugger can observe. + +## Mutable references + +The debugger is currently blind to any state mutated via a mutable reference. For example, in: + +``` +let mut x = 1; +let y = &mut x; +*y = 2; +``` + +The update on `x` will not be observed by the debugger. That means, when running `vars` from the debugger REPL, or inspecting the _local variables_ pane in the VS Code debugger, `x` will appear with value 1 despite having executed `*y = 2;`. + +## Variables of type function or mutable references are opaque + +When inspecting variables, any variable of type `Function` or `MutableReference` will render its value as `<>` or `<>`. + +## Debugger instrumentation affects resulting ACIR + +In order to make the state of local variables observable, the debugger compiles Noir circuits interleaving foreign calls that track any mutations to them. While this works (except in the cases described above) and doesn't introduce any behavior changes, it does as a side effect produce bigger bytecode. In particular, when running the command `opcodes` on the REPL debugger, you will notice Unconstrained VM blocks that look like this: + +``` +... +5 BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [], q_c: 2 }), Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(2))], q_c: 0 })] + | outputs=[] + 5.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 5.1 | Mov { destination: RegisterIndex(3), source: RegisterIndex(1) } + 5.2 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 5.3 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 5.4 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 5.5 | Mov { destination: RegisterIndex(3), source: RegisterIndex(3) } + 5.6 | Call { location: 8 } + 5.7 | Stop + 5.8 | ForeignCall { function: "__debug_var_assign", destinations: [], inputs: [RegisterIndex(RegisterIndex(2)), RegisterIndex(RegisterIndex(3))] } +... +``` + +If you are interested in debugging/inspecting compiled ACIR without these synthetic changes, you can invoke the REPL debugger with the `--skip-instrumentation` flag or launch the VS Code debugger with the `skipConfiguration` property set to true in its launch configuration. You can find more details about those in the [Debugger REPL reference](debugger_repl.md) and the [VS Code Debugger reference](debugger_vscode.md). + +:::note +Skipping debugger instrumentation means you won't be able to inspect values of local variables. +::: + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_repl.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_repl.md new file mode 100644 index 000000000000..46e2011304e5 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_repl.md @@ -0,0 +1,360 @@ +--- +title: REPL Debugger +description: + Noir Debugger REPL options and commands. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + REPL, + ] +sidebar_position: 1 +--- + +## Running the REPL debugger + +`nargo debug [OPTIONS] [WITNESS_NAME]` + +Runs the Noir REPL debugger. If a `WITNESS_NAME` is provided the debugger writes the resulting execution witness to a `WITNESS_NAME` file. + +### Options + +| Option | Description | +| --------------------- | ------------------------------------------------------------ | +| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover]| +| `--package ` | The name of the package to debug | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +None of these options are required. + +:::note +Since the debugger starts by compiling the target package, all Noir compiler options are also available. Check out the [compiler reference](../nargo_commands.md#nargo-compile) to learn more about the compiler options. +::: + +## REPL commands + +Once the debugger is running, it accepts the following commands. + +#### `help` (h) + +Displays the menu of available commands. + +``` +> help +Available commands: + + opcodes display ACIR opcodes + into step into to the next opcode + next step until a new source location is reached + out step until a new source location is reached + and the current stack frame is finished + break LOCATION:OpcodeLocation add a breakpoint at an opcode location + over step until a new source location is reached + without diving into function calls + restart restart the debugging session + delete LOCATION:OpcodeLocation delete breakpoint at an opcode location + witness show witness map + witness index:u32 display a single witness from the witness map + witness index:u32 value:String update a witness with the given value + memset index:usize value:String update a memory cell with the given + value + continue continue execution until the end of the + program + vars show variable values available at this point + in execution + stacktrace display the current stack trace + memory show memory (valid when executing unconstrained code) value + step step to the next ACIR opcode + +Other commands: + + help Show this help message + quit Quit repl + +``` + +### Stepping through programs + +#### `next` (n) + +Step until the next Noir source code location. While other commands, such as [`into`](#into-i) and [`step`](#step-s), allow for finer grained control of the program's execution at the opcode level, `next` is source code centric. For example: + +``` +3 ... +4 fn main(x: u32) { +5 assert(entry_point(x) == 2); +6 swap_entry_point(x, x + 1); +7 -> assert(deep_entry_point(x) == 4); +8 multiple_values_entry_point(x); +9 } +``` + + +Using `next` here would cause the debugger to jump to the definition of `deep_entry_point` (if available). + +If you want to step over `deep_entry_point` and go straight to line 8, use [the `over` command](#over) instead. + +#### `over` + +Step until the next source code location, without diving into function calls. For example: + +``` +3 ... +4 fn main(x: u32) { +5 assert(entry_point(x) == 2); +6 swap_entry_point(x, x + 1); +7 -> assert(deep_entry_point(x) == 4); +8 multiple_values_entry_point(x); +9 } +``` + + +Using `over` here would cause the debugger to execute until line 8 (`multiple_values_entry_point(x);`). + +If you want to step into `deep_entry_point` instead, use [the `next` command](#next-n). + +#### `out` + +Step until the end of the current function call. For example: + +``` + 3 ... + 4 fn main(x: u32) { + 5 assert(entry_point(x) == 2); + 6 swap_entry_point(x, x + 1); + 7 -> assert(deep_entry_point(x) == 4); + 8 multiple_values_entry_point(x); + 9 } + 10 + 11 unconstrained fn returns_multiple_values(x: u32) -> (u32, u32, u32, u32) { + 12 ... + ... + 55 + 56 unconstrained fn deep_entry_point(x: u32) -> u32 { + 57 -> level_1(x + 1) + 58 } + +``` + +Running `out` here will resume execution until line 8. + +#### `step` (s) + +Skips to the next ACIR code. A compiled Noir program is a sequence of ACIR opcodes. However, an unconstrained VM opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. + +Using the `step` command at this point would result in the debugger stopping at ACIR opcode 2, `EXPR`, skipping unconstrained computation steps. + +Use [the `into` command](#into-i) instead if you want to follow unconstrained computation step by step. + +#### `into` (i) + +Steps into the next opcode. A compiled Noir program is a sequence of ACIR opcodes. However, a BRILLIG opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. + +Using the `into` command at this point would result in the debugger stopping at opcode 1.0, `Mov ...`, allowing the debugger user to follow unconstrained computation step by step. + +Use [the `step` command](#step-s) instead if you want to skip to the next ACIR code directly. + +#### `continue` (c) + +Continues execution until the next breakpoint, or the end of the program. + +#### `restart` (res) + +Interrupts execution, and restarts a new debugging session from scratch. + +#### `opcodes` (o) + +Display the program's ACIR opcode sequence. For example: + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +### Breakpoints + +#### `break [Opcode]` (or shorthand `b [Opcode]`) + +Sets a breakpoint on the specified opcode index. To get a list of the program opcode numbers, see [the `opcode` command](#opcodes-o). For example: + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +In this example, issuing a `break 1.2` command adds break on opcode 1.2, as denoted by the `*` character: + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | * Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +Running [the `continue` command](#continue-c) at this point would cause the debugger to execute the program until opcode 1.2. + +#### `delete [Opcode]` (or shorthand `d [Opcode]`) + +Deletes a breakpoint at an opcode location. Usage is analogous to [the `break` command](#). + +### Variable inspection + +#### vars + +Show variable values available at this point in execution. + +:::note +The ability to inspect variable values from the debugger depends on compilation to be run in a special debug instrumentation mode. This instrumentation weaves variable tracing code with the original source code. + +So variable value inspection comes at the expense of making the resulting ACIR bytecode bigger and harder to understand and optimize. + +If you find this compromise unacceptable, you can run the debugger with the flag `--skip-debug-instrumentation`. This will compile your circuit without any additional debug information, so the resulting ACIR bytecode will be identical to the one produced by standard Noir compilation. However, if you opt for this, the `vars` command will not be available while debugging. +::: + + +### Stacktrace + +#### `stacktrace` + +Displays the current stack trace. + + +### Witness map + +#### `witness` (w) + +Show witness map. For example: + +``` +_0 = 0 +_1 = 2 +_2 = 1 +``` + +#### `witness [Witness Index]` + +Display a single witness from the witness map. For example: + +``` +> witness 1 +_1 = 2 +``` + +#### `witness [Witness Index] [New value]` + +Overwrite the given index with a new value. For example: + +``` +> witness 1 3 +_1 = 3 +``` + + +### Unconstrained VM memory + +#### `memory` + +Show unconstrained VM memory state. For example: + +``` +> memory +At opcode 1.13: Store { destination_pointer: RegisterIndex(0), source: RegisterIndex(3) } +... +> registers +0 = 0 +1 = 10 +2 = 0 +3 = 1 +4 = 1 +5 = 2³² +6 = 1 +> into +At opcode 1.14: Const { destination: RegisterIndex(5), value: Value { inner: 1 } } +... +> memory +0 = 1 +> +``` + +In the example above: we start with clean memory, then step through a `Store` opcode which stores the value of register 3 (1) into the memory address stored in register 0 (0). Thus now `memory` shows memory address 0 contains value 1. + +:::note +This command is only functional while the debugger is executing unconstrained code. +::: + +#### `memset [Memory address] [New value]` + +Update a memory cell with the given value. For example: + +``` +> memory +0 = 1 +> memset 0 2 +> memory +0 = 2 +> memset 1 4 +> memory +0 = 2 +1 = 4 +> +``` + +:::note +This command is only functional while the debugger is executing unconstrained code. +::: \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_vscode.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_vscode.md new file mode 100644 index 000000000000..c027332b3b04 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_vscode.md @@ -0,0 +1,82 @@ +--- +title: VS Code Debugger +description: + VS Code Debugger configuration and features. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + VS Code, + IDE, + ] +sidebar_position: 0 +--- + +# VS Code Noir Debugger Reference + +The Noir debugger enabled by the vscode-noir extension ships with default settings such that the most common scenario should run without any additional configuration steps. + +These defaults can nevertheless be overridden by defining a launch configuration file. This page provides a reference for the properties you can override via a launch configuration file, as well as documenting the Nargo `dap` command, which is a dependency of the VS Code Noir debugger. + + +## Creating and editing launch configuration files + +To create a launch configuration file from VS Code, open the _debug pane_, and click on _create a launch.json file_. + +![Creating a launch configuration file](@site/static/img/debugger/ref1-create-launch.png) + +A `launch.json` file will be created, populated with basic defaults. + +### Noir Debugger launch.json properties + +#### projectFolder + +_String, optional._ + +Absolute path to the Nargo project to debug. By default, it is dynamically determined by looking for the nearest `Nargo.toml` file to the active file at the moment of launching the debugger. + +#### proverName + +_String, optional._ + +Name of the prover input to use. Defaults to `Prover`, which looks for a file named `Prover.toml` at the `projectFolder`. + +#### generateAcir + +_Boolean, optional._ + +If true, generate ACIR opcodes instead of unconstrained opcodes which will be closer to release binaries but less convenient for debugging. Defaults to `false`. + +#### skipInstrumentation + +_Boolean, optional._ + +Skips variables debugging instrumentation of code, making debugging less convenient but the resulting binary smaller and closer to production. Defaults to `false`. + +:::note +Skipping instrumentation causes the debugger to be unable to inspect local variables. +::: + +## `nargo dap [OPTIONS]` + +When run without any option flags, it starts the Nargo Debug Adapter Protocol server, which acts as the debugging backend for the VS Code Noir Debugger. + +All option flags are related to preflight checks. The Debug Adapter Protocol specifies how errors are to be informed from a running DAP server, but it doesn't specify mechanisms to communicate server initialization errors between the DAP server and its client IDE. + +Thus `nargo dap` ships with a _preflight check_ mode. If flag `--preflight-check` and the rest of the `--preflight-*` flags are provided, Nargo will run the same initialization routine except it will not start the DAP server. + +`vscode-noir` will then run `nargo dap` in preflight check mode first before a debugging session starts. If the preflight check ends in error, vscode-noir will present stderr and stdout output from this process through its own Output pane in VS Code. This makes it possible for users to diagnose what pieces of configuration might be wrong or missing in case of initialization errors. + +If the preflight check succeeds, `vscode-noir` proceeds to start the DAP server normally but running `nargo dap` without any additional flags. + +### Options + +| Option | Description | +| --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | +| `--preflight-check` | If present, dap runs in preflight check mode. | +| `--preflight-project-folder ` | Absolute path to the project to debug for preflight check. | +| `--preflight-prover-name ` | Name of prover file to use for preflight check | +| `--preflight-generate-acir` | Optional. If present, compile in ACIR mode while running preflight check. | +| `--preflight-skip-instrumentation` | Optional. If present, compile without introducing debug instrumentation while running preflight check. | +| `-h, --help` | Print help. | diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/nargo_commands.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/nargo_commands.md new file mode 100644 index 000000000000..537363599664 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/nargo_commands.md @@ -0,0 +1,580 @@ +--- +title: Nargo +description: + Noir CLI Commands for Noir Prover and Verifier to create, execute, prove and verify programs, + generate Solidity verifier smart contract and compile into JSON file containing ACIR + representation and ABI of circuit. +keywords: + [ + Nargo, + Noir CLI, + Noir Prover, + Noir Verifier, + generate Solidity verifier, + compile JSON file, + ACIR representation, + ABI of circuit, + TypeScript, + ] +sidebar_position: 0 +--- + +# Command-Line Help for `nargo` + +This document contains the help content for the `nargo` command-line program. + +**Command Overview:** + +* [`nargo`↴](#nargo) +* [`nargo check`↴](#nargo-check) +* [`nargo fmt`↴](#nargo-fmt) +* [`nargo compile`↴](#nargo-compile) +* [`nargo new`↴](#nargo-new) +* [`nargo init`↴](#nargo-init) +* [`nargo execute`↴](#nargo-execute) +* [`nargo debug`↴](#nargo-debug) +* [`nargo test`↴](#nargo-test) +* [`nargo info`↴](#nargo-info) +* [`nargo lsp`↴](#nargo-lsp) +* [`nargo generate-completion-script`↴](#nargo-generate-completion-script) + +## `nargo` + +Noir's package manager + +**Usage:** `nargo ` + +###### **Subcommands:** + +* `check` — Check a local package and all of its dependencies for errors +* `fmt` — Format the Noir files in a workspace +* `compile` — Compile the program and its secret execution trace into ACIR format +* `new` — Create a Noir project in a new directory +* `init` — Create a Noir project in the current directory +* `execute` — Executes a circuit to calculate its return value +* `debug` — Executes a circuit in debug mode +* `test` — Run the tests for this program +* `info` — Provides detailed information on each of a program's function (represented by a single circuit) +* `lsp` — Starts the Noir LSP server +* `generate-completion-script` — Generates a shell completion script for your favorite shell + +###### **Options:** + + + + +## `nargo check` + +Check a local package and all of its dependencies for errors + +**Usage:** `nargo check [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to run the command on. By default run on the first one found moving up along the ancestors of the current directory +* `--workspace` — Run on all packages in the workspace + + Possible values: `true`, `false` + +* `--overwrite` — Force overwrite of existing files + + Possible values: `true`, `false` + +* `--expression-width ` — Specify the backend expression width that should be targeted +* `--bounded-codegen` — 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 + + Default value: `false` + + Possible values: `true`, `false` + +* `--force` — Force a full recompilation + + Possible values: `true`, `false` + +* `--print-acir` — Display the ACIR for compiled circuit + + Possible values: `true`, `false` + +* `--deny-warnings` — Treat all warnings as errors + + Possible values: `true`, `false` + +* `--silence-warnings` — Suppress warnings + + Possible values: `true`, `false` + +* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" +* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check` — Flag to turn on the compiler check for missing Brillig call constraints. Warning: This can degrade compilation speed but will also find some correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check-lookback` — Flag to turn on the lookback feature of the Brillig call constraints check, allowing tracking argument values before the call happens preventing certain rare false positives (leads to a slowdown on large rollout functions) + + Possible values: `true`, `false` + +* `--pedantic-solving` — Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. This is disabled by default + + Default value: `false` + + Possible values: `true`, `false` + + + + +## `nargo fmt` + +Format the Noir files in a workspace + +**Usage:** `nargo fmt [OPTIONS]` + +###### **Options:** + +* `--check` — Run noirfmt in check mode + + Possible values: `true`, `false` + +* `--package ` — The name of the package to run the command on. By default run on the first one found moving up along the ancestors of the current directory +* `--workspace` — Run on all packages in the workspace + + Possible values: `true`, `false` + + + + +## `nargo compile` + +Compile the program and its secret execution trace into ACIR format + +**Usage:** `nargo compile [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to run the command on. By default run on the first one found moving up along the ancestors of the current directory +* `--workspace` — Run on all packages in the workspace + + Possible values: `true`, `false` + +* `--expression-width ` — Specify the backend expression width that should be targeted +* `--bounded-codegen` — 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 + + Default value: `false` + + Possible values: `true`, `false` + +* `--force` — Force a full recompilation + + Possible values: `true`, `false` + +* `--print-acir` — Display the ACIR for compiled circuit + + Possible values: `true`, `false` + +* `--deny-warnings` — Treat all warnings as errors + + Possible values: `true`, `false` + +* `--silence-warnings` — Suppress warnings + + Possible values: `true`, `false` + +* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" +* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check` — Flag to turn on the compiler check for missing Brillig call constraints. Warning: This can degrade compilation speed but will also find some correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check-lookback` — Flag to turn on the lookback feature of the Brillig call constraints check, allowing tracking argument values before the call happens preventing certain rare false positives (leads to a slowdown on large rollout functions) + + Possible values: `true`, `false` + +* `--pedantic-solving` — Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. This is disabled by default + + Default value: `false` + + Possible values: `true`, `false` + + + + +## `nargo new` + +Create a Noir project in a new directory + +**Usage:** `nargo new [OPTIONS] ` + +###### **Arguments:** + +* `` — The path to save the new project + +###### **Options:** + +* `--name ` — Name of the package [default: package directory name] +* `--lib` — Use a library template + + Possible values: `true`, `false` + +* `--bin` — Use a binary template [default] + + Possible values: `true`, `false` + +* `--contract` — Use a contract template + + Possible values: `true`, `false` + + + + +## `nargo init` + +Create a Noir project in the current directory + +**Usage:** `nargo init [OPTIONS]` + +###### **Options:** + +* `--name ` — Name of the package [default: current directory name] +* `--lib` — Use a library template + + Possible values: `true`, `false` + +* `--bin` — Use a binary template [default] + + Possible values: `true`, `false` + +* `--contract` — Use a contract template + + Possible values: `true`, `false` + + + + +## `nargo execute` + +Executes a circuit to calculate its return value + +**Usage:** `nargo execute [OPTIONS] [WITNESS_NAME]` + +###### **Arguments:** + +* `` — Write the execution witness to named file + +Defaults to the name of the package being executed. + +###### **Options:** + +* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover + + Default value: `Prover` +* `--package ` — The name of the package to run the command on. By default run on the first one found moving up along the ancestors of the current directory +* `--workspace` — Run on all packages in the workspace + + Possible values: `true`, `false` + +* `--expression-width ` — Specify the backend expression width that should be targeted +* `--bounded-codegen` — 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 + + Default value: `false` + + Possible values: `true`, `false` + +* `--force` — Force a full recompilation + + Possible values: `true`, `false` + +* `--print-acir` — Display the ACIR for compiled circuit + + Possible values: `true`, `false` + +* `--deny-warnings` — Treat all warnings as errors + + Possible values: `true`, `false` + +* `--silence-warnings` — Suppress warnings + + Possible values: `true`, `false` + +* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" +* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check` — Flag to turn on the compiler check for missing Brillig call constraints. Warning: This can degrade compilation speed but will also find some correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check-lookback` — Flag to turn on the lookback feature of the Brillig call constraints check, allowing tracking argument values before the call happens preventing certain rare false positives (leads to a slowdown on large rollout functions) + + Possible values: `true`, `false` + +* `--pedantic-solving` — Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. This is disabled by default + + Default value: `false` + + Possible values: `true`, `false` + +* `--oracle-resolver ` — JSON RPC url to solve oracle calls + + + +## `nargo debug` + +Executes a circuit in debug mode + +**Usage:** `nargo debug [OPTIONS] [WITNESS_NAME]` + +###### **Arguments:** + +* `` — Write the execution witness to named file + +###### **Options:** + +* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover + + Default value: `Prover` +* `--package ` — The name of the package to execute +* `--expression-width ` — Specify the backend expression width that should be targeted +* `--bounded-codegen` — 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 + + Default value: `false` + + Possible values: `true`, `false` + +* `--force` — Force a full recompilation + + Possible values: `true`, `false` + +* `--print-acir` — Display the ACIR for compiled circuit + + Possible values: `true`, `false` + +* `--deny-warnings` — Treat all warnings as errors + + Possible values: `true`, `false` + +* `--silence-warnings` — Suppress warnings + + Possible values: `true`, `false` + +* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" +* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check` — Flag to turn on the compiler check for missing Brillig call constraints. Warning: This can degrade compilation speed but will also find some correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check-lookback` — Flag to turn on the lookback feature of the Brillig call constraints check, allowing tracking argument values before the call happens preventing certain rare false positives (leads to a slowdown on large rollout functions) + + Possible values: `true`, `false` + +* `--pedantic-solving` — Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. This is disabled by default + + Default value: `false` + + Possible values: `true`, `false` + +* `--acir-mode` — Force ACIR output (disabling instrumentation) + + Possible values: `true`, `false` + +* `--skip-instrumentation ` — Disable vars debug instrumentation (enabled by default) + + Possible values: `true`, `false` + + + + +## `nargo test` + +Run the tests for this program + +**Usage:** `nargo test [OPTIONS] [TEST_NAMES]...` + +###### **Arguments:** + +* `` — If given, only tests with names containing this string will be run + +###### **Options:** + +* `--show-output` — Display output of `println` statements + + Possible values: `true`, `false` + +* `--exact` — Only run tests that match exactly + + Possible values: `true`, `false` + +* `--package ` — The name of the package to run the command on. By default run on the first one found moving up along the ancestors of the current directory +* `--workspace` — Run on all packages in the workspace + + Possible values: `true`, `false` + +* `--expression-width ` — Specify the backend expression width that should be targeted +* `--bounded-codegen` — 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 + + Default value: `false` + + Possible values: `true`, `false` + +* `--force` — Force a full recompilation + + Possible values: `true`, `false` + +* `--print-acir` — Display the ACIR for compiled circuit + + Possible values: `true`, `false` + +* `--deny-warnings` — Treat all warnings as errors + + Possible values: `true`, `false` + +* `--silence-warnings` — Suppress warnings + + Possible values: `true`, `false` + +* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" +* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check` — Flag to turn on the compiler check for missing Brillig call constraints. Warning: This can degrade compilation speed but will also find some correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check-lookback` — Flag to turn on the lookback feature of the Brillig call constraints check, allowing tracking argument values before the call happens preventing certain rare false positives (leads to a slowdown on large rollout functions) + + Possible values: `true`, `false` + +* `--pedantic-solving` — Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. This is disabled by default + + Default value: `false` + + Possible values: `true`, `false` + +* `--oracle-resolver ` — JSON RPC url to solve oracle calls +* `--test-threads ` — Number of threads used for running tests in parallel + + Default value: `4` +* `--format ` — Configure formatting of output + + Possible values: + - `pretty`: + Print verbose output + - `terse`: + Display one character per test + - `json`: + Output a JSON Lines document + +* `-q`, `--quiet` — Display one character per test instead of one line + + Possible values: `true`, `false` + + + + +## `nargo info` + +Provides detailed information on each of a program's function (represented by a single circuit) + +Current information provided per circuit: 1. The number of ACIR opcodes 2. Counts the final number gates in the circuit used by a backend + +**Usage:** `nargo info [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to run the command on. By default run on the first one found moving up along the ancestors of the current directory +* `--workspace` — Run on all packages in the workspace + + Possible values: `true`, `false` + +* `--profile-execution` + + Possible values: `true`, `false` + +* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover + + Default value: `Prover` +* `--expression-width ` — Specify the backend expression width that should be targeted +* `--bounded-codegen` — 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 + + Default value: `false` + + Possible values: `true`, `false` + +* `--force` — Force a full recompilation + + Possible values: `true`, `false` + +* `--print-acir` — Display the ACIR for compiled circuit + + Possible values: `true`, `false` + +* `--deny-warnings` — Treat all warnings as errors + + Possible values: `true`, `false` + +* `--silence-warnings` — Suppress warnings + + Possible values: `true`, `false` + +* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" +* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check` — Flag to turn on the compiler check for missing Brillig call constraints. Warning: This can degrade compilation speed but will also find some correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check-lookback` — Flag to turn on the lookback feature of the Brillig call constraints check, allowing tracking argument values before the call happens preventing certain rare false positives (leads to a slowdown on large rollout functions) + + Possible values: `true`, `false` + +* `--pedantic-solving` — Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. This is disabled by default + + Default value: `false` + + Possible values: `true`, `false` + + + + +## `nargo lsp` + +Starts the Noir LSP server + +Starts an LSP server which allows IDEs such as VS Code to display diagnostics in Noir source. + +VS Code Noir Language Support: https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir + +**Usage:** `nargo lsp` + + + +## `nargo generate-completion-script` + +Generates a shell completion script for your favorite shell + +**Usage:** `nargo generate-completion-script ` + +###### **Arguments:** + +* `` — The shell to generate completions for. One of: bash, elvish, fish, powershell, zsh + + + +
+ + + This document was generated automatically by + clap-markdown. + + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/noir_codegen.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/noir_codegen.md new file mode 100644 index 000000000000..e4c362f96100 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/noir_codegen.md @@ -0,0 +1,116 @@ +--- +title: Noir Codegen for TypeScript +description: Learn how to use Noir codegen to generate TypeScript bindings +keywords: [Nargo, Noir, compile, TypeScript] +sidebar_position: 3 +--- + +When using TypeScript, it is extra work to interpret Noir program outputs in a type-safe way. Third party libraries may exist for popular Noir programs, but they are either hard to find or unmaintained. + +Now you can generate TypeScript bindings for your Noir programs in two steps: + +1. Exporting Noir functions using `nargo export` +2. Using the TypeScript module `noir_codegen` to generate TypeScript binding + +**Note:** you can only export functions from a Noir *library* (not binary or contract program types). + +## Installation + +### Your TypeScript project + +If you don't already have a TypeScript project you can add the module with `yarn` (or `npm`), then initialize it: + +```bash +yarn add typescript -D +npx tsc --init +``` + +### Add TypeScript module - `noir_codegen` + +The following command will add the module to your project's devDependencies: + +```bash +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](../getting_started/noir_installation.md). + +If you're in a new project, make a `circuits` folder and create a new Noir library: + +```bash +mkdir circuits && cd circuits +nargo new --lib myNoirLib +``` + +## Usage + +### Export ABI of specified functions + +First go to the `.nr` files in your Noir library, and add the `#[export]` macro to each function that you want to use in TypeScript. + +```rust +#[export] +fn your_function(... +``` + +From your Noir library (where `Nargo.toml` is), run the following command: + +```bash +nargo export +``` + +You will now have an `export` directory with a .json file per exported function. + +You can also specify the directory of Noir programs using `--program-dir`, for example: + +```bash +nargo export --program-dir=./circuits/myNoirLib +``` + +### Generate TypeScript bindings from exported functions + +To use the `noir-codegen` package we added to the TypeScript project: + +```bash +yarn noir-codegen ./export/your_function.json +``` + +This creates an `exports` directory with an `index.ts` file containing all exported functions. + +**Note:** adding `--out-dir` allows you to specify an output dir for your TypeScript bindings to go. Eg: + +```bash +yarn noir-codegen ./export/*.json --out-dir ./path/to/output/dir +``` + +## Example .nr function to .ts output + +Consider a Noir library with this function: + +```rust +#[export] +fn not_equal(x: Field, y: Field) -> bool { + x != y +} +``` + +After the export and codegen steps, you should have an `index.ts` like: + +```typescript +export type Field = string; + + +export const is_equal_circuit: CompiledCircuit = +{"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"private"}],"return_type":{"abi_type":{"kind":"boolean"},"visibility":"private"}},"bytecode":"H4sIAAAAAAAA/7WUMQ7DIAxFQ0Krrr2JjSGYLVcpKrn/CaqqDQN12WK+hPBgmWd/wEyHbF1SS923uhOs3pfoChI+wKXMAXzIKyNj4PB0TFTYc0w5RUjoqeAeEu1wqK0F54RGkWvW44LPzExnlkbMEs4JNZmN8PxS42uHv82T8a3Jeyn2Ks+VLPcO558HmyLMCDOXAXXtpPt4R/Rt9T36ss6dS9HGPx/eG17nGegKBQAA"}; + +export async function is_equal(x: Field, y: Field, foreignCallHandler?: ForeignCallHandler): Promise { + const program = new Noir(is_equal_circuit); + const args: InputMap = { x, y }; + const { returnValue } = await program.execute(args, foreignCallHandler); + return returnValue as boolean; +} +``` + +Now the `is_equal()` function and relevant types are readily available for use in TypeScript. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/cspell.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/cspell.json new file mode 100644 index 000000000000..6791ea20f27e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/cspell.json @@ -0,0 +1,8 @@ +{ + "words": [ + "ACIR", + "flamegraph", + "flamegraphs", + "lookback" + ] +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/debugger.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/debugger.md new file mode 100644 index 000000000000..200b5fc423ac --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/debugger.md @@ -0,0 +1,26 @@ +--- +title: Debugger +description: Learn about the Noir Debugger, in its REPL or VS Code versions. +keywords: [Nargo, VSCode, Visual Studio Code, REPL, Debugger] +sidebar_position: 2 +--- + +# Noir Debugger + +There are currently two ways of debugging Noir programs: + +1. From VS Code, via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). +2. Via the REPL debugger, which ships with Nargo. + +In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/noir_installation.md) and vscode-noir: + +- Noir & Nargo ≥0.28.0 +- Noir's VS Code extension ≥0.0.11 + +:::info +At the moment, the debugger supports debugging binary projects, but not contracts. +::: + +We cover the VS Code Noir debugger more in depth in [its VS Code debugger how-to guide](../how_to/debugger/debugging_with_vs_code.md) and [the reference](../reference/debugger/debugger_vscode.md). + +The REPL debugger is discussed at length in [the REPL debugger how-to guide](../how_to/debugger/debugging_with_the_repl.md) and [the reference](../reference/debugger/debugger_repl.md). diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/language_server.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/language_server.md new file mode 100644 index 000000000000..81e0356ef8a1 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/language_server.md @@ -0,0 +1,43 @@ +--- +title: Language Server +description: Learn about the Noir Language Server, how to install the components, and configuration that may be required. +keywords: [Nargo, Language Server, LSP, VSCode, Visual Studio Code] +sidebar_position: 0 +--- + +This section helps you install and configure the Noir Language Server. + +The Language Server Protocol (LSP) has two components, the [Server](#language-server) and the [Client](#language-client). Below we describe each in the context of Noir. + +## Language Server + +The Server component is provided by the Nargo command line tool that you installed at the beginning of this guide. +As long as Nargo is installed and you've used it to run other commands in this guide, it should be good to go! + +If you'd like to verify that the `nargo lsp` command is available, you can run `nargo --help` and look for `lsp` in the list of commands. If you see it, you're using a version of Noir with LSP support. + +## Language Client + +The Client component is usually an editor plugin that launches the Server. It communicates LSP messages between the editor and the Server. For example, when you save a file, the Client will alert the Server, so it can try to compile the project and report any errors. + +Currently, Noir provides a Language Client for Visual Studio Code via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). + +> **Note:** Noir's Language Server Protocol support currently assumes users' VSCode workspace root to be the same as users' Noir project root (i.e. where Nargo.toml lies). +> +> If LSP features seem to be missing / malfunctioning, make sure you are opening your Noir project directly (instead of as a sub-folder) in your VSCode instance. + +When your language server is running correctly and the VSCode plugin is installed, you should see handy codelens buttons for compilation, measuring circuit size, execution, and tests: + +![Compile and Execute](@site/static/img/codelens_compile_execute.png) +![Run test](@site/static/img/codelens_run_test.png) + +You should also see your tests in the `testing` panel: + +![Testing panel](@site/static/img/codelens_testing_panel.png) + +### Configuration + +- **Noir: Enable LSP** - If checked, the extension will launch the Language Server via `nargo lsp` and communicate with it. +- **Noir: Nargo Flags** - Additional flags may be specified if you require them to be added when the extension calls `nargo lsp`. +- **Noir: Nargo Path** - An absolute path to a Nargo binary with the `lsp` command. This may be useful if Nargo is not within the `PATH` of your editor. +- **Noir > Trace: Server** - Setting this to `"messages"` or `"verbose"` will log LSP messages between the Client and Server. Useful for debugging. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/profiler.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/profiler.md new file mode 100644 index 000000000000..1f906226f8ce --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/profiler.md @@ -0,0 +1,115 @@ +--- +title: Noir Profiler +description: Learn about the Noir Profiler, how to generate execution flamegraphs, identify bottlenecks, and visualize optimizations. +keywords: [profiling, profiler, flamegraph] +sidebar_position: 0 +--- + +## Noir Profiler + +`noir-profiler` is a sampling profiler designed to analyze and visualize Noir programs. It assists developers to identify bottlenecks by mapping execution data back to the original source code. + +### Installation + +`noir-profiler` comes out of the box with [noirup](../getting_started/noir_installation.md). Test that you have the profiler installed by running `noir-profiler --version`. + +### Usage + +Let's start by creating a simple Noir program. All this program aims to do is zero out an array past some dynamic index. + +```rust +fn main(ptr: pub u32, mut array: [u32; 32]) -> pub [u32; 32] { + for i in 0..32 { + if i > ptr { + array[i] = 0; + } + } + array +} +``` +You can use these values for the `Prover.toml`: +```toml +ptr = 1 +array = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] +``` + +Running `nargo info` we can get some information about the opcodes produced by this program, but it doesn't give us a lot of info on its own. Compile and execute this program normally using `nargo compile` and `nargo execute`. + +### Generating an ACIR opcode flamegraph + +The program on its own is quite high-level. Let's get a more granular look at what is happening by using `noir-profiler`. + +After compiling the program, run the following: +```sh +noir-profiler opcodes --artifact-path ./target/program.json --output ./target/ +``` +Below you can see an example flamegraph with a total 387 opcodes (using `nargo` version 1.0.0-beta.2): +![ACIR Flamegraph Unoptimized](@site/static/img/tooling/profiler/acir-flamegraph-unoptimized.png) + +You should now have a flamegraph that maps ACIR opcodes to their corresponding locations in the source code. We strongly recommend generating these graphs yourself as you follow this guide. Opening the flamegraph in a browser provides a more interactive experience, allowing you to click into and examine different regions of the graph. Simply viewing the image file won't offer the same level of insight. + +We can see that the majority of opcodes come from the write to `array[i]`. Now that we have some more information about our program's bottlenecks, let's optimize it. + +#### Transform conditional writes into reads + +We can improve our circuit's efficiency using [unconstrained functions](../noir/concepts/unconstrained.md). + +Let's replace expensive array writes with array gets with the new code below: +```rust +fn main(ptr: pub u32, array: [u32; 32]) -> pub [u32; 32] { + // Safety: Sets all elements after `ptr` in `array` to zero. + let zeroed_array = unsafe { zero_out_array(ptr, array) }; + for i in 0..32 { + if i > ptr { + assert_eq(zeroed_array[i], 0); + } else { + assert_eq(zeroed_array[i], array[i]); + } + } + zeroed_array +} + +unconstrained fn zero_out_array(ptr: u32, mut array: [u32; 32]) -> [u32; 32] { + for i in 0..32 { + if i > ptr { + array[i] = 0; + } + } + array +} +``` +We chose to instead write our array inside of the unconstrained function. Then inside of our circuit we assert on every value in the array returned from the unconstrained function. + +This new program produces the following ACIR opcodes flamegraph with a total of 284 opcodes: +![ACIR Flamegraph Optimized](@site/static/img/tooling/profiler/acir-flamegraph-optimized.png) + +In the above image we searched for the ACIR opcodes due to `i > ptr` in the source code. Trigger a search by clicking on "Search" in the top right corner of the flamegraph. In the bottom right corner of the image above, you will note that the flamegraph displays the percentage of all opcodes associated with that search. Searching for `memory::op` in the optimized flamegraph will result in no matches. This is due to no longer using a dynamic array in our circuit. By dynamic array, we are referring to using a dynamic index (values reliant upon witness inputs) when working with arrays. Most of the memory operations, have now been replaced with arithmetic operations as we are reading two arrays from known constant indices. + +### Generate a backend gates flamegraph + +Unfortunately, ACIR opcodes do not give us a full picture of where the cost of this program lies. +The `gates` command also accepts a backend binary. In the [quick start guide](../getting_started/quick_start.md#proving-backend) you can see how to get started with the [Barretenberg proving backend](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg). + +Run the following command: +```sh +noir-profiler gates --artifact-path ./target/program.json --backend-path bb --output ./target +``` +`--backend-path` accepts a path to the backend binary. In the above command we assume that you have the backend binary path saved in your PATH. If you do not, you will have to pass the binary's absolute path. + +This produces the following flamegraph with 3,737 total backend gates (using `bb` version 0.76.4): +![Gates Flamegraph Unoptimized](@site/static/img/tooling/profiler/gates-flamegraph-unoptimized.png) + +Searching for ACIR `memory::op` opcodes, they look to cause about 18.2% of the backend gates. + +You will notice that the majority of the backend gates come from the ACIR range opcodes. This is due to the way UltraHonk handles range constraints, which is the backend used in this example. UltraHonk uses lookup tables internally for its range gates. These can take up the majority of the gates for a small circuit, but whose impact becomes more meaningful in larger circuits. If our array was much larger, range gates would become a much smaller percentage of our total circuit. +Here is an example backend gates flamegraph for the same program in this guide but with an array of size 2048: +![Gates Flamegraph Unoptimized 2048](@site/static/img/tooling/profiler/gates-flamegraph-unoptimized-2048.png) +Every backend implements ACIR opcodes differently, so it is important to profile both the ACIR and the backend gates to get a full picture. + +Now let's generate a graph for our optimized circuit with an array of size 32. We get the following flamegraph that produces 3,062 total backend gates: +![Gates Flamegraph Optimized](@site/static/img/tooling/profiler/gates-flamegraph-optimized.png) + +In the optimized flamegraph, we searched for the backend gates due to `i > ptr` in the source code. The backend gates associated with this call stack were only 3.8% of the total backend gates. If we look back to the ACIR flamegraph, that same code was the cause of 43.3% ACIR opcodes. This discrepancy reiterates the earlier point about profiling both the ACIR opcodes and backend gates. + +For posterity, here is the flamegraph for the same program with a size 2048 array: +![Gates Flamegraph Optimized 2048](@site/static/img/tooling/profiler/gates-flamegraph-optimized-2048.png) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/security.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/security.md new file mode 100644 index 000000000000..e14481efc317 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/security.md @@ -0,0 +1,61 @@ +--- +title: Security checks +description: Security checks currently provided by the compiler +keywords: [Nargo, Security, Brillig, Unconstrained] +sidebar_position: 2 +--- + +# Security checks + +Two compilation security passes exist currently to ensure soundness of compiled code. Problems they catch are reported as "bugs" (as opposed to errors) in the compiler output. For example: + +``` +**bug**: Brillig function call isn't properly covered by a manual constraint +``` + +### Independent subgraph detection + +This pass examines the instruction flow graph to see if the final function would involve values that don't come from any provided inputs and don't result in the outputs. That would mean there are no constraints ensuring the required continuity. + +This check is enabled by default and can be disabled by passing the `--skip-underconstrained-check` option to `nargo`. + +### Brillig manual constraint coverage + +The results of a Brillig function call must be constrained to ensure security, adhering to these rules: every resulting value (including every array element of a resulting array) has to be involved in a later constraint (i.e. assert, range check) against either one of the arguments of the call, or a constant. In this context, involvement means that a descendant value (e.g. a result of a chain of operations over the value) of a result has to be checked against a descendant value of an argument. For example: + +```rust +unconstrained fn factor(v0: Field) -> [Field; 2] { + ... +} + +fn main f0 (foo: Field) -> [Field; 2] { + let factored = unsafe { factor(foo) }; + assert(factored[0] * factored[1] == foo); + return factored +} +``` + +Here, the results of `factor` are two elements of the returned array. The value `factored[0] * factored[1]` is a descendant of both of them, so both are involved in a constraint against the argument value in the `assert`. Hence, the call to an unconstrained function is properly covered. + +This pass checks if the constraint coverage of Brillig calls is sufficient in these terms. + +The check is at the moment disabled by default due to performance concerns and can be enabled by passing the `--enable-brillig-constraints-check` option to `nargo`. + +#### Lookback option + +Certain false positives of this check can be avoided by providing the `--enable-brillig-constraints-check-lookback` option to `nargo`, which can be slower at compile-time but additionally ensures that descendants of call argument values coming from operations *preceding* the call itself would be followed. For example, consider this case: + +```rust +unconstrained fn unconstrained_add(v0: Field, v1: Field) -> Field { + v0 + v1 +} + +fn main f0 (v0: Field, v1: Field) { + let foo = v0 + v1; + let bar = unsafe { unconstrained_add(v0, v1) }; + assert(foo == bar); + return bar +} +``` + +Normally, the addition operation over `v0` and `v1` happening before the call itself would prevent the call from being (correctly) considered properly constrained. With this option enabled, the false positive goes away at the cost of the check becoming somewhat less performant on large unrolled loops. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/testing.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/testing.md new file mode 100644 index 000000000000..866677da5679 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/testing.md @@ -0,0 +1,79 @@ +--- +title: Testing in Noir +description: Learn how to use Nargo to test your Noir program in a quick and easy way +keywords: [Nargo, testing, Noir, compile, test] +sidebar_position: 1 +--- + +You can test your Noir programs using Noir circuits. + +Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if +you run `nargo test`. + +For example if you have a program like: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test] +fn test_add() { + assert(add(2,2) == 4); + assert(add(0,1) == 1); + assert(add(1,0) == 1); +} +``` + +Running `nargo test` will test that the `test_add` function can be executed while satisfying all +the constraints which allows you to test that add returns the expected values. Test functions can't +have any arguments currently. + +### Test fail + +You can write tests that are expected to fail by using the decorator `#[test(should_fail)]`. For example: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test(should_fail)] +fn test_add() { + assert(add(2,2) == 5); +} +``` + +You can be more specific and make it fail with a specific reason by using `should_fail_with = ""`: + +```rust +fn main(african_swallow_avg_speed : Field) { + assert(african_swallow_avg_speed == 65, "What is the airspeed velocity of an unladen swallow"); +} + +#[test] +fn test_king_arthur() { + main(65); +} + +#[test(should_fail_with = "What is the airspeed velocity of an unladen swallow")] +fn test_bridgekeeper() { + main(32); +} +``` + +The string given to `should_fail_with` doesn't need to exactly match the failure reason, it just needs to be a substring of it: + +```rust +fn main(african_swallow_avg_speed : Field) { + assert(african_swallow_avg_speed == 65, "What is the airspeed velocity of an unladen swallow"); +} + +#[test] +fn test_king_arthur() { + main(65); +} + +#[test(should_fail_with = "airspeed velocity")] +fn test_bridgekeeper() { + main(32); +} +``` \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tutorials/noirjs_app.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tutorials/noirjs_app.md new file mode 100644 index 000000000000..40e5d94180de --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tutorials/noirjs_app.md @@ -0,0 +1,304 @@ +--- +title: Building a web app with Noir and Barretenberg +description: Learn how to setup a new app that uses Noir to generate and verify zero-knowledge SNARK proofs in a typescript or javascript environment. +keywords: [how to, guide, javascript, typescript, noir, barretenberg, zero-knowledge, proofs, app] +sidebar_position: 0 +pagination_next: noir/concepts/data_types/index +--- + +NoirJS is a Typescript package meant to work both in a browser and a server environment. + +In this tutorial, we will combine NoirJS with Aztec's Barretenberg backend to build a simple web app. From here, you should get an idea on how to proceed with your own Noir projects! + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). + +## Dependencies + +Before we start, we want to make sure we have Node installed. For convenience (and speed), we can just install [Bun](https://bun.sh) as our package manager, and Node will work out-of-the-box: + +```bash +curl -fsSL https://bun.sh/install | bash +``` + +Let's go barebones. Doing the bare minimum is not only simple, but also allows you to easily adapt it to almost any frontend framework. + +Barebones means we can immediately start with the dependencies even on an empty folder 😈: + +```bash +bun i @noir-lang/noir_wasm@1.0.0-beta.2 @noir-lang/noir_js@1.0.0-beta.2 @aztec/bb.js@0.72.1 +``` + +Wait, what are these dependencies? + +- `noir_wasm` is the `wasm` version of the Noir compiler. Although most developers prefer to use `nargo` for compiling, there's nothing wrong with `noir_wasm`. We like `noir_wasm`. +- `noir_js` is the main Noir package. It will execute our program, and generate the witness that will be sent to the backend. +- `bb.js` is the Typescript interface for Aztec's Barretenberg proving backend. It also uses the `wasm` version in order to run on the browser. + +:::info + +In this guide, we will install versions pinned to 1.0.0-beta.2. These work with Barretenberg version 0.72.1, so we are using that one version too. Feel free to try with older or later versions, though! + +::: + +## Setting up our Noir program + +ZK is a powerful technology. An app that reveals computational correctness but doesn't reveal some of its inputs is almost unbelievable, yet Noir makes it as easy as a single line of code. + +:::tip + +It's not just you. We also enjoy syntax highlighting. [Check out the Language Server](../tooling/language_server.md) + +::: + +All you need is a `main.nr` and a `Nargo.toml` file. You can follow the [noirup](../getting_started/noir_installation.md) installation and just run `noirup -v 1.0.0-beta.2`, or just create them by hand: + +```bash +mkdir -p circuit/src +touch circuit/src/main.nr circuit/Nargo.toml +``` + +To make our program interesting, let's give it a real use-case scenario: Bob wants to prove he is older than 18, without disclosing his age. Open `main.nr` and write: + +```rust +fn main(age: u8) { + assert(age >= 18); +} +``` + +This program accepts a private input called age, and simply proves this number is higher than 18. But to run this code, we need to give the compiler a `Nargo.toml` with at least a name and a type: + +```toml +[package] +name = "circuit" +type = "bin" +``` + +This is all that we need to get started with Noir. + +![my heart is ready for you, noir.js](@site/static/img/memes/titanic.jpeg) + +## Setting up our app + +Remember when apps only had one `html` and one `js` file? Well, that's enough for Noir webapps. Let's create them: + +```bash +touch index.html index.js +``` + +And add something useful to our HTML file: + +```html + + + + + + +

Noir app

+
+ + +
+
+

Logs

+

Proof

+
+ + +``` + +It _could_ be a beautiful UI... Depending on which universe you live in. In any case, we're using some scary CSS to make two boxes that will show cool things on the screen. + +As for the JS, real madmen could just `console.log` everything, but let's say we want to see things happening (the true initial purpose of JS... right?). Here's some boilerplate for that. Just paste it in `index.js`: + +```js +const show = (id, content) => { + const container = document.getElementById(id); + container.appendChild(document.createTextNode(content)); + container.appendChild(document.createElement("br")); +}; + +document.getElementById("submit").addEventListener("click", async () => { + try { + // noir goes here + } catch { + show("logs", "Oh 💔"); + } +}); + +``` + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit + └── src + └── main.nr + Nargo.toml + index.js + package.json + index.html + ...etc +``` + +::: + +## Compile compile compile + +Finally we're up for something cool. But before we can execute a Noir program, we need to compile it into ACIR: an abstract representation. Here's where `noir_wasm` comes in. + +`noir_wasm` expects a filesystem so it can resolve dependencies. While we could use the `public` folder, let's just import those using the nice `?url` syntax provided by vite. At the top of the file: + +```js +import { compile, createFileManager } from "@noir-lang/noir_wasm" + +import main from "./circuit/src/main.nr?url"; +import nargoToml from "./circuit/Nargo.toml?url"; +``` + +Compiling on the browser is common enough that `createFileManager` already gives us a nice in-memory filesystem we can use. So all we need to compile is fetching these files, writing them to our filesystem, and compile. Add this function: + +```js +export async function getCircuit() { + const fm = createFileManager("/"); + const { body } = await fetch(main); + const { body: nargoTomlBody } = await fetch(nargoToml); + + fm.writeFile("./src/main.nr", body); + fm.writeFile("./Nargo.toml", nargoTomlBody); + return await compile(fm); +} +``` + +:::tip + +As you can imagine, with `node` it's all conveniently easier since you get native access to `fs`... + +::: + +## Some more JS + +We're starting with the good stuff now. We want to execute our circuit to get the witness, and then feed that witness to Barretenberg. Luckily, both packages are quite easy to work with. Let's import them at the top of the file: + +```js +import { UltraHonkBackend } from '@aztec/bb.js'; +import { Noir } from '@noir-lang/noir_js'; +``` + +And instantiate them inside our try-catch block: + +```ts +// try { +const { program } = await getCircuit(); +const noir = new Noir(program); +const backend = new UltraHonkBackend(program.bytecode); +// } +``` + +:::warning + +WASMs are not always easy to work with. In our case, `vite` likes serving them with the wrong MIME type. There are different fixes but we found the easiest one is just YOLO instantiating the WASMs manually. Paste this at the top of the file, just below the other imports, and it will work just fine: + +```js +import initNoirC from "@noir-lang/noirc_abi"; +import initACVM from "@noir-lang/acvm_js"; +import acvm from "@noir-lang/acvm_js/web/acvm_js_bg.wasm?url"; +import noirc from "@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm?url"; +await Promise.all([initACVM(fetch(acvm)), initNoirC(fetch(noirc))]); +``` + +::: + +## Executing and proving + +Now for the app itself. We're capturing whatever is in the input when people press the submit button. Inside our `try` block, let's just grab that input and get its value. Noir will gladly execute it, and give us a witness: + +```js +const age = document.getElementById("age").value; +show("logs", "Generating witness... ⏳"); +const { witness } = await noir.execute({ age }); +show("logs", "Generated witness... ✅"); + +``` + +:::note + +For the remainder of the tutorial, everything will be happening inside the `try` block + +::: + +Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: + +```js +show("logs", "Generating proof... ⏳"); +const proof = await backend.generateProof(witness); +show("logs", "Generated proof... ✅"); +show("results", proof.proof); +``` + +Our program is technically **done** . You're probably eager to see stuff happening! To serve this in a convenient way, we can use a bundler like `vite` by creating a `vite.config.js` file: + +```bash +touch vite.config.js +``` + +`vite` helps us with a little catch: `bb.js` in particular uses top-level awaits which aren't supported everywhere. So we can add this to the `vite.config.js` to make the bundler optimize them: + +```js +export default { optimizeDeps: { esbuildOptions: { target: "esnext" } } }; +``` + +This should be enough for vite. We don't even need to install it, just run: + +```bash +bunx vite +``` + +If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. + +![Noir Webapp UI](@site/static/img/tutorials/noirjs_webapp/webapp1.png) + +Now, our circuit requires a private input `fn main(age: u8)`, and fails if it is less than 18. Let's see if it works. Submit any number above 18 (as long as it fits in 8 bits) and you should get a valid proof. Otherwise the proof won't even generate correctly. + +By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. + +## Verifying + +Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: + +```js +show('logs', 'Verifying proof... ⌛'); +const isValid = await backend.verifyProof(proof); +show("logs", `Proof is ${isValid ? "valid" : "invalid"}... ✅`); +``` + +You have successfully generated a client-side Noir web app! + +![coded app without math knowledge](@site/static/img/memes/flextape.jpeg) + +## Next steps + +At this point, you have a working ZK app that works on the browser. Actually, it works on a mobile phone too! + +If you want to continue learning by doing, here are some challenges for you: + +- Install [nargo](https://noir-lang.org/docs/getting_started/noir_installation) and write [Noir tests](../tooling/testing) +- Change the circuit to accept a [public input](../noir/concepts/data_types/#private--public-types) as the cutoff age. It could be different depending on the purpose, for example! +- Enjoy Noir's Rust-like syntax and write a struct `Country` that implements a trait `MinAge` with a method `get_min_age`. Then, make a struct `Person` have an `u8` as its age and a country of type `Country`. You can pass a `person` in JS just like a JSON object `person: { age, country: { min_age: 18 }}` + +The world is your stage, just have fun with ZK! You can see how noirjs is used in some common frameworks in the [awesome-noir repo](https://github.com/noir-lang/awesome-noir?tab=readme-ov-file#boilerplates). diff --git a/noir/noir-repo/docs/versioned_sidebars/version-v1.0.0-beta.3-sidebars.json b/noir/noir-repo/docs/versioned_sidebars/version-v1.0.0-beta.3-sidebars.json new file mode 100644 index 000000000000..b9ad026f69ff --- /dev/null +++ b/noir/noir-repo/docs/versioned_sidebars/version-v1.0.0-beta.3-sidebars.json @@ -0,0 +1,93 @@ +{ + "sidebar": [ + { + "type": "doc", + "id": "index" + }, + { + "type": "category", + "label": "Getting Started", + "items": [ + { + "type": "autogenerated", + "dirName": "getting_started" + } + ] + }, + { + "type": "category", + "label": "The Noir Language", + "items": [ + { + "type": "autogenerated", + "dirName": "noir" + } + ] + }, + { + "type": "html", + "value": "
", + "defaultStyle": true + }, + { + "type": "category", + "label": "How To Guides", + "items": [ + { + "type": "autogenerated", + "dirName": "how_to" + } + ] + }, + { + "type": "category", + "label": "Explainers", + "items": [ + { + "type": "autogenerated", + "dirName": "explainers" + } + ] + }, + { + "type": "category", + "label": "Tutorials", + "items": [ + { + "type": "autogenerated", + "dirName": "tutorials" + } + ] + }, + { + "type": "category", + "label": "Reference", + "items": [ + { + "type": "autogenerated", + "dirName": "reference" + } + ] + }, + { + "type": "category", + "label": "Tooling", + "items": [ + { + "type": "autogenerated", + "dirName": "tooling" + } + ] + }, + { + "type": "html", + "value": "
", + "defaultStyle": true + }, + { + "type": "doc", + "id": "migration_notes", + "label": "Migration notes" + } + ] +} diff --git a/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh b/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh index b227f82914e2..c792b4183119 100755 --- a/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh +++ b/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh @@ -6,18 +6,18 @@ BACKEND=${BACKEND:-bb} nargo compile # TODO: backend should automatically generate vk if necessary. -$BACKEND OLD_API write_vk -b ./target/hello_world.json -$BACKEND OLD_API contract -o ./src/contract.sol +$BACKEND write_vk -b ./target/hello_world.json +$BACKEND contract -o ./src/contract.sol # We now generate a proof and check whether the verifier contract will verify it. nargo execute --pedantic-solving witness PROOF_PATH=./target/proof -$BACKEND OLD_API prove -b ./target/hello_world.json -w ./target/witness.gz -o $PROOF_PATH +$BACKEND prove -b ./target/hello_world.json -w ./target/witness.gz -o $PROOF_PATH # Sanity check that proof is valid. -$BACKEND OLD_API verify -k ./target/vk -p ./target/proof +$BACKEND verify -k ./target/vk -p ./target/proof NUM_PUBLIC_INPUTS=2 PUBLIC_INPUT_BYTES=$((32 * $NUM_PUBLIC_INPUTS)) diff --git a/noir/noir-repo/examples/prove_and_verify/prove_and_verify.sh b/noir/noir-repo/examples/prove_and_verify/prove_and_verify.sh index 92288bc92e02..411f5258caf4 100755 --- a/noir/noir-repo/examples/prove_and_verify/prove_and_verify.sh +++ b/noir/noir-repo/examples/prove_and_verify/prove_and_verify.sh @@ -7,8 +7,8 @@ nargo execute --pedantic-solving witness # TODO: `bb` should create `proofs` directory if it doesn't exist. mkdir -p proofs -$BACKEND OLD_API prove -b ./target/hello_world.json -w ./target/witness.gz +$BACKEND prove -b ./target/hello_world.json -w ./target/witness.gz # TODO: backend should automatically generate vk if necessary. -$BACKEND OLD_API write_vk -b ./target/hello_world.json -$BACKEND OLD_API verify -k ./target/vk -p ./proofs/proof +$BACKEND write_vk -b ./target/hello_world.json +$BACKEND verify -k ./target/vk -p ./proofs/proof diff --git a/noir/noir-repo/examples/recursion/generate_recursive_proof.sh b/noir/noir-repo/examples/recursion/generate_recursive_proof.sh index e05dd854d69f..09b01d547b68 100755 --- a/noir/noir-repo/examples/recursion/generate_recursive_proof.sh +++ b/noir/noir-repo/examples/recursion/generate_recursive_proof.sh @@ -4,16 +4,16 @@ set -eu BACKEND=${BACKEND:-bb} nargo execute sum_witness --package sum -$BACKEND OLD_API prove -b ./target/sum.json -w ./target/sum_witness.gz -o ./target/sum_proof --recursive +$BACKEND prove -b ./target/sum.json -w ./target/sum_witness.gz -o ./target/sum_proof --recursive # Once we have generated our inner proof, we must use this to generate inputs to `recurse_leaf`` -$BACKEND OLD_API write_vk -b ./target/sum.json -o ./target/sum_key --recursive -$BACKEND OLD_API vk_as_fields -k ./target/sum_key -o ./target/sum_vk_as_fields +$BACKEND write_vk -b ./target/sum.json -o ./target/sum_key --recursive +$BACKEND vk_as_fields -k ./target/sum_key -o ./target/sum_vk_as_fields VK_HASH=$(jq -r '.[0]' ./target/sum_vk_as_fields) VK_AS_FIELDS=$(jq -r '.[1:]' ./target/sum_vk_as_fields) -FULL_PROOF_AS_FIELDS="$($BACKEND OLD_API proof_as_fields -p ./target/sum_proof -k ./target/sum_key -o -)" +FULL_PROOF_AS_FIELDS="$($BACKEND proof_as_fields -p ./target/sum_proof -k ./target/sum_key -o -)" # sum has 3 public inputs PUBLIC_INPUTS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[:3]') PROOF_AS_FIELDS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[3:]') @@ -28,19 +28,19 @@ echo "public_inputs = $PUBLIC_INPUTS" >> $RECURSE_LEAF_PROVER_TOML # We can now execute and prove `recurse_leaf` nargo execute recurse_leaf_witness --package recurse_leaf -$BACKEND OLD_API prove -b ./target/recurse_leaf.json -w ./target/recurse_leaf_witness.gz -o ./target/recurse_leaf_proof --recursive +$BACKEND prove -b ./target/recurse_leaf.json -w ./target/recurse_leaf_witness.gz -o ./target/recurse_leaf_proof --recursive # Let's do a sanity check that the proof we've generated so far is valid. -$BACKEND OLD_API write_vk -b ./target/recurse_leaf.json -o ./target/recurse_leaf_key --recursive -$BACKEND OLD_API verify -p ./target/recurse_leaf_proof -k ./target/recurse_leaf_key +$BACKEND write_vk -b ./target/recurse_leaf.json -o ./target/recurse_leaf_key --recursive +$BACKEND verify -p ./target/recurse_leaf_proof -k ./target/recurse_leaf_key # Now we generate the final `recurse_node` proof similarly to how we did for `recurse_leaf`. -$BACKEND OLD_API vk_as_fields -k ./target/recurse_leaf_key -o ./target/recurse_leaf_vk_as_fields +$BACKEND vk_as_fields -k ./target/recurse_leaf_key -o ./target/recurse_leaf_vk_as_fields VK_HASH=$(jq -r '.[0]' ./target/recurse_leaf_vk_as_fields) VK_AS_FIELDS=$(jq -r '.[1:]' ./target/recurse_leaf_vk_as_fields) -FULL_PROOF_AS_FIELDS="$($BACKEND OLD_API proof_as_fields -p ./target/recurse_leaf_proof -k ./target/recurse_leaf_key -o -)" +FULL_PROOF_AS_FIELDS="$($BACKEND proof_as_fields -p ./target/recurse_leaf_proof -k ./target/recurse_leaf_key -o -)" # recurse_leaf has 4 public inputs (excluding aggregation object) PUBLIC_INPUTS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[:4]') PROOF_AS_FIELDS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[4:]') @@ -54,8 +54,8 @@ echo "public_inputs = $PUBLIC_INPUTS" >> $RECURSE_NODE_PROVER_TOML # We can now execute and prove `recurse_node` nargo execute recurse_node_witness --package recurse_node -$BACKEND OLD_API prove -b ./target/recurse_node.json -w ./target/recurse_node_witness.gz -o ./target/recurse_node_proof +$BACKEND prove -b ./target/recurse_node.json -w ./target/recurse_node_witness.gz -o ./target/recurse_node_proof # We finally verify that the generated recursive proof is valid. -$BACKEND OLD_API write_vk -b ./target/recurse_node.json -o ./target/recurse_node_key -$BACKEND OLD_API verify -p ./target/recurse_node_proof -k ./target/recurse_node_key +$BACKEND write_vk -b ./target/recurse_node.json -o ./target/recurse_node_key +$BACKEND verify -p ./target/recurse_node_proof -k ./target/recurse_node_key diff --git a/noir/noir-repo/noir_stdlib/src/cmp.nr b/noir/noir-repo/noir_stdlib/src/cmp.nr index 16bcd4390f78..24621bb37934 100644 --- a/noir/noir-repo/noir_stdlib/src/cmp.nr +++ b/noir/noir-repo/noir_stdlib/src/cmp.nr @@ -35,6 +35,11 @@ impl Eq for Field { } } +impl Eq for u128 { + fn eq(self, other: u128) -> bool { + self == other + } +} impl Eq for u64 { fn eq(self, other: u64) -> bool { self == other @@ -232,6 +237,17 @@ comptime fn derive_ord(s: StructDefinition) -> Quoted { // Note: Field deliberately does not implement Ord +impl Ord for u128 { + fn cmp(self, other: u128) -> Ordering { + if self < other { + Ordering::less() + } else if self > other { + Ordering::greater() + } else { + Ordering::equal() + } + } +} impl Ord for u64 { fn cmp(self, other: u64) -> Ordering { if self < other { diff --git a/noir/noir-repo/noir_stdlib/src/convert.nr b/noir/noir-repo/noir_stdlib/src/convert.nr index 1e5c1484b5fe..7e7348c2b37d 100644 --- a/noir/noir-repo/noir_stdlib/src/convert.nr +++ b/noir/noir-repo/noir_stdlib/src/convert.nr @@ -45,6 +45,22 @@ impl From for u64 { } } +impl From for u128 { + fn from(value: u8) -> u128 { + value as u128 + } +} +impl From for u128 { + fn from(value: u32) -> u128 { + value as u128 + } +} +impl From for u128 { + fn from(value: u64) -> u128 { + value as u128 + } +} + impl From for Field { fn from(value: u8) -> Field { value as Field @@ -61,6 +77,12 @@ impl From for Field { } } +impl From for Field { + fn from(value: u128) -> Field { + value as Field + } +} + // Signed integers impl From for i32 { @@ -142,6 +164,7 @@ comptime fn generate_as_primitive_impls(_: FunctionDefinition) -> Quoted { quote { u16 }, quote { u32 }, quote { u64 }, + quote { u128 }, quote { i8 }, quote { i16 }, quote { i32 }, diff --git a/noir/noir-repo/noir_stdlib/src/default.nr b/noir/noir-repo/noir_stdlib/src/default.nr index 01f10a368b14..229e5e92003f 100644 --- a/noir/noir-repo/noir_stdlib/src/default.nr +++ b/noir/noir-repo/noir_stdlib/src/default.nr @@ -47,6 +47,12 @@ impl Default for u64 { } } +impl Default for u128 { + fn default() -> u128 { + 0 + } +} + impl Default for i8 { fn default() -> i8 { 0 diff --git a/noir/noir-repo/noir_stdlib/src/ecdsa_secp256k1.nr b/noir/noir-repo/noir_stdlib/src/ecdsa_secp256k1.nr index ec39e9997152..1b0f32343b98 100644 --- a/noir/noir-repo/noir_stdlib/src/ecdsa_secp256k1.nr +++ b/noir/noir-repo/noir_stdlib/src/ecdsa_secp256k1.nr @@ -19,4 +19,3 @@ pub fn verify_signature_slice( ) -> bool // docs:end:ecdsa_secp256k1_slice {} - diff --git a/noir/noir-repo/noir_stdlib/src/hash/keccak.nr b/noir/noir-repo/noir_stdlib/src/hash/keccak.nr deleted file mode 100644 index 75be7982e667..000000000000 --- a/noir/noir-repo/noir_stdlib/src/hash/keccak.nr +++ /dev/null @@ -1,155 +0,0 @@ -use crate::runtime::is_unconstrained; - -global BLOCK_SIZE_IN_BYTES: u32 = 136; //(1600 - BITS * 2) / WORD_SIZE; -global WORD_SIZE: u32 = 8; // Limbs are made up of u64s so 8 bytes each. -global LIMBS_PER_BLOCK: u32 = BLOCK_SIZE_IN_BYTES / WORD_SIZE; -global NUM_KECCAK_LANES: u32 = 25; - -#[foreign(keccakf1600)] -pub fn keccakf1600(input: [u64; 25]) -> [u64; 25] {} - -#[no_predicates] -#[deprecated("keccak256 is being deprecated from the stdlib, use https://github.com/noir-lang/keccak256 instead")] -pub(crate) fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] { - assert(N >= message_size); - - // Copy input to block bytes. For that we'll need at least input bytes (N) - // but we want it to be padded to a multiple of BLOCK_SIZE_IN_BYTES. - let mut block_bytes = [0; ((N / BLOCK_SIZE_IN_BYTES) + 1) * BLOCK_SIZE_IN_BYTES]; - if is_unconstrained() { - for i in 0..message_size { - block_bytes[i] = input[i]; - } - } else { - for i in 0..N { - if i < message_size { - block_bytes[i] = input[i]; - } - } - } - - //1. format_input_lanes - let max_blocks = (N + BLOCK_SIZE_IN_BYTES) / BLOCK_SIZE_IN_BYTES; - //maximum number of bytes to hash - let real_max_blocks = (message_size + BLOCK_SIZE_IN_BYTES) / BLOCK_SIZE_IN_BYTES; - let real_blocks_bytes = real_max_blocks * BLOCK_SIZE_IN_BYTES; - - block_bytes[message_size] = 1; - block_bytes[real_blocks_bytes - 1] = 0x80; - - // populate a vector of 64-bit limbs from our byte array - let mut sliced_buffer = - [0; (((N / BLOCK_SIZE_IN_BYTES) + 1) * BLOCK_SIZE_IN_BYTES) / WORD_SIZE]; - for i in 0..sliced_buffer.len() { - let limb_start = WORD_SIZE * i; - - let mut sliced = 0; - let mut v = 1; - for k in 0..WORD_SIZE { - sliced += v * (block_bytes[limb_start + k] as Field); - v *= 256; - } - - sliced_buffer[i] = sliced as u64; - } - - //2. sponge_absorb - let mut state: [u64; NUM_KECCAK_LANES] = [0; NUM_KECCAK_LANES]; - // When in an unconstrained runtime we can take advantage of runtime loop bounds, - // thus allowing us to simplify the loop body. - if is_unconstrained() { - for i in 0..real_max_blocks { - if (i == 0) { - for j in 0..LIMBS_PER_BLOCK { - state[j] = sliced_buffer[j]; - } - } else { - for j in 0..LIMBS_PER_BLOCK { - state[j] = state[j] ^ sliced_buffer[i * LIMBS_PER_BLOCK + j]; - } - } - state = keccakf1600(state); - } - } else { - // `real_max_blocks` is guaranteed to at least be `1` - // We peel out the first block as to avoid a conditional inside of the loop. - // Otherwise, a dynamic predicate can cause a blowup in a constrained runtime. - for j in 0..LIMBS_PER_BLOCK { - state[j] = sliced_buffer[j]; - } - state = keccakf1600(state); - for i in 1..max_blocks { - if i < real_max_blocks { - for j in 0..LIMBS_PER_BLOCK { - state[j] = state[j] ^ sliced_buffer[i * LIMBS_PER_BLOCK + j]; - } - state = keccakf1600(state); - } - } - } - - //3. sponge_squeeze - let mut result = [0; 32]; - for i in 0..4 { - let lane = state[i] as Field; - let lane_le: [u8; 8] = lane.to_le_bytes(); - for j in 0..8 { - result[8 * i + j] = lane_le[j]; - } - } - result -} - -mod tests { - use super::keccak256; - - #[test] - fn smoke_test() { - let input = [0xbd]; - let result = [ - 0x5a, 0x50, 0x2f, 0x9f, 0xca, 0x46, 0x7b, 0x26, 0x6d, 0x5b, 0x78, 0x33, 0x65, 0x19, - 0x37, 0xe8, 0x05, 0x27, 0x0c, 0xa3, 0xf3, 0xaf, 0x1c, 0x0d, 0xd2, 0x46, 0x2d, 0xca, - 0x4b, 0x3b, 0x1a, 0xbf, - ]; - assert_eq(keccak256(input, input.len()), result); - } - - #[test] - fn hash_hello_world() { - let input = "Hello world!".as_bytes(); - let result = [ - 0xec, 0xd0, 0xe1, 0x8, 0xa9, 0x8e, 0x19, 0x2a, 0xf1, 0xd2, 0xc2, 0x50, 0x55, 0xf4, 0xe3, - 0xbe, 0xd7, 0x84, 0xb5, 0xc8, 0x77, 0x20, 0x4e, 0x73, 0x21, 0x9a, 0x52, 0x3, 0x25, 0x1f, - 0xea, 0xab, - ]; - assert_eq(keccak256(input, input.len()), result); - } - - #[test] - fn var_size_hash() { - let input = [ - 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, - 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, - 223, - ]; - let result = [ - 226, 37, 115, 94, 94, 196, 72, 116, 194, 105, 79, 233, 65, 12, 30, 94, 181, 131, 170, - 219, 171, 166, 236, 88, 143, 67, 255, 160, 248, 214, 39, 129, - ]; - assert_eq(keccak256(input, 13), result); - } - - #[test] - fn hash_longer_than_136_bytes() { - let input = "123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789" - .as_bytes(); - assert(input.len() > 136); - - let result = [ - 0x1d, 0xca, 0xeb, 0xdf, 0xd9, 0xd6, 0x24, 0x67, 0x1c, 0x18, 0x16, 0xda, 0xd, 0x8a, 0xeb, - 0xa8, 0x75, 0x71, 0x2c, 0xc, 0x89, 0xe0, 0x25, 0x2, 0xe8, 0xb6, 0x5e, 0x16, 0x5, 0x55, - 0xe4, 0x40, - ]; - assert_eq(keccak256(input, input.len()), result); - } -} diff --git a/noir/noir-repo/noir_stdlib/src/hash/mod.nr b/noir/noir-repo/noir_stdlib/src/hash/mod.nr index ee78d04c91b6..7a492d373cc9 100644 --- a/noir/noir-repo/noir_stdlib/src/hash/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/hash/mod.nr @@ -1,18 +1,28 @@ pub mod poseidon; pub mod poseidon2; -pub mod keccak; -pub mod sha256; -pub mod sha512; use crate::default::Default; use crate::embedded_curve_ops::{ EmbeddedCurvePoint, EmbeddedCurveScalar, multi_scalar_mul, multi_scalar_mul_array_return, }; use crate::meta::derive_via; -use crate::uint128::U128; -// Kept for backwards compatibility -pub use sha256::{digest, sha256, sha256_compression, sha256_var}; +#[foreign(sha256_compression)] +// docs:start:sha256_compression +pub fn sha256_compression(input: [u32; 16], state: [u32; 8]) -> [u32; 8] {} +// docs:end:sha256_compression + +#[foreign(keccakf1600)] +// docs:start:keccakf1600 +pub fn keccakf1600(input: [u64; 25]) -> [u64; 25] {} +// docs:end:keccakf1600 + +pub mod keccak { + #[deprecated("This function has been moved to std::hash::keccakf1600")] + pub fn keccakf1600(input: [u64; 25]) -> [u64; 25] { + super::keccakf1600(input) + } +} #[foreign(blake2s)] // docs:start:blake2s @@ -114,13 +124,6 @@ pub fn hash_to_field(inputs: [Field]) -> Field { sum } -// docs:start:keccak256 -pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] -// docs:end:keccak256 -{ - crate::hash::keccak::keccak256(input, message_size) -} - #[foreign(poseidon2_permutation)] pub fn poseidon2_permutation(_input: [Field; N], _state_length: u32) -> [Field; N] {} @@ -241,6 +244,15 @@ impl Hash for u64 { } } +impl Hash for u128 { + fn hash(self, state: &mut H) + where + H: Hasher, + { + H::write(state, self as Field); + } +} + impl Hash for i8 { fn hash(self, state: &mut H) where @@ -293,16 +305,6 @@ impl Hash for () { {} } -impl Hash for U128 { - fn hash(self, state: &mut H) - where - H: Hasher, - { - H::write(state, self.lo as Field); - H::write(state, self.hi as Field); - } -} - impl Hash for [T; N] where T: Hash, diff --git a/noir/noir-repo/noir_stdlib/src/hash/sha256.nr b/noir/noir-repo/noir_stdlib/src/hash/sha256.nr deleted file mode 100644 index a8bd71a21111..000000000000 --- a/noir/noir-repo/noir_stdlib/src/hash/sha256.nr +++ /dev/null @@ -1,845 +0,0 @@ -use crate::runtime::is_unconstrained; - -// Implementation of SHA-256 mapping a byte array of variable length to -// 32 bytes. - -// A message block is up to 64 bytes taken from the input. -global BLOCK_SIZE: u32 = 64; - -// The first index in the block where the 8 byte message size will be written. -global MSG_SIZE_PTR: u32 = 56; - -// Size of the message block when packed as 4-byte integer array. -global INT_BLOCK_SIZE: u32 = 16; - -// A `u32` integer consists of 4 bytes. -global INT_SIZE: u32 = 4; - -// Index of the integer in the `INT_BLOCK` where the length is written. -global INT_SIZE_PTR: u32 = MSG_SIZE_PTR / INT_SIZE; - -// Magic numbers for bit shifting. -// Works with actual bit shifting as well as the compiler turns them into * and / -// but circuit execution appears to be 10% faster this way. -global TWO_POW_8: u32 = 256; -global TWO_POW_16: u32 = TWO_POW_8 * 256; -global TWO_POW_24: u32 = TWO_POW_16 * 256; -global TWO_POW_32: u64 = TWO_POW_24 as u64 * 256; - -// Index of a byte in a 64 byte block; ie. 0..=63 -type BLOCK_BYTE_PTR = u32; - -// The foreign function to compress blocks works on 16 pieces of 4-byte integers, instead of 64 bytes. -type INT_BLOCK = [u32; INT_BLOCK_SIZE]; - -// A message block is a slice of the original message of a fixed size, -// potentially padded with zeros, with neighbouring 4 bytes packed into integers. -type MSG_BLOCK = INT_BLOCK; - -// The hash is 32 bytes. -type HASH = [u8; 32]; - -// The state accumulates the blocks. -// Its overall size is the same as the `HASH`. -type STATE = [u32; 8]; - -// docs:start:sha256 -#[deprecated("sha256 is being deprecated from the stdlib, use https://github.com/noir-lang/sha256 instead")] -pub fn sha256(input: [u8; N]) -> HASH -// docs:end:sha256 -{ - digest(input) -} - -#[foreign(sha256_compression)] -pub fn sha256_compression(_input: INT_BLOCK, _state: STATE) -> STATE {} - -// SHA-256 hash function -#[no_predicates] -#[deprecated("sha256 is being deprecated from the stdlib, use https://github.com/noir-lang/sha256 instead")] -pub fn digest(msg: [u8; N]) -> HASH { - sha256_var(msg, N as u64) -} - -// Variable size SHA-256 hash -#[deprecated("sha256 is being deprecated from the stdlib, use https://github.com/noir-lang/sha256 instead")] -pub fn sha256_var(msg: [u8; N], message_size: u64) -> HASH { - let message_size = message_size as u32; - let num_blocks = N / BLOCK_SIZE; - let mut msg_block: MSG_BLOCK = [0; INT_BLOCK_SIZE]; - // Intermediate hash, starting with the canonical initial value - let mut h: STATE = [ - 1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, - 1541459225, - ]; - // Pointer into msg_block on a 64 byte scale - let mut msg_byte_ptr = 0; - for i in 0..num_blocks { - let msg_start = BLOCK_SIZE * i; - // Safety: the msg_block is checked below in verify_msg_block - let (new_msg_block, new_msg_byte_ptr) = - unsafe { build_msg_block(msg, message_size, msg_start) }; - - if msg_start < message_size { - msg_block = new_msg_block; - } - - if !is_unconstrained() { - // Verify the block we are compressing was appropriately constructed - let new_msg_byte_ptr = verify_msg_block(msg, message_size, msg_block, msg_start); - if msg_start < message_size { - msg_byte_ptr = new_msg_byte_ptr; - } - } else if msg_start < message_size { - msg_byte_ptr = new_msg_byte_ptr; - } - - // If the block is filled, compress it. - // An un-filled block is handled after this loop. - if (msg_start < message_size) & (msg_byte_ptr == BLOCK_SIZE) { - h = sha256_compression(msg_block, h); - } - } - - let modulo = N % BLOCK_SIZE; - // Handle setup of the final msg block. - // This case is only hit if the msg is less than the block size, - // or our message cannot be evenly split into blocks. - if modulo != 0 { - let msg_start = BLOCK_SIZE * num_blocks; - // Safety: the msg_block is checked below in verify_msg_block - let (new_msg_block, new_msg_byte_ptr) = - unsafe { build_msg_block(msg, message_size, msg_start) }; - - if msg_start < message_size { - msg_block = new_msg_block; - } - - if !is_unconstrained() { - let new_msg_byte_ptr = verify_msg_block(msg, message_size, msg_block, msg_start); - if msg_start < message_size { - msg_byte_ptr = new_msg_byte_ptr; - verify_msg_block_padding(msg_block, msg_byte_ptr); - } - } else if msg_start < message_size { - msg_byte_ptr = new_msg_byte_ptr; - } - } - - // If we had modulo == 0 then it means the last block was full, - // and we can reset the pointer to zero to overwrite it. - if msg_byte_ptr == BLOCK_SIZE { - msg_byte_ptr = 0; - } - - // Pad the rest such that we have a [u32; 2] block at the end representing the length - // of the message, and a block of 1 0 ... 0 following the message (i.e. [1 << 7, 0, ..., 0]). - // Here we rely on the fact that everything beyond the available input is set to 0. - msg_block = update_block_item( - msg_block, - msg_byte_ptr, - |msg_item| set_item_byte_then_zeros(msg_item, msg_byte_ptr, 1 << 7), - ); - msg_byte_ptr = msg_byte_ptr + 1; - let last_block = msg_block; - - // If we don't have room to write the size, compress the block and reset it. - if msg_byte_ptr > MSG_SIZE_PTR { - h = sha256_compression(msg_block, h); - // `attach_len_to_msg_block` will zero out everything after the `msg_byte_ptr`. - msg_byte_ptr = 0; - } - - // Safety: the msg_len is checked below in verify_msg_len - msg_block = unsafe { attach_len_to_msg_block(msg_block, msg_byte_ptr, message_size) }; - - if !is_unconstrained() { - verify_msg_len(msg_block, last_block, msg_byte_ptr, message_size); - } - - hash_final_block(msg_block, h) -} - -// Take `BLOCK_SIZE` number of bytes from `msg` starting at `msg_start`. -// Returns the block and the length that has been copied rather than padded with zeros. -unconstrained fn build_msg_block( - msg: [u8; N], - message_size: u32, - msg_start: u32, -) -> (MSG_BLOCK, BLOCK_BYTE_PTR) { - let mut msg_block: MSG_BLOCK = [0; INT_BLOCK_SIZE]; - - // We insert `BLOCK_SIZE` bytes (or up to the end of the message) - let block_input = if msg_start + BLOCK_SIZE > message_size { - if message_size < msg_start { - // This function is sometimes called with `msg_start` past the end of the message. - // In this case we return an empty block and zero pointer to signal that the result should be ignored. - 0 - } else { - message_size - msg_start - } - } else { - BLOCK_SIZE - }; - - // Figure out the number of items in the int array that we have to pack. - // e.g. if the input is [0,1,2,3,4,5] then we need to pack it as 2 items: [0123, 4500] - let mut int_input = block_input / INT_SIZE; - if block_input % INT_SIZE != 0 { - int_input = int_input + 1; - }; - - for i in 0..int_input { - let mut msg_item: u32 = 0; - // Always construct the integer as 4 bytes, even if it means going beyond the input. - for j in 0..INT_SIZE { - let k = i * INT_SIZE + j; - let msg_byte = if k < block_input { - msg[msg_start + k] - } else { - 0 - }; - msg_item = lshift8(msg_item, 1) + msg_byte as u32; - } - msg_block[i] = msg_item; - } - - // Returning the index as if it was a 64 byte array. - // We have to project it down to 16 items and bit shifting to get a byte back if we need it. - (msg_block, block_input) -} - -// Verify the block we are compressing was appropriately constructed by `build_msg_block` -// and matches the input data. Returns the index of the first unset item. -// If `message_size` is less than `msg_start` then this is called with the old non-empty block; -// in that case we can skip verification, ie. no need to check that everything is zero. -fn verify_msg_block( - msg: [u8; N], - message_size: u32, - msg_block: MSG_BLOCK, - msg_start: u32, -) -> BLOCK_BYTE_PTR { - let mut msg_byte_ptr = 0; - let mut msg_end = msg_start + BLOCK_SIZE; - if msg_end > N { - msg_end = N; - } - // We might have to go beyond the input to pad the fields. - if msg_end % INT_SIZE != 0 { - msg_end = msg_end + INT_SIZE - msg_end % INT_SIZE; - } - - // Reconstructed packed item. - let mut msg_item: u32 = 0; - - // Inclusive at the end so that we can compare the last item. - let mut i: u32 = 0; - for k in msg_start..=msg_end { - if k % INT_SIZE == 0 { - // If we consumed some input we can compare against the block. - if (msg_start < message_size) & (k > msg_start) { - assert_eq(msg_block[i], msg_item as u32); - i = i + 1; - msg_item = 0; - } - } - // Shift the accumulator - msg_item = lshift8(msg_item, 1); - // If we have input to consume, add it at the rightmost position. - if k < message_size & k < msg_end { - msg_item = msg_item + msg[k] as u32; - msg_byte_ptr = msg_byte_ptr + 1; - } - } - - msg_byte_ptr -} - -// Verify the block we are compressing was appropriately padded with zeros by `build_msg_block`. -// This is only relevant for the last, potentially partially filled block. -fn verify_msg_block_padding(msg_block: MSG_BLOCK, msg_byte_ptr: BLOCK_BYTE_PTR) { - // Check all the way to the end of the block. - verify_msg_block_zeros(msg_block, msg_byte_ptr, INT_BLOCK_SIZE); -} - -// Verify that a region of ints in the message block are (partially) zeroed, -// up to an (exclusive) maximum which can either be the end of the block -// or just where the size is to be written. -fn verify_msg_block_zeros( - msg_block: MSG_BLOCK, - mut msg_byte_ptr: BLOCK_BYTE_PTR, - max_int_byte_ptr: u32, -) { - // This variable is used to get around the compiler under-constrained check giving a warning. - // We want to check against a constant zero, but if it does not come from the circuit inputs - // or return values the compiler check will issue a warning. - let zero = msg_block[0] - msg_block[0]; - - // First integer which is supposed to be (partially) zero. - let mut int_byte_ptr = msg_byte_ptr / INT_SIZE; - - // Check partial zeros. - let modulo = msg_byte_ptr % INT_SIZE; - if modulo != 0 { - let zeros = INT_SIZE - modulo; - let mask = if zeros == 3 { - TWO_POW_24 - } else if zeros == 2 { - TWO_POW_16 - } else { - TWO_POW_8 - }; - assert_eq(msg_block[int_byte_ptr] % mask, zero); - int_byte_ptr = int_byte_ptr + 1; - } - - // Check the rest of the items. - for i in 0..max_int_byte_ptr { - if i >= int_byte_ptr { - assert_eq(msg_block[i], zero); - } - } -} - -// Verify that up to the byte pointer the two blocks are equal. -// At the byte pointer the new block can be partially zeroed. -fn verify_msg_block_equals_last( - msg_block: MSG_BLOCK, - last_block: MSG_BLOCK, - mut msg_byte_ptr: BLOCK_BYTE_PTR, -) { - // msg_byte_ptr is the position at which they are no longer have to be the same. - // First integer which is supposed to be (partially) zero contains that pointer. - let mut int_byte_ptr = msg_byte_ptr / INT_SIZE; - - // Check partial zeros. - let modulo = msg_byte_ptr % INT_SIZE; - if modulo != 0 { - // Reconstruct the partially zero item from the last block. - let last_field = last_block[int_byte_ptr]; - let mut msg_item: u32 = 0; - // Reset to where they are still equal. - msg_byte_ptr = msg_byte_ptr - modulo; - for i in 0..INT_SIZE { - msg_item = lshift8(msg_item, 1); - if i < modulo { - msg_item = msg_item + get_item_byte(last_field, msg_byte_ptr) as u32; - msg_byte_ptr = msg_byte_ptr + 1; - } - } - assert_eq(msg_block[int_byte_ptr], msg_item); - } - - for i in 0..INT_SIZE_PTR { - if i < int_byte_ptr { - assert_eq(msg_block[i], last_block[i]); - } - } -} - -// Apply a function on the block item which the pointer indicates. -fn update_block_item( - mut msg_block: MSG_BLOCK, - msg_byte_ptr: BLOCK_BYTE_PTR, - f: fn[Env](u32) -> u32, -) -> MSG_BLOCK { - let i = msg_byte_ptr / INT_SIZE; - msg_block[i] = f(msg_block[i]); - msg_block -} - -// Set the rightmost `zeros` number of bytes to 0. -fn set_item_zeros(item: u32, zeros: u8) -> u32 { - lshift8(rshift8(item, zeros), zeros) -} - -// Replace one byte in the item with a value, and set everything after it to zero. -fn set_item_byte_then_zeros(msg_item: u32, msg_byte_ptr: BLOCK_BYTE_PTR, msg_byte: u8) -> u32 { - let zeros = INT_SIZE - msg_byte_ptr % INT_SIZE; - let zeroed_item = set_item_zeros(msg_item, zeros as u8); - let new_item = byte_into_item(msg_byte, msg_byte_ptr); - zeroed_item + new_item -} - -// Get a byte of a message item according to its overall position in the `BLOCK_SIZE` space. -fn get_item_byte(mut msg_item: u32, msg_byte_ptr: BLOCK_BYTE_PTR) -> u8 { - // How many times do we have to shift to the right to get to the position we want? - let max_shifts = INT_SIZE - 1; - let shifts = max_shifts - msg_byte_ptr % INT_SIZE; - msg_item = rshift8(msg_item, shifts as u8); - // At this point the byte we want is in the rightmost position. - msg_item as u8 -} - -// Project a byte into a position in a field based on the overall block pointer. -// For example putting 1 into pointer 5 would be 100, because overall we would -// have [____, 0100] with indexes [0123,4567]. -fn byte_into_item(msg_byte: u8, msg_byte_ptr: BLOCK_BYTE_PTR) -> u32 { - let mut msg_item = msg_byte as u32; - // How many times do we have to shift to the left to get to the position we want? - let max_shifts = INT_SIZE - 1; - let shifts = max_shifts - msg_byte_ptr % INT_SIZE; - lshift8(msg_item, shifts as u8) -} - -// Construct a field out of 4 bytes. -fn make_item(b0: u8, b1: u8, b2: u8, b3: u8) -> u32 { - let mut item = b0 as u32; - item = lshift8(item, 1) + b1 as u32; - item = lshift8(item, 1) + b2 as u32; - item = lshift8(item, 1) + b3 as u32; - item -} - -// Shift by 8 bits to the left between 0 and 4 times. -// Checks `is_unconstrained()` to just use a bitshift if we're running in an unconstrained context, -// otherwise multiplies by 256. -fn lshift8(item: u32, shifts: u8) -> u32 { - if is_unconstrained() { - if item == 0 { - 0 - } else { - // Brillig wouldn't shift 0<<4 without overflow. - item << (8 * shifts) - } - } else { - // We can do a for loop up to INT_SIZE or an if-else. - if shifts == 0 { - item - } else if shifts == 1 { - item * TWO_POW_8 - } else if shifts == 2 { - item * TWO_POW_16 - } else if shifts == 3 { - item * TWO_POW_24 - } else { - // Doesn't make sense, but it's most likely called on 0 anyway. - 0 - } - } -} - -// Shift by 8 bits to the right between 0 and 4 times. -// Checks `is_unconstrained()` to just use a bitshift if we're running in an unconstrained context, -// otherwise divides by 256. -fn rshift8(item: u32, shifts: u8) -> u32 { - if is_unconstrained() { - item >> (8 * shifts) - } else { - // Division wouldn't work on `Field`. - if shifts == 0 { - item - } else if shifts == 1 { - item / TWO_POW_8 - } else if shifts == 2 { - item / TWO_POW_16 - } else if shifts == 3 { - item / TWO_POW_24 - } else { - 0 - } - } -} - -// Zero out all bytes between the end of the message and where the length is appended, -// then write the length into the last 8 bytes of the block. -unconstrained fn attach_len_to_msg_block( - mut msg_block: MSG_BLOCK, - mut msg_byte_ptr: BLOCK_BYTE_PTR, - message_size: u32, -) -> MSG_BLOCK { - // We assume that `msg_byte_ptr` is less than 57 because if not then it is reset to zero before calling this function. - // In any case, fill blocks up with zeros until the last 64 bits (i.e. until msg_byte_ptr = 56). - // There can be one item which has to be partially zeroed. - let modulo = msg_byte_ptr % INT_SIZE; - if modulo != 0 { - // Index of the block in which we find the item we need to partially zero. - let i = msg_byte_ptr / INT_SIZE; - let zeros = INT_SIZE - modulo; - msg_block[i] = set_item_zeros(msg_block[i], zeros as u8); - msg_byte_ptr = msg_byte_ptr + zeros; - } - - // The rest can be zeroed without bit shifting anything. - for i in (msg_byte_ptr / INT_SIZE)..INT_SIZE_PTR { - msg_block[i] = 0; - } - - // Set the last two 4 byte ints as the first/second half of the 8 bytes of the length. - let len = 8 * message_size; - let len_bytes: [u8; 8] = (len as Field).to_be_bytes(); - for i in 0..=1 { - let shift = i * 4; - msg_block[INT_SIZE_PTR + i] = make_item( - len_bytes[shift], - len_bytes[shift + 1], - len_bytes[shift + 2], - len_bytes[shift + 3], - ); - } - msg_block -} - -// Verify that the message length was correctly written by `attach_len_to_msg_block`, -// and that everything between the byte pointer and the size pointer was zeroed, -// and that everything before the byte pointer was untouched. -fn verify_msg_len( - msg_block: MSG_BLOCK, - last_block: MSG_BLOCK, - msg_byte_ptr: BLOCK_BYTE_PTR, - message_size: u32, -) { - // Check zeros up to the size pointer. - verify_msg_block_zeros(msg_block, msg_byte_ptr, INT_SIZE_PTR); - - // Check that up to the pointer we match the last block. - verify_msg_block_equals_last(msg_block, last_block, msg_byte_ptr); - - // We verify the message length was inserted correctly by reversing the byte decomposition. - let mut reconstructed_len: u64 = 0; - for i in INT_SIZE_PTR..INT_BLOCK_SIZE { - reconstructed_len = reconstructed_len * TWO_POW_32; - reconstructed_len = reconstructed_len + msg_block[i] as u64; - } - let len = 8 * message_size as u64; - assert_eq(reconstructed_len, len); -} - -// Perform the final compression, then transform the `STATE` into `HASH`. -fn hash_final_block(msg_block: MSG_BLOCK, mut state: STATE) -> HASH { - let mut out_h: HASH = [0; 32]; // Digest as sequence of bytes - // Hash final padded block - state = sha256_compression(msg_block, state); - - // Return final hash as byte array - for j in 0..8 { - let h_bytes: [u8; 4] = (state[j] as Field).to_be_bytes(); - for k in 0..4 { - out_h[4 * j + k] = h_bytes[k]; - } - } - - out_h -} - -mod tests { - use super::{ - attach_len_to_msg_block, build_msg_block, byte_into_item, get_item_byte, make_item, - set_item_byte_then_zeros, set_item_zeros, - }; - use super::INT_BLOCK; - use super::sha256_var; - - #[test] - fn smoke_test() { - let input = [0xbd]; - let result = [ - 0x68, 0x32, 0x57, 0x20, 0xaa, 0xbd, 0x7c, 0x82, 0xf3, 0x0f, 0x55, 0x4b, 0x31, 0x3d, - 0x05, 0x70, 0xc9, 0x5a, 0xcc, 0xbb, 0x7d, 0xc4, 0xb5, 0xaa, 0xe1, 0x12, 0x04, 0xc0, - 0x8f, 0xfe, 0x73, 0x2b, - ]; - assert_eq(sha256_var(input, input.len() as u64), result); - } - - #[test] - fn msg_just_over_block() { - let input = [ - 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, - 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, - 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, - 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, - ]; - let result = [ - 91, 122, 146, 93, 52, 109, 133, 148, 171, 61, 156, 70, 189, 238, 153, 7, 222, 184, 94, - 24, 65, 114, 192, 244, 207, 199, 87, 232, 192, 224, 171, 207, - ]; - assert_eq(sha256_var(input, input.len() as u64), result); - } - - #[test] - fn msg_multiple_over_block() { - let input = [ - 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, - 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, - 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, - 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 115, 45, 97, 115, - 99, 105, 105, 13, 10, 109, 105, 109, 101, 45, 118, 101, 114, 115, 105, 111, 110, 58, 49, - 46, 48, 32, 40, 77, 97, 99, 32, 79, 83, 32, 88, 32, 77, 97, 105, 108, 32, 49, 54, 46, - 48, 32, 92, 40, 51, 55, 51, 49, 46, 53, 48, 48, 46, 50, 51, 49, 92, 41, 41, 13, 10, 115, - 117, 98, 106, 101, 99, 116, 58, 72, 101, 108, 108, 111, 13, 10, 109, 101, 115, 115, 97, - 103, 101, 45, 105, 100, 58, 60, 56, 70, 56, 49, 57, 68, 51, 50, 45, 66, 54, 65, 67, 45, - 52, 56, 57, 68, 45, 57, 55, 55, 70, 45, 52, 51, 56, 66, 66, 67, 52, 67, 65, 66, 50, 55, - 64, 109, 101, 46, 99, 111, 109, 62, 13, 10, 100, 97, 116, 101, 58, 83, 97, 116, 44, 32, - 50, 54, 32, 65, 117, 103, 32, 50, 48, 50, 51, 32, 49, 50, 58, 50, 53, 58, 50, 50, 32, - 43, 48, 52, 48, 48, 13, 10, 116, 111, 58, 122, 107, 101, 119, 116, 101, 115, 116, 64, - 103, 109, 97, 105, 108, 46, 99, 111, 109, 13, 10, 100, 107, 105, 109, 45, 115, 105, 103, - 110, 97, 116, 117, 114, 101, 58, 118, 61, 49, 59, 32, 97, 61, 114, 115, 97, 45, 115, - 104, 97, 50, 53, 54, 59, 32, 99, 61, 114, 101, 108, 97, 120, 101, 100, 47, 114, 101, - 108, 97, 120, 101, 100, 59, 32, 100, 61, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, - 59, 32, 115, 61, 49, 97, 49, 104, 97, 105, 59, 32, 116, 61, 49, 54, 57, 51, 48, 51, 56, - 51, 51, 55, 59, 32, 98, 104, 61, 55, 120, 81, 77, 68, 117, 111, 86, 86, 85, 52, 109, 48, - 87, 48, 87, 82, 86, 83, 114, 86, 88, 77, 101, 71, 83, 73, 65, 83, 115, 110, 117, 99, 75, - 57, 100, 74, 115, 114, 99, 43, 118, 85, 61, 59, 32, 104, 61, 102, 114, 111, 109, 58, 67, - 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 77, 105, 109, 101, 45, 86, 101, - 114, 115, 105, 111, 110, 58, 83, 117, 98, 106, 101, 99, - ]; - let result = [ - 116, 90, 151, 31, 78, 22, 138, 180, 211, 189, 69, 76, 227, 200, 155, 29, 59, 123, 154, - 60, 47, 153, 203, 129, 157, 251, 48, 2, 79, 11, 65, 47, - ]; - assert_eq(sha256_var(input, input.len() as u64), result); - } - - #[test] - fn msg_just_under_block() { - let input = [ - 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, - 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, - 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, - 108, 97, 105, 110, 59, - ]; - let result = [ - 143, 140, 76, 173, 222, 123, 102, 68, 70, 149, 207, 43, 39, 61, 34, 79, 216, 252, 213, - 165, 74, 16, 110, 74, 29, 64, 138, 167, 30, 1, 9, 119, - ]; - assert_eq(sha256_var(input, input.len() as u64), result); - } - - #[test] - fn msg_big_not_block_multiple() { - let input = [ - 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, - 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, - 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, - 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 115, 45, 97, 115, - 99, 105, 105, 13, 10, 109, 105, 109, 101, 45, 118, 101, 114, 115, 105, 111, 110, 58, 49, - 46, 48, 32, 40, 77, 97, 99, 32, 79, 83, 32, 88, 32, 77, 97, 105, 108, 32, 49, 54, 46, - 48, 32, 92, 40, 51, 55, 51, 49, 46, 53, 48, 48, 46, 50, 51, 49, 92, 41, 41, 13, 10, 115, - 117, 98, 106, 101, 99, 116, 58, 72, 101, 108, 108, 111, 13, 10, 109, 101, 115, 115, 97, - 103, 101, 45, 105, 100, 58, 60, 56, 70, 56, 49, 57, 68, 51, 50, 45, 66, 54, 65, 67, 45, - 52, 56, 57, 68, 45, 57, 55, 55, 70, 45, 52, 51, 56, 66, 66, 67, 52, 67, 65, 66, 50, 55, - 64, 109, 101, 46, 99, 111, 109, 62, 13, 10, 100, 97, 116, 101, 58, 83, 97, 116, 44, 32, - 50, 54, 32, 65, 117, 103, 32, 50, 48, 50, 51, 32, 49, 50, 58, 50, 53, 58, 50, 50, 32, - 43, 48, 52, 48, 48, 13, 10, 116, 111, 58, 122, 107, 101, 119, 116, 101, 115, 116, 64, - 103, 109, 97, 105, 108, 46, 99, 111, 109, 13, 10, 100, 107, 105, 109, 45, 115, 105, 103, - 110, 97, 116, 117, 114, 101, 58, 118, 61, 49, 59, 32, 97, 61, 114, 115, 97, 45, 115, - 104, 97, 50, 53, 54, 59, 32, 99, 61, 114, 101, 108, 97, 120, 101, 100, 47, 114, 101, - 108, 97, 120, 101, 100, 59, 32, 100, 61, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, - 59, 32, 115, 61, 49, 97, 49, 104, 97, 105, 59, 32, 116, 61, 49, 54, 57, 51, 48, 51, 56, - 51, 51, 55, 59, 32, 98, 104, 61, 55, 120, 81, 77, 68, 117, 111, 86, 86, 85, 52, 109, 48, - 87, 48, 87, 82, 86, 83, 114, 86, 88, 77, 101, 71, 83, 73, 65, 83, 115, 110, 117, 99, 75, - 57, 100, 74, 115, 114, 99, 43, 118, 85, 61, 59, 32, 104, 61, 102, 114, 111, 109, 58, 67, - 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 77, 105, 109, 101, 45, 86, 101, - 114, 115, 105, 111, 110, 58, 83, 117, 98, 106, 101, 99, 116, 58, 77, 101, 115, 115, 97, - 103, 101, 45, 73, 100, 58, 68, 97, 116, 101, 58, 116, 111, 59, 32, 98, 61, - ]; - let result = [ - 112, 144, 73, 182, 208, 98, 9, 238, 54, 229, 61, 145, 222, 17, 72, 62, 148, 222, 186, - 55, 192, 82, 220, 35, 66, 47, 193, 200, 22, 38, 26, 186, - ]; - assert_eq(sha256_var(input, input.len() as u64), result); - } - - #[test] - fn msg_big_with_padding() { - let input = [ - 48, 130, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 48, 130, 1, 17, - 48, 37, 2, 1, 1, 4, 32, 176, 223, 31, 133, 108, 84, 158, 102, 70, 11, 165, 175, 196, 12, - 201, 130, 25, 131, 46, 125, 156, 194, 28, 23, 55, 133, 157, 164, 135, 136, 220, 78, 48, - 37, 2, 1, 2, 4, 32, 190, 82, 180, 235, 222, 33, 79, 50, 152, 136, 142, 35, 116, 224, 6, - 242, 156, 141, 128, 248, 10, 61, 98, 86, 248, 45, 207, 210, 90, 232, 175, 38, 48, 37, 2, - 1, 3, 4, 32, 0, 194, 104, 108, 237, 246, 97, 230, 116, 198, 69, 110, 26, 87, 17, 89, - 110, 199, 108, 250, 36, 21, 39, 87, 110, 102, 250, 213, 174, 131, 171, 174, 48, 37, 2, - 1, 11, 4, 32, 136, 155, 87, 144, 111, 15, 152, 127, 85, 25, 154, 81, 20, 58, 51, 75, - 193, 116, 234, 0, 60, 30, 29, 30, 183, 141, 72, 247, 255, 203, 100, 124, 48, 37, 2, 1, - 12, 4, 32, 41, 234, 106, 78, 31, 11, 114, 137, 237, 17, 92, 71, 134, 47, 62, 78, 189, - 233, 201, 214, 53, 4, 47, 189, 201, 133, 6, 121, 34, 131, 64, 142, 48, 37, 2, 1, 13, 4, - 32, 91, 222, 210, 193, 62, 222, 104, 82, 36, 41, 138, 253, 70, 15, 148, 208, 156, 45, - 105, 171, 241, 195, 185, 43, 217, 162, 146, 201, 222, 89, 238, 38, 48, 37, 2, 1, 14, 4, - 32, 76, 123, 216, 13, 51, 227, 72, 245, 59, 193, 238, 166, 103, 49, 23, 164, 171, 188, - 194, 197, 156, 187, 249, 28, 198, 95, 69, 15, 182, 56, 54, 38, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]; - let result = [ - 32, 85, 108, 174, 127, 112, 178, 182, 8, 43, 134, 123, 192, 211, 131, 66, 184, 240, 212, - 181, 240, 180, 106, 195, 24, 117, 54, 129, 19, 10, 250, 53, - ]; - let message_size = 297; - assert_eq(sha256_var(input, message_size), result); - } - - #[test] - fn msg_big_no_padding() { - let input = [ - 48, 130, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 48, 130, 1, 17, - 48, 37, 2, 1, 1, 4, 32, 176, 223, 31, 133, 108, 84, 158, 102, 70, 11, 165, 175, 196, 12, - 201, 130, 25, 131, 46, 125, 156, 194, 28, 23, 55, 133, 157, 164, 135, 136, 220, 78, 48, - 37, 2, 1, 2, 4, 32, 190, 82, 180, 235, 222, 33, 79, 50, 152, 136, 142, 35, 116, 224, 6, - 242, 156, 141, 128, 248, 10, 61, 98, 86, 248, 45, 207, 210, 90, 232, 175, 38, 48, 37, 2, - 1, 3, 4, 32, 0, 194, 104, 108, 237, 246, 97, 230, 116, 198, 69, 110, 26, 87, 17, 89, - 110, 199, 108, 250, 36, 21, 39, 87, 110, 102, 250, 213, 174, 131, 171, 174, 48, 37, 2, - 1, 11, 4, 32, 136, 155, 87, 144, 111, 15, 152, 127, 85, 25, 154, 81, 20, 58, 51, 75, - 193, 116, 234, 0, 60, 30, 29, 30, 183, 141, 72, 247, 255, 203, 100, 124, 48, 37, 2, 1, - 12, 4, 32, 41, 234, 106, 78, 31, 11, 114, 137, 237, 17, 92, 71, 134, 47, 62, 78, 189, - 233, 201, 214, 53, 4, 47, 189, 201, 133, 6, 121, 34, 131, 64, 142, 48, 37, 2, 1, 13, 4, - 32, 91, 222, 210, 193, 62, 222, 104, 82, 36, 41, 138, 253, 70, 15, 148, 208, 156, 45, - 105, 171, 241, 195, 185, 43, 217, 162, 146, 201, 222, 89, 238, 38, 48, 37, 2, 1, 14, 4, - 32, 76, 123, 216, 13, 51, 227, 72, 245, 59, 193, 238, 166, 103, 49, 23, 164, 171, 188, - 194, 197, 156, 187, 249, 28, 198, 95, 69, 15, 182, 56, 54, 38, - ]; - let result = [ - 32, 85, 108, 174, 127, 112, 178, 182, 8, 43, 134, 123, 192, 211, 131, 66, 184, 240, 212, - 181, 240, 180, 106, 195, 24, 117, 54, 129, 19, 10, 250, 53, - ]; - assert_eq(sha256_var(input, input.len() as u64), result); - } - - #[test] - fn same_msg_len_variable_padding() { - let input = [ - 29, 81, 165, 84, 243, 114, 101, 37, 242, 146, 127, 99, 69, 145, 39, 72, 213, 39, 253, - 179, 218, 37, 217, 201, 172, 93, 198, 50, 249, 70, 15, 30, 162, 112, 187, 40, 140, 9, - 236, 53, 32, 44, 38, 163, 113, 254, 192, 197, 44, 89, 71, 130, 169, 242, 17, 211, 214, - 72, 19, 178, 186, 168, 147, 127, 99, 101, 252, 227, 8, 147, 150, 85, 97, 158, 17, 107, - 218, 244, 82, 113, 247, 91, 208, 214, 60, 244, 87, 137, 173, 201, 130, 18, 66, 56, 198, - 149, 207, 189, 175, 120, 123, 224, 177, 167, 251, 159, 143, 110, 68, 183, 189, 70, 126, - 32, 35, 164, 44, 30, 44, 12, 65, 18, 62, 239, 242, 2, 248, 104, 2, 178, 64, 28, 126, 36, - 137, 24, 14, 116, 91, 98, 90, 159, 218, 102, 45, 11, 110, 223, 245, 184, 52, 99, 59, - 245, 136, 175, 3, 72, 164, 146, 145, 116, 22, 66, 24, 49, 193, 121, 3, 60, 37, 41, 97, - 3, 190, 66, 195, 225, 63, 46, 3, 118, 4, 208, 15, 1, 40, 254, 235, 151, 123, 70, 180, - 170, 44, 172, 90, 4, 254, 53, 239, 116, 246, 67, 56, 129, 61, 22, 169, 213, 65, 27, 216, - 116, 162, 239, 214, 207, 126, 177, 20, 100, 25, 48, 143, 84, 215, 70, 197, 53, 65, 70, - 86, 172, 61, 62, 9, 212, 167, 169, 133, 41, 126, 213, 196, 33, 192, 238, 0, 63, 246, - 215, 58, 128, 110, 101, 92, 3, 170, 214, 130, 149, 52, 81, 125, 118, 233, 3, 118, 193, - 104, 207, 120, 115, 77, 253, 191, 122, 0, 107, 164, 207, 113, 81, 169, 36, 201, 228, 74, - 134, 131, 218, 178, 35, 30, 216, 101, 2, 103, 174, 87, 95, 50, 50, 215, 157, 5, 210, - 188, 54, 211, 78, 45, 199, 96, 121, 241, 241, 176, 226, 194, 134, 130, 89, 217, 210, - 186, 32, 140, 39, 91, 103, 212, 26, 87, 32, 72, 144, 228, 230, 117, 99, 188, 50, 15, 69, - 79, 179, 50, 12, 106, 86, 218, 101, 73, 142, 243, 29, 250, 122, 228, 233, 29, 255, 22, - 121, 114, 125, 103, 41, 250, 241, 179, 126, 158, 198, 116, 209, 65, 94, 98, 228, 175, - 169, 96, 3, 9, 233, 133, 214, 55, 161, 164, 103, 80, 85, 24, 186, 64, 167, 92, 131, 53, - 101, 202, 47, 25, 104, 118, 155, 14, 12, 12, 25, 116, 45, 221, 249, 28, 246, 212, 200, - 157, 167, 169, 56, 197, 181, 4, 245, 146, 1, 140, 234, 191, 212, 228, 125, 87, 81, 86, - 119, 30, 63, 129, 143, 32, 96, - ]; - - // Prepare inputs of different lengths - let mut input_511 = [0; 511]; - let mut input_512 = [0; 512]; // Next block - let mut input_575 = [0; 575]; - let mut input_576 = [0; 576]; // Next block - for i in 0..input.len() { - input_511[i] = input[i]; - input_512[i] = input[i]; - input_575[i] = input[i]; - input_576[i] = input[i]; - } - - // Compute hashes of all inputs (with same message length) - let fixed_length_hash = super::sha256(input); - let var_full_length_hash = sha256_var(input, input.len() as u64); - let var_length_hash_511 = sha256_var(input_511, input.len() as u64); - let var_length_hash_512 = sha256_var(input_512, input.len() as u64); - let var_length_hash_575 = sha256_var(input_575, input.len() as u64); - let var_length_hash_576 = sha256_var(input_576, input.len() as u64); - - // All of the above should have produced the same hash - assert_eq(var_full_length_hash, fixed_length_hash); - assert_eq(var_length_hash_511, fixed_length_hash); - assert_eq(var_length_hash_512, fixed_length_hash); - assert_eq(var_length_hash_575, fixed_length_hash); - assert_eq(var_length_hash_576, fixed_length_hash); - } - - #[test] - fn test_get_item_byte() { - let fld = make_item(10, 20, 30, 40); - assert_eq(fld, 0x0a141e28); - assert_eq(get_item_byte(fld, 0), 10); - assert_eq(get_item_byte(fld, 4), 10); - assert_eq(get_item_byte(fld, 6), 30); - } - - #[test] - fn test_byte_into_item() { - let fld = make_item(0, 20, 0, 0); - assert_eq(byte_into_item(20, 1), fld); - assert_eq(byte_into_item(20, 5), fld); - } - - #[test] - fn test_set_item_zeros() { - let fld0 = make_item(10, 20, 30, 40); - let fld1 = make_item(10, 0, 0, 0); - assert_eq(set_item_zeros(fld0, 3), fld1); - assert_eq(set_item_zeros(fld0, 4), 0); - assert_eq(set_item_zeros(0, 4), 0); - } - - #[test] - fn test_set_item_byte_then_zeros() { - let fld0 = make_item(10, 20, 30, 40); - let fld1 = make_item(10, 50, 0, 0); - assert_eq(set_item_byte_then_zeros(fld0, 1, 50), fld1); - } - - #[test] - fn test_build_msg_block_start_0() { - let input = [ - 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, - 101, 115, 46, 48, - ]; - assert_eq(input.len(), 22); - - // Safety: testing context - let (msg_block, msg_byte_ptr) = unsafe { build_msg_block(input, input.len(), 0) }; - assert_eq(msg_byte_ptr, input.len()); - assert_eq(msg_block[0], make_item(input[0], input[1], input[2], input[3])); - assert_eq(msg_block[1], make_item(input[4], input[5], input[6], input[7])); - assert_eq(msg_block[5], make_item(input[20], input[21], 0, 0)); - assert_eq(msg_block[6], 0); - } - - #[test] - fn test_build_msg_block_start_1() { - let input = [ - 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, - 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, - 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, - 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, - ]; - assert_eq(input.len(), 68); - // Safety: test context - let (msg_block, msg_byte_ptr) = unsafe { build_msg_block(input, input.len(), 64) }; - assert_eq(msg_byte_ptr, 4); - assert_eq(msg_block[0], make_item(input[64], input[65], input[66], input[67])); - assert_eq(msg_block[1], 0); - } - - #[test] - fn test_attach_len_to_msg_block() { - let input: INT_BLOCK = [ - 2152555847, 1397309779, 1936618851, 1262052426, 1936876331, 1985297723, 543702374, - 1919905082, 1131376244, 1701737517, 1417244773, 978151789, 1697470053, 1920166255, - 1849316213, 1651139939, - ]; - // Safety: testing context - let msg_block = unsafe { attach_len_to_msg_block(input, 1, 448) }; - assert_eq(msg_block[0], ((1 << 7) as u32) * 256 * 256 * 256); - assert_eq(msg_block[1], 0); - assert_eq(msg_block[15], 3584); - } -} diff --git a/noir/noir-repo/noir_stdlib/src/hash/sha512.nr b/noir/noir-repo/noir_stdlib/src/hash/sha512.nr deleted file mode 100644 index 5630139c1f12..000000000000 --- a/noir/noir-repo/noir_stdlib/src/hash/sha512.nr +++ /dev/null @@ -1,165 +0,0 @@ -// Implementation of SHA-512 mapping a byte array of variable length to -// 64 bytes. -// Internal functions act on 64-bit unsigned integers for simplicity. -// Auxiliary mappings; names as in FIPS PUB 180-4 -fn rotr64(a: u64, b: u8) -> u64 // 64-bit right rotation -{ - // None of the bits overlap between `(a >> b)` and `(a << (64 - b))` - // Addition is then equivalent to OR, with fewer constraints. - (a >> b) + (a << (64 - b)) -} - -fn sha_ch(x: u64, y: u64, z: u64) -> u64 { - (x & y) ^ (!x & z) -} - -fn sha_maj(x: u64, y: u64, z: u64) -> u64 { - (x & y) ^ (x & z) ^ (y & z) -} - -fn sha_bigma0(x: u64) -> u64 { - rotr64(x, 28) ^ rotr64(x, 34) ^ rotr64(x, 39) -} - -fn sha_bigma1(x: u64) -> u64 { - rotr64(x, 14) ^ rotr64(x, 18) ^ rotr64(x, 41) -} - -fn sha_sigma0(x: u64) -> u64 { - rotr64(x, 1) ^ rotr64(x, 8) ^ (x >> 7) -} - -fn sha_sigma1(x: u64) -> u64 { - rotr64(x, 19) ^ rotr64(x, 61) ^ (x >> 6) -} - -fn sha_w(msg: [u64; 16]) -> [u64; 80] // Expanded message blocks -{ - let mut w: [u64; 80] = [0; 80]; - - for j in 0..16 { - w[j] = msg[j]; - } - - for j in 16..80 { - w[j] = crate::wrapping_add( - crate::wrapping_add(sha_sigma1(w[j - 2]), w[j - 7]), - crate::wrapping_add(sha_sigma0(w[j - 15]), w[j - 16]), - ); - } - w -} - -// SHA-512 compression function -#[no_predicates] -fn sha_c(msg: [u64; 16], hash: [u64; 8]) -> [u64; 8] { - // noir-fmt:ignore - let K: [u64; 80] = [4794697086780616226, 8158064640168781261, 13096744586834688815, 16840607885511220156, 4131703408338449720, 6480981068601479193, 10538285296894168987, 12329834152419229976, 15566598209576043074, 1334009975649890238, 2608012711638119052, 6128411473006802146, 8268148722764581231, 9286055187155687089, 11230858885718282805, 13951009754708518548, 16472876342353939154, 17275323862435702243, 1135362057144423861, 2597628984639134821, 3308224258029322869, 5365058923640841347, 6679025012923562964, 8573033837759648693, 10970295158949994411, 12119686244451234320, 12683024718118986047, 13788192230050041572, 14330467153632333762, 15395433587784984357, 489312712824947311, 1452737877330783856, 2861767655752347644, 3322285676063803686, 5560940570517711597, 5996557281743188959, 7280758554555802590, 8532644243296465576, 9350256976987008742, 10552545826968843579, 11727347734174303076, 12113106623233404929, 14000437183269869457, 14369950271660146224, 15101387698204529176, 15463397548674623760, 17586052441742319658, 1182934255886127544, 1847814050463011016, 2177327727835720531, 2830643537854262169, 3796741975233480872, 4115178125766777443, 5681478168544905931, 6601373596472566643, 7507060721942968483, 8399075790359081724, 8693463985226723168, 9568029438360202098, 10144078919501101548, 10430055236837252648, 11840083180663258601, 13761210420658862357, 14299343276471374635, 14566680578165727644, 15097957966210449927, 16922976911328602910, 17689382322260857208, 500013540394364858, 748580250866718886, 1242879168328830382, 1977374033974150939, 2944078676154940804, 3659926193048069267, 4368137639120453308, 4836135668995329356, 5532061633213252278, 6448918945643986474, 6902733635092675308, 7801388544844847127]; // first 64 bits of fractional parts of cube roots of first 80 primes - let mut out_h: [u64; 8] = hash; - let w = sha_w(msg); - for j in 0..80 { - let out1 = crate::wrapping_add(out_h[7], sha_bigma1(out_h[4])); - let out2 = crate::wrapping_add(out1, sha_ch(out_h[4], out_h[5], out_h[6])); - let t1 = crate::wrapping_add(crate::wrapping_add(out2, K[j]), w[j]); - let t2 = crate::wrapping_add(sha_bigma0(out_h[0]), sha_maj(out_h[0], out_h[1], out_h[2])); - out_h[7] = out_h[6]; - out_h[6] = out_h[5]; - out_h[5] = out_h[4]; - out_h[4] = crate::wrapping_add(out_h[3], t1); - out_h[3] = out_h[2]; - out_h[2] = out_h[1]; - out_h[1] = out_h[0]; - out_h[0] = crate::wrapping_add(t1, t2); - } - - out_h -} -// Convert 128-byte array to array of 16 u64s -fn msg_u8_to_u64(msg: [u8; 128]) -> [u64; 16] { - let mut msg64: [u64; 16] = [0; 16]; - - for i in 0..16 { - let mut msg_field: Field = 0; - for j in 0..8 { - msg_field = msg_field * 256 + msg[128 - 8 * (i + 1) + j] as Field; - } - msg64[15 - i] = msg_field as u64; - } - - msg64 -} -// SHA-512 hash function -pub fn digest(msg: [u8; N]) -> [u8; 64] { - let mut msg_block: [u8; 128] = [0; 128]; - // noir-fmt:ignore - let mut h: [u64; 8] = [7640891576956012808, 13503953896175478587, 4354685564936845355, 11912009170470909681, 5840696475078001361, 11170449401992604703, 2270897969802886507, 6620516959819538809]; // Intermediate hash, starting with the canonical initial value - let mut c: [u64; 8] = [0; 8]; // Compression of current message block as sequence of u64 - let mut out_h: [u8; 64] = [0; 64]; // Digest as sequence of bytes - let mut i: u64 = 0; // Message byte pointer - for k in 0..msg.len() { - // Populate msg_block - msg_block[i] = msg[k]; - i = i + 1; - if i == 128 { - // Enough to hash block - c = sha_c(msg_u8_to_u64(msg_block), h); - for j in 0..8 { - h[j] = crate::wrapping_add(h[j], c[j]); - } - - i = 0; - } - } - // Pad the rest such that we have a [u64; 2] block at the end representing the length - // of the message, and a block of 1 0 ... 0 following the message (i.e. [1 << 7, 0, ..., 0]). - msg_block[i] = 1 << 7; - i += 1; - // If i >= 113, there aren't enough bits in the current message block to accomplish this, so - // the 1 and 0s fill up the current block, which we then compress accordingly. - if i >= 113 { - // Not enough bits (128) to store length. Fill up with zeros. - if i < 128 { - for _i in 113..128 { - if i <= 127 { - msg_block[i] = 0; - i += 1; - } - } - } - c = sha_c(msg_u8_to_u64(msg_block), h); - for j in 0..8 { - h[j] = crate::wrapping_add(h[j], c[j]); - } - - i = 0; - } - - let len = 8 * msg.len(); - let len_bytes: [u8; 16] = (len as Field).to_le_bytes(); - for _i in 0..128 { - // In any case, fill blocks up with zeros until the last 128 (i.e. until i = 112). - if i < 112 { - msg_block[i] = 0; - i += 1; - } else if i < 128 { - for j in 0..16 { - msg_block[127 - j] = len_bytes[j]; - } - i += 16; // Done. - } - } - // Hash final padded block - c = sha_c(msg_u8_to_u64(msg_block), h); - for j in 0..8 { - h[j] = crate::wrapping_add(h[j], c[j]); - } - // Return final hash as byte array - for j in 0..8 { - let h_bytes: [u8; 8] = (h[7 - j] as Field).to_le_bytes(); - for k in 0..8 { - out_h[63 - 8 * j - k] = h_bytes[k]; - } - } - - out_h -} diff --git a/noir/noir-repo/noir_stdlib/src/lib.nr b/noir/noir-repo/noir_stdlib/src/lib.nr index d5c360792d91..3df401675513 100644 --- a/noir/noir-repo/noir_stdlib/src/lib.nr +++ b/noir/noir-repo/noir_stdlib/src/lib.nr @@ -6,8 +6,6 @@ pub mod merkle; pub mod ecdsa_secp256k1; pub mod ecdsa_secp256r1; pub mod embedded_curve_ops; -pub mod sha256; -pub mod sha512; pub mod field; pub mod collections; pub mod compat; @@ -19,7 +17,6 @@ pub mod cmp; pub mod ops; pub mod default; pub mod prelude; -pub mod uint128; pub mod runtime; pub mod meta; pub mod append; @@ -121,8 +118,46 @@ where pub fn as_witness(x: Field) {} mod tests { + use super::wrapping_mul; + #[test(should_fail_with = "custom message")] fn test_static_assert_custom_message() { super::static_assert(1 == 2, "custom message"); } + + #[test(should_fail)] + fn test_wrapping_mul() { + // This currently fails. + // See: https://github.com/noir-lang/noir/issues/7528 + let zero: u128 = 0; + let one: u128 = 1; + let two_pow_64: u128 = 0x10000000000000000; + let u128_max: u128 = 0xffffffffffffffffffffffffffffffff; + + // 1*0==0 + assert_eq(zero, wrapping_mul(zero, one)); + + // 0*1==0 + assert_eq(zero, wrapping_mul(one, zero)); + + // 1*1==1 + assert_eq(one, wrapping_mul(one, one)); + + // 0 * ( 1 << 64 ) == 0 + assert_eq(zero, wrapping_mul(zero, two_pow_64)); + + // ( 1 << 64 ) * 0 == 0 + assert_eq(zero, wrapping_mul(two_pow_64, zero)); + + // 1 * ( 1 << 64 ) == 1 << 64 + assert_eq(two_pow_64, wrapping_mul(two_pow_64, one)); + + // ( 1 << 64 ) * 1 == 1 << 64 + assert_eq(two_pow_64, wrapping_mul(one, two_pow_64)); + + // ( 1 << 64 ) * ( 1 << 64 ) == 1 << 64 + assert_eq(zero, wrapping_mul(two_pow_64, two_pow_64)); + // -1 * -1 == 1 + assert_eq(one, wrapping_mul(u128_max, u128_max)); + } } diff --git a/noir/noir-repo/noir_stdlib/src/meta/op.nr b/noir/noir-repo/noir_stdlib/src/meta/op.nr index 67c9b4cdb643..7dc846e3318b 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/op.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/op.nr @@ -222,4 +222,3 @@ impl BinaryOp { } } } - diff --git a/noir/noir-repo/noir_stdlib/src/ops/arith.nr b/noir/noir-repo/noir_stdlib/src/ops/arith.nr index 195ae7775803..3c5f011662c8 100644 --- a/noir/noir-repo/noir_stdlib/src/ops/arith.nr +++ b/noir/noir-repo/noir_stdlib/src/ops/arith.nr @@ -10,6 +10,11 @@ impl Add for Field { } } +impl Add for u128 { + fn add(self, other: u128) -> u128 { + self + other + } +} impl Add for u64 { fn add(self, other: u64) -> u64 { self + other @@ -69,6 +74,11 @@ impl Sub for Field { } } +impl Sub for u128 { + fn sub(self, other: u128) -> u128 { + self - other + } +} impl Sub for u64 { fn sub(self, other: u64) -> u64 { self - other @@ -128,6 +138,11 @@ impl Mul for Field { } } +impl Mul for u128 { + fn mul(self, other: u128) -> u128 { + self * other + } +} impl Mul for u64 { fn mul(self, other: u64) -> u64 { self * other @@ -187,6 +202,11 @@ impl Div for Field { } } +impl Div for u128 { + fn div(self, other: u128) -> u128 { + self / other + } +} impl Div for u64 { fn div(self, other: u64) -> u64 { self / other @@ -240,6 +260,11 @@ pub trait Rem { } // docs:end:rem-trait +impl Rem for u128 { + fn rem(self, other: u128) -> u128 { + self % other + } +} impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other @@ -321,4 +346,3 @@ impl Neg for i64 { } } // docs:end:neg-trait-impls - diff --git a/noir/noir-repo/noir_stdlib/src/ops/bit.nr b/noir/noir-repo/noir_stdlib/src/ops/bit.nr index 70c52ac38b00..3b1f99430cd9 100644 --- a/noir/noir-repo/noir_stdlib/src/ops/bit.nr +++ b/noir/noir-repo/noir_stdlib/src/ops/bit.nr @@ -11,6 +11,11 @@ impl Not for bool { } } +impl Not for u128 { + fn not(self) -> u128 { + !self + } +} impl Not for u64 { fn not(self) -> u64 { !self @@ -71,6 +76,11 @@ impl BitOr for bool { } } +impl BitOr for u128 { + fn bitor(self, other: u128) -> u128 { + self | other + } +} impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other @@ -130,6 +140,11 @@ impl BitAnd for bool { } } +impl BitAnd for u128 { + fn bitand(self, other: u128) -> u128 { + self & other + } +} impl BitAnd for u64 { fn bitand(self, other: u64) -> u64 { self & other @@ -189,6 +204,11 @@ impl BitXor for bool { } } +impl BitXor for u128 { + fn bitxor(self, other: u128) -> u128 { + self ^ other + } +} impl BitXor for u64 { fn bitxor(self, other: u64) -> u64 { self ^ other @@ -242,8 +262,8 @@ pub trait Shl { } // docs:end:shl-trait -impl Shl for u32 { - fn shl(self, other: u8) -> u32 { +impl Shl for u128 { + fn shl(self, other: u8) -> u128 { self << other } } @@ -252,6 +272,11 @@ impl Shl for u64 { self << other } } +impl Shl for u32 { + fn shl(self, other: u8) -> u32 { + self << other + } +} impl Shl for u16 { fn shl(self, other: u8) -> u16 { self << other @@ -295,6 +320,11 @@ pub trait Shr { } // docs:end:shr-trait +impl Shr for u128 { + fn shr(self, other: u8) -> u128 { + self >> other + } +} impl Shr for u64 { fn shr(self, other: u8) -> u64 { self >> other @@ -341,4 +371,3 @@ impl Shr for i64 { self >> other } } - diff --git a/noir/noir-repo/noir_stdlib/src/prelude.nr b/noir/noir-repo/noir_stdlib/src/prelude.nr index a4a6c35b615e..7aa60456b6dd 100644 --- a/noir/noir-repo/noir_stdlib/src/prelude.nr +++ b/noir/noir-repo/noir_stdlib/src/prelude.nr @@ -7,4 +7,3 @@ pub use crate::default::Default; pub use crate::meta::{derive, derive_via}; pub use crate::option::Option; pub use crate::panic::panic; -pub use crate::uint128::U128; diff --git a/noir/noir-repo/noir_stdlib/src/sha256.nr b/noir/noir-repo/noir_stdlib/src/sha256.nr deleted file mode 100644 index 534c954d3dc1..000000000000 --- a/noir/noir-repo/noir_stdlib/src/sha256.nr +++ /dev/null @@ -1,10 +0,0 @@ -// This file is kept for backwards compatibility. -#[deprecated("sha256 is being deprecated from the stdlib, use https://github.com/noir-lang/sha256 instead")] -pub fn digest(msg: [u8; N]) -> [u8; 32] { - crate::hash::sha256::digest(msg) -} - -#[deprecated("sha256 is being deprecated from the stdlib, use https://github.com/noir-lang/sha256 instead")] -pub fn sha256_var(msg: [u8; N], message_size: u64) -> [u8; 32] { - crate::hash::sha256::sha256_var(msg, message_size) -} diff --git a/noir/noir-repo/noir_stdlib/src/sha512.nr b/noir/noir-repo/noir_stdlib/src/sha512.nr deleted file mode 100644 index 27b53f4395f6..000000000000 --- a/noir/noir-repo/noir_stdlib/src/sha512.nr +++ /dev/null @@ -1,5 +0,0 @@ -// This file is kept for backwards compatibility. -#[deprecated] -pub fn digest(msg: [u8; N]) -> [u8; 64] { - crate::hash::sha512::digest(msg) -} diff --git a/noir/noir-repo/noir_stdlib/src/uint128.nr b/noir/noir-repo/noir_stdlib/src/uint128.nr deleted file mode 100644 index f41958e0e306..000000000000 --- a/noir/noir-repo/noir_stdlib/src/uint128.nr +++ /dev/null @@ -1,572 +0,0 @@ -use crate::cmp::{Eq, Ord, Ordering}; -use crate::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Not, Rem, Shl, Shr, Sub}; -use crate::static_assert; -use super::{convert::AsPrimitive, default::Default}; - -global pow64: Field = 18446744073709551616; //2^64; -global pow63: Field = 9223372036854775808; // 2^63; -pub struct U128 { - pub(crate) lo: Field, - pub(crate) hi: Field, -} - -impl U128 { - - pub fn from_u64s_le(lo: u64, hi: u64) -> U128 { - // in order to handle multiplication, we need to represent the product of two u64 without overflow - assert(crate::field::modulus_num_bits() as u32 > 128); - U128 { lo: lo as Field, hi: hi as Field } - } - - pub fn from_u64s_be(hi: u64, lo: u64) -> U128 { - U128::from_u64s_le(lo, hi) - } - - pub fn zero() -> U128 { - U128 { lo: 0, hi: 0 } - } - - pub fn one() -> U128 { - U128 { lo: 1, hi: 0 } - } - pub fn from_le_bytes(bytes: [u8; 16]) -> U128 { - let mut lo = 0; - let mut base = 1; - for i in 0..8 { - lo += (bytes[i] as Field) * base; - base *= 256; - } - let mut hi = 0; - base = 1; - for i in 8..16 { - hi += (bytes[i] as Field) * base; - base *= 256; - } - U128 { lo, hi } - } - - pub fn to_be_bytes(self: Self) -> [u8; 16] { - let lo: [u8; 8] = self.lo.to_be_bytes(); - let hi: [u8; 8] = self.hi.to_be_bytes(); - let mut bytes = [0; 16]; - for i in 0..8 { - bytes[i] = hi[i]; - bytes[i + 8] = lo[i]; - } - bytes - } - - pub fn to_le_bytes(self: Self) -> [u8; 16] { - let lo: [u8; 8] = self.lo.to_le_bytes(); - let hi: [u8; 8] = self.hi.to_le_bytes(); - let mut bytes = [0; 16]; - for i in 0..8 { - bytes[i] = lo[i]; - bytes[i + 8] = hi[i]; - } - bytes - } - - pub fn from_hex(hex: str) -> U128 { - let bytes = hex.as_bytes(); - // string must starts with "0x" - assert((bytes[0] == 48) & (bytes[1] == 120), "Invalid hexadecimal string"); - static_assert(N < 35, "Input does not fit into a U128"); - - let mut lo = 0; - let mut hi = 0; - let mut base = 1; - if N <= 18 { - for i in 0..N - 2 { - lo += U128::decode_ascii(bytes[N - i - 1]) * base; - base = base * 16; - } - } else { - for i in 0..16 { - lo += U128::decode_ascii(bytes[N - i - 1]) * base; - base = base * 16; - } - base = 1; - for i in 17..N - 1 { - hi += U128::decode_ascii(bytes[N - i]) * base; - base = base * 16; - } - } - U128 { lo: lo as Field, hi: hi as Field } - } - - unconstrained fn unconstrained_check_is_upper_ascii(ascii: u8) -> bool { - ((ascii >= 65) & (ascii <= 90)) // Between 'A' and 'Z' - } - - pub(crate) fn decode_ascii(ascii: u8) -> Field { - ( - if ascii < 58 { - ascii - 48 - } else { - // Safety: optionally adds 32 and then check (below) the result is in 'a..f' range - let ascii = - ascii + 32 * (unsafe { U128::unconstrained_check_is_upper_ascii(ascii) as u8 }); - assert(ascii >= 97); // enforce >= 'a' - assert(ascii <= 102); // enforce <= 'f' - ascii - 87 - } - ) as Field - } - - // TODO: Replace with a faster version. - // A circuit that uses this function can be slow to compute - // (we're doing up to 127 calls to compute the quotient) - unconstrained fn unconstrained_div(self: Self, b: U128) -> (U128, U128) { - if b == U128::zero() { - // Return 0,0 to avoid eternal loop - (U128::zero(), U128::zero()) - } else if self < b { - (U128::zero(), self) - } else if self == b { - (U128::one(), U128::zero()) - } else { - let (q, r) = if b.hi as u64 >= pow63 as u64 { - // The result of multiplication by 2 would overflow - (U128::zero(), self) - } else { - self.unconstrained_div(b * U128::from_u64s_le(2, 0)) - }; - let q_mul_2 = q * U128::from_u64s_le(2, 0); - if r < b { - (q_mul_2, r) - } else { - (q_mul_2 + U128::one(), r - b) - } - } - } - - pub fn from_integer(i: T) -> U128 - where - T: AsPrimitive, - { - let f = i.as_(); - // Reject values which would overflow a u128 - f.assert_max_bit_size::<128>(); - let lo = f as u64 as Field; - let hi = (f - lo) / pow64; - U128 { lo, hi } - } - - pub fn to_integer(self) -> T - where - Field: AsPrimitive, - { - AsPrimitive::as_(self.lo + self.hi * pow64) - } - - fn wrapping_mul(self: Self, b: U128) -> U128 { - let low = self.lo * b.lo; - let lo = low as u64 as Field; - let carry = (low - lo) / pow64; - let high = self.lo * b.hi + self.hi * b.lo + carry; - let hi = high as u64 as Field; - U128 { lo, hi } - } -} - -impl Add for U128 { - fn add(self: Self, b: U128) -> U128 { - let low = self.lo + b.lo; - let lo = low as u64 as Field; - let carry = (low - lo) / pow64; - let high = self.hi + b.hi + carry; - let hi = high as u64 as Field; - assert(hi == high, "attempt to add with overflow"); - U128 { lo, hi } - } -} - -impl Sub for U128 { - fn sub(self: Self, b: U128) -> U128 { - let low = pow64 + self.lo - b.lo; - let lo = low as u64 as Field; - let borrow = (low == lo) as Field; - let high = self.hi - b.hi - borrow; - let hi = high as u64 as Field; - assert(hi == high, "attempt to subtract with underflow"); - U128 { lo, hi } - } -} - -impl Mul for U128 { - fn mul(self: Self, b: U128) -> U128 { - assert(self.hi * b.hi == 0, "attempt to multiply with overflow"); - let low = self.lo * b.lo; - let lo = low as u64 as Field; - let carry = (low - lo) / pow64; - let high = if crate::field::modulus_num_bits() as u32 > 196 { - (self.lo + self.hi) * (b.lo + b.hi) - low + carry - } else { - self.lo * b.hi + self.hi * b.lo + carry - }; - let hi = high as u64 as Field; - assert(hi == high, "attempt to multiply with overflow"); - U128 { lo, hi } - } -} - -impl Div for U128 { - fn div(self: Self, b: U128) -> U128 { - // Safety: euclidian division is asserted to be correct: assert(a == b * q + r); and assert(r < b); - // Furthermore, U128 addition and multiplication ensures that b * q + r does not overflow - unsafe { - let (q, r) = self.unconstrained_div(b); - let a = b * q + r; - assert_eq(self, a); - assert(r < b); - q - } - } -} - -impl Rem for U128 { - fn rem(self: Self, b: U128) -> U128 { - // Safety: cf div() above - unsafe { - let (q, r) = self.unconstrained_div(b); - let a = b * q + r; - assert_eq(self, a); - assert(r < b); - - r - } - } -} - -impl Eq for U128 { - fn eq(self: Self, b: U128) -> bool { - (self.lo == b.lo) & (self.hi == b.hi) - } -} - -impl Ord for U128 { - fn cmp(self, other: Self) -> Ordering { - let hi_ordering = (self.hi as u64).cmp((other.hi as u64)); - let lo_ordering = (self.lo as u64).cmp((other.lo as u64)); - - if hi_ordering == Ordering::equal() { - lo_ordering - } else { - hi_ordering - } - } -} - -impl Not for U128 { - fn not(self) -> U128 { - U128 { lo: (!(self.lo as u64)) as Field, hi: (!(self.hi as u64)) as Field } - } -} - -impl BitOr for U128 { - fn bitor(self, other: U128) -> U128 { - U128 { - lo: ((self.lo as u64) | (other.lo as u64)) as Field, - hi: ((self.hi as u64) | (other.hi as u64)) as Field, - } - } -} - -impl BitAnd for U128 { - fn bitand(self, other: U128) -> U128 { - U128 { - lo: ((self.lo as u64) & (other.lo as u64)) as Field, - hi: ((self.hi as u64) & (other.hi as u64)) as Field, - } - } -} - -impl BitXor for U128 { - fn bitxor(self, other: U128) -> U128 { - U128 { - lo: ((self.lo as u64) ^ (other.lo as u64)) as Field, - hi: ((self.hi as u64) ^ (other.hi as u64)) as Field, - } - } -} - -impl Shl for U128 { - fn shl(self, other: u8) -> U128 { - assert(other < 128, "attempt to shift left with overflow"); - let exp_bits: [u1; 7] = (other as Field).to_be_bits(); - - let mut r: Field = 2; - let mut y: Field = 1; - for i in 1..8 { - let bit = exp_bits[7 - i] as Field; - y = bit * (r * y) + (1 - bit) * y; - r *= r; - } - self.wrapping_mul(U128::from_integer(y)) - } -} - -impl Shr for U128 { - fn shr(self, other: u8) -> U128 { - assert(other < 128, "attempt to shift right with overflow"); - let exp_bits: [u1; 7] = (other as Field).to_be_bits(); - - let mut r: Field = 2; - let mut y: Field = 1; - for i in 1..8 { - let bit = exp_bits[7 - i] as Field; - y = bit * (r * y) + (1 - bit) * y; - r *= r; - } - self / U128::from_integer(y) - } -} - -impl Default for U128 { - fn default() -> Self { - U128::zero() - } -} - -mod tests { - use crate::default::Default; - use crate::ops::Not; - use crate::uint128::{pow63, pow64, U128}; - - #[test] - fn test_not(lo: u64, hi: u64) { - let num = U128::from_u64s_le(lo, hi); - let not_num = num.not(); - - assert_eq(not_num.hi, (hi.not() as Field)); - assert_eq(not_num.lo, (lo.not() as Field)); - - let not_not_num = not_num.not(); - assert_eq(num, not_not_num); - } - #[test] - fn test_construction() { - // Check little-endian u64 is inversed with big-endian u64 construction - let a = U128::from_u64s_le(2, 1); - let b = U128::from_u64s_be(1, 2); - assert_eq(a, b); - // Check byte construction is equivalent - let c = U128::from_le_bytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let d = U128::from_u64s_le(0x0706050403020100, 0x0f0e0d0c0b0a0908); - assert_eq(c, d); - } - #[test] - fn test_byte_decomposition() { - let a = U128::from_u64s_le(0x0706050403020100, 0x0f0e0d0c0b0a0908); - // Get big-endian and little-endian byte decompostions - let le_bytes_a = a.to_le_bytes(); - let be_bytes_a = a.to_be_bytes(); - - // Check equivalence - for i in 0..16 { - assert_eq(le_bytes_a[i], be_bytes_a[15 - i]); - } - // Reconstruct U128 from byte decomposition - let b = U128::from_le_bytes(le_bytes_a); - // Check that it's the same element - assert_eq(a, b); - } - #[test] - fn test_hex_constuction() { - let a = U128::from_u64s_le(0x1, 0x2); - let b = U128::from_hex("0x20000000000000001"); - assert_eq(a, b); - - let c = U128::from_hex("0xffffffffffffffffffffffffffffffff"); - let d = U128::from_u64s_le(0xffffffffffffffff, 0xffffffffffffffff); - assert_eq(c, d); - - let e = U128::from_hex("0x00000000000000000000000000000000"); - let f = U128::from_u64s_le(0, 0); - assert_eq(e, f); - } - - // Ascii decode tests - - #[test] - fn test_ascii_decode_correct_range() { - // '0'..'9' range - for i in 0..10 { - let decoded = U128::decode_ascii(48 + i); - assert_eq(decoded, i as Field); - } - // 'A'..'F' range - for i in 0..6 { - let decoded = U128::decode_ascii(65 + i); - assert_eq(decoded, (i + 10) as Field); - } - // 'a'..'f' range - for i in 0..6 { - let decoded = U128::decode_ascii(97 + i); - assert_eq(decoded, (i + 10) as Field); - } - } - - #[test(should_fail)] - fn test_ascii_decode_range_less_than_48_fails_0() { - crate::println(U128::decode_ascii(0)); - } - #[test(should_fail)] - fn test_ascii_decode_range_less_than_48_fails_1() { - crate::println(U128::decode_ascii(47)); - } - - #[test(should_fail)] - fn test_ascii_decode_range_58_64_fails_0() { - let _ = U128::decode_ascii(58); - } - #[test(should_fail)] - fn test_ascii_decode_range_58_64_fails_1() { - let _ = U128::decode_ascii(64); - } - #[test(should_fail)] - fn test_ascii_decode_range_71_96_fails_0() { - let _ = U128::decode_ascii(71); - } - #[test(should_fail)] - fn test_ascii_decode_range_71_96_fails_1() { - let _ = U128::decode_ascii(96); - } - #[test(should_fail)] - fn test_ascii_decode_range_greater_than_102_fails() { - let _ = U128::decode_ascii(103); - } - - #[test(should_fail)] - fn test_ascii_decode_regression() { - // This code will actually fail because of ascii_decode, - // but in the past it was possible to create a value > (1<<128) - let a = U128::from_hex("0x~fffffffffffffffffffffffffffffff"); - let b: Field = a.to_integer(); - let c: [u8; 17] = b.to_le_bytes(); - assert(c[16] != 0); - } - - #[test] - fn test_unconstrained_div() { - // Test the potential overflow case - let a = U128::from_u64s_le(0x0, 0xffffffffffffffff); - let b = U128::from_u64s_le(0x0, 0xfffffffffffffffe); - let c = U128::one(); - let d = U128::from_u64s_le(0x0, 0x1); - // Safety: testing context - unsafe { - let (q, r) = a.unconstrained_div(b); - assert_eq(q, c); - assert_eq(r, d); - } - - let a = U128::from_u64s_le(2, 0); - let b = U128::one(); - // Check the case where a is a multiple of b - // Safety: testing context - unsafe { - let (c, d) = a.unconstrained_div(b); - assert_eq((c, d), (a, U128::zero())); - } - - // Check where b is a multiple of a - // Safety: testing context - unsafe { - let (c, d) = b.unconstrained_div(a); - assert_eq((c, d), (U128::zero(), b)); - } - - // Dividing by zero returns 0,0 - let a = U128::from_u64s_le(0x1, 0x0); - let b = U128::zero(); - // Safety: testing context - unsafe { - let (c, d) = a.unconstrained_div(b); - assert_eq((c, d), (U128::zero(), U128::zero())); - } - // Dividing 1<<127 by 1<<127 (special case) - let a = U128::from_u64s_le(0x0, pow63 as u64); - let b = U128::from_u64s_le(0x0, pow63 as u64); - // Safety: testing context - unsafe { - let (c, d) = a.unconstrained_div(b); - assert_eq((c, d), (U128::one(), U128::zero())); - } - } - - #[test] - fn integer_conversions() { - // Maximum - let start: Field = 0xffffffffffffffffffffffffffffffff; - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - - // Minimum - let start: Field = 0x0; - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - - // Low limb - let start: Field = 0xffffffffffffffff; - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - - // High limb - let start: Field = 0xffffffffffffffff0000000000000000; - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - } - - #[test] - fn integer_conversions_fuzz(lo: u64, hi: u64) { - let start: Field = (lo as Field) + pow64 * (hi as Field); - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - } - - #[test] - fn test_wrapping_mul() { - // 1*0==0 - assert_eq(U128::zero(), U128::zero().wrapping_mul(U128::one())); - - // 0*1==0 - assert_eq(U128::zero(), U128::one().wrapping_mul(U128::zero())); - - // 1*1==1 - assert_eq(U128::one(), U128::one().wrapping_mul(U128::one())); - - // 0 * ( 1 << 64 ) == 0 - assert_eq(U128::zero(), U128::zero().wrapping_mul(U128::from_u64s_le(0, 1))); - - // ( 1 << 64 ) * 0 == 0 - assert_eq(U128::zero(), U128::from_u64s_le(0, 1).wrapping_mul(U128::zero())); - - // 1 * ( 1 << 64 ) == 1 << 64 - assert_eq(U128::from_u64s_le(0, 1), U128::from_u64s_le(0, 1).wrapping_mul(U128::one())); - - // ( 1 << 64 ) * 1 == 1 << 64 - assert_eq(U128::from_u64s_le(0, 1), U128::one().wrapping_mul(U128::from_u64s_le(0, 1))); - - // ( 1 << 64 ) * ( 1 << 64 ) == 1 << 64 - assert_eq(U128::zero(), U128::from_u64s_le(0, 1).wrapping_mul(U128::from_u64s_le(0, 1))); - // -1 * -1 == 1 - assert_eq( - U128::one(), - U128::from_u64s_le(0xffffffffffffffff, 0xffffffffffffffff).wrapping_mul( - U128::from_u64s_le(0xffffffffffffffff, 0xffffffffffffffff), - ), - ); - } - - #[test] - fn test_default() { - assert_eq(U128::default(), U128::zero()); - } -} diff --git a/noir/noir-repo/rust-toolchain.toml b/noir/noir-repo/rust-toolchain.toml index e647d5cbf460..ed1915d2613a 100644 --- a/noir/noir-repo/rust-toolchain.toml +++ b/noir/noir-repo/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.75.0" +channel = "1.85.0" components = [ "rust-src" ] -targets = [ "wasm32-unknown-unknown", "wasm32-wasi", "aarch64-apple-darwin" ] +targets = [ "wasm32-unknown-unknown", "wasm32-wasip1", "aarch64-apple-darwin" ] profile = "default" diff --git a/noir/noir-repo/scripts/bump-aztec-packges-commit.sh b/noir/noir-repo/scripts/bump-aztec-packages-commit.sh similarity index 100% rename from noir/noir-repo/scripts/bump-aztec-packges-commit.sh rename to noir/noir-repo/scripts/bump-aztec-packages-commit.sh diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256/Prover.toml b/noir/noir-repo/test_programs/benchmarks/bench_sha256/Prover.toml deleted file mode 100644 index 66779dea9d7b..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256/Prover.toml +++ /dev/null @@ -1 +0,0 @@ -input = [1,2] diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_sha256/src/main.nr deleted file mode 100644 index c94d359239dd..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256/src/main.nr +++ /dev/null @@ -1,4 +0,0 @@ - -fn main(input: [u8; 2]) -> pub [u8; 32] { - std::hash::sha256(input) -} \ No newline at end of file diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Prover.toml b/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Prover.toml deleted file mode 100644 index 542b4a08dd6f..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Prover.toml +++ /dev/null @@ -1,102 +0,0 @@ -input = [ - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], -] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/src/main.nr deleted file mode 100644 index 6e4bfc27c8f2..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/src/main.nr +++ /dev/null @@ -1,10 +0,0 @@ -global SIZE: u32 = 100; - -fn main(input: [[u8; 2]; SIZE]) -> pub [[u8; 32]; SIZE] { - let mut results: [[u8; 32]; SIZE] = [[0; 32]; SIZE]; - for i in 0..SIZE { - results[i] = std::hash::sha256(input[i]); - } - - results -} diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Nargo.toml b/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Nargo.toml deleted file mode 100644 index c1dc76df3942..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "bench_sha256_30" -version = "0.1.0" -type = "bin" -authors = [""] - -[dependencies] diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Prover.toml b/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Prover.toml deleted file mode 100644 index 7792a9ab8e36..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Prover.toml +++ /dev/null @@ -1,32 +0,0 @@ -input = [ - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], -] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/src/main.nr deleted file mode 100644 index 0a4288114e34..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/src/main.nr +++ /dev/null @@ -1,10 +0,0 @@ -global SIZE: u32 = 30; - -fn main(input: [[u8; 2]; SIZE]) -> pub [[u8; 32]; SIZE] { - let mut results: [[u8; 32]; SIZE] = [[0; 32]; SIZE]; - for i in 0..SIZE { - results[i] = std::hash::sha256(input[i]); - } - - results -} diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Prover.toml b/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Prover.toml deleted file mode 100644 index ba4bbc1540dc..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Prover.toml +++ /dev/null @@ -1,191 +0,0 @@ -# 2*64+60=188 bytes -input = [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 72, - 73, - 74, - 75, - 76, - 77, - 78, - 79, - 80, - 81, - 82, - 83, - 84, - 85, - 86, - 87, - 88, - 89, - 90, - 91, - 92, - 93, - 94, - 95, - 96, - 97, - 98, - 99, - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 72, - 73, - 74, - 75, - 76, - 77, - 78, - 79, - 80, - 81, - 82, - 83, - 84, - 85, - 86, - 87, -] diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/src/main.nr deleted file mode 100644 index c47bdc2a5610..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/src/main.nr +++ /dev/null @@ -1,7 +0,0 @@ -// Input size long enough that we have to compress a few times -// and then pad the last block out. -global INPUT_SIZE: u32 = 2 * 64 + 60; - -fn main(input: [u8; INPUT_SIZE]) -> pub [u8; 32] { - std::hash::sha256(input) -} diff --git a/noir/noir-repo/test_programs/compilation_report.sh b/noir/noir-repo/test_programs/compilation_report.sh index 6f7ef254477b..aa1bbef39dec 100755 --- a/noir/noir-repo/test_programs/compilation_report.sh +++ b/noir/noir-repo/test_programs/compilation_report.sh @@ -6,7 +6,7 @@ current_dir=$(pwd) base_path="$current_dir/execution_success" # Tests to be profiled for compilation report -tests_to_profile=("sha256_regression" "regression_4709" "ram_blowup_regression" "global_var_regression_entry_points") +tests_to_profile=("regression_4709" "ram_blowup_regression" "global_var_regression_entry_points") echo "[ " > $current_dir/compilation_report.json diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Nargo.toml b/noir/noir-repo/test_programs/compile_failure/broken_impl/Nargo.toml similarity index 51% rename from noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Nargo.toml rename to noir/noir-repo/test_programs/compile_failure/broken_impl/Nargo.toml index d0c90d75088e..a200adc27b07 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Nargo.toml +++ b/noir/noir-repo/test_programs/compile_failure/broken_impl/Nargo.toml @@ -1,7 +1,7 @@ [package] -name = "bench_sha256_100" -version = "0.1.0" +name = "broken_impl" type = "bin" authors = [""] +compiler_version = ">=0.26.0" [dependencies] diff --git a/noir/noir-repo/test_programs/compile_failure/broken_impl/src/main.nr b/noir/noir-repo/test_programs/compile_failure/broken_impl/src/main.nr new file mode 100644 index 000000000000..033b0e741ed6 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_failure/broken_impl/src/main.nr @@ -0,0 +1,2 @@ +// This used to crash the compiler +impl< Foo for diff --git a/noir/noir-repo/test_programs/compile_success_empty/brillig_integer_binary_operations/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/brillig_integer_binary_operations/src/main.nr index 032fbf457b63..2c02582c14d9 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/brillig_integer_binary_operations/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/brillig_integer_binary_operations/src/main.nr @@ -77,4 +77,3 @@ unconstrained fn shr(x: u32, y: u8) -> u32 { unconstrained fn shl(x: u32, y: u8) -> u32 { x << y } - diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_as_field/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_as_field/src/main.nr deleted file mode 100644 index e13db54b80bd..000000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_as_field/src/main.nr +++ /dev/null @@ -1,6 +0,0 @@ -fn main() { - comptime { - let _: U128 = U128::from_integer(1); - } -} - diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_as_field/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/comptime_as_primitive/Nargo.toml similarity index 100% rename from noir/noir-repo/test_programs/compile_success_empty/comptime_as_field/Nargo.toml rename to noir/noir-repo/test_programs/compile_success_empty/comptime_as_primitive/Nargo.toml diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_as_primitive/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_as_primitive/src/main.nr new file mode 100644 index 000000000000..392ccf2ebfc6 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_as_primitive/src/main.nr @@ -0,0 +1,7 @@ +fn main() { + comptime { + let x: u64 = 1; + let y = x as Field; + let _ = y as u128; + } +} 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 index 8cdd15aaa0ec..adee6e6fe935 100644 --- 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 @@ -26,4 +26,3 @@ fn hello_world() {} comptime fn call(x: Quoted) -> Quoted { quote { $x() } } - diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/Nargo.toml deleted file mode 100644 index 38a46ba0dbec..000000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/Nargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "comptime_from_field" -type = "bin" -authors = [""] - -[dependencies] diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/src/main.nr deleted file mode 100644 index 722c5c7eb329..000000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/src/main.nr +++ /dev/null @@ -1,6 +0,0 @@ -fn main() { - comptime { - let _: Field = U128::from_hex("0x0").to_integer(); - } -} - diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_function_definition/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_function_definition/src/main.nr index 4ce641cbe7df..57bcd2ba90ec 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_function_definition/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_function_definition/src/main.nr @@ -116,7 +116,7 @@ mod test_as_typed_expr_1 { comptime fn foo(module: Module) -> Quoted { let method = module.functions().filter(|f| f.name() == quote { method })[0]; let func = method.as_typed_expr(); - quote { + quote { pub fn bar() -> i32 { $func(1) } @@ -140,7 +140,7 @@ mod test_as_typed_expr_2 { comptime fn foo(module: Module) -> Quoted { let method = module.functions().filter(|f| f.name() == quote { method })[0]; let func = method.as_typed_expr(); - quote { + quote { pub fn bar() -> u32 { // Safety: test program unsafe { $func([1, 2, 3, 0]) } @@ -166,7 +166,7 @@ mod test_as_typed_expr_3 { comptime fn foo(module: Module) -> Quoted { let method = module.functions().filter(|f| f.name() == quote { method })[0]; let func = method.as_typed_expr(); - quote { + quote { pub fn bar() -> u32 { // Safety: test program comptime { $func(([1, 2, 3, 0], "a")) } diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/Nargo.toml deleted file mode 100644 index 47c8654804d6..000000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "comptime_keccak" -type = "bin" -authors = [""] -compiler_version = ">=0.33.0" - -[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/src/main.nr deleted file mode 100644 index dc4e88b7ab2d..000000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/src/main.nr +++ /dev/null @@ -1,32 +0,0 @@ -// Tests a very simple program. -// -// The features being tested is keccak256 in brillig -fn main() { - comptime { - let x = 0xbd; - let result = [ - 0x5a, 0x50, 0x2f, 0x9f, 0xca, 0x46, 0x7b, 0x26, 0x6d, 0x5b, 0x78, 0x33, 0x65, 0x19, - 0x37, 0xe8, 0x05, 0x27, 0x0c, 0xa3, 0xf3, 0xaf, 0x1c, 0x0d, 0xd2, 0x46, 0x2d, 0xca, - 0x4b, 0x3b, 0x1a, 0xbf, - ]; - // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field - // The padding is taken care of by the program - let digest = keccak256([x as u8], 1); - assert(digest == result); - //#1399: variable message size - let message_size = 4; - let hash_a = keccak256([1, 2, 3, 4], message_size); - let hash_b = keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); - - assert(hash_a == hash_b); - - let message_size_big = 8; - let hash_c = keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); - - assert(hash_a != hash_c); - } -} - -comptime fn keccak256(data: [u8; N], msg_len: u32) -> [u8; 32] { - std::hash::keccak256(data, msg_len) -} 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 11025d450246..5573e8ad798b 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 @@ -31,4 +31,3 @@ pub fn neg_at_comptime() { let _result = -value; } } - diff --git a/noir/noir-repo/test_programs/compile_success_empty/conditional_regression_to_bits/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/conditional_regression_to_bits/src/main.nr index dd80d2c576f2..ef0eb4cf0689 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/conditional_regression_to_bits/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/conditional_regression_to_bits/src/main.nr @@ -23,4 +23,3 @@ fn main() { } assert(c1 == 1); } - diff --git a/noir/noir-repo/test_programs/compile_success_empty/enums/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/enums/src/main.nr index 03fc10a1c35a..3b12af1c15d5 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/enums/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/enums/src/main.nr @@ -3,6 +3,7 @@ fn main() { foo_tests(); option_tests(); abc_tests(); + match_on_structs(); } fn primitive_tests() { @@ -19,8 +20,20 @@ fn primitive_tests() { false => fail(), true => (), } + + // Ensure we can match on MIN and use globals as constants + let i64_min = I64_MIN; + match i64_min { + 9_223_372_036_854_775_807 => fail(), + -9_223_372_036_854_775_807 => fail(), + 0 => fail(), + I64_MIN => (), + _ => fail(), + } } +global I64_MIN: i64 = -9_223_372_036_854_775_808; + enum Foo { A(Field, Field), B(u32), @@ -103,7 +116,7 @@ fn abc_tests() { // Mut is only to throw the optimizer off a bit so we can see // the `eq`s that get generated before they're removed because each of these are constant let mut tuple = (ABC::A, ABC::B); - match tuple { + let _ = match tuple { (ABC::A, _) => 1, (_, ABC::A) => 2, (_, ABC::B) => 3, @@ -114,3 +127,27 @@ fn abc_tests() { _ => 0, }; } + +fn match_on_structs() { + let foo = MyStruct { x: 10, y: 20 }; + match foo { + MyStruct { x, y } => { + assert_eq(x, 10); + assert_eq(y, 20); + }, + } + + match MyOption::Some(foo) { + MyOption::Some(MyStruct { x: x2, y: y2 }) => { + assert_eq(x2, 10); + assert_eq(y2, 20); + }, + MyOption::None => fail(), + MyOption::Maybe => fail(), + } +} + +struct MyStruct { + x: i32, + y: Field, +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/eq_derivation_with_numeric_generics/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/eq_derivation_with_numeric_generics/src/main.nr index 3307aeb15adc..e2f3cb46d11d 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/eq_derivation_with_numeric_generics/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/eq_derivation_with_numeric_generics/src/main.nr @@ -9,4 +9,3 @@ fn main() { let bar = Foo { a: [0; 10], b: 28 }; assert(foo != bar); } - diff --git a/noir/noir-repo/test_programs/compile_success_empty/trait_override_implementation/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/trait_override_implementation/src/main.nr index 12a625b08432..589232d00d8e 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/trait_override_implementation/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/trait_override_implementation/src/main.nr @@ -80,4 +80,3 @@ fn main(x: Field) { assert(bar_mut.f4() == 4, "14"); assert(bar_mut.f5() == 50, "15"); } - diff --git a/noir/noir-repo/test_programs/compile_success_empty/unquote_struct/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/unquote_struct/src/main.nr index 12c683a94a85..c7820c094b7a 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/unquote_struct/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/unquote_struct/src/main.nr @@ -1,3 +1,5 @@ +use std::meta::unquote; + fn main() { let foo = Foo { x: 4, y: 4 }; foo.assert_equal(); @@ -21,3 +23,16 @@ comptime fn output_struct(f: FunctionDefinition) -> Quoted { } } } + +// The following code proves that `Bar::` can be unquoted +// into a constructor position without losing its generic types. + +pub struct Bar {} + +pub fn bar() { + let _ = comptime { + let x = quote { Bar:: }; + let q = quote { $x {} }; + unquote!(q) + }; +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/unquote_trait_constraint_in_trait_impl_position/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/unquote_trait_constraint_in_trait_impl_position/src/main.nr index 92ddf99f1c0b..8bf3863bbe3f 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/unquote_trait_constraint_in_trait_impl_position/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/unquote_trait_constraint_in_trait_impl_position/src/main.nr @@ -21,4 +21,3 @@ comptime fn foo(_: FunctionDefinition) -> Quoted { } } } - diff --git a/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Nargo.toml b/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Nargo.toml new file mode 100644 index 000000000000..c04a74f24df4 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "u128_multiplication_overflow" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] diff --git a/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Prover.toml b/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Prover.toml new file mode 100644 index 000000000000..90212f582036 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Prover.toml @@ -0,0 +1,2 @@ +x = "257295058452732708167448229940904157557" +y = "85070591730234615865843651857942052864" diff --git a/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/src/main.nr b/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/src/main.nr new file mode 100644 index 000000000000..45884059a881 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/src/main.nr @@ -0,0 +1,7 @@ +fn main(x: u128, y: u128) -> pub u128 { + // Multiplying 257295058452732708167448229940904157557 * 85070591730234615865843651857942052864 + // using bn254 field elements wraps the maximum field element value, + // resulting in 31631953497925087476338759149270597631 which is less than u128::MAX. + // We want this to be caught as an overflow. + x * y +} diff --git a/noir/noir-repo/test_programs/execution_report.sh b/noir/noir-repo/test_programs/execution_report.sh index 5c916ef6bd75..c7b12d996811 100755 --- a/noir/noir-repo/test_programs/execution_report.sh +++ b/noir/noir-repo/test_programs/execution_report.sh @@ -6,7 +6,7 @@ current_dir=$(pwd) base_path="$current_dir/execution_success" # Tests to be profiled for execution report -tests_to_profile=("sha256_regression" "regression_4709" "ram_blowup_regression" "global_var_regression_entry_points") +tests_to_profile=("regression_4709" "ram_blowup_regression" "global_var_regression_entry_points") echo "[" > $current_dir/execution_report.json diff --git a/noir/noir-repo/test_programs/execution_success/6/Prover.toml b/noir/noir-repo/test_programs/execution_success/6/Prover.toml index 1c52aef063c9..d370032bd530 100644 --- a/noir/noir-repo/test_programs/execution_success/6/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/6/Prover.toml @@ -3,37 +3,4 @@ # used : https://emn178.github.io/online-tools/sha256.html x = [104, 101, 108, 108, 111] -result = [ - 0x2c, - 0xf2, - 0x4d, - 0xba, - 0x5f, - 0xb0, - 0xa3, - 0x0e, - 0x26, - 0xe8, - 0x3b, - 0x2a, - 0xc5, - 0xb9, - 0xe2, - 0x9e, - 0x1b, - 0x16, - 0x1e, - 0x5c, - 0x1f, - 0xa7, - 0x42, - 0x5e, - 0x73, - 0x04, - 0x33, - 0x62, - 0x93, - 0x8b, - 0x98, - 0x24, -] +result = [234, 143, 22, 61, 179, 134, 130, 146, 94, 68, 145, 197, 229, 141, 75, 179, 80, 110, 248, 193, 78, 183, 138, 134, 233, 8, 197, 98, 74, 103, 32, 15] diff --git a/noir/noir-repo/test_programs/execution_success/6/src/main.nr b/noir/noir-repo/test_programs/execution_success/6/src/main.nr index 5b71174614f5..e950e309df2d 100644 --- a/noir/noir-repo/test_programs/execution_success/6/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/6/src/main.nr @@ -1,11 +1,11 @@ -// Sha256 circuit where the input is 5 bytes -// not five field elements since sha256 operates over +// blake3 circuit where the input is 5 bytes +// not five field elements since blake3 operates over // bytes. // // If you do not cast, it will take all the bytes from the field element! fn main(x: [u8; 5], result: pub [u8; 32]) { - let mut digest = std::hash::sha256(x); + let mut digest = std::hash::blake3(x); digest[0] = 5 as u8; - digest = std::hash::sha256(x); + digest = std::hash::blake3(x); assert(digest == result); } diff --git a/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/Prover.toml b/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/Prover.toml index cc60eb8a8baa..f852a79a1033 100644 --- a/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/Prover.toml @@ -1,4 +1,4 @@ index = "1" leaf = ["51", "109", "224", "175", "60", "42", "79", "222", "117", "255", "174", "79", "126", "242", "74", "34", "100", "35", "20", "200", "109", "89", "191", "219", "41", "10", "118", "217", "165", "224", "215", "109"] path = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63"] -root = [79, 230, 126, 184, 98, 125, 226, 58, 117, 45, 140, 15, 72, 118, 89, 173, 117, 161, 166, 0, 214, 125, 13, 16, 113, 81, 173, 156, 97, 15, 57, 216] +root = [186, 47, 168, 70, 152, 149, 203, 90, 138, 188, 96, 15, 111, 179, 82, 106, 198, 166, 172, 38, 110, 187, 182, 64, 29, 101, 171, 221, 89, 105, 243, 22] diff --git a/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr b/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr index 260d609928b8..603ae704d704 100644 --- a/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr @@ -18,7 +18,7 @@ fn compute_root(leaf: [u8; 32], path: [u8; 64], _index: u32, root: [u8; 32]) { hash_input[j + b] = path[offset + j]; } - current = std::hash::sha256(hash_input); + current = std::hash::blake3(hash_input); index = index >> 1; } diff --git a/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/Prover.toml b/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/Prover.toml index 1f2915324144..85b480415b1b 100644 --- a/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/Prover.toml @@ -1,5 +1,5 @@ y = "3" -hash_result = [50, 53, 90, 252, 105, 236, 223, 30, 135, 229, 193, 172, 51, 139, 8, 32, 188, 104, 151, 115, 129, 168, 27, 71, 203, 47, 40, 228, 89, 177, 129, 100] +hash_result = [77, 43, 36, 42, 132, 232, 186, 191, 119, 43, 192, 121, 66, 137, 143, 205, 13, 23, 80, 25, 162, 45, 100, 31, 178, 150, 138, 4, 72, 73, 120, 70] [[x]] a = "1" @@ -20,4 +20,4 @@ a = "7" b = ["8", "9", "22"] [x.bar] -inner = ["106", "107", "108"] \ No newline at end of file +inner = ["106", "107", "108"] diff --git a/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/src/main.nr b/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/src/main.nr index 15a2747eaa9d..14f110a23a0b 100644 --- a/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/src/main.nr @@ -15,6 +15,6 @@ fn main(mut x: [Foo; 3], y: pub Field, hash_result: pub [u8; 32]) { // Make sure that we are passing a dynamic array to the black box function call // by setting the array using a dynamic index here hash_input[y - 1] = 0; - let hash = std::hash::sha256(hash_input); + let hash = std::hash::blake3(hash_input); assert_eq(hash, hash_result); } diff --git a/noir/noir-repo/test_programs/execution_success/bit_and/src/main.nr b/noir/noir-repo/test_programs/execution_success/bit_and/src/main.nr index 5a0aa17e3ede..2e2ce62def7c 100644 --- a/noir/noir-repo/test_programs/execution_success/bit_and/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/bit_and/src/main.nr @@ -13,4 +13,3 @@ fn main(x: Field, y: Field, a: Field, b: Field) { let b_as_u8 = b as u8; assert((a_as_u8 & b_as_u8) == a_as_u8); } - diff --git a/noir/noir-repo/test_programs/execution_success/bool_not/src/main.nr b/noir/noir-repo/test_programs/execution_success/bool_not/src/main.nr index 935d8cc074de..d848225b3f26 100644 --- a/noir/noir-repo/test_programs/execution_success/bool_not/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/bool_not/src/main.nr @@ -1,4 +1,3 @@ fn main(x: u1) { assert(!x == 0); } - diff --git a/noir/noir-repo/test_programs/execution_success/bool_or/src/main.nr b/noir/noir-repo/test_programs/execution_success/bool_or/src/main.nr index 6cb959e61e64..87bbb757fbcd 100644 --- a/noir/noir-repo/test_programs/execution_success/bool_or/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/bool_or/src/main.nr @@ -3,4 +3,3 @@ fn main(x: u1, y: u1) { assert(x | y | x == 1); } - diff --git a/noir/noir-repo/test_programs/execution_success/brillig_calls_conditionals/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_calls_conditionals/src/main.nr index e8bce638ebe8..8d0e78bd6235 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_calls_conditionals/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_calls_conditionals/src/main.nr @@ -36,4 +36,3 @@ unconstrained fn entry_point(x: u32) -> u32 { result } - diff --git a/noir/noir-repo/test_programs/execution_success/brillig_cow_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_cow_regression/src/main.nr index 69273bc3dcac..4b70f2961b66 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_cow_regression/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_cow_regression/src/main.nr @@ -172,6 +172,6 @@ unconstrained fn main(kernel_data: DataToHash) -> pub [Field; NUM_FIELDS_PER_SHA } } - let sha_digest = std::hash::sha256(hash_input_flattened); - U256::from_bytes32(sha_digest).to_u128_limbs() + let blake3_digest = std::hash::blake3(hash_input_flattened); + U256::from_bytes32(blake3_digest).to_u128_limbs() } diff --git a/noir/noir-repo/test_programs/execution_success/brillig_nested_arrays/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_nested_arrays/src/main.nr index 3f5e5a0f8eb5..7835bca55a75 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_nested_arrays/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_nested_arrays/src/main.nr @@ -41,4 +41,3 @@ fn main(x: Field, y: Field) { create_and_assert_inside_brillig(x, y); } } - diff --git a/noir/noir-repo/test_programs/execution_success/brillig_pedersen/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_pedersen/src/main.nr index 55f81882bd4d..e4d2d2d0e784 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_pedersen/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_pedersen/src/main.nr @@ -24,4 +24,3 @@ unconstrained fn main( let hash = std::hash::pedersen_commitment_with_separator([state], 0); assert(std::hash::pedersen_commitment_with_separator([43], 0).x == hash.x); } - diff --git a/noir/noir-repo/test_programs/execution_success/cast_bool/src/main.nr b/noir/noir-repo/test_programs/execution_success/cast_bool/src/main.nr index 422d3b98f83f..f6742fb62f3f 100644 --- a/noir/noir-repo/test_programs/execution_success/cast_bool/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/cast_bool/src/main.nr @@ -3,4 +3,3 @@ fn main(x: Field, y: Field) { let t = z as u8; assert(t == 1); } - diff --git a/noir/noir-repo/test_programs/execution_success/conditional_1/Prover.toml b/noir/noir-repo/test_programs/execution_success/conditional_1/Prover.toml index baad8be126a5..b06d750fda0b 100644 --- a/noir/noir-repo/test_programs/execution_success/conditional_1/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/conditional_1/Prover.toml @@ -3,7 +3,7 @@ a=0 x = [104, 101, 108, 108, 111] result = [ - 0x2c, + 234, 0xf2, 0x4d, 0xba, diff --git a/noir/noir-repo/test_programs/execution_success/conditional_1/src/main.nr b/noir/noir-repo/test_programs/execution_success/conditional_1/src/main.nr index eedb8a697d1d..e0292c733bd1 100644 --- a/noir/noir-repo/test_programs/execution_success/conditional_1/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/conditional_1/src/main.nr @@ -53,7 +53,7 @@ fn main(a: u32, mut c: [u32; 4], x: [u8; 5], result: pub [u8; 32]) { let mut y = 0; if a == 0 { - let digest = std::hash::sha256(x); + let digest = std::hash::blake3(x); y = digest[0]; } else { y = 5; diff --git a/noir/noir-repo/test_programs/execution_success/conditional_regression_661/src/main.nr b/noir/noir-repo/test_programs/execution_success/conditional_regression_661/src/main.nr index 1036acc6da4b..a8459515634b 100644 --- a/noir/noir-repo/test_programs/execution_success/conditional_regression_661/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/conditional_regression_661/src/main.nr @@ -25,4 +25,3 @@ fn issue_661_bar(a: [u32; 4]) -> [u32; 4] { b[0] = a[0] + 1; b } - diff --git a/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/Prover.toml b/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/Prover.toml index baad8be126a5..5f098ae39d81 100644 --- a/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/Prover.toml @@ -2,37 +2,4 @@ c=[2, 4, 3, 0, ] a=0 x = [104, 101, 108, 108, 111] -result = [ - 0x2c, - 0xf2, - 0x4d, - 0xba, - 0x5f, - 0xb0, - 0xa3, - 0x0e, - 0x26, - 0xe8, - 0x3b, - 0x2a, - 0xc5, - 0xb9, - 0xe2, - 0x9e, - 0x1b, - 0x16, - 0x1e, - 0x5c, - 0x1f, - 0xa7, - 0x42, - 0x5e, - 0x73, - 0x04, - 0x33, - 0x62, - 0x93, - 0x8b, - 0x98, - 0x24, -] +result = [234, 143, 22, 61, 179, 134, 130, 146, 94, 68, 145, 197, 229, 141, 75, 179, 80, 110, 248, 193, 78, 183, 138, 134, 233, 8, 197, 98, 74, 103, 32, 15] diff --git a/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/src/main.nr b/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/src/main.nr index de5ad20a6423..9312655c8f36 100644 --- a/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/src/main.nr @@ -24,9 +24,9 @@ fn bar(x: Field) { } fn call_intrinsic(x: [u8; 5], result: [u8; 32]) { - let mut digest = std::hash::sha256(x); + let mut digest = std::hash::blake3(x); digest[0] = 5 as u8; - digest = std::hash::sha256(x); + digest = std::hash::blake3(x); assert(digest == result); } 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 ecc7794cf9ea..02c2a7d06832 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 @@ -9,4 +9,3 @@ fn main(mut x: u32, y: call_data(0) u32, z: call_data(0) [u32; 4]) -> return_dat unconstrained fn foo(x: u32) -> u32 { x + 1 } - diff --git a/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/Prover.toml b/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/Prover.toml index 412c7b36e4c2..e78fc19cb71b 100644 --- a/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/Prover.toml @@ -33,46 +33,6 @@ hashed_message = [ 0xc1, 0xe2, ] -message = [ - 0x49, - 0x6e, - 0x73, - 0x74, - 0x72, - 0x75, - 0x63, - 0x74, - 0x69, - 0x6f, - 0x6e, - 0x73, - 0x20, - 0x75, - 0x6e, - 0x63, - 0x6c, - 0x65, - 0x61, - 0x72, - 0x2c, - 0x20, - 0x61, - 0x73, - 0x6b, - 0x20, - 0x61, - 0x67, - 0x61, - 0x69, - 0x6e, - 0x20, - 0x6c, - 0x61, - 0x74, - 0x65, - 0x72, - 0x2e, -] pub_key_x = [ 0xa0, 0x43, diff --git a/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/src/main.nr b/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/src/main.nr index 00d420089fc9..1c94bf8961e5 100644 --- a/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/src/main.nr @@ -1,14 +1,4 @@ -fn main( - message: [u8; 38], - hashed_message: [u8; 32], - pub_key_x: [u8; 32], - pub_key_y: [u8; 32], - signature: [u8; 64], -) { - // Hash the message, since secp256k1 expects a hashed_message - let expected = std::hash::sha256(message); - assert(hashed_message == expected); - +fn main(hashed_message: [u8; 32], pub_key_x: [u8; 32], pub_key_y: [u8; 32], signature: [u8; 64]) { let valid_signature = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); assert(valid_signature); diff --git a/noir/noir-repo/test_programs/execution_success/fold_complex_outputs/src/main.nr b/noir/noir-repo/test_programs/execution_success/fold_complex_outputs/src/main.nr index b433c8b416cc..b83a88c4a7d6 100644 --- a/noir/noir-repo/test_programs/execution_success/fold_complex_outputs/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/fold_complex_outputs/src/main.nr @@ -69,4 +69,3 @@ fn map_fields(mut input: ParentStruct) -> ParentStruct { input } - diff --git a/noir/noir-repo/test_programs/execution_success/global_var_entry_point_used_in_another_entry/src/main.nr b/noir/noir-repo/test_programs/execution_success/global_var_entry_point_used_in_another_entry/src/main.nr index 364c27cc4ab7..d57416772717 100644 --- a/noir/noir-repo/test_programs/execution_success/global_var_entry_point_used_in_another_entry/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/global_var_entry_point_used_in_another_entry/src/main.nr @@ -41,4 +41,3 @@ unconstrained fn entry_point_one_diff_global(x: Field, y: Field) { let z = THREE + x + y; assert(z == 4); } - diff --git a/noir/noir-repo/test_programs/execution_success/if_else_chain/src/main.nr b/noir/noir-repo/test_programs/execution_success/if_else_chain/src/main.nr index 2705d5b31110..2c80acd273cd 100644 --- a/noir/noir-repo/test_programs/execution_success/if_else_chain/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/if_else_chain/src/main.nr @@ -12,4 +12,3 @@ fn main(a: u32, mut c: [u32; 4]) { assert(c[0] == 10); } } - diff --git a/noir/noir-repo/test_programs/execution_success/keccak256/Prover.toml b/noir/noir-repo/test_programs/execution_success/keccak256/Prover.toml deleted file mode 100644 index d65c4011d3f8..000000000000 --- a/noir/noir-repo/test_programs/execution_success/keccak256/Prover.toml +++ /dev/null @@ -1,35 +0,0 @@ -x = 0xbd -result = [ - 0x5a, - 0x50, - 0x2f, - 0x9f, - 0xca, - 0x46, - 0x7b, - 0x26, - 0x6d, - 0x5b, - 0x78, - 0x33, - 0x65, - 0x19, - 0x37, - 0xe8, - 0x05, - 0x27, - 0x0c, - 0xa3, - 0xf3, - 0xaf, - 0x1c, - 0x0d, - 0xd2, - 0x46, - 0x2d, - 0xca, - 0x4b, - 0x3b, - 0x1a, - 0xbf, -] diff --git a/noir/noir-repo/test_programs/execution_success/keccak256/src/main.nr b/noir/noir-repo/test_programs/execution_success/keccak256/src/main.nr deleted file mode 100644 index 1e13fa028b79..000000000000 --- a/noir/noir-repo/test_programs/execution_success/keccak256/src/main.nr +++ /dev/null @@ -1,20 +0,0 @@ -// docs:start:keccak256 -fn main(x: Field, result: [u8; 32]) { - // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field - // The padding is taken care of by the program - let digest = std::hash::keccak256([x as u8], 1); - assert(digest == result); - - //#1399: variable message size - let message_size = 4; - let hash_a = std::hash::keccak256([1, 2, 3, 4], message_size); - let hash_b = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); - - assert(hash_a == hash_b); - - let message_size_big = 8; - let hash_c = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); - - assert(hash_a != hash_c); -} -// docs:end:keccak256 diff --git a/noir/noir-repo/test_programs/execution_success/nested_array_dynamic/src/main.nr b/noir/noir-repo/test_programs/execution_success/nested_array_dynamic/src/main.nr index 2c53822d6b94..4ac6b4124ae5 100644 --- a/noir/noir-repo/test_programs/execution_success/nested_array_dynamic/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/nested_array_dynamic/src/main.nr @@ -73,4 +73,3 @@ fn main(mut x: [Foo; 4], y: pub Field) { foo_parents[y - 2].foos[y - 3].b = foo_parents[y - 2].foos[y - 2].b; assert(foo_parents[1].foos[0].b == [20, 19, 5000]); } - diff --git a/noir/noir-repo/test_programs/execution_success/nested_arrays_from_brillig/src/main.nr b/noir/noir-repo/test_programs/execution_success/nested_arrays_from_brillig/src/main.nr index 387454b9e21e..66021005ce3a 100644 --- a/noir/noir-repo/test_programs/execution_success/nested_arrays_from_brillig/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/nested_arrays_from_brillig/src/main.nr @@ -24,4 +24,3 @@ fn main(values: [Field; 6]) { let notes = unsafe { create_inside_brillig(values) }; assert(access_nested(notes) == (2 + 4 + 3 + 1)); } - diff --git a/noir/noir-repo/test_programs/execution_success/pedersen_check/src/main.nr b/noir/noir-repo/test_programs/execution_success/pedersen_check/src/main.nr index c71b2b570da4..00afa4b8e276 100644 --- a/noir/noir-repo/test_programs/execution_success/pedersen_check/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/pedersen_check/src/main.nr @@ -17,4 +17,3 @@ fn main(x: Field, y: Field, salt: Field, out_x: Field, out_y: Field, out_hash: F let hash = std::hash::pedersen_commitment([state]); assert(std::hash::pedersen_commitment([43]).x == hash.x); } - diff --git a/noir/noir-repo/test_programs/execution_success/pedersen_commitment/src/main.nr b/noir/noir-repo/test_programs/execution_success/pedersen_commitment/src/main.nr index c2225edcdf11..e9a7ea4816f1 100644 --- a/noir/noir-repo/test_programs/execution_success/pedersen_commitment/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/pedersen_commitment/src/main.nr @@ -5,4 +5,3 @@ fn main(x: Field, y: Field, expected_commitment: std::embedded_curve_ops::Embedd assert_eq(commitment.y, expected_commitment.y); } // docs:end:pedersen-commitment - diff --git a/noir/noir-repo/test_programs/execution_success/pedersen_hash/src/main.nr b/noir/noir-repo/test_programs/execution_success/pedersen_hash/src/main.nr index de981d44bca5..c55e947930b4 100644 --- a/noir/noir-repo/test_programs/execution_success/pedersen_hash/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/pedersen_hash/src/main.nr @@ -4,4 +4,3 @@ fn main(x: Field, y: Field, expected_hash: Field) { assert_eq(hash, expected_hash); } // docs:end:pedersen-hash - diff --git a/noir/noir-repo/test_programs/execution_success/ram_blowup_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/ram_blowup_regression/src/main.nr index 6deb54dd21d7..5f63bf03e558 100644 --- a/noir/noir-repo/test_programs/execution_success/ram_blowup_regression/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/ram_blowup_regression/src/main.nr @@ -20,9 +20,9 @@ pub fn field_from_bytes_32_trunc(bytes32: [u8; 32]) -> Field { low + high * v } -pub fn sha256_to_field(bytes_to_hash: [u8; N]) -> Field { - let sha256_hashed = std::hash::sha256(bytes_to_hash); - let hash_in_a_field = field_from_bytes_32_trunc(sha256_hashed); +pub fn blake3_to_field(bytes_to_hash: [u8; N]) -> Field { + let blake3_hashed = std::hash::blake3(bytes_to_hash); + let hash_in_a_field = field_from_bytes_32_trunc(blake3_hashed); hash_in_a_field } @@ -36,6 +36,6 @@ fn main(tx_effects_hash_input: [Field; TX_EFFECTS_HASH_INPUT_FIELDS]) -> pub Fie } } - let sha_digest = sha256_to_field(hash_input_flattened); - sha_digest + let blake3_digest = blake3_to_field(hash_input_flattened); + blake3_digest } diff --git a/noir/noir-repo/test_programs/execution_success/reference_only_used_as_alias/src/main.nr b/noir/noir-repo/test_programs/execution_success/reference_only_used_as_alias/src/main.nr index c8dd38d9aca4..cf84d1030205 100644 --- a/noir/noir-repo/test_programs/execution_success/reference_only_used_as_alias/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/reference_only_used_as_alias/src/main.nr @@ -90,4 +90,3 @@ fn main(input: [Field; 4], randomness: Field, context_input: u32) { let event0 = ExampleEvent0 { value0: input[0], value1: input[1] }; event0.emit(encode_event_with_randomness(&mut context, randomness)); } - diff --git a/noir/noir-repo/test_programs/execution_success/regression_4449/Prover.toml b/noir/noir-repo/test_programs/execution_success/regression_4449/Prover.toml index 81af476bcc98..12b95c0dbfa1 100644 --- a/noir/noir-repo/test_programs/execution_success/regression_4449/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/regression_4449/Prover.toml @@ -1,3 +1,3 @@ x = 0xbd -result = [204, 59, 83, 197, 18, 1, 128, 43, 247, 28, 104, 225, 106, 13, 20, 187, 42, 26, 67, 150, 48, 75, 238, 168, 121, 247, 142, 160, 71, 222, 97, 188] \ No newline at end of file +result = [3, 128, 126, 121, 21, 242, 202, 74, 58, 183, 180, 171, 169, 186, 245, 81, 206, 26, 69, 29, 25, 207, 152, 152, 52, 33, 40, 106, 200, 237, 90, 156] diff --git a/noir/noir-repo/test_programs/execution_success/regression_4449/src/main.nr b/noir/noir-repo/test_programs/execution_success/regression_4449/src/main.nr index 3fda39bd8741..88b80dabc7f5 100644 --- a/noir/noir-repo/test_programs/execution_success/regression_4449/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/regression_4449/src/main.nr @@ -5,7 +5,7 @@ fn main(x: u8, result: [u8; 32]) { for i in 0..70 { let y = x + i; let a = [y, x, 32, 0, y + 1, y - 1, y - 2, 5]; - digest = std::sha256::digest(a); + digest = std::hash::blake3(a); } assert(digest == result); diff --git a/noir/noir-repo/test_programs/execution_success/sha256/Nargo.toml b/noir/noir-repo/test_programs/execution_success/regression_7195/Nargo.toml similarity index 50% rename from noir/noir-repo/test_programs/execution_success/sha256/Nargo.toml rename to noir/noir-repo/test_programs/execution_success/regression_7195/Nargo.toml index 255d2156ef67..d9ef0fa7388d 100644 --- a/noir/noir-repo/test_programs/execution_success/sha256/Nargo.toml +++ b/noir/noir-repo/test_programs/execution_success/regression_7195/Nargo.toml @@ -1,6 +1,6 @@ [package] -name = "sha256" +name = "regression_7195" type = "bin" authors = [""] -[dependencies] +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/regression_7195/Prover.toml b/noir/noir-repo/test_programs/execution_success/regression_7195/Prover.toml new file mode 100644 index 000000000000..8c12ebba6cf7 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/regression_7195/Prover.toml @@ -0,0 +1,2 @@ +x = "1" +y = "2" diff --git a/noir/noir-repo/test_programs/execution_success/regression_7195/src/main.nr b/noir/noir-repo/test_programs/execution_success/regression_7195/src/main.nr new file mode 100644 index 000000000000..004b388d40da --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/regression_7195/src/main.nr @@ -0,0 +1,21 @@ +fn bar(y: Field) { + assert(y != 0); +} + +fn foo(x: Field) { + // Safety: test + let y = unsafe { baz(x) }; + bar(y); +} + +unconstrained fn baz(x: Field) -> Field { + x +} + +fn main(x: Field, y: pub Field) { + // Safety: test + let x = unsafe { baz(x) }; + foo(x); + foo(y); + assert(x != y); +} diff --git a/noir/noir-repo/test_programs/execution_success/sha2_byte/Nargo.toml b/noir/noir-repo/test_programs/execution_success/regression_7451/Nargo.toml similarity index 68% rename from noir/noir-repo/test_programs/execution_success/sha2_byte/Nargo.toml rename to noir/noir-repo/test_programs/execution_success/regression_7451/Nargo.toml index efd691fce585..92740ca82f3e 100644 --- a/noir/noir-repo/test_programs/execution_success/sha2_byte/Nargo.toml +++ b/noir/noir-repo/test_programs/execution_success/regression_7451/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "sha2_byte" +name = "regression_7451" type = "bin" authors = [""] diff --git a/noir/noir-repo/test_programs/execution_success/regression_7451/Prover.toml b/noir/noir-repo/test_programs/execution_success/regression_7451/Prover.toml new file mode 100644 index 000000000000..7b70754b0690 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/regression_7451/Prover.toml @@ -0,0 +1 @@ +x="1" diff --git a/noir/noir-repo/test_programs/execution_success/regression_7451/src/main.nr b/noir/noir-repo/test_programs/execution_success/regression_7451/src/main.nr new file mode 100644 index 000000000000..c8d990a9c82e --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/regression_7451/src/main.nr @@ -0,0 +1,12 @@ +fn main(x: u8) { + // Safety: test code + let predicate = unsafe { return_true() }; + let tmp: u8 = if predicate { 0xff } else { 0 }; + + let y = tmp & x; + assert_eq(y, 1); +} + +unconstrained fn return_true() -> bool { + true +} diff --git a/noir/noir-repo/test_programs/execution_success/sha256/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256/Prover.toml deleted file mode 100644 index b4bf41623709..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256/Prover.toml +++ /dev/null @@ -1,38 +0,0 @@ - -x = 0xbd -result = [ - 0x68, - 0x32, - 0x57, - 0x20, - 0xaa, - 0xbd, - 0x7c, - 0x82, - 0xf3, - 0x0f, - 0x55, - 0x4b, - 0x31, - 0x3d, - 0x05, - 0x70, - 0xc9, - 0x5a, - 0xcc, - 0xbb, - 0x7d, - 0xc4, - 0xb5, - 0xaa, - 0xe1, - 0x12, - 0x04, - 0xc0, - 0x8f, - 0xfe, - 0x73, - 0x2b, -] -input = [0, 0] -toggle = false \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256/src/main.nr deleted file mode 100644 index 8e5e46b9837e..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256/src/main.nr +++ /dev/null @@ -1,27 +0,0 @@ -// Sha256 example -// -// Calls Sha256 from the standard library. -// -// The Compiler sees this special function and creates an ACIR gate -// -// The ACIR SHA256 gate is passed to PLONK who should -// know how to create the necessary constraints. -// -// Not yet here: For R1CS, it is more about manipulating arithmetic gates to get performance -// This can be done in ACIR! -fn main(x: Field, result: [u8; 32], input: [u8; 2], toggle: bool) { - // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field - // The padding is taken care of by the program - // docs:start:sha256_var - let digest = std::hash::sha256_var([x as u8], 1); - // docs:end:sha256_var - assert(digest == result); - - let digest = std::hash::sha256([x as u8]); - assert(digest == result); - - // variable size - let size: Field = 1 + toggle as Field; - let var_sha = std::hash::sha256_var(input, size as u64); - assert(var_sha == std::hash::sha256_var(input, 1)); -} diff --git a/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Nargo.toml deleted file mode 100644 index f7076311e1dc..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "sha256_brillig_performance_regression" -type = "bin" -authors = [""] -compiler_version = ">=0.33.0" - -[dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Prover.toml deleted file mode 100644 index 5bb7f3542573..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Prover.toml +++ /dev/null @@ -1,16 +0,0 @@ -input_amount = "1" -minimum_output_amount = "2" -secret_hash_for_L1_to_l2_message = "3" -uniswap_fee_tier = "4" - -[aztec_recipient] -inner = "5" - -[caller_on_L1] -inner = "6" - -[input_asset_bridge_portal_address] -inner = "7" - -[output_asset_bridge_portal_address] -inner = "8" diff --git a/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/src/main.nr deleted file mode 100644 index 42cc6d4ff3bc..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/src/main.nr +++ /dev/null @@ -1,104 +0,0 @@ -// Performance regression extracted from an aztec protocol contract. - -unconstrained fn main( - input_asset_bridge_portal_address: EthAddress, - input_amount: Field, - uniswap_fee_tier: Field, - output_asset_bridge_portal_address: EthAddress, - minimum_output_amount: Field, - aztec_recipient: AztecAddress, - secret_hash_for_L1_to_l2_message: Field, - caller_on_L1: EthAddress, -) -> pub Field { - let mut hash_bytes = [0; 260]; // 8 fields of 32 bytes each + 4 bytes fn selector - let input_token_portal_bytes: [u8; 32] = - input_asset_bridge_portal_address.to_field().to_be_bytes(); - let in_amount_bytes: [u8; 32] = input_amount.to_be_bytes(); - let uniswap_fee_tier_bytes: [u8; 32] = uniswap_fee_tier.to_be_bytes(); - let output_token_portal_bytes: [u8; 32] = - output_asset_bridge_portal_address.to_field().to_be_bytes(); - let amount_out_min_bytes: [u8; 32] = minimum_output_amount.to_be_bytes(); - let aztec_recipient_bytes: [u8; 32] = aztec_recipient.to_field().to_be_bytes(); - let secret_hash_for_L1_to_l2_message_bytes: [u8; 32] = - secret_hash_for_L1_to_l2_message.to_be_bytes(); - let caller_on_L1_bytes: [u8; 32] = caller_on_L1.to_field().to_be_bytes(); - - // The purpose of including the following selector is to make the message unique to that specific call. Note that - // it has nothing to do with calling the function. - let selector = comptime { - std::hash::keccak256( - "swap_public(address,uint256,uint24,address,uint256,bytes32,bytes32,address)".as_bytes(), - 75, - ) - }; - - hash_bytes[0] = selector[0]; - hash_bytes[1] = selector[1]; - hash_bytes[2] = selector[2]; - hash_bytes[3] = selector[3]; - - for i in 0..32 { - hash_bytes[i + 4] = input_token_portal_bytes[i]; - hash_bytes[i + 36] = in_amount_bytes[i]; - hash_bytes[i + 68] = uniswap_fee_tier_bytes[i]; - hash_bytes[i + 100] = output_token_portal_bytes[i]; - hash_bytes[i + 132] = amount_out_min_bytes[i]; - hash_bytes[i + 164] = aztec_recipient_bytes[i]; - hash_bytes[i + 196] = secret_hash_for_L1_to_l2_message_bytes[i]; - hash_bytes[i + 228] = caller_on_L1_bytes[i]; - } - - let content_hash = sha256_to_field(hash_bytes); - content_hash -} - -// Convert a 32 byte array to a field element by truncating the final byte -pub fn field_from_bytes_32_trunc(bytes32: [u8; 32]) -> Field { - // Convert it to a field element - let mut v = 1; - let mut high = 0 as Field; - let mut low = 0 as Field; - - for i in 0..15 { - // covers bytes 16..30 (31 is truncated and ignored) - low = low + (bytes32[15 + 15 - i] as Field) * v; - v = v * 256; - // covers bytes 0..14 - high = high + (bytes32[14 - i] as Field) * v; - } - // covers byte 15 - low = low + (bytes32[15] as Field) * v; - - low + high * v -} - -pub fn sha256_to_field(bytes_to_hash: [u8; N]) -> Field { - let sha256_hashed = std::hash::sha256(bytes_to_hash); - let hash_in_a_field = field_from_bytes_32_trunc(sha256_hashed); - - hash_in_a_field -} - -pub trait ToField { - fn to_field(self) -> Field; -} - -pub struct EthAddress { - inner: Field, -} - -impl ToField for EthAddress { - fn to_field(self) -> Field { - self.inner - } -} - -pub struct AztecAddress { - pub inner: Field, -} - -impl ToField for AztecAddress { - fn to_field(self) -> Field { - self.inner - } -} diff --git a/noir/noir-repo/test_programs/execution_success/sha256_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/sha256_regression/Nargo.toml deleted file mode 100644 index ce98d000bcb7..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_regression/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "sha256_regression" -type = "bin" -authors = [""] -compiler_version = ">=0.33.0" - -[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256_regression/Prover.toml deleted file mode 100644 index ea0a0f2e8a71..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_regression/Prover.toml +++ /dev/null @@ -1,14 +0,0 @@ -msg_just_over_block = [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116] -msg_multiple_of_block = [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 115, 45, 97, 115, 99, 105, 105, 13, 10, 109, 105, 109, 101, 45, 118, 101, 114, 115, 105, 111, 110, 58, 49, 46, 48, 32, 40, 77, 97, 99, 32, 79, 83, 32, 88, 32, 77, 97, 105, 108, 32, 49, 54, 46, 48, 32, 92, 40, 51, 55, 51, 49, 46, 53, 48, 48, 46, 50, 51, 49, 92, 41, 41, 13, 10, 115, 117, 98, 106, 101, 99, 116, 58, 72, 101, 108, 108, 111, 13, 10, 109, 101, 115, 115, 97, 103, 101, 45, 105, 100, 58, 60, 56, 70, 56, 49, 57, 68, 51, 50, 45, 66, 54, 65, 67, 45, 52, 56, 57, 68, 45, 57, 55, 55, 70, 45, 52, 51, 56, 66, 66, 67, 52, 67, 65, 66, 50, 55, 64, 109, 101, 46, 99, 111, 109, 62, 13, 10, 100, 97, 116, 101, 58, 83, 97, 116, 44, 32, 50, 54, 32, 65, 117, 103, 32, 50, 48, 50, 51, 32, 49, 50, 58, 50, 53, 58, 50, 50, 32, 43, 48, 52, 48, 48, 13, 10, 116, 111, 58, 122, 107, 101, 119, 116, 101, 115, 116, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 13, 10, 100, 107, 105, 109, 45, 115, 105, 103, 110, 97, 116, 117, 114, 101, 58, 118, 61, 49, 59, 32, 97, 61, 114, 115, 97, 45, 115, 104, 97, 50, 53, 54, 59, 32, 99, 61, 114, 101, 108, 97, 120, 101, 100, 47, 114, 101, 108, 97, 120, 101, 100, 59, 32, 100, 61, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 59, 32, 115, 61, 49, 97, 49, 104, 97, 105, 59, 32, 116, 61, 49, 54, 57, 51, 48, 51, 56, 51, 51, 55, 59, 32, 98, 104, 61, 55, 120, 81, 77, 68, 117, 111, 86, 86, 85, 52, 109, 48, 87, 48, 87, 82, 86, 83, 114, 86, 88, 77, 101, 71, 83, 73, 65, 83, 115, 110, 117, 99, 75, 57, 100, 74, 115, 114, 99, 43, 118, 85, 61, 59, 32, 104, 61, 102, 114, 111, 109, 58, 67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 77, 105, 109, 101, 45, 86, 101, 114, 115, 105, 111, 110, 58, 83, 117, 98, 106, 101, 99] -msg_just_under_block = [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59] -msg_big_not_block_multiple = [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 115, 45, 97, 115, 99, 105, 105, 13, 10, 109, 105, 109, 101, 45, 118, 101, 114, 115, 105, 111, 110, 58, 49, 46, 48, 32, 40, 77, 97, 99, 32, 79, 83, 32, 88, 32, 77, 97, 105, 108, 32, 49, 54, 46, 48, 32, 92, 40, 51, 55, 51, 49, 46, 53, 48, 48, 46, 50, 51, 49, 92, 41, 41, 13, 10, 115, 117, 98, 106, 101, 99, 116, 58, 72, 101, 108, 108, 111, 13, 10, 109, 101, 115, 115, 97, 103, 101, 45, 105, 100, 58, 60, 56, 70, 56, 49, 57, 68, 51, 50, 45, 66, 54, 65, 67, 45, 52, 56, 57, 68, 45, 57, 55, 55, 70, 45, 52, 51, 56, 66, 66, 67, 52, 67, 65, 66, 50, 55, 64, 109, 101, 46, 99, 111, 109, 62, 13, 10, 100, 97, 116, 101, 58, 83, 97, 116, 44, 32, 50, 54, 32, 65, 117, 103, 32, 50, 48, 50, 51, 32, 49, 50, 58, 50, 53, 58, 50, 50, 32, 43, 48, 52, 48, 48, 13, 10, 116, 111, 58, 122, 107, 101, 119, 116, 101, 115, 116, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 13, 10, 100, 107, 105, 109, 45, 115, 105, 103, 110, 97, 116, 117, 114, 101, 58, 118, 61, 49, 59, 32, 97, 61, 114, 115, 97, 45, 115, 104, 97, 50, 53, 54, 59, 32, 99, 61, 114, 101, 108, 97, 120, 101, 100, 47, 114, 101, 108, 97, 120, 101, 100, 59, 32, 100, 61, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 59, 32, 115, 61, 49, 97, 49, 104, 97, 105, 59, 32, 116, 61, 49, 54, 57, 51, 48, 51, 56, 51, 51, 55, 59, 32, 98, 104, 61, 55, 120, 81, 77, 68, 117, 111, 86, 86, 85, 52, 109, 48, 87, 48, 87, 82, 86, 83, 114, 86, 88, 77, 101, 71, 83, 73, 65, 83, 115, 110, 117, 99, 75, 57, 100, 74, 115, 114, 99, 43, 118, 85, 61, 59, 32, 104, 61, 102, 114, 111, 109, 58, 67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 77, 105, 109, 101, 45, 86, 101, 114, 115, 105, 111, 110, 58, 83, 117, 98, 106, 101, 99, 116, 58, 77, 101, 115, 115, 97, 103, 101, 45, 73, 100, 58, 68, 97, 116, 101, 58, 116, 111, 59, 32, 98, 61] -msg_big_with_padding = [48, 130, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 48, 130, 1, 17, 48, 37, 2, 1, 1, 4, 32, 176, 223, 31, 133, 108, 84, 158, 102, 70, 11, 165, 175, 196, 12, 201, 130, 25, 131, 46, 125, 156, 194, 28, 23, 55, 133, 157, 164, 135, 136, 220, 78, 48, 37, 2, 1, 2, 4, 32, 190, 82, 180, 235, 222, 33, 79, 50, 152, 136, 142, 35, 116, 224, 6, 242, 156, 141, 128, 248, 10, 61, 98, 86, 248, 45, 207, 210, 90, 232, 175, 38, 48, 37, 2, 1, 3, 4, 32, 0, 194, 104, 108, 237, 246, 97, 230, 116, 198, 69, 110, 26, 87, 17, 89, 110, 199, 108, 250, 36, 21, 39, 87, 110, 102, 250, 213, 174, 131, 171, 174, 48, 37, 2, 1, 11, 4, 32, 136, 155, 87, 144, 111, 15, 152, 127, 85, 25, 154, 81, 20, 58, 51, 75, 193, 116, 234, 0, 60, 30, 29, 30, 183, 141, 72, 247, 255, 203, 100, 124, 48, 37, 2, 1, 12, 4, 32, 41, 234, 106, 78, 31, 11, 114, 137, 237, 17, 92, 71, 134, 47, 62, 78, 189, 233, 201, 214, 53, 4, 47, 189, 201, 133, 6, 121, 34, 131, 64, 142, 48, 37, 2, 1, 13, 4, 32, 91, 222, 210, 193, 62, 222, 104, 82, 36, 41, 138, 253, 70, 15, 148, 208, 156, 45, 105, 171, 241, 195, 185, 43, 217, 162, 146, 201, 222, 89, 238, 38, 48, 37, 2, 1, 14, 4, 32, 76, 123, 216, 13, 51, 227, 72, 245, 59, 193, 238, 166, 103, 49, 23, 164, 171, 188, 194, 197, 156, 187, 249, 28, 198, 95, 69, 15, 182, 56, 54, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] -msg_big_no_padding = [48, 130, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 48, 130, 1, 17, 48, 37, 2, 1, 1, 4, 32, 176, 223, 31, 133, 108, 84, 158, 102, 70, 11, 165, 175, 196, 12, 201, 130, 25, 131, 46, 125, 156, 194, 28, 23, 55, 133, 157, 164, 135, 136, 220, 78, 48, 37, 2, 1, 2, 4, 32, 190, 82, 180, 235, 222, 33, 79, 50, 152, 136, 142, 35, 116, 224, 6, 242, 156, 141, 128, 248, 10, 61, 98, 86, 248, 45, 207, 210, 90, 232, 175, 38, 48, 37, 2, 1, 3, 4, 32, 0, 194, 104, 108, 237, 246, 97, 230, 116, 198, 69, 110, 26, 87, 17, 89, 110, 199, 108, 250, 36, 21, 39, 87, 110, 102, 250, 213, 174, 131, 171, 174, 48, 37, 2, 1, 11, 4, 32, 136, 155, 87, 144, 111, 15, 152, 127, 85, 25, 154, 81, 20, 58, 51, 75, 193, 116, 234, 0, 60, 30, 29, 30, 183, 141, 72, 247, 255, 203, 100, 124, 48, 37, 2, 1, 12, 4, 32, 41, 234, 106, 78, 31, 11, 114, 137, 237, 17, 92, 71, 134, 47, 62, 78, 189, 233, 201, 214, 53, 4, 47, 189, 201, 133, 6, 121, 34, 131, 64, 142, 48, 37, 2, 1, 13, 4, 32, 91, 222, 210, 193, 62, 222, 104, 82, 36, 41, 138, 253, 70, 15, 148, 208, 156, 45, 105, 171, 241, 195, 185, 43, 217, 162, 146, 201, 222, 89, 238, 38, 48, 37, 2, 1, 14, 4, 32, 76, 123, 216, 13, 51, 227, 72, 245, 59, 193, 238, 166, 103, 49, 23, 164, 171, 188, 194, 197, 156, 187, 249, 28, 198, 95, 69, 15, 182, 56, 54, 38] -message_size = 297 - -# Results matched against ethers library -result_just_over_block = [91, 122, 146, 93, 52, 109, 133, 148, 171, 61, 156, 70, 189, 238, 153, 7, 222, 184, 94, 24, 65, 114, 192, 244, 207, 199, 87, 232, 192, 224, 171, 207] -result_multiple_of_block = [116, 90, 151, 31, 78, 22, 138, 180, 211, 189, 69, 76, 227, 200, 155, 29, 59, 123, 154, 60, 47, 153, 203, 129, 157, 251, 48, 2, 79, 11, 65, 47] -result_just_under_block = [143, 140, 76, 173, 222, 123, 102, 68, 70, 149, 207, 43, 39, 61, 34, 79, 216, 252, 213, 165, 74, 16, 110, 74, 29, 64, 138, 167, 30, 1, 9, 119] -result_big = [112, 144, 73, 182, 208, 98, 9, 238, 54, 229, 61, 145, 222, 17, 72, 62, 148, 222, 186, 55, 192, 82, 220, 35, 66, 47, 193, 200, 22, 38, 26, 186] -result_big_with_padding = [32, 85, 108, 174, 127, 112, 178, 182, 8, 43, 134, 123, 192, 211, 131, 66, 184, 240, 212, 181, 240, 180, 106, 195, 24, 117, 54, 129, 19, 10, 250, 53] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256_regression/src/main.nr deleted file mode 100644 index dbbcc07e501e..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_regression/src/main.nr +++ /dev/null @@ -1,39 +0,0 @@ -// A bunch of different test cases for sha256_var in the stdlib -fn main( - msg_just_over_block: [u8; 68], - result_just_over_block: pub [u8; 32], - msg_multiple_of_block: [u8; 448], - result_multiple_of_block: pub [u8; 32], - // We want to make sure we are testing a message with a size >= 57 but < 64 - msg_just_under_block: [u8; 60], - result_just_under_block: pub [u8; 32], - msg_big_not_block_multiple: [u8; 472], - result_big: pub [u8; 32], - // This message is only 297 elements and we want to hash only a variable amount - msg_big_with_padding: [u8; 700], - // This is the same as `msg_big_with_padding` but with no padding - msg_big_no_padding: [u8; 297], - message_size: u64, - result_big_with_padding: pub [u8; 32], -) { - let hash = std::hash::sha256_var(msg_just_over_block, msg_just_over_block.len() as u64); - assert_eq(hash, result_just_over_block); - - let hash = std::hash::sha256_var(msg_multiple_of_block, msg_multiple_of_block.len() as u64); - assert_eq(hash, result_multiple_of_block); - - let hash = std::hash::sha256_var(msg_just_under_block, msg_just_under_block.len() as u64); - assert_eq(hash, result_just_under_block); - - let hash = std::hash::sha256_var( - msg_big_not_block_multiple, - msg_big_not_block_multiple.len() as u64, - ); - assert_eq(hash, result_big); - - let hash_padding = std::hash::sha256_var(msg_big_with_padding, message_size); - assert_eq(hash_padding, result_big_with_padding); - - let hash_no_padding = std::hash::sha256_var(msg_big_no_padding, message_size); - assert_eq(hash_no_padding, result_big_with_padding); -} diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Nargo.toml deleted file mode 100644 index a80677c585d7..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "sha256_var_padding_regression" -type = "bin" -authors = [""] -compiler_version = ">=0.34.0" - -[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Prover.toml deleted file mode 100644 index 7b20e8701281..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -preimage = [29, 81, 165, 84, 243, 114, 101, 37, 242, 146, 127, 99, 69, 145, 39, 72, 213, 39, 253, 179, 218, 37, 217, 201, 172, 93, 198, 50, 249, 70, 15, 30, 162, 112, 187, 40, 140, 9, 236, 53, 32, 44, 38, 163, 113, 254, 192, 197, 44, 89, 71, 130, 169, 242, 17, 211, 214, 72, 19, 178, 186, 168, 147, 127, 99, 101, 252, 227, 8, 147, 150, 85, 97, 158, 17, 107, 218, 244, 82, 113, 247, 91, 208, 214, 60, 244, 87, 137, 173, 201, 130, 18, 66, 56, 198, 149, 207, 189, 175, 120, 123, 224, 177, 167, 251, 159, 143, 110, 68, 183, 189, 70, 126, 32, 35, 164, 44, 30, 44, 12, 65, 18, 62, 239, 242, 2, 248, 104, 2, 178, 64, 28, 126, 36, 137, 24, 14, 116, 91, 98, 90, 159, 218, 102, 45, 11, 110, 223, 245, 184, 52, 99, 59, 245, 136, 175, 3, 72, 164, 146, 145, 116, 22, 66, 24, 49, 193, 121, 3, 60, 37, 41, 97, 3, 190, 66, 195, 225, 63, 46, 3, 118, 4, 208, 15, 1, 40, 254, 235, 151, 123, 70, 180, 170, 44, 172, 90, 4, 254, 53, 239, 116, 246, 67, 56, 129, 61, 22, 169, 213, 65, 27, 216, 116, 162, 239, 214, 207, 126, 177, 20, 100, 25, 48, 143, 84, 215, 70, 197, 53, 65, 70, 86, 172, 61, 62, 9, 212, 167, 169, 133, 41, 126, 213, 196, 33, 192, 238, 0, 63, 246, 215, 58, 128, 110, 101, 92, 3, 170, 214, 130, 149, 52, 81, 125, 118, 233, 3, 118, 193, 104, 207, 120, 115, 77, 253, 191, 122, 0, 107, 164, 207, 113, 81, 169, 36, 201, 228, 74, 134, 131, 218, 178, 35, 30, 216, 101, 2, 103, 174, 87, 95, 50, 50, 215, 157, 5, 210, 188, 54, 211, 78, 45, 199, 96, 121, 241, 241, 176, 226, 194, 134, 130, 89, 217, 210, 186, 32, 140, 39, 91, 103, 212, 26, 87, 32, 72, 144, 228, 230, 117, 99, 188, 50, 15, 69, 79, 179, 50, 12, 106, 86, 218, 101, 73, 142, 243, 29, 250, 122, 228, 233, 29, 255, 22, 121, 114, 125, 103, 41, 250, 241, 179, 126, 158, 198, 116, 209, 65, 94, 98, 228, 175, 169, 96, 3, 9, 233, 133, 214, 55, 161, 164, 103, 80, 85, 24, 186, 64, 167, 92, 131, 53, 101, 202, 47, 25, 104, 118, 155, 14, 12, 12, 25, 116, 45, 221, 249, 28, 246, 212, 200, 157, 167, 169, 56, 197, 181, 4, 245, 146, 1, 140, 234, 191, 212, 228, 125, 87, 81, 86, 119, 30, 63, 129, 143, 32, 96] -result = [205, 74, 73, 134, 202, 93, 199, 152, 171, 244, 133, 193, 132, 40, 42, 9, 248, 11, 99, 200, 135, 58, 220, 227, 45, 253, 183, 241, 69, 69, 80, 219] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/src/main.nr deleted file mode 100644 index 13f87a0efc51..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/src/main.nr +++ /dev/null @@ -1,29 +0,0 @@ -// Test to check sha256_var produces same results irrespective of number of padding bytes after message.length -// Ref: https://github.com/noir-lang/noir/issues/6163, https://gist.github.com/jp4g/d5953faae9eadb2909357474f7901e58 -fn main(preimage: [u8; 448], result: [u8; 32]) { - // Construct arrays of different lengths - let mut preimage_511 = [0; 511]; - let mut preimage_512 = [0; 512]; // Next block - let mut preimage_575 = [0; 575]; - let mut preimage_576 = [0; 576]; // Next block - for i in 0..preimage.len() { - preimage_511[i] = preimage[i]; - preimage_512[i] = preimage[i]; - preimage_575[i] = preimage[i]; - preimage_576[i] = preimage[i]; - } - let fixed_length_hash = std::hash::sha256::digest(preimage); - let var_full_length_hash = std::hash::sha256::sha256_var(preimage, preimage.len() as u64); - let var_length_hash_511 = std::hash::sha256::sha256_var(preimage_511, preimage.len() as u64); - let var_length_hash_512 = std::hash::sha256::sha256_var(preimage_512, preimage.len() as u64); - let var_length_hash_575 = std::hash::sha256::sha256_var(preimage_575, preimage.len() as u64); - let var_length_hash_576 = std::hash::sha256::sha256_var(preimage_576, preimage.len() as u64); - - // All of the above should have produced the same hash (result) - assert(fixed_length_hash == result); - assert(var_full_length_hash == result); - assert(var_length_hash_511 == result); - assert(var_length_hash_512 == result); - assert(var_length_hash_575 == result); - assert(var_length_hash_576 == result); -} diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Nargo.toml deleted file mode 100644 index 3e141ee5d5f6..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "sha256_var_size_regression" -type = "bin" -authors = [""] -compiler_version = ">=0.33.0" - -[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Prover.toml deleted file mode 100644 index df632a428584..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Prover.toml +++ /dev/null @@ -1,3 +0,0 @@ -enable = [true, false] -foo = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] -toggle = false diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/src/main.nr deleted file mode 100644 index 4278cdda8a30..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/src/main.nr +++ /dev/null @@ -1,17 +0,0 @@ -global NUM_HASHES: u32 = 2; - -fn main(foo: [u8; 95], toggle: bool, enable: [bool; NUM_HASHES]) { - let mut result = [[0; 32]; NUM_HASHES]; - let mut const_result = [[0; 32]; NUM_HASHES]; - let size: Field = 93 + toggle as Field * 2; - for i in 0..NUM_HASHES { - if enable[i] { - result[i] = std::sha256::sha256_var(foo, size as u64); - const_result[i] = std::sha256::sha256_var(foo, 93); - } - } - - for i in 0..NUM_HASHES { - assert_eq(result[i], const_result[i]); - } -} diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Nargo.toml deleted file mode 100644 index e8f3e6bbe643..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "sha256_var_witness_const_regression" -type = "bin" -authors = [""] -compiler_version = ">=0.33.0" - -[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Prover.toml deleted file mode 100644 index 7b91051c1a0c..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -input = [0, 0] -toggle = false \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/src/main.nr deleted file mode 100644 index 97c4435d41d2..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/src/main.nr +++ /dev/null @@ -1,9 +0,0 @@ -fn main(input: [u8; 2], toggle: bool) { - let size: Field = 1 + toggle as Field; - assert(!toggle); - - let variable_sha = std::sha256::sha256_var(input, size as u64); - let constant_sha = std::sha256::sha256_var(input, 1); - - assert_eq(variable_sha, constant_sha); -} diff --git a/noir/noir-repo/test_programs/execution_success/sha2_byte/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha2_byte/Prover.toml deleted file mode 100644 index 2f82f14a58d4..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha2_byte/Prover.toml +++ /dev/null @@ -1,5 +0,0 @@ -# Values obtainable from https://emn178.github.io/online-tools/sha256.html and https://emn178.github.io/online-tools/sha512.html -x = 0xbd -result256 = [0x68, 0x32, 0x57, 0x20, 0xaa, 0xbd, 0x7c, 0x82, 0xf3, 0x0f, 0x55, 0x4b, 0x31, 0x3d, 0x05, 0x70, 0xc9, 0x5a, 0xcc, 0xbb, 0x7d, 0xc4, 0xb5, 0xaa, 0xe1, 0x12, 0x04, 0xc0, 0x8f, 0xfe, 0x73, 0x2b] -result512 = [0x29, 0x6e, 0x22, 0x67, 0xd7, 0x4c, 0x27, 0x8d, 0xaa, 0xaa, 0x94, 0x0d, 0x17, 0xb0, 0xcf, 0xb7, 0x4a, 0x50, 0x83, 0xf8, 0xe0, 0x69, 0x72, 0x6d, 0x8c, 0x84, 0x1c, 0xbe, 0x59, 0x6e, 0x04, 0x31, 0xcb, 0x77, 0x41, 0xa5, 0xb5, 0x0f, 0x71, 0x66, 0x6c, 0xfd, 0x54, 0xba, 0xcb, 0x7b, 0x00, 0xae, 0xa8, 0x91, 0x49, 0x9c, 0xf4, 0xef, 0x6a, 0x03, 0xc8, 0xa8, 0x3f, 0xe3, 0x7c, 0x3f, 0x7b, 0xaf] - diff --git a/noir/noir-repo/test_programs/execution_success/sha2_byte/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha2_byte/src/main.nr deleted file mode 100644 index a1663642c69b..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha2_byte/src/main.nr +++ /dev/null @@ -1,8 +0,0 @@ -// Test Noir implementations of SHA256 and SHA512 on a one-byte message. -fn main(x: Field, result256: [u8; 32], result512: [u8; 64]) { - let digest256 = std::hash::sha256([x as u8]); - assert(digest256 == result256); - - let digest512 = std::hash::sha512::digest([x as u8]); - assert(digest512 == result512); -} diff --git a/noir/noir-repo/test_programs/execution_success/keccak256/Nargo.toml b/noir/noir-repo/test_programs/execution_success/shift_right_overflow/Nargo.toml similarity index 63% rename from noir/noir-repo/test_programs/execution_success/keccak256/Nargo.toml rename to noir/noir-repo/test_programs/execution_success/shift_right_overflow/Nargo.toml index 7e48c3b342cb..a883299fbc26 100644 --- a/noir/noir-repo/test_programs/execution_success/keccak256/Nargo.toml +++ b/noir/noir-repo/test_programs/execution_success/shift_right_overflow/Nargo.toml @@ -1,6 +1,5 @@ [package] -name = "keccak256" +name = "shift_right_overflow" type = "bin" authors = [""] - [dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/shift_right_overflow/Prover.toml b/noir/noir-repo/test_programs/execution_success/shift_right_overflow/Prover.toml new file mode 100644 index 000000000000..57cb8b2eac81 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/shift_right_overflow/Prover.toml @@ -0,0 +1 @@ +x = 9 diff --git a/noir/noir-repo/test_programs/execution_success/shift_right_overflow/src/main.nr b/noir/noir-repo/test_programs/execution_success/shift_right_overflow/src/main.nr new file mode 100644 index 000000000000..c5d23ab5cd97 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/shift_right_overflow/src/main.nr @@ -0,0 +1,5 @@ +fn main(x: u8) { + // This would previously overflow in ACIR. Now it returns zero. + let value = 1 >> x; + assert_eq(value, 0); +} diff --git a/noir/noir-repo/test_programs/execution_success/slice_dynamic_index/src/main.nr b/noir/noir-repo/test_programs/execution_success/slice_dynamic_index/src/main.nr index 2ab633eb98c0..c6fd64d58059 100644 --- a/noir/noir-repo/test_programs/execution_success/slice_dynamic_index/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/slice_dynamic_index/src/main.nr @@ -305,4 +305,3 @@ fn dynamic_slice_merge_push_then_pop(mut slice: [Field], x: Field, y: Field) { let (_, elem) = slice.pop_back(); assert(elem == y); } - 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 index 67901f10f299..ba20fdd61f1f 100644 --- 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 @@ -805,4 +805,3 @@ fn main() { // 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/struct_fields_ordering/src/main.nr b/noir/noir-repo/test_programs/execution_success/struct_fields_ordering/src/main.nr index 1a2e2d462e2d..fe26ac31424a 100644 --- a/noir/noir-repo/test_programs/execution_success/struct_fields_ordering/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/struct_fields_ordering/src/main.nr @@ -9,4 +9,3 @@ fn main(y: pub myStruct) { assert(y.foo == 5); assert(y.bar == 7); } - diff --git a/noir/noir-repo/test_programs/execution_success/submodules/src/main.nr b/noir/noir-repo/test_programs/execution_success/submodules/src/main.nr index f937af746274..b58e64b4990c 100644 --- a/noir/noir-repo/test_programs/execution_success/submodules/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/submodules/src/main.nr @@ -12,4 +12,3 @@ mod my_submodule { pub fn my_helper() {} } - diff --git a/noir/noir-repo/test_programs/execution_success/u128/Nargo.toml b/noir/noir-repo/test_programs/execution_success/u128/Nargo.toml deleted file mode 100644 index c1dcd84db046..000000000000 --- a/noir/noir-repo/test_programs/execution_success/u128/Nargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "u128" -type = "bin" -authors = [""] - -[dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/u128/Prover.toml b/noir/noir-repo/test_programs/execution_success/u128/Prover.toml deleted file mode 100644 index 961db9825a70..000000000000 --- a/noir/noir-repo/test_programs/execution_success/u128/Prover.toml +++ /dev/null @@ -1,7 +0,0 @@ -x = "3" -y = "4" -z = "7" -hexa ="0x1f03a" -[big_int] -lo = 1 -hi = 2 \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/u128/src/main.nr b/noir/noir-repo/test_programs/execution_success/u128/src/main.nr deleted file mode 100644 index 4ea934378143..000000000000 --- a/noir/noir-repo/test_programs/execution_success/u128/src/main.nr +++ /dev/null @@ -1,42 +0,0 @@ -fn main(mut x: u32, y: u32, z: u32, big_int: U128, hexa: str<7>) { - let a = U128::from_u64s_le(x as u64, x as u64); - let b = U128::from_u64s_le(y as u64, x as u64); - let c = a + b; - assert(c.lo == z as Field); - assert(c.hi == 2 * x as Field); - assert(U128::from_hex(hexa).lo == 0x1f03a); - let t1 = U128::from_hex("0x9d9c7a87771f03a23783f9d9c7a8777"); - let t2 = U128::from_hex("0x45a26c708BFCF39041"); - let t = t1 + t2; - assert(t.lo == 0xc5e4b029996e17b8); - assert(t.hi == 0x09d9c7a87771f07f); - let t3 = U128::from_le_bytes(t.to_le_bytes()); - assert(t == t3); - - let t4 = t - t2; - assert(t4 == t1); - - let t5 = U128::from_u64s_le(0, 1); - let t6 = U128::from_u64s_le(1, 0); - assert((t5 - t6).hi == 0); - - assert( - (U128::from_hex("0x71f03a23783f9d9c7a8777") * U128::from_hex("0x8BFCF39041")).hi - == U128::from_hex("0x3e4e0471b873470e247c824e61445537").hi, - ); - let q = U128::from_hex("0x3e4e0471b873470e247c824e61445537") / U128::from_hex("0x8BFCF39041"); - assert(q == U128::from_hex("0x71f03a23783f9d9c7a8777")); - - assert(big_int.hi == 2); - - let mut small_int = U128::from_integer(x); - assert(small_int.lo == x as Field); - assert(x == small_int.to_integer()); - let shift = small_int << (x as u8); - assert(shift == U128::from_integer(x << (x as u8))); - assert(shift >> (x as u8) == small_int); - assert(shift >> 127 == U128::from_integer(0)); - assert(shift << 127 == U128::from_integer(0)); - assert(U128::from_integer(3).to_integer() == 3); -} - diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256/Nargo.toml b/noir/noir-repo/test_programs/execution_success/u128_type/Nargo.toml similarity index 52% rename from noir/noir-repo/test_programs/benchmarks/bench_sha256/Nargo.toml rename to noir/noir-repo/test_programs/execution_success/u128_type/Nargo.toml index 488b94ca8586..f86639a9e745 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256/Nargo.toml +++ b/noir/noir-repo/test_programs/execution_success/u128_type/Nargo.toml @@ -1,7 +1,7 @@ [package] -name = "bench_sha256" -version = "0.1.0" +name = "u128_type" type = "bin" authors = [""] +compiler_version = ">=0.23.0" [dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/u128_type/Prover.toml b/noir/noir-repo/test_programs/execution_success/u128_type/Prover.toml new file mode 100644 index 000000000000..75da077f2e6d --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/u128_type/Prover.toml @@ -0,0 +1,3 @@ +x = 12345 +y = 2345678 +z = 2 diff --git a/noir/noir-repo/test_programs/execution_success/u128_type/src/main.nr b/noir/noir-repo/test_programs/execution_success/u128_type/src/main.nr new file mode 100644 index 000000000000..acfa3d28eec2 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/u128_type/src/main.nr @@ -0,0 +1,22 @@ +fn main(x: u128, y: u128, z: u8) { + let const_x = 12345; + let const_y = 2345678; + let const_z = 2; + + assert_eq(x + y, const_x + const_y); + assert_eq(y - x, const_y - const_x); + assert_eq(x * y, const_x * const_y); + assert_eq(y / x, const_y / const_x); + assert_eq(y % x, const_y % const_x); + assert_eq(!x, !const_x); + assert_eq(x ^ y, const_x ^ const_y); + assert_eq(x & y, const_x & const_y); + assert_eq(x | y, const_x | const_y); + assert_eq(x >> z, const_x >> const_z); + assert_eq(x << z, const_x << const_z); + assert_eq(x < y, const_x < const_y); + assert_eq(x <= y, const_x <= const_y); + assert_eq(x != y, const_x != const_y); + assert_eq(y > x, const_y > const_x); + assert_eq(y >= x, const_y >= const_x); +} diff --git a/noir/noir-repo/test_programs/execution_success/wrapping_operations/src/main.nr b/noir/noir-repo/test_programs/execution_success/wrapping_operations/src/main.nr index d8345884c828..0c4be84a7659 100644 --- a/noir/noir-repo/test_programs/execution_success/wrapping_operations/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/wrapping_operations/src/main.nr @@ -3,4 +3,3 @@ fn main(x: u8, y: u8) { assert(std::wrapping_add(y, 1) == x); assert(std::wrapping_mul(y, y) == 1); } - diff --git a/noir/noir-repo/test_programs/memory_report.sh b/noir/noir-repo/test_programs/memory_report.sh index eb83004affdb..ff64449fcf5e 100755 --- a/noir/noir-repo/test_programs/memory_report.sh +++ b/noir/noir-repo/test_programs/memory_report.sh @@ -8,7 +8,7 @@ PARSE_MEMORY=$(realpath "$(dirname "$0")/parse_memory.sh") # Tests to be profiled for memory report -tests_to_profile=("keccak256" "workspace" "regression_4709" "ram_blowup_regression" "global_var_regression_entry_points") +tests_to_profile=("workspace" "regression_4709" "ram_blowup_regression" "global_var_regression_entry_points") current_dir=$(pwd) base_path="$current_dir/execution_success" diff --git a/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr index fa2c55e69347..cfd985a55eaa 100644 --- a/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr @@ -269,4 +269,3 @@ fn test_vec_any_not_default() { vec.extend_from_array([2, 4]); assert(!vec.any(|v| v == default_value)); } - diff --git a/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr index 019f757591ee..de6fd15f068e 100644 --- a/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr @@ -20,4 +20,3 @@ unconstrained fn test_overflow_mul() { let b: u8 = 2; assert_eq(a * b, 0); } - diff --git a/noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/src/main.nr index 446692c485bb..3b1f2963b9f9 100644 --- a/noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/src/main.nr @@ -117,13 +117,16 @@ fn test_ecdsa_secp256r1() { /// Test that sha256_compression is implemented. #[test] -fn test_sha256() { +fn test_sha256_compression() { + let input: [u32; 16] = [0xbd; 16]; + let state: [u32; 8] = [0; 8]; + let hash = comptime { - let input: [u8; 1] = [0xbd]; - std::hash::sha256(input) + let input: [u32; 16] = [0xbd; 16]; + let state: [u32; 8] = [0; 8]; + std::hash::sha256_compression(input, state) }; - assert_eq(hash[0], 0x68); - assert_eq(hash[31], 0x2b); + assert_eq(hash, std::hash::sha256_compression(input, state)); } /// Test that `embedded_curve_add` and `multi_scalar_mul` are implemented. diff --git a/noir/noir-repo/test_programs/noir_test_success/global_eval/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/global_eval/src/main.nr index 87a2d50a9162..da962bf52032 100644 --- a/noir/noir-repo/test_programs/noir_test_success/global_eval/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/global_eval/src/main.nr @@ -1,20 +1,9 @@ -use std::uint128::U128; - // These definitions require `to_be_bits` and `to_le_bits` to be supported at comptime. global BITS_BE_13: [u1; 4] = (13 as Field).to_be_bits(); global BITS_LE_13: [u1; 4] = (13 as Field).to_le_bits(); -// Examples from #6691 which use the above behind the scenes. -global POW64_A: Field = 2.pow_32(64); -global POW64_B: Field = (U128::one() << 64).to_integer(); - #[test] fn test_be_and_le_bits() { assert_eq(BITS_BE_13, [1, 1, 0, 1]); assert_eq(BITS_LE_13, [1, 0, 1, 1]); } - -#[test] -fn test_pow64() { - assert_eq(POW64_A, POW64_B); -} diff --git a/noir/noir-repo/test_programs/noir_test_success/mock_oracle/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/mock_oracle/src/main.nr index bdffd62bb809..3bde9a0b3ef8 100644 --- a/noir/noir-repo/test_programs/noir_test_success/mock_oracle/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/mock_oracle/src/main.nr @@ -151,4 +151,3 @@ fn test_mock_struct_field() { assert_eq(20, struct_field(point, another_array)); } } - diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Nargo.toml b/noir/noir-repo/test_programs/noir_test_success/u128_type/Nargo.toml similarity index 52% rename from noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Nargo.toml rename to noir/noir-repo/test_programs/noir_test_success/u128_type/Nargo.toml index ae66d7ed5a60..f86639a9e745 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Nargo.toml +++ b/noir/noir-repo/test_programs/noir_test_success/u128_type/Nargo.toml @@ -1,7 +1,7 @@ [package] -name = "bench_sha256_long" -version = "0.1.0" +name = "u128_type" type = "bin" authors = [""] +compiler_version = ">=0.23.0" [dependencies] diff --git a/noir/noir-repo/test_programs/noir_test_success/u128_type/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/u128_type/src/main.nr new file mode 100644 index 000000000000..c0bca7b3d7aa --- /dev/null +++ b/noir/noir-repo/test_programs/noir_test_success/u128_type/src/main.nr @@ -0,0 +1,49 @@ +#[test] +fn test_u128_sub() { + let a: u128 = 671967750576550571863734675757137222; + let b = 671967750576550571863734675757137221; + let c = a - 1; + assert(c == b); +} + +#[test] +fn test_u128_add() { + let a: u128 = 671967750576550571863734675757137222; + let b: u128 = 671967750576550571863734675757137221; + let c: u128 = b + 1; + assert(c == a); +} + +#[test] +fn test_u128_mul() { + let a: u128 = 671967750576550571863734675757137222; + let b: u128 = 335983875288275285931867337878568611; + let c: u128 = b * 2; + assert(c == a); +} + +#[test] +fn test_u128_div() { + let a: u128 = 671967750576550571863734675757137222; + let b: u128 = 335983875288275285931867337878568611; + let c = a / 2; + assert(c == b); +} + +#[test] +fn test_u128_not() { + let a: u128 = 1; + let b = !a; + assert_eq(b, 340282366920938463463374607431768211454); + + let a: u128 = 340282366920938463463374607431768211454; + let b = !a; + assert_eq(b, 1); +} + +#[test] +fn test_max_u128() { + let a: u128 = 340282366920938463463374607431768211455; + let c = 170141183460469231731687303715884105727; + assert_eq(a / 2, c); +} diff --git a/noir/noir-repo/tooling/acvm_cli/src/cli/execute_cmd.rs b/noir/noir-repo/tooling/acvm_cli/src/cli/execute_cmd.rs index 6f6fba7cd1e7..c1d4533230e7 100644 --- a/noir/noir-repo/tooling/acvm_cli/src/cli/execute_cmd.rs +++ b/noir/noir-repo/tooling/acvm_cli/src/cli/execute_cmd.rs @@ -1,14 +1,14 @@ use std::io::{self, Write}; use std::path::PathBuf; +use acir::FieldElement; use acir::circuit::Program; use acir::native_types::{WitnessMap, WitnessStack}; -use acir::FieldElement; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; use nargo::PrintOutput; -use nargo::{foreign_calls::DefaultForeignCallBuilder, ops::execute_program}; +use nargo::foreign_calls::DefaultForeignCallBuilder; use noir_artifact_cli::errors::CliError; use noir_artifact_cli::fs::artifact::read_bytecode_from_file; use noir_artifact_cli::fs::witness::save_witness_to_dir; @@ -56,7 +56,7 @@ fn run_command(args: ExecuteCommand) -> Result { )?; if args.output_witness.is_some() { save_witness_to_dir( - output_witness, + &output_witness, &args.output_witness.unwrap(), &args.working_directory, )?; @@ -80,7 +80,8 @@ pub(crate) fn execute_program_from_witness( ) -> Result, CliError> { let program: Program = Program::deserialize_program(bytecode).map_err(CliError::CircuitDeserializationError)?; - execute_program( + + nargo::ops::execute_program( &program, inputs_map, &Bn254BlackBoxSolver(pedantic_solving), diff --git a/noir/noir-repo/tooling/acvm_cli/src/fs/witness.rs b/noir/noir-repo/tooling/acvm_cli/src/fs/witness.rs index 5c2d2a9ad058..d1b88758cc74 100644 --- a/noir/noir-repo/tooling/acvm_cli/src/fs/witness.rs +++ b/noir/noir-repo/tooling/acvm_cli/src/fs/witness.rs @@ -1,8 +1,8 @@ //! These witness functions are only used by the ACVM CLI and we'll most likely deprecate them. use std::{collections::BTreeMap, path::Path}; -use acir::{native_types::Witness, FieldElement}; -use acvm::acir::{native_types::WitnessMap, AcirField}; +use acir::{FieldElement, native_types::Witness}; +use acvm::acir::{AcirField, native_types::WitnessMap}; use noir_artifact_cli::errors::{CliError, FilesystemError}; diff --git a/noir/noir-repo/tooling/acvm_cli/src/main.rs b/noir/noir-repo/tooling/acvm_cli/src/main.rs index a30360a947ce..8f9edc48f32c 100644 --- a/noir/noir-repo/tooling/acvm_cli/src/main.rs +++ b/noir/noir-repo/tooling/acvm_cli/src/main.rs @@ -8,7 +8,7 @@ mod cli; use std::env; use tracing_appender::rolling; -use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter}; +use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan}; mod fs; diff --git a/noir/noir-repo/tooling/artifact_cli/src/bin/execute.rs b/noir/noir-repo/tooling/artifact_cli/src/bin/execute.rs index b66177a3d572..8bca901e4852 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/bin/execute.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/bin/execute.rs @@ -1,10 +1,10 @@ #![forbid(unsafe_code)] #![warn(unreachable_pub)] -use clap::{command, Parser, Subcommand}; +use clap::{Parser, Subcommand, command}; use color_eyre::eyre; use const_format::formatcp; -use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter}; +use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan}; use noir_artifact_cli::commands::execute_cmd; diff --git a/noir/noir-repo/tooling/artifact_cli/src/commands/execute_cmd.rs b/noir/noir-repo/tooling/artifact_cli/src/commands/execute_cmd.rs index 9a117329c151..df5d469e75b4 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/commands/execute_cmd.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/commands/execute_cmd.rs @@ -1,18 +1,15 @@ -use std::{collections::BTreeMap, path::PathBuf}; +use std::path::PathBuf; -use acir::{circuit::Program, native_types::WitnessStack, FieldElement}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; -use color_eyre::eyre::{self, bail}; use crate::{ - errors::CliError, - fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}, Artifact, + errors::CliError, + execution::{self, ExecutionResults}, }; -use nargo::{foreign_calls::DefaultForeignCallBuilder, NargoError, PrintOutput}; -use noirc_abi::{input_parser::InputValue, Abi}; -use noirc_artifacts::debug::DebugArtifact; +use nargo::{PrintOutput, foreign_calls::DefaultForeignCallBuilder}; +use noirc_driver::CompiledProgram; use super::parse_and_normalize_path; @@ -21,106 +18,84 @@ use super::parse_and_normalize_path; pub struct ExecuteCommand { /// Path to the JSON build artifact (either a program or a contract). #[clap(long, short, value_parser = parse_and_normalize_path)] - artifact_path: PathBuf, + pub artifact_path: PathBuf, /// Path to the Prover.toml file which contains the inputs and the /// optional return value in ABI format. #[clap(long, short, value_parser = parse_and_normalize_path)] - prover_file: PathBuf, + pub prover_file: PathBuf, /// Path to the directory where the output witness should be saved. /// If empty then the results are discarded. #[clap(long, short, value_parser = parse_and_normalize_path)] - output_dir: Option, + pub output_dir: Option, /// Write the execution witness to named file /// /// Defaults to the name of the circuit being executed. #[clap(long, short)] - witness_name: Option, + pub witness_name: Option, /// Name of the function to execute, if the artifact is a contract. #[clap(long)] - contract_fn: Option, + pub contract_fn: Option, /// JSON RPC url to solve oracle calls. #[clap(long)] - oracle_resolver: Option, + pub oracle_resolver: Option, /// Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. #[clap(long, default_value_t = false)] - pedantic_solving: bool, + pub pedantic_solving: bool, } -pub fn run(args: ExecuteCommand) -> eyre::Result<()> { +pub fn run(args: ExecuteCommand) -> Result<(), CliError> { let artifact = Artifact::read_from_file(&args.artifact_path)?; + let artifact_name = args.artifact_path.file_stem().and_then(|s| s.to_str()).unwrap_or_default(); - let circuit = match artifact { - Artifact::Program(program) => Circuit { - name: None, - abi: program.abi, - bytecode: program.bytecode, - debug_symbols: program.debug_symbols, - file_map: program.file_map, - }, + let (circuit, circuit_name): (CompiledProgram, String) = match artifact { + Artifact::Program(program) => (program.into(), artifact_name.to_string()), Artifact::Contract(contract) => { - let names = - contract.functions.iter().map(|f| f.name.clone()).collect::>().join(","); + let names = || contract.functions.iter().map(|f| f.name.clone()).collect::>(); let Some(ref name) = args.contract_fn else { - bail!("--contract-fn missing; options: [{names}]"); + return Err(CliError::MissingContractFn { names: names() }); }; - let Some(function) = contract.functions.into_iter().find(|f| f.name == *name) else { - bail!("unknown --contract-fn '{name}'; options: [{names}]"); + let Some(program) = contract.function_as_compiled_program(name) else { + return Err(CliError::UnknownContractFn { name: name.clone(), names: names() }); }; - Circuit { - name: Some(name.clone()), - abi: function.abi, - bytecode: function.bytecode, - debug_symbols: function.debug_symbols, - file_map: contract.file_map, - } + (program, format!("{artifact_name}::{name}")) } }; match execute(&circuit, &args) { - Ok(solved) => { - save_witness(circuit, args, solved)?; - } - Err(CliError::CircuitExecutionError(err)) => { - show_diagnostic(circuit, err); + Ok(results) => { + execution::save_and_check_witness( + &circuit, + results, + &circuit_name, + args.output_dir.as_deref(), + args.witness_name.as_deref(), + )?; } Err(e) => { - bail!("failed to execute the circuit: {e}"); + if let CliError::CircuitExecutionError(ref err) = e { + execution::show_diagnostic(&circuit, err); + } + // Still returning the error to facilitate command forwarding, to indicate that the command failed. + return Err(e); } } Ok(()) } -/// Parameters necessary to execute a circuit, display execution failures, etc. -struct Circuit { - name: Option, - abi: Abi, - bytecode: Program, - debug_symbols: noirc_errors::debug_info::ProgramDebugInfo, - file_map: BTreeMap, -} - -struct SolvedWitnesses { - expected_return: Option, - actual_return: Option, - witness_stack: WitnessStack, -} - /// Execute a circuit and return the output witnesses. -fn execute(circuit: &Circuit, args: &ExecuteCommand) -> Result { - let (input_map, expected_return) = read_inputs_from_file(&args.prover_file, &circuit.abi)?; - - let initial_witness = circuit.abi.encode(&input_map, None)?; - +fn execute(circuit: &CompiledProgram, args: &ExecuteCommand) -> Result { // TODO: Build a custom foreign call executor that reads from the Oracle transcript, - // and use it as a base for the default executor; see `DefaultForeignCallBuilder::build_with_base` + // and use it as a base for the default executor. Using it as the innermost rather + // than top layer so that any extra `print` added for debugging is handled by the + // default, rather than trying to match it to the transcript. let mut foreign_call_executor = DefaultForeignCallBuilder { output: PrintOutput::Stdout, enable_mocks: false, @@ -130,80 +105,7 @@ fn execute(circuit: &Circuit, args: &ExecuteCommand) -> Result) { - if let Some(diagnostic) = nargo::errors::try_to_diagnose_runtime_error( - &err, - &circuit.abi, - &circuit.debug_symbols.debug_infos, - ) { - let debug_artifact = DebugArtifact { - debug_symbols: circuit.debug_symbols.debug_infos, - file_map: circuit.file_map, - }; - diagnostic.report(&debug_artifact, false); - } -} - -/// Print information about the witness and compare to expectations, -/// returning errors if something isn't right. -fn save_witness( - circuit: Circuit, - args: ExecuteCommand, - solved: SolvedWitnesses, -) -> eyre::Result<()> { - let artifact = args.artifact_path.file_stem().and_then(|s| s.to_str()).unwrap_or_default(); - let name = circuit - .name - .as_ref() - .map(|name| format!("{artifact}.{name}")) - .unwrap_or_else(|| artifact.to_string()); - - println!("[{}] Circuit witness successfully solved", name); - - if let Some(ref witness_dir) = args.output_dir { - let witness_path = save_witness_to_dir( - solved.witness_stack, - &args.witness_name.unwrap_or_else(|| name.clone()), - witness_dir, - )?; - println!("[{}] Witness saved to {}", name, witness_path.display()); - } - - // Check that the circuit returned a non-empty result if the ABI expects a return value. - if let Some(ref expected) = circuit.abi.return_type { - if solved.actual_return.is_none() { - bail!("Missing return witness; expected a value of type {expected:?}"); - } - } - - // Check that if the prover file contained a `return` entry then that's what we got. - if let Some(expected) = solved.expected_return { - match solved.actual_return { - None => { - bail!("Missing return witness;\nexpected:\n{expected:?}"); - } - Some(actual) if actual != expected => { - bail!("Unexpected return witness;\nexpected:\n{expected:?}\ngot:\n{actual:?}"); - } - _ => {} - } - } - - Ok(()) + execution::execute(circuit, &blackbox_solver, &mut foreign_call_executor, &args.prover_file) } diff --git a/noir/noir-repo/tooling/artifact_cli/src/commands/mod.rs b/noir/noir-repo/tooling/artifact_cli/src/commands/mod.rs index 78f3b19292f2..9049b3695b7a 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/commands/mod.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/commands/mod.rs @@ -1,3 +1,4 @@ +//! This module is for commands that we might want to invoke from `nargo` as-is. use std::path::PathBuf; use color_eyre::eyre; diff --git a/noir/noir-repo/tooling/artifact_cli/src/errors.rs b/noir/noir-repo/tooling/artifact_cli/src/errors.rs index 5f302f78695a..6c87273cb370 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/errors.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/errors.rs @@ -1,6 +1,10 @@ use acir::FieldElement; use nargo::NargoError; -use noirc_abi::errors::{AbiError, InputParserError}; +use noirc_abi::{ + AbiReturnType, + errors::{AbiError, InputParserError}, + input_parser::InputValue, +}; use std::path::PathBuf; use thiserror::Error; @@ -61,4 +65,16 @@ pub enum CliError { #[error("Failed to serialize output witness: {0}")] OutputWitnessSerializationFailed(#[from] toml::ser::Error), + + #[error("Unexpected return value: expected {expected:?}; got {actual:?}")] + UnexpectedReturn { expected: InputValue, actual: Option }, + + #[error("Missing return witnesses; expected {expected:?}")] + MissingReturn { expected: AbiReturnType }, + + #[error("Missing contract function name; options: {names:?}")] + MissingContractFn { names: Vec }, + + #[error("Unknown contract function '{name}'; options: {names:?}")] + UnknownContractFn { name: String, names: Vec }, } diff --git a/noir/noir-repo/tooling/artifact_cli/src/execution.rs b/noir/noir-repo/tooling/artifact_cli/src/execution.rs new file mode 100644 index 000000000000..2e19ab551613 --- /dev/null +++ b/noir/noir-repo/tooling/artifact_cli/src/execution.rs @@ -0,0 +1,135 @@ +use std::path::Path; + +use acir::{FieldElement, native_types::WitnessStack}; +use acvm::BlackBoxFunctionSolver; +use nargo::{NargoError, foreign_calls::ForeignCallExecutor}; +use noirc_abi::input_parser::InputValue; +use noirc_artifacts::debug::DebugArtifact; +use noirc_driver::CompiledProgram; + +use crate::{ + errors::CliError, + fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}, +}; + +/// Results of a circuit execution. +#[derive(Clone, Debug)] +pub struct ExecutionResults { + pub witness_stack: WitnessStack, + pub return_values: ReturnValues, +} + +/// The decoded `return` witnesses. +#[derive(Clone, Debug)] +pub struct ReturnValues { + /// The `return` value from the `Prover.toml` file, if present. + pub expected_return: Option, + /// The `return` value from the circuit execution. + pub actual_return: Option, +} + +/// Execute a circuit and return the output witnesses. +pub fn execute( + circuit: &CompiledProgram, + blackbox_solver: &B, + foreign_call_executor: &mut E, + prover_file: &Path, +) -> Result +where + B: BlackBoxFunctionSolver, + E: ForeignCallExecutor, +{ + let (input_map, expected_return) = read_inputs_from_file(prover_file, &circuit.abi)?; + + let initial_witness = circuit.abi.encode(&input_map, None)?; + + let witness_stack = nargo::ops::execute_program( + &circuit.program, + initial_witness, + blackbox_solver, + foreign_call_executor, + )?; + + let main_witness = + &witness_stack.peek().expect("Should have at least one witness on the stack").witness; + + let (_, actual_return) = circuit.abi.decode(main_witness)?; + + Ok(ExecutionResults { + witness_stack, + return_values: ReturnValues { actual_return, expected_return }, + }) +} + +/// Print an error stack trace, if possible. +pub fn show_diagnostic(circuit: &CompiledProgram, err: &NargoError) { + if let Some(diagnostic) = + nargo::errors::try_to_diagnose_runtime_error(err, &circuit.abi, &circuit.debug) + { + let debug_artifact = DebugArtifact { + debug_symbols: circuit.debug.clone(), + file_map: circuit.file_map.clone(), + }; + + diagnostic.report(&debug_artifact, false); + } +} + +/// Print some information and save the witness if an output directory is specified, +/// then checks if the expected return values were the ones we expected. +pub fn save_and_check_witness( + circuit: &CompiledProgram, + results: ExecutionResults, + circuit_name: &str, + witness_dir: Option<&Path>, + witness_name: Option<&str>, +) -> Result<(), CliError> { + println!("[{}] Circuit witness successfully solved", circuit_name); + // Save first, so that we can potentially look at the output if the expectations fail. + if let Some(witness_dir) = witness_dir { + save_witness(&results.witness_stack, circuit_name, witness_dir, witness_name)?; + } + check_witness(circuit, results.return_values) +} + +/// Save the witness stack to a file. +pub fn save_witness( + witness_stack: &WitnessStack, + circuit_name: &str, + witness_dir: &Path, + witness_name: Option<&str>, +) -> Result<(), CliError> { + let witness_name = witness_name.unwrap_or(circuit_name); + let witness_path = save_witness_to_dir(witness_stack, witness_name, witness_dir)?; + println!("[{}] Witness saved to {}", circuit_name, witness_path.display()); + Ok(()) +} + +/// Compare return values to expectations, returning errors if something unexpected was returned. +pub fn check_witness( + circuit: &CompiledProgram, + return_values: ReturnValues, +) -> Result<(), CliError> { + // Check that the circuit returned a non-empty result if the ABI expects a return value. + if let Some(ref expected) = circuit.abi.return_type { + if return_values.actual_return.is_none() { + return Err(CliError::MissingReturn { expected: expected.clone() }); + } + } + + // Check that if the prover file contained a `return` entry then that's what we got. + if let Some(expected) = return_values.expected_return { + match return_values.actual_return { + None => { + return Err(CliError::UnexpectedReturn { expected, actual: None }); + } + Some(actual) => { + if actual != expected { + return Err(CliError::UnexpectedReturn { expected, actual: Some(actual) }); + } + } + } + } + + Ok(()) +} diff --git a/noir/noir-repo/tooling/artifact_cli/src/fs/artifact.rs b/noir/noir-repo/tooling/artifact_cli/src/fs/artifact.rs index 856fc472fc73..1a55764a9fed 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/fs/artifact.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/fs/artifact.rs @@ -1,16 +1,18 @@ -use std::path::Path; +use std::path::{Path, PathBuf}; use crate::{ - errors::{CliError, FilesystemError}, Artifact, + errors::{CliError, FilesystemError}, }; use noirc_artifacts::contract::ContractArtifact; use noirc_artifacts::program::ProgramArtifact; +use noirc_driver::CrateName; +use serde::de::Error; impl Artifact { /// Try to parse an artifact as a binary program or a contract pub fn read_from_file(path: &Path) -> Result { - let json = std::fs::read(path).map_err(FilesystemError::from)?; + let json = std::fs::read(path.with_extension("json")).map_err(FilesystemError::from)?; let as_program = || serde_json::from_slice::(&json).map(Artifact::Program); let as_contract = @@ -35,3 +37,55 @@ pub fn read_bytecode_from_file( .map_err(|e| FilesystemError::InvalidBytecodeFile(file_path, e.to_string()))?; Ok(bytecode) } + +/// Read a `ProgramArtifact`. Returns error if it turns out to be a `ContractArtifact`. +pub fn read_program_from_file(path: &Path) -> Result { + match Artifact::read_from_file(path)? { + Artifact::Program(program) => Ok(program), + Artifact::Contract(contract) => { + let msg = format!( + "expected a program artifact but found a contract in {}: {}", + path.display(), + contract.name + ); + Err(CliError::ArtifactDeserializationError(serde_json::Error::custom(msg))) + } + } +} + +pub fn save_program_to_file( + program_artifact: &ProgramArtifact, + crate_name: &CrateName, + output_dir: &Path, +) -> Result { + let circuit_name: String = crate_name.into(); + save_build_artifact_to_file(program_artifact, &circuit_name, output_dir) +} + +pub fn save_contract_to_file( + compiled_contract: &ContractArtifact, + circuit_name: &str, + output_dir: &Path, +) -> Result { + save_build_artifact_to_file(compiled_contract, circuit_name, output_dir) +} + +fn save_build_artifact_to_file( + build_artifact: &T, + artifact_name: &str, + output_dir: &Path, +) -> Result { + let artifact_path = output_dir.join(artifact_name).with_extension("json"); + let bytes = serde_json::to_vec(build_artifact)?; + write_to_file(&bytes, &artifact_path)?; + Ok(artifact_path) +} + +/// Create the parent directory if needed and write the bytes to a file. +pub fn write_to_file(bytes: &[u8], path: &Path) -> Result<(), FilesystemError> { + if let Some(dir) = path.parent() { + std::fs::create_dir_all(dir)?; + } + std::fs::write(path, bytes)?; + Ok(()) +} diff --git a/noir/noir-repo/tooling/artifact_cli/src/fs/inputs.rs b/noir/noir-repo/tooling/artifact_cli/src/fs/inputs.rs index f115af92041b..753343af8c65 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/fs/inputs.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/fs/inputs.rs @@ -1,6 +1,6 @@ use noirc_abi::{ - input_parser::{Format, InputValue}, Abi, InputMap, MAIN_RETURN_NAME, + input_parser::{Format, InputValue}, }; use std::{collections::BTreeMap, path::Path}; diff --git a/noir/noir-repo/tooling/artifact_cli/src/fs/witness.rs b/noir/noir-repo/tooling/artifact_cli/src/fs/witness.rs index f486a53f14d8..fee31ec1f224 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/fs/witness.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/fs/witness.rs @@ -1,16 +1,16 @@ use std::path::{Path, PathBuf}; -use acir::{native_types::WitnessStackError, FieldElement}; +use acir::{FieldElement, native_types::WitnessStackError}; use acvm::acir::native_types::WitnessStack; -use crate::errors::FilesystemError; +use crate::errors::{CliError, FilesystemError}; /// Write `witness.gz` to the output directory. pub fn save_witness_to_dir( - witnesses: WitnessStack, + witnesses: &WitnessStack, witness_name: &str, witness_dir: &Path, -) -> Result { +) -> Result { std::fs::create_dir_all(witness_dir)?; let witness_path = witness_dir.join(witness_name).with_extension("gz"); diff --git a/noir/noir-repo/tooling/artifact_cli/src/lib.rs b/noir/noir-repo/tooling/artifact_cli/src/lib.rs index 2cd2341b7b70..ac4316f5801a 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/lib.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/lib.rs @@ -2,6 +2,7 @@ use noirc_artifacts::{contract::ContractArtifact, program::ProgramArtifact}; pub mod commands; pub mod errors; +pub mod execution; pub mod fs; /// A parsed JSON build artifact. diff --git a/noir/noir-repo/tooling/debugger/src/context.rs b/noir/noir-repo/tooling/debugger/src/context.rs index 9741749df643..79e03672e8d7 100644 --- a/noir/noir-repo/tooling/debugger/src/context.rs +++ b/noir/noir-repo/tooling/debugger/src/context.rs @@ -5,22 +5,22 @@ use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; use acvm::acir::native_types::{Witness, WitnessMap, WitnessStack}; use acvm::brillig_vm::MemoryValue; use acvm::pwg::{ - ACVMStatus, AcirCallWaitInfo, BrilligSolver, BrilligSolverStatus, ForeignCallWaitInfo, - OpcodeNotSolvable, StepResult, ACVM, + ACVM, ACVMStatus, AcirCallWaitInfo, BrilligSolver, BrilligSolverStatus, ForeignCallWaitInfo, + OpcodeNotSolvable, StepResult, }; use acvm::{BlackBoxFunctionSolver, FieldElement}; use codespan_reporting::files::{Files, SimpleFile}; use fm::FileId; -use nargo::errors::{ExecutionError, Location}; use nargo::NargoError; +use nargo::errors::{ExecutionError, Location}; use noirc_artifacts::debug::{DebugArtifact, StackFrame}; use noirc_driver::DebugFile; use thiserror::Error; use std::collections::BTreeMap; -use std::collections::{hash_set::Iter, HashSet}; +use std::collections::{HashSet, hash_set::Iter}; /// A Noir program is composed by /// `n` ACIR circuits @@ -429,6 +429,12 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { .filter(|v: &Vec| !v.is_empty()) } + /// Returns the `FileId` of the file associated with the innermost function on the call stack. + pub(super) fn get_current_file(&mut self) -> Option { + self.get_current_source_location() + .and_then(|locations| locations.last().map(|location| location.file)) + } + /// Returns the (possible) stack of source locations corresponding to the /// given opcode location. Due to compiler inlining it's possible for this /// function to return multiple source locations. An empty vector means that @@ -788,11 +794,11 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } pub(super) fn get_variables(&self) -> Vec> { - return self.foreign_call_executor.get_variables(); + self.foreign_call_executor.get_variables() } pub(super) fn current_stack_frame(&self) -> Option> { - return self.foreign_call_executor.current_stack_frame(); + self.foreign_call_executor.current_stack_frame() } fn breakpoint_reached(&self) -> bool { @@ -954,13 +960,13 @@ mod tests { use crate::foreign_calls::DefaultDebugForeignCallExecutor; use acvm::{ acir::{ + AcirField, brillig::{HeapVector, IntegerBitSize}, circuit::{ brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}, opcodes::{AcirFunctionId, BlockId, BlockType}, }, native_types::Expression, - AcirField, }, blackbox_solver::StubbedBlackBoxSolver, brillig_vm::brillig::{ diff --git a/noir/noir-repo/tooling/debugger/src/dap.rs b/noir/noir-repo/tooling/debugger/src/dap.rs index 0d2095954e07..1df27d8ea6fd 100644 --- a/noir/noir-repo/tooling/debugger/src/dap.rs +++ b/noir/noir-repo/tooling/debugger/src/dap.rs @@ -1,8 +1,8 @@ use std::collections::BTreeMap; use std::io::{Read, Write}; -use acvm::acir::circuit::brillig::BrilligBytecode; use acvm::acir::circuit::Circuit; +use acvm::acir::circuit::brillig::BrilligBytecode; use acvm::acir::native_types::WitnessMap; use acvm::{BlackBoxFunctionSolver, FieldElement}; use nargo::PrintOutput; @@ -466,14 +466,14 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< } fn map_source_breakpoints(&mut self, args: &SetBreakpointsArguments) -> Vec { - let Some(ref source) = &args.source.path else { + let Some(source) = &args.source.path else { return vec![]; }; let Some(file_id) = self.find_file_id(source) else { eprintln!("WARN: file ID for source {source} not found"); return vec![]; }; - let Some(ref breakpoints) = &args.breakpoints else { + let Some(breakpoints) = &args.breakpoints else { return vec![]; }; let mut breakpoints_to_set: Vec<(DebugLocation, i64)> = vec![]; diff --git a/noir/noir-repo/tooling/debugger/src/foreign_calls.rs b/noir/noir-repo/tooling/debugger/src/foreign_calls.rs index b92e22844ea9..efae3df407a2 100644 --- a/noir/noir-repo/tooling/debugger/src/foreign_calls.rs +++ b/noir/noir-repo/tooling/debugger/src/foreign_calls.rs @@ -1,13 +1,13 @@ use acvm::{ + AcirField, FieldElement, acir::brillig::{ForeignCallParam, ForeignCallResult}, pwg::ForeignCallWaitInfo, - AcirField, FieldElement, }; use nargo::{ + PrintOutput, foreign_calls::{ - layers::Layer, DefaultForeignCallBuilder, ForeignCallError, ForeignCallExecutor, + DefaultForeignCallBuilder, ForeignCallError, ForeignCallExecutor, layers::Layer, }, - PrintOutput, }; use noirc_artifacts::debug::{DebugArtifact, DebugVars, StackFrame}; use noirc_errors::debug_info::{DebugFnId, DebugVarId}; @@ -66,7 +66,7 @@ impl DefaultDebugForeignCallExecutor { pub fn from_artifact<'a>( output: PrintOutput<'a>, artifact: &DebugArtifact, - ) -> impl DebugForeignCallExecutor + 'a { + ) -> impl DebugForeignCallExecutor + use<'a> { let mut ex = Self::default(); ex.load_artifact(artifact); Self::make(output, ex) diff --git a/noir/noir-repo/tooling/debugger/src/lib.rs b/noir/noir-repo/tooling/debugger/src/lib.rs index 37ac088ca355..f0dc859beb3e 100644 --- a/noir/noir-repo/tooling/debugger/src/lib.rs +++ b/noir/noir-repo/tooling/debugger/src/lib.rs @@ -19,8 +19,9 @@ pub fn run_repl_session>( solver: &B, program: CompiledProgram, initial_witness: WitnessMap, + raw_source_printing: bool, ) -> Result>, NargoError> { - repl::run(solver, program, initial_witness) + repl::run(solver, program, initial_witness, raw_source_printing) } pub fn run_dap_loop>( diff --git a/noir/noir-repo/tooling/debugger/src/repl.rs b/noir/noir-repo/tooling/debugger/src/repl.rs index eda3cbfd895b..081561469853 100644 --- a/noir/noir-repo/tooling/debugger/src/repl.rs +++ b/noir/noir-repo/tooling/debugger/src/repl.rs @@ -1,12 +1,12 @@ use crate::context::{DebugCommandResult, DebugContext, DebugLocation}; +use acvm::AcirField; use acvm::acir::brillig::BitSize; use acvm::acir::circuit::brillig::{BrilligBytecode, BrilligFunctionId}; use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; use acvm::acir::native_types::{Witness, WitnessMap, WitnessStack}; -use acvm::brillig_vm::brillig::Opcode as BrilligOpcode; use acvm::brillig_vm::MemoryValue; -use acvm::AcirField; +use acvm::brillig_vm::brillig::Opcode as BrilligOpcode; use acvm::{BlackBoxFunctionSolver, FieldElement}; use nargo::{NargoError, PrintOutput}; use noirc_driver::CompiledProgram; @@ -14,7 +14,7 @@ use noirc_driver::CompiledProgram; use crate::foreign_calls::DefaultDebugForeignCallExecutor; use noirc_artifacts::debug::DebugArtifact; -use easy_repl::{command, CommandStatus, Repl}; +use easy_repl::{CommandStatus, Repl, command}; use noirc_printable_type::PrintableValueDisplay; use std::cell::RefCell; @@ -32,6 +32,10 @@ pub struct ReplDebugger<'a, B: BlackBoxFunctionSolver> { // Brillig functions referenced from the ACIR circuits above unconstrained_functions: &'a [BrilligBytecode], + + // whether to print the source without highlighting, pretty-printing, + // or line numbers + raw_source_printing: bool, } impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { @@ -41,6 +45,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { debug_artifact: &'a DebugArtifact, initial_witness: WitnessMap, unconstrained_functions: &'a [BrilligBytecode], + raw_source_printing: bool, ) -> Self { let foreign_call_executor = Box::new(DefaultDebugForeignCallExecutor::from_artifact( PrintOutput::Stdout, @@ -68,6 +73,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { initial_witness, last_result, unconstrained_functions, + raw_source_printing, } } @@ -97,7 +103,11 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { } } let locations = self.context.get_source_location_for_debug_location(&location); - print_source_code_location(self.debug_artifact, &locations); + print_source_code_location( + self.debug_artifact, + &locations, + self.raw_source_printing, + ); } } } @@ -125,7 +135,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { } } let locations = self.context.get_source_location_for_debug_location(debug_location); - print_source_code_location(self.debug_artifact, &locations); + print_source_code_location(self.debug_artifact, &locations, self.raw_source_printing); } pub fn show_current_call_stack(&self) { @@ -233,6 +243,24 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { } } + fn add_breakpoint_at_line(&mut self, line_number: i64) { + let Some(current_file) = self.context.get_current_file() else { + println!("No current file."); + return; + }; + + let best_location = + self.context.find_opcode_for_source_location(¤t_file, line_number); + + match best_location { + Some(location) => { + println!("Added breakpoint at line {}", line_number); + self.add_breakpoint_at(location) + } + None => println!("No opcode at line {}", line_number), + } + } + fn delete_breakpoint_at(&mut self, location: DebugLocation) { if self.context.delete_breakpoint(&location) { println!("Breakpoint at {location} deleted"); @@ -427,6 +455,7 @@ pub fn run>( blackbox_solver: &B, program: CompiledProgram, initial_witness: WitnessMap, + raw_source_printing: bool, ) -> Result>, NargoError> { let circuits = &program.program.functions; let debug_artifact = @@ -438,6 +467,7 @@ pub fn run>( debug_artifact, initial_witness, unconstrained_functions, + raw_source_printing, )); let ref_context = &context; @@ -524,6 +554,16 @@ pub fn run>( } }, ) + .add( + "break", + command! { + "add a breakpoint at a line of the current file", + (line_number: i64) => |line_number| { + ref_context.borrow_mut().add_breakpoint_at_line(line_number); + Ok(CommandStatus::Done) + } + }, + ) .add( "break", command! { diff --git a/noir/noir-repo/tooling/debugger/src/source_code_printer.rs b/noir/noir-repo/tooling/debugger/src/source_code_printer.rs index b3682c9016fb..a756de8d98ba 100644 --- a/noir/noir-repo/tooling/debugger/src/source_code_printer.rs +++ b/noir/noir-repo/tooling/debugger/src/source_code_printer.rs @@ -30,7 +30,11 @@ struct LocationPrintContext { // Given a DebugArtifact and an OpcodeLocation, prints all the source code // locations the OpcodeLocation maps to, with some surrounding context and // visual aids to highlight the location itself. -pub(super) fn print_source_code_location(debug_artifact: &DebugArtifact, locations: &[Location]) { +pub(super) fn print_source_code_location( + debug_artifact: &DebugArtifact, + locations: &[Location], + raw_source_printing: bool, +) { let locations = locations.iter(); for loc in locations { @@ -41,9 +45,11 @@ pub(super) fn print_source_code_location(debug_artifact: &DebugArtifact, locatio for line in lines { match line { PrintedLine::Skip => {} - PrintedLine::Ellipsis { line_number } => print_ellipsis(line_number), + PrintedLine::Ellipsis { line_number } => { + print_ellipsis(line_number, raw_source_printing) + } PrintedLine::Content { line_number, cursor, content, highlight } => { - print_content(line_number, cursor, content, highlight) + print_content(line_number, cursor, content, highlight, raw_source_printing) } } } @@ -57,11 +63,29 @@ fn print_location_path(debug_artifact: &DebugArtifact, loc: Location) { println!("At {}:{line_number}:{column_number}", debug_artifact.name(loc.file).unwrap()); } -fn print_ellipsis(line_number: usize) { +fn print_ellipsis(line_number: usize, raw_source_printing: bool) { + if raw_source_printing { + println!("..."); + return; + } + println!("{:>3} {:2} {}", line_number.dimmed(), "", "...".dimmed()); } -fn print_content(line_number: usize, cursor: &str, content: &str, highlight: Option>) { +fn print_content( + line_number: usize, + cursor: &str, + content: &str, + highlight: Option>, + raw_source_printing: bool, +) { + if raw_source_printing { + if cursor == "->" && highlight.is_some() { + println!("{}", content); + } + return; + } + match highlight { Some(highlight) => { println!( @@ -220,17 +244,17 @@ fn render_location<'a>( #[cfg(test)] mod tests { - use crate::source_code_printer::render_location; use crate::source_code_printer::PrintedLine::Content; + use crate::source_code_printer::render_location; use acvm::acir::circuit::OpcodeLocation; use fm::FileManager; use noirc_artifacts::debug::DebugArtifact; - use noirc_errors::{debug_info::DebugInfo, Location, Span}; + use noirc_errors::{Location, Span, debug_info::DebugInfo}; use std::collections::BTreeMap; use std::ops::Range; use std::path::Path; use std::path::PathBuf; - use tempfile::{tempdir, TempDir}; + use tempfile::{TempDir, tempdir}; // Returns the absolute path to the file fn create_dummy_file(dir: &TempDir, file_name: &Path) -> PathBuf { diff --git a/noir/noir-repo/tooling/debugger/tests/debug.rs b/noir/noir-repo/tooling/debugger/tests/debug.rs index eb43cf9cc6dd..079857420853 100644 --- a/noir/noir-repo/tooling/debugger/tests/debug.rs +++ b/noir/noir-repo/tooling/debugger/tests/debug.rs @@ -49,4 +49,115 @@ mod tests { // Exit the bash session. dbg_session.send_line("exit").expect("Failed to quit bash session"); } + + #[test] + fn debugger_expected_call_stack() { + let nargo_bin = + cargo_bin("nargo").into_os_string().into_string().expect("Cannot parse nargo path"); + + let timeout_seconds = 30; + let mut dbg_session = + spawn_bash(Some(timeout_seconds * 1000)).expect("Could not start bash session"); + + let test_program_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../test_programs/execution_success/regression_7195"); + let test_program_dir = test_program_path.display(); + + // Start debugger and test that it loads for the given program. + dbg_session + .execute( + &format!( + "{nargo_bin} debug --raw-source-printing true --program-dir {test_program_dir} --force-brillig --expression-width 3" + ), + ".*\\Starting debugger.*", + ) + .expect("Could not start debugger"); + + let num_steps = 16; + for _ in 1..=num_steps { + // While running the debugger, issue a "next" cmd, + // which should run to the program to the next source line given + // we haven't set any breakpoints. + // ">" is the debugger's prompt, so finding one + // after running "next" indicates that the + // debugger has not panicked for this step. + dbg_session + .send_line("next") + .expect("Debugger panicked while attempting to step through program."); + dbg_session + .exp_string(">") + .expect("Failed while waiting for debugger to step through program."); + } + + let mut lines = vec![]; + while let Ok(line) = dbg_session.read_line() { + if !(line.starts_with(">next") || line.starts_with("At ") || line.starts_with("...")) { + lines.push(line); + } + } + + let lines_expected_to_contain: Vec<&str> = vec![ + "> next", + " let x = unsafe { baz(x) };", + "unconstrained fn baz(x: Field) -> Field {", + "> next", + " let x = unsafe { baz(x) };", + "unconstrained fn baz(x: Field) -> Field {", + "> next", + " let x = unsafe { baz(x) };", + "}", + "> next", + " let x = unsafe { baz(x) };", + "> next", + " foo(x);", + "fn foo(x: Field) {", + "> next", + " foo(x);", + "fn foo(x: Field) {", + "> next", + " foo(x);", + " let y = unsafe { baz(x) };", + "unconstrained fn baz(x: Field) -> Field {", + "> next", + " foo(x);", + " let y = unsafe { baz(x) };", + "unconstrained fn baz(x: Field) -> Field {", + "> next", + " foo(x);", + " let y = unsafe { baz(x) };", + "}", + "> next", + " foo(x);", + " let y = unsafe { baz(x) };", + "> next", + " foo(x);", + " bar(y);", + "fn bar(y: Field) {", + "> next", + " foo(x);", + " bar(y);", + "fn bar(y: Field) {", + "> next", + " foo(x);", + " bar(y);", + " assert(y != 0);", + ]; + + for (line, line_expected_to_contain) in lines.into_iter().zip(lines_expected_to_contain) { + let ascii_line: String = line.chars().filter(char::is_ascii).collect(); + let line_expected_to_contain = line_expected_to_contain.trim_start(); + assert!( + ascii_line.contains(line_expected_to_contain), + "{:?}\ndid not contain\n{:?}", + ascii_line, + line_expected_to_contain, + ); + } + + // Run the "quit" command + dbg_session.send_line("quit").expect("Failed to quit debugger"); + + // Exit the bash session. + dbg_session.send_line("exit").expect("Failed to quit bash session"); + } } diff --git a/noir/noir-repo/tooling/fuzzer/src/dictionary/mod.rs b/noir/noir-repo/tooling/fuzzer/src/dictionary/mod.rs index 7fe1c4cd602c..4f15a336a3e9 100644 --- a/noir/noir-repo/tooling/fuzzer/src/dictionary/mod.rs +++ b/noir/noir-repo/tooling/fuzzer/src/dictionary/mod.rs @@ -6,16 +6,16 @@ use std::collections::HashSet; use acvm::{ + AcirField, acir::{ circuit::{ + Circuit, Opcode, Program, brillig::{BrilligBytecode, BrilligInputs}, opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum}, - Circuit, Opcode, Program, }, native_types::Expression, }, brillig_vm::brillig::Opcode as BrilligOpcode, - AcirField, }; /// Constructs a [HashSet] of values pulled from a [Program] which are likely to be correspond diff --git a/noir/noir-repo/tooling/fuzzer/src/lib.rs b/noir/noir-repo/tooling/fuzzer/src/lib.rs index 324be323fc21..471f3da88f65 100644 --- a/noir/noir-repo/tooling/fuzzer/src/lib.rs +++ b/noir/noir-repo/tooling/fuzzer/src/lib.rs @@ -4,11 +4,11 @@ //! Code is used under the MIT license. use acvm::{ + FieldElement, acir::{ circuit::Program, native_types::{WitnessMap, WitnessStack}, }, - FieldElement, }; use dictionary::build_dictionary_from_program; use noirc_abi::InputMap; diff --git a/noir/noir-repo/tooling/fuzzer/src/strategies/int.rs b/noir/noir-repo/tooling/fuzzer/src/strategies/int.rs index 22dded7c7b76..5fb8d469a926 100644 --- a/noir/noir-repo/tooling/fuzzer/src/strategies/int.rs +++ b/noir/noir-repo/tooling/fuzzer/src/strategies/int.rs @@ -54,20 +54,12 @@ impl IntStrategy { /// Maximum allowed positive number. fn type_max(&self) -> i128 { - if self.bits < 128 { - (1i128 << (self.bits - 1)) - 1 - } else { - i128::MAX - } + if self.bits < 128 { (1i128 << (self.bits - 1)) - 1 } else { i128::MAX } } /// Minimum allowed negative number. fn type_min(&self) -> i128 { - if self.bits < 128 { - -(1i128 << (self.bits - 1)) - } else { - i128::MIN - } + if self.bits < 128 { -(1i128 << (self.bits - 1)) } else { i128::MIN } } } diff --git a/noir/noir-repo/tooling/fuzzer/src/strategies/mod.rs b/noir/noir-repo/tooling/fuzzer/src/strategies/mod.rs index 99c7ca56f2e7..4c8181ea804a 100644 --- a/noir/noir-repo/tooling/fuzzer/src/strategies/mod.rs +++ b/noir/noir-repo/tooling/fuzzer/src/strategies/mod.rs @@ -4,7 +4,7 @@ use proptest::prelude::*; use acvm::{AcirField, FieldElement}; -use noirc_abi::{input_parser::InputValue, Abi, AbiType, InputMap, Sign}; +use noirc_abi::{Abi, AbiType, InputMap, Sign, input_parser::InputValue}; use std::collections::{BTreeMap, HashSet}; use uint::UintStrategy; diff --git a/noir/noir-repo/tooling/fuzzer/src/strategies/uint.rs b/noir/noir-repo/tooling/fuzzer/src/strategies/uint.rs index 402e65597524..904a5cef2878 100644 --- a/noir/noir-repo/tooling/fuzzer/src/strategies/uint.rs +++ b/noir/noir-repo/tooling/fuzzer/src/strategies/uint.rs @@ -78,11 +78,7 @@ impl UintStrategy { /// Maximum integer that fits in the given bit width. fn type_max(&self) -> u128 { - if self.bits < 128 { - (1 << self.bits) - 1 - } else { - u128::MAX - } + if self.bits < 128 { (1 << self.bits) - 1 } else { u128::MAX } } } diff --git a/noir/noir-repo/tooling/inspector/Cargo.toml b/noir/noir-repo/tooling/inspector/Cargo.toml index 2124f7e9a282..d7ab641e4837 100644 --- a/noir/noir-repo/tooling/inspector/Cargo.toml +++ b/noir/noir-repo/tooling/inspector/Cargo.toml @@ -26,3 +26,4 @@ const_format.workspace = true acir.workspace = true noirc_artifacts.workspace = true noirc_artifacts_info.workspace = true +noir_artifact_cli.workspace = true diff --git a/noir/noir-repo/tooling/inspector/src/cli/info_cmd.rs b/noir/noir-repo/tooling/inspector/src/cli/info_cmd.rs index 6a9db2676f2b..34107ebea3a9 100644 --- a/noir/noir-repo/tooling/inspector/src/cli/info_cmd.rs +++ b/noir/noir-repo/tooling/inspector/src/cli/info_cmd.rs @@ -2,8 +2,8 @@ use std::path::PathBuf; use clap::Args; use color_eyre::eyre; -use noirc_artifacts::program::ProgramArtifact; -use noirc_artifacts_info::{count_opcodes_and_gates_in_program, show_info_report, InfoReport}; +use noir_artifact_cli::Artifact; +use noirc_artifacts_info::{InfoReport, count_opcodes_and_gates_in_program, show_info_report}; #[derive(Debug, Clone, Args)] pub(crate) struct InfoCommand { @@ -13,22 +13,42 @@ pub(crate) struct InfoCommand { /// Output a JSON formatted report. Changes to this format are not currently considered breaking. #[clap(long, hide = true)] json: bool, + + /// Name of the function to print, if the artifact is a contract. + #[clap(long)] + contract_fn: Option, } pub(crate) fn run(args: InfoCommand) -> eyre::Result<()> { - let file = std::fs::File::open(args.artifact.clone())?; - let artifact: ProgramArtifact = serde_json::from_reader(file)?; - - let package_name = args - .artifact - .with_extension("") - .file_name() - .map(|s| s.to_string_lossy().to_string()) - .unwrap_or_else(|| "artifact".to_string()); - - let program_info = count_opcodes_and_gates_in_program(artifact, package_name.to_string(), None); - - let info_report = InfoReport { programs: vec![program_info] }; + let artifact = Artifact::read_from_file(&args.artifact)?; + + let programs = match artifact { + Artifact::Program(program) => { + let package_name = args + .artifact + .with_extension("") + .file_name() + .map(|s| s.to_string_lossy().to_string()) + .unwrap_or_else(|| "artifact".to_string()); + + vec![count_opcodes_and_gates_in_program(program, package_name, None)] + } + Artifact::Contract(contract) => contract + .functions + .into_iter() + .filter(|f| args.contract_fn.as_ref().map(|n| *n == f.name).unwrap_or(true)) + .map(|f| { + let package_name = format!("{}::{}", contract.name, f.name); + let program = f.into_compiled_program( + contract.noir_version.clone(), + contract.file_map.clone(), + ); + count_opcodes_and_gates_in_program(program.into(), package_name, None) + }) + .collect::>(), + }; + + let info_report = InfoReport { programs }; show_info_report(info_report, args.json); Ok(()) diff --git a/noir/noir-repo/tooling/inspector/src/cli/mod.rs b/noir/noir-repo/tooling/inspector/src/cli/mod.rs index 8cce6ec3a6f2..411f2076973d 100644 --- a/noir/noir-repo/tooling/inspector/src/cli/mod.rs +++ b/noir/noir-repo/tooling/inspector/src/cli/mod.rs @@ -1,4 +1,4 @@ -use clap::{command, Parser, Subcommand}; +use clap::{Parser, Subcommand, command}; use color_eyre::eyre; use const_format::formatcp; diff --git a/noir/noir-repo/tooling/inspector/src/cli/print_acir_cmd.rs b/noir/noir-repo/tooling/inspector/src/cli/print_acir_cmd.rs index f3dfe528973f..c3580a715d88 100644 --- a/noir/noir-repo/tooling/inspector/src/cli/print_acir_cmd.rs +++ b/noir/noir-repo/tooling/inspector/src/cli/print_acir_cmd.rs @@ -2,20 +2,38 @@ use std::path::PathBuf; use clap::Args; use color_eyre::eyre; -use noirc_artifacts::program::ProgramArtifact; +use noir_artifact_cli::Artifact; #[derive(Debug, Clone, Args)] pub(crate) struct PrintAcirCommand { /// The artifact to print artifact: PathBuf, + + /// Name of the function to print, if the artifact is a contract. + #[clap(long)] + contract_fn: Option, } pub(crate) fn run(args: PrintAcirCommand) -> eyre::Result<()> { - let file = std::fs::File::open(args.artifact.clone())?; - let artifact: ProgramArtifact = serde_json::from_reader(file)?; + let artifact = Artifact::read_from_file(&args.artifact)?; - println!("Compiled ACIR for main:"); - println!("{}", artifact.bytecode); + match artifact { + Artifact::Program(program) => { + println!("Compiled ACIR for main:"); + println!("{}", program.bytecode); + } + Artifact::Contract(contract) => { + println!("Compiled circuits for contract '{}':", contract.name); + for function in contract + .functions + .into_iter() + .filter(|f| args.contract_fn.as_ref().map(|n| *n == f.name).unwrap_or(true)) + { + println!("Compiled ACIR for function '{}':", function.name); + println!("{}", function.bytecode); + } + } + } Ok(()) } diff --git a/noir/noir-repo/tooling/lsp/Cargo.toml b/noir/noir-repo/tooling/lsp/Cargo.toml index a055a9a6bcec..d0b67f53c241 100644 --- a/noir/noir-repo/tooling/lsp/Cargo.toml +++ b/noir/noir-repo/tooling/lsp/Cargo.toml @@ -31,6 +31,7 @@ thiserror.workspace = true fm.workspace = true rayon.workspace = true fxhash.workspace = true +iter-extended.workspace = true convert_case = "0.6.0" num-bigint.workspace = true diff --git a/noir/noir-repo/tooling/lsp/src/attribute_reference_finder.rs b/noir/noir-repo/tooling/lsp/src/attribute_reference_finder.rs index e3f31b65b463..384c575f0c65 100644 --- a/noir/noir-repo/tooling/lsp/src/attribute_reference_finder.rs +++ b/noir/noir-repo/tooling/lsp/src/attribute_reference_finder.rs @@ -9,6 +9,7 @@ use std::collections::BTreeMap; use fm::FileId; use noirc_errors::Span; use noirc_frontend::{ + ParsedModule, ast::{AttributeTarget, Visitor}, graph::CrateId, hir::{ @@ -19,7 +20,6 @@ use noirc_frontend::{ parser::ParsedSubModule, token::MetaAttribute, usage_tracker::UsageTracker, - ParsedModule, }; use crate::modules::module_def_id_to_reference_id; @@ -65,7 +65,7 @@ impl<'a> AttributeReferenceFinder<'a> { } } -impl<'a> Visitor for AttributeReferenceFinder<'a> { +impl Visitor for AttributeReferenceFinder<'_> { fn visit_parsed_submodule(&mut self, parsed_sub_module: &ParsedSubModule, _span: Span) -> bool { // Switch `self.module_id` to the submodule let previous_module_id = self.module_id; @@ -90,7 +90,7 @@ impl<'a> Visitor for AttributeReferenceFinder<'a> { attribute: &MetaAttribute, _target: AttributeTarget, ) -> bool { - if !self.includes_span(attribute.span) { + if !self.includes_span(attribute.location.span) { return false; } diff --git a/noir/noir-repo/tooling/lsp/src/lib.rs b/noir/noir-repo/tooling/lsp/src/lib.rs index c722bfdfd3e1..784ac8cf93d6 100644 --- a/noir/noir-repo/tooling/lsp/src/lib.rs +++ b/noir/noir-repo/tooling/lsp/src/lib.rs @@ -15,35 +15,35 @@ use std::{ use acvm::{BlackBoxFunctionSolver, FieldElement}; use async_lsp::{ - router::Router, AnyEvent, AnyNotification, AnyRequest, ClientSocket, Error, LspService, - ResponseError, + AnyEvent, AnyNotification, AnyRequest, ClientSocket, Error, LspService, ResponseError, + router::Router, }; -use fm::{codespan_files as files, FileManager}; +use fm::{FileManager, codespan_files as files}; use fxhash::FxHashSet; use lsp_types::{ + CodeLens, request::{ CodeActionRequest, Completion, DocumentSymbolRequest, HoverRequest, InlayHintRequest, PrepareRenameRequest, References, Rename, SignatureHelpRequest, }, - CodeLens, }; use nargo::{ package::{Package, PackageType}, parse_all, workspace::Workspace, }; -use nargo_toml::{find_file_manifest, resolve_workspace_from_toml, PackageSelection}; -use noirc_driver::{file_manager_with_stdlib, prepare_crate, NOIR_ARTIFACT_VERSION_STRING}; +use nargo_toml::{PackageSelection, find_file_manifest, resolve_workspace_from_toml}; +use noirc_driver::{NOIR_ARTIFACT_VERSION_STRING, file_manager_with_stdlib, prepare_crate}; use noirc_frontend::{ + ParsedModule, graph::{CrateGraph, CrateId, CrateName}, hir::{ - def_map::{parse_file, CrateDefMap}, Context, FunctionNameMatch, ParsedFiles, + def_map::{CrateDefMap, parse_file}, }, node_interner::NodeInterner, parser::ParserError, usage_tracker::UsageTracker, - ParsedModule, }; use rayon::prelude::*; @@ -52,12 +52,11 @@ use notifications::{ on_did_open_text_document, on_did_save_text_document, on_exit, on_initialized, }; use requests::{ - on_code_action_request, on_code_lens_request, on_completion_request, + LspInitializationOptions, on_code_action_request, on_code_lens_request, on_completion_request, on_document_symbol_request, on_formatting, on_goto_declaration_request, on_goto_definition_request, on_goto_type_definition_request, on_hover_request, on_initialize, on_inlay_hint_request, on_prepare_rename_request, on_references_request, on_rename_request, on_shutdown, on_signature_help_request, on_test_run_request, on_tests_request, - LspInitializationOptions, }; use serde_json::Value as JsonValue; use thiserror::Error; @@ -74,12 +73,14 @@ mod types; mod use_segment_positions; mod utils; mod visibility; +mod with_file; #[cfg(test)] mod test_utils; use solver::WrapperSolver; -use types::{notification, request, NargoTest, NargoTestId, Position, Range, Url}; +use types::{NargoTest, NargoTestId, Position, Range, Url, notification, request}; +use with_file::parsed_module_with_file; #[derive(Debug, Error)] pub enum LspError { @@ -237,11 +238,7 @@ fn get_package_tests_in_crate( }) .collect(); - if package_tests.is_empty() { - None - } else { - Some(package_tests) - } + if package_tests.is_empty() { None } else { Some(package_tests) } } fn byte_span_to_range<'a, F: files::Files<'a> + ?Sized>( @@ -388,18 +385,22 @@ fn parse_diff(file_manager: &FileManager, state: &mut LspState) -> ParsedFiles { }) .collect(); - let cache_hits: Vec<_> = noir_file_hashes + let cache_hits = noir_file_hashes .par_iter() .filter_map(|(file_id, file_path, current_hash)| { let cached_version = state.cached_parsed_files.get(file_path); - if let Some((hash, cached_parsing)) = cached_version { + if let Some((hash, (parsed_module, errors))) = cached_version { if hash == current_hash { - return Some((*file_id, cached_parsing.clone())); + // The cached ParsedModule might have FileIDs in it that are different than the file_id we get here, + // so we must replace all of those FileIDs with the one here. + let parsed_module = + parsed_module_with_file(parsed_module.clone(), *file_id); + return Some((*file_id, (parsed_module, errors.clone()))); } } None }) - .collect(); + .collect::>(); let cache_hits_ids: FxHashSet<_> = cache_hits.iter().map(|(file_id, _)| *file_id).collect(); diff --git a/noir/noir-repo/tooling/lsp/src/modules.rs b/noir/noir-repo/tooling/lsp/src/modules.rs index 56529949b3ea..4e7ef64b2c08 100644 --- a/noir/noir-repo/tooling/lsp/src/modules.rs +++ b/noir/noir-repo/tooling/lsp/src/modules.rs @@ -248,15 +248,12 @@ pub(crate) fn module_def_id_relative_path( interner, ) } else { - let Some(module_full_path) = relative_module_full_path( + relative_module_full_path( module_def_id, current_module_id, current_module_parent_id, interner, - ) else { - return None; - }; - module_full_path + )? }; let path = if defining_module.is_some() || !matches!(module_def_id, ModuleDefId::ModuleId(..)) { diff --git a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs index ac8bf7309799..b7ba8cd47615 100644 --- a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs @@ -3,25 +3,25 @@ use std::ops::ControlFlow; use std::path::PathBuf; use crate::{ - insert_all_files_for_workspace_into_file_manager, PackageCacheData, WorkspaceCacheData, + PackageCacheData, WorkspaceCacheData, insert_all_files_for_workspace_into_file_manager, }; use async_lsp::{ErrorCode, LanguageClient, ResponseError}; -use fm::{FileId, FileManager, FileMap}; +use fm::{FileManager, FileMap}; use fxhash::FxHashMap as HashMap; use lsp_types::{DiagnosticRelatedInformation, DiagnosticTag, Url}; use noirc_driver::check_crate; use noirc_errors::reporter::CustomLabel; -use noirc_errors::{DiagnosticKind, FileDiagnostic, Location}; +use noirc_errors::{CustomDiagnostic, DiagnosticKind, Location}; use crate::types::{ - notification, Diagnostic, DiagnosticSeverity, DidChangeConfigurationParams, - DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams, - DidSaveTextDocumentParams, InitializedParams, NargoPackageTests, PublishDiagnosticsParams, + Diagnostic, DiagnosticSeverity, DidChangeConfigurationParams, DidChangeTextDocumentParams, + DidCloseTextDocumentParams, DidOpenTextDocumentParams, DidSaveTextDocumentParams, + InitializedParams, NargoPackageTests, PublishDiagnosticsParams, notification, }; use crate::{ - byte_span_to_range, get_package_tests_in_crate, parse_diff, resolve_workspace_for_source_path, - LspState, + LspState, byte_span_to_range, get_package_tests_in_crate, parse_diff, + resolve_workspace_for_source_path, }; pub(super) fn on_initialized( @@ -191,16 +191,16 @@ fn publish_diagnostics( package_root_dir: &PathBuf, files: &FileMap, fm: &FileManager, - file_diagnostics: Vec, + custom_diagnostics: Vec, ) { let mut diagnostics_per_url: HashMap> = HashMap::default(); - for file_diagnostic in file_diagnostics.into_iter() { - let file_id = file_diagnostic.file_id; - let path = fm.path(file_id).expect("file must exist to have emitted diagnostic"); + for custom_diagnostic in custom_diagnostics.into_iter() { + let file = custom_diagnostic.file; + let path = fm.path(file).expect("file must exist to have emitted diagnostic"); if let Ok(uri) = Url::from_file_path(path) { if let Some(diagnostic) = - file_diagnostic_to_diagnostic(file_diagnostic, files, fm, uri.clone()) + custom_diagnostic_to_diagnostic(custom_diagnostic, files, fm, uri.clone()) { diagnostics_per_url.entry(uri).or_default().push(diagnostic); } @@ -232,21 +232,18 @@ fn publish_diagnostics( state.files_with_errors.insert(package_root_dir.clone(), new_files_with_errors); } -fn file_diagnostic_to_diagnostic( - file_diagnostic: FileDiagnostic, +fn custom_diagnostic_to_diagnostic( + diagnostic: CustomDiagnostic, files: &FileMap, fm: &FileManager, uri: Url, ) -> Option { - let file_id = file_diagnostic.file_id; - let diagnostic = file_diagnostic.diagnostic; - if diagnostic.secondaries.is_empty() { return None; } - let span = diagnostic.secondaries.first().unwrap().span; - let range = byte_span_to_range(files, file_id, span.into())?; + let span = diagnostic.secondaries.first().unwrap().location.span; + let range = byte_span_to_range(files, diagnostic.file, span.into())?; let severity = match diagnostic.kind { DiagnosticKind::Error => DiagnosticSeverity::ERROR, @@ -266,7 +263,7 @@ fn file_diagnostic_to_diagnostic( let secondaries = diagnostic .secondaries .into_iter() - .filter_map(|secondary| secondary_to_related_information(secondary, file_id, files, fm)); + .filter_map(|secondary| secondary_to_related_information(secondary, files, fm)); let notes = diagnostic.notes.into_iter().map(|message| DiagnosticRelatedInformation { location: lsp_types::Location { uri: uri.clone(), range }, message, @@ -293,14 +290,13 @@ fn file_diagnostic_to_diagnostic( fn secondary_to_related_information( secondary: CustomLabel, - file_id: FileId, files: &FileMap, fm: &FileManager, ) -> Option { - let secondary_file = secondary.file.unwrap_or(file_id); + let secondary_file = secondary.location.file; let path = fm.path(secondary_file)?; let uri = Url::from_file_path(path).ok()?; - let range = byte_span_to_range(files, secondary_file, secondary.span.into())?; + let range = byte_span_to_range(files, secondary_file, secondary.location.span.into())?; let message = secondary.message; Some(DiagnosticRelatedInformation { location: lsp_types::Location { uri, range }, message }) } diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action.rs index d5512855b3be..6d74ce2d1f96 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action.rs @@ -11,6 +11,10 @@ use lsp_types::{ TextDocumentPositionParams, TextEdit, Url, WorkspaceEdit, }; use noirc_errors::Span; +use noirc_frontend::{ + ParsedModule, + parser::{Item, ItemKind, ParsedSubModule}, +}; use noirc_frontend::{ ast::{ CallExpression, ConstructorExpression, ItemVisibility, MethodCallExpression, NoirTraitImpl, @@ -21,14 +25,10 @@ use noirc_frontend::{ node_interner::{NodeInterner, Reexport}, usage_tracker::UsageTracker, }; -use noirc_frontend::{ - parser::{Item, ItemKind, ParsedSubModule}, - ParsedModule, -}; use crate::{ - modules::get_ancestor_module_reexport, use_segment_positions::UseSegmentPositions, utils, - visibility::module_def_id_is_visible, LspState, + LspState, modules::get_ancestor_module_reexport, use_segment_positions::UseSegmentPositions, + utils, visibility::module_def_id_is_visible, }; use super::{process_request, to_lsp_location}; @@ -44,7 +44,7 @@ mod tests; pub(crate) fn on_code_action_request( state: &mut LspState, params: CodeActionParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let uri = params.text_document.clone().uri; let position = params.range.start; let text_document_position_params = @@ -56,7 +56,7 @@ pub(crate) fn on_code_action_request( utils::range_to_byte_span(args.files, file_id, ¶ms.range).and_then(|byte_range| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let mut finder = CodeActionFinder::new( uri, @@ -233,16 +233,16 @@ impl<'a> CodeActionFinder<'a> { } } -impl<'a> Visitor for CodeActionFinder<'a> { +impl Visitor for CodeActionFinder<'_> { fn visit_item(&mut self, item: &Item) -> bool { if let ItemKind::Import(use_tree, _) = &item.kind { - if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { + if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.location.span) { self.auto_import_line = (lsp_location.range.end.line + 1) as usize; } self.use_segment_positions.add(use_tree); } - self.includes_span(item.span) + self.includes_span(item.location.span) } fn visit_parsed_submodule(&mut self, parsed_sub_module: &ParsedSubModule, span: Span) -> bool { @@ -306,7 +306,7 @@ impl<'a> Visitor for CodeActionFinder<'a> { } if call.is_macro_call { - self.remove_bang_from_call(call.func.span); + self.remove_bang_from_call(call.func.location.span); } true diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/fill_struct_fields.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/fill_struct_fields.rs index fc8be7c51630..0f9188388ee2 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/fill_struct_fields.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/fill_struct_fields.rs @@ -1,5 +1,5 @@ use lsp_types::TextEdit; -use noirc_errors::{Location, Span}; +use noirc_errors::Span; use noirc_frontend::{ ast::{ConstructorExpression, UnresolvedTypeData}, node_interner::ReferenceId, @@ -9,7 +9,7 @@ use crate::byte_span_to_range; use super::CodeActionFinder; -impl<'a> CodeActionFinder<'a> { +impl CodeActionFinder<'_> { pub(super) fn fill_struct_fields(&mut self, constructor: &ConstructorExpression, span: Span) { if !self.includes_span(span) { return; @@ -19,7 +19,7 @@ impl<'a> CodeActionFinder<'a> { return; }; - let location = Location::new(path.span, self.file); + let location = path.location; let Some(ReferenceId::Type(type_id)) = self.interner.find_referenced(location) else { return; }; diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/implement_missing_members.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/implement_missing_members.rs index c29caf79848a..a39df735b34f 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/implement_missing_members.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/implement_missing_members.rs @@ -11,7 +11,7 @@ use crate::{byte_span_to_range, trait_impl_method_stub_generator::TraitImplMetho use super::CodeActionFinder; -impl<'a> CodeActionFinder<'a> { +impl CodeActionFinder<'_> { pub(super) fn implement_missing_members( &mut self, noir_trait_impl: &NoirTraitImpl, diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/import_or_qualify.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/import_or_qualify.rs index 1141aca23d2d..070dcfcde901 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/import_or_qualify.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/import_or_qualify.rs @@ -6,13 +6,13 @@ use crate::{ byte_span_to_range, modules::module_def_id_relative_path, use_segment_positions::{ - use_completion_item_additional_text_edits, UseCompletionItemAdditionTextEditsRequest, + UseCompletionItemAdditionTextEditsRequest, use_completion_item_additional_text_edits, }, }; use super::CodeActionFinder; -impl<'a> CodeActionFinder<'a> { +impl CodeActionFinder<'_> { pub(super) fn import_or_qualify(&mut self, path: &Path) { if path.segments.len() != 1 { return; diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/import_trait.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/import_trait.rs index 52b3e66033a2..379b627f8785 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/import_trait.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/import_trait.rs @@ -11,13 +11,13 @@ use crate::{ modules::module_def_id_relative_path, requests::TraitReexport, use_segment_positions::{ - use_completion_item_additional_text_edits, UseCompletionItemAdditionTextEditsRequest, + UseCompletionItemAdditionTextEditsRequest, use_completion_item_additional_text_edits, }, }; use super::CodeActionFinder; -impl<'a> CodeActionFinder<'a> { +impl CodeActionFinder<'_> { pub(super) fn import_trait_in_method_call(&mut self, method_call: &MethodCallExpression) { // First see if the method name already points to a function. let name_location = Location::new(method_call.method_name.span(), self.file); @@ -36,7 +36,7 @@ impl<'a> CodeActionFinder<'a> { } // Find out the type of the object - let object_location = Location::new(method_call.object.span, self.file); + let object_location = method_call.object.location; let Some(typ) = self.interner.type_at_location(object_location) else { return; }; diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_bang_from_call.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_bang_from_call.rs index 90f4fef0efd2..e2ef2ef84eed 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_bang_from_call.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_bang_from_call.rs @@ -1,12 +1,12 @@ use lsp_types::TextEdit; use noirc_errors::{Location, Span}; -use noirc_frontend::{node_interner::ReferenceId, QuotedType, Type}; +use noirc_frontend::{QuotedType, Type, node_interner::ReferenceId}; use crate::byte_span_to_range; use super::CodeActionFinder; -impl<'a> CodeActionFinder<'a> { +impl CodeActionFinder<'_> { pub(super) fn remove_bang_from_call(&mut self, span: Span) { // If we can't find the referenced function, there's nothing we can do let Some(ReferenceId::Function(func_id)) = diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_unused_import.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_unused_import.rs index 4822a9d61ec8..41d43baa656f 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_unused_import.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_unused_import.rs @@ -1,19 +1,20 @@ use std::collections::HashMap; +use fm::FileId; use lsp_types::TextEdit; -use noirc_errors::Span; +use noirc_errors::{Location, Span}; use noirc_frontend::{ + ParsedModule, ast::{Ident, ItemVisibility, UseTree, UseTreeKind}, parser::{Item, ItemKind}, usage_tracker::UnusedItem, - ParsedModule, }; use crate::byte_span_to_range; use super::CodeActionFinder; -impl<'a> CodeActionFinder<'a> { +impl CodeActionFinder<'_> { pub(super) fn remove_unused_import( &mut self, use_tree: &UseTree, @@ -80,11 +81,7 @@ fn use_tree_without_unused_import( match &use_tree.kind { UseTreeKind::Path(name, alias) => { let ident = alias.as_ref().unwrap_or(name); - if unused_items.contains_key(ident) { - (None, 1) - } else { - (Some(use_tree.clone()), 0) - } + if unused_items.contains_key(ident) { (None, 1) } else { (Some(use_tree.clone()), 0) } } UseTreeKind::List(use_trees) => { let mut new_use_trees: Vec = Vec::new(); @@ -106,12 +103,12 @@ fn use_tree_without_unused_import( let mut prefix = use_tree.prefix.clone(); prefix.segments.extend(new_use_tree.prefix.segments); - Some(UseTree { prefix, kind: new_use_tree.kind, span: use_tree.span }) + Some(UseTree { prefix, kind: new_use_tree.kind, location: use_tree.location }) } else { Some(UseTree { prefix: use_tree.prefix.clone(), kind: UseTreeKind::List(new_use_trees), - span: use_tree.span, + location: use_tree.location, }) }; @@ -130,7 +127,7 @@ fn use_tree_to_string(use_tree: UseTree, visibility: ItemVisibility, nesting: us let parsed_module = ParsedModule { items: vec![Item { kind: ItemKind::Import(use_tree, visibility), - span: Span::from(0..source.len() as u32), + location: Location::new(Span::from(0..source.len() as u32), FileId::dummy()), doc_comments: Vec::new(), }], inner_doc_comments: Vec::new(), diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/tests.rs index f5748e29d97b..23026d16d943 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/tests.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/tests.rs @@ -56,11 +56,7 @@ pub(crate) async fn assert_code_action(title: &str, src: &str, expected: &str) { .iter() .filter_map(|action| { if let CodeActionOrCommand::CodeAction(action) = action { - if action.title == title { - Some(action) - } else { - None - } + if action.title == title { Some(action) } else { None } } else { None } 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 ab98ab8bf100..1870e8e06024 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 @@ -7,9 +7,8 @@ use noirc_driver::check_crate; use noirc_frontend::hir::FunctionNameMatch; use crate::{ - byte_span_to_range, prepare_source, resolve_workspace_for_source_path, + LspState, byte_span_to_range, prepare_source, resolve_workspace_for_source_path, types::{CodeLens, CodeLensParams, CodeLensResult, Command}, - LspState, }; const ARROW: &str = "▶\u{fe0e}"; @@ -40,7 +39,7 @@ fn package_selection_args(workspace: &Workspace, package: &Package) -> Vec impl Future> { +) -> impl Future> + use<> { future::ready(on_code_lens_request_inner(state, params)) } diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion.rs b/noir/noir-repo/tooling/lsp/src/requests/completion.rs index 2f06f6607712..eab8522693f8 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion.rs @@ -15,6 +15,7 @@ use kinds::{FunctionCompletionKind, FunctionKind, RequestedItems}; use lsp_types::{CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse}; use noirc_errors::{Location, Span}; use noirc_frontend::{ + DataType, Kind, ParsedModule, Type, TypeBinding, ast::{ AsTraitPath, AttributeTarget, BlockExpression, CallExpression, ConstructorExpression, Expression, ExpressionKind, ForLoopStatement, GenericTypeArgs, Ident, IfExpression, @@ -35,17 +36,16 @@ use noirc_frontend::{ node_interner::{FuncId, NodeInterner, ReferenceId, TypeId}, parser::{Item, ItemKind, ParsedSubModule}, token::{MetaAttribute, Token, Tokens}, - DataType, Kind, ParsedModule, Type, TypeBinding, }; use sort_text::underscore_sort_text; use crate::{ - requests::to_lsp_location, trait_impl_method_stub_generator::TraitImplMethodStubGenerator, + LspState, requests::to_lsp_location, + trait_impl_method_stub_generator::TraitImplMethodStubGenerator, use_segment_positions::UseSegmentPositions, utils, visibility::module_def_id_is_visible, - LspState, }; -use super::{process_request, TraitReexport}; +use super::{TraitReexport, process_request}; mod auto_import; mod builtins; @@ -57,7 +57,7 @@ mod tests; pub(crate) fn on_completion_request( state: &mut LspState, params: CompletionParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let uri = params.text_document_position.clone().text_document.uri; let result = process_request(state, params.text_document_position.clone(), |args| { @@ -72,7 +72,7 @@ pub(crate) fn on_completion_request( let file = args.files.get_file(file_id).unwrap(); let source = file.source(); let byte = source.as_bytes().get(byte_index - 1).copied(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let mut finder = NodeFinder::new( args.files, @@ -196,7 +196,7 @@ impl<'a> NodeFinder<'a> { let span = if let UnresolvedTypeData::Named(path, _, _) = &constructor_expression.typ.typ { path.last_ident().span() } else { - constructor_expression.typ.span + constructor_expression.typ.location.span }; let location = Location::new(span, self.file); @@ -241,7 +241,7 @@ impl<'a> NodeFinder<'a> { requested_items: RequestedItems, mut in_the_middle: bool, ) { - if !self.includes_span(path.span) { + if !self.includes_span(path.location.span) { return; } @@ -268,7 +268,10 @@ impl<'a> NodeFinder<'a> { let substring = ident.0.contents[0..offset].to_string(); let ident = Ident::new( substring, - Span::from(ident.span().start()..ident.span().start() + offset as u32), + Location::new( + Span::from(ident.span().start()..ident.span().start() + offset as u32), + ident.location().file, + ), ); idents.push(ident); in_the_middle = true; @@ -605,7 +608,7 @@ impl<'a> NodeFinder<'a> { self.complete_tuple_fields(types, self_prefix); } Type::TypeVariable(var) | Type::NamedGeneric(var, _) => { - if let TypeBinding::Bound(ref typ) = &*var.borrow() { + if let TypeBinding::Bound(typ) = &*var.borrow() { return self.complete_type_fields_and_methods( typ, prefix, @@ -1020,7 +1023,7 @@ impl<'a> NodeFinder<'a> { noir_function: &NoirFunction, ) { // First find the trait - let location = Location::new(noir_trait_impl.r#trait.span, self.file); + let location = noir_trait_impl.r#trait.location; let Some(ReferenceId::Trait(trait_id)) = self.interner.find_referenced(location) else { return; }; @@ -1199,16 +1202,16 @@ impl<'a> NodeFinder<'a> { } } -impl<'a> Visitor for NodeFinder<'a> { +impl Visitor for NodeFinder<'_> { fn visit_item(&mut self, item: &Item) -> bool { if let ItemKind::Import(use_tree, _) = &item.kind { - if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { + if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.location.span) { self.auto_import_line = (lsp_location.range.end.line + 1) as usize; } self.use_segment_positions.add(use_tree); } - self.includes_span(item.span) + self.includes_span(item.location.span) } fn visit_import( @@ -1342,11 +1345,11 @@ impl<'a> Visitor for NodeFinder<'a> { self.type_parameters.clear(); self.collect_type_parameters_in_generics(&type_impl.generics); - for (method, span) in &type_impl.methods { - method.item.accept(*span, self); + for (method, location) in &type_impl.methods { + method.item.accept(location.span, self); // Optimization: stop looking in functions past the completion cursor - if span.end() as usize > self.byte_index { + if location.span.end() as usize > self.byte_index { break; } } @@ -1425,7 +1428,7 @@ impl<'a> Visitor for NodeFinder<'a> { // we don't want to insert arguments, because they are already there (even if // they could be wrong) just because inserting them would lead to broken code. if let ExpressionKind::Variable(path) = &call_expression.func.kind { - if self.includes_span(path.span) { + if self.includes_span(path.location.span) { self.find_in_path_impl(path, RequestedItems::AnyItems, true); return false; } @@ -1439,8 +1442,8 @@ impl<'a> Visitor for NodeFinder<'a> { // as "foo(...)" but if we are at a dot right after "foo" it means it's // the above case and we want to suggest methods of foo's type. let after_dot = self.byte == Some(b'.'); - if after_dot && call_expression.func.span.end() as usize == self.byte_index - 1 { - let location = Location::new(call_expression.func.span, self.file); + if after_dot && call_expression.func.location.span.end() as usize == self.byte_index - 1 { + let location = call_expression.func.location; if let Some(typ) = self.interner.type_at_location(location) { let prefix = ""; let self_prefix = false; @@ -1470,7 +1473,7 @@ impl<'a> Visitor for NodeFinder<'a> { // we don't want to insert arguments, because they are already there (even if // they could be wrong) just because inserting them would lead to broken code. if self.includes_span(method_call_expression.method_name.span()) { - let location = Location::new(method_call_expression.object.span, self.file); + let location = method_call_expression.object.location; if let Some(typ) = self.interner.type_at_location(location) { let prefix = method_call_expression.method_name.to_string(); let offset = @@ -1500,7 +1503,7 @@ impl<'a> Visitor for NodeFinder<'a> { statement.accept(self); // Optimization: stop looking in statements past the completion cursor - if statement.span.end() as usize > self.byte_index { + if statement.location.span.end() as usize > self.byte_index { break; } } @@ -1648,9 +1651,9 @@ impl<'a> Visitor for NodeFinder<'a> { // to complete for `bar`, not for `foo & bar`. if self.completion_items.is_empty() && self.byte == Some(b'.') - && expression.span.end() as usize == self.byte_index - 1 + && expression.location.span.end() as usize == self.byte_index - 1 { - let location = Location::new(expression.span, self.file); + let location = expression.location; if let Some(typ) = self.interner.type_at_location(location) { let prefix = ""; let self_prefix = false; @@ -1723,7 +1726,7 @@ impl<'a> Visitor for NodeFinder<'a> { if self.byte_index == ident.span().end() as usize { // Assuming member_access_expression is of the form `foo.bar`, we are right after `bar` - let location = Location::new(member_access_expression.lhs.span, self.file); + let location = member_access_expression.lhs.location; if let Some(typ) = self.interner.type_at_location(location) { let prefix = ident.to_string().to_case(Case::Snake); let self_prefix = false; @@ -1780,7 +1783,7 @@ impl<'a> Visitor for NodeFinder<'a> { } fn visit_unresolved_type(&mut self, unresolved_type: &UnresolvedType) -> bool { - self.includes_span(unresolved_type.span) + self.includes_span(unresolved_type.location.span) } fn visit_named_type( @@ -1833,7 +1836,7 @@ impl<'a> Visitor for NodeFinder<'a> { } fn visit_meta_attribute(&mut self, attribute: &MetaAttribute, target: AttributeTarget) -> bool { - if self.byte_index == attribute.name.span.end() as usize { + if self.byte_index == attribute.name.location.span.end() as usize { self.suggest_builtin_attributes(&attribute.name.to_string(), target); } @@ -1846,7 +1849,7 @@ impl<'a> Visitor for NodeFinder<'a> { let mut last_was_dollar = false; for token in &tokens.0 { - let span = token.to_span(); + let span = token.span(); if span.end() as usize > self.byte_index { break; } @@ -1903,13 +1906,10 @@ fn get_field_type(typ: &Type, name: &str) -> Option { } } Type::Alias(alias_type, generics) => Some(alias_type.borrow().get_type(generics)), - Type::TypeVariable(var) | Type::NamedGeneric(var, _) => { - if let TypeBinding::Bound(ref typ) = &*var.borrow() { - get_field_type(typ, name) - } else { - None - } - } + Type::TypeVariable(var) | Type::NamedGeneric(var, _) => match &*var.borrow() { + TypeBinding::Bound(typ) => get_field_type(typ, name), + _ => None, + }, _ => None, } } @@ -1921,13 +1921,10 @@ fn get_array_element_type(typ: Type) -> Option { let typ = alias_type.borrow().get_type(&generics); get_array_element_type(typ) } - Type::TypeVariable(var) | Type::NamedGeneric(var, _) => { - if let TypeBinding::Bound(typ) = &*var.borrow() { - get_array_element_type(typ.clone()) - } else { - None - } - } + Type::TypeVariable(var) | Type::NamedGeneric(var, _) => match &*var.borrow() { + TypeBinding::Bound(typ) => get_array_element_type(typ.clone()), + _ => None, + }, _ => None, } } diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs index 08d155f333c8..cc80c8d8e010 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs @@ -3,18 +3,18 @@ use noirc_frontend::{ast::ItemVisibility, hir::def_map::ModuleDefId, node_intern use crate::{ modules::{get_ancestor_module_reexport, module_def_id_relative_path}, use_segment_positions::{ - use_completion_item_additional_text_edits, UseCompletionItemAdditionTextEditsRequest, + UseCompletionItemAdditionTextEditsRequest, use_completion_item_additional_text_edits, }, }; use super::{ + NodeFinder, kinds::{FunctionCompletionKind, FunctionKind, RequestedItems}, name_matches, sort_text::auto_import_sort_text, - NodeFinder, }; -impl<'a> NodeFinder<'a> { +impl NodeFinder<'_> { pub(super) fn complete_auto_imports( &mut self, prefix: &str, diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs index 10267d4719bc..ce5b5f35f461 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs @@ -3,15 +3,16 @@ use noirc_frontend::{ast::AttributeTarget, token::Keyword}; use strum::IntoEnumIterator; use super::{ + NodeFinder, completion_items::{ completion_item_with_trigger_parameter_hints_command, simple_completion_item, snippet_completion_item, }, kinds::FunctionCompletionKind, - name_matches, NodeFinder, + name_matches, }; -impl<'a> NodeFinder<'a> { +impl NodeFinder<'_> { pub(super) fn builtin_functions_completion( &mut self, prefix: &str, @@ -150,8 +151,8 @@ impl<'a> NodeFinder<'a> { } } -pub(super) fn builtin_integer_types() -> [&'static str; 8] { - ["i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64"] +pub(super) fn builtin_integer_types() -> [&'static str; 9] { + ["i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", "u128"] } /// If a keyword corresponds to a built-in type, returns that type's name. diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs index cfd11bfe1adf..bc266c03f768 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs @@ -3,29 +3,29 @@ use lsp_types::{ InsertTextFormat, MarkupContent, MarkupKind, }; use noirc_frontend::{ + QuotedType, Type, ast::AttributeTarget, hir::def_map::{ModuleDefId, ModuleId}, hir_def::{function::FuncMeta, stmt::HirPattern}, node_interner::{FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId, TypeId}, - QuotedType, Type, }; use crate::{ modules::{relative_module_full_path, relative_module_id_path}, use_segment_positions::{ - use_completion_item_additional_text_edits, UseCompletionItemAdditionTextEditsRequest, + UseCompletionItemAdditionTextEditsRequest, use_completion_item_additional_text_edits, }, }; use super::{ + FunctionCompletionKind, FunctionKind, NodeFinder, RequestedItems, TraitReexport, sort_text::{ crate_or_module_sort_text, default_sort_text, new_sort_text, operator_sort_text, self_mismatch_sort_text, }, - FunctionCompletionKind, FunctionKind, NodeFinder, RequestedItems, TraitReexport, }; -impl<'a> NodeFinder<'a> { +impl NodeFinder<'_> { pub(super) fn module_def_id_completion_items( &self, module_def_id: ModuleDefId, @@ -44,7 +44,7 @@ impl<'a> NodeFinder<'a> { }, RequestedItems::OnlyTraits => match module_def_id { ModuleDefId::FunctionId(_) | ModuleDefId::GlobalId(_) | ModuleDefId::TypeId(_) => { - return Vec::new() + return Vec::new(); } ModuleDefId::ModuleId(_) | ModuleDefId::TypeAliasId(_) diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs index ba6faada6f4f..042b10b9cbab 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs @@ -1,4 +1,4 @@ -use noirc_frontend::{ast::AttributeTarget, Type}; +use noirc_frontend::{Type, ast::AttributeTarget}; /// When suggest a function as a result of completion, whether to autocomplete its name or its name and parameters. #[derive(Clone, Copy, PartialEq, Eq, Debug)] diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs index cbe1a93391a7..3958b92deb08 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs @@ -67,11 +67,7 @@ mod completion_tests { .await .expect("Could not execute on_completion_request"); - if let Some(CompletionResponse::Array(items)) = response { - items - } else { - vec![] - } + if let Some(CompletionResponse::Array(items)) = response { items } else { vec![] } } fn assert_items_match(mut items: Vec, mut expected: Vec) { @@ -799,9 +795,11 @@ mod completion_tests { "#; let items = get_completions(src).await; - assert!(items - .iter() - .any(|item| item.label == "true" && item.kind == Some(CompletionItemKind::KEYWORD))); + assert!( + items + .iter() + .any(|item| item.label == "true" && item.kind == Some(CompletionItemKind::KEYWORD)) + ); } #[test] @@ -2659,8 +2657,8 @@ fn main() { } #[test] - async fn test_suggests_only_macro_call_if_comptime_function_returns_quoted_and_outside_comptime( - ) { + async fn test_suggests_only_macro_call_if_comptime_function_returns_quoted_and_outside_comptime() + { let src = r#" comptime fn foobar() -> Quoted {} 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 b32b2fc7ad7f..4827d8827afb 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/document_symbol.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/document_symbol.rs @@ -8,12 +8,12 @@ use lsp_types::{ }; use noirc_errors::Span; use noirc_frontend::{ + ParsedModule, ast::{ Expression, FunctionReturnType, Ident, LetStatement, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, TypeImpl, UnresolvedType, UnresolvedTypeData, Visitor, }, parser::ParsedSubModule, - ParsedModule, }; use crate::LspState; @@ -23,7 +23,7 @@ use super::process_request; pub(crate) fn on_document_symbol_request( state: &mut LspState, params: DocumentSymbolParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let Ok(file_path) = params.text_document.uri.to_file_path() else { return future::ready(Ok(None)); }; @@ -37,7 +37,7 @@ pub(crate) fn on_document_symbol_request( args.files.get_file_id(&PathString::from_path(file_path)).map(|file_id| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let mut collector = DocumentSymbolCollector::new(file_id, args.files); let symbols = collector.collect(&parsed_module); @@ -75,7 +75,7 @@ impl<'a> DocumentSymbolCollector<'a> { }; let span = if let Some(typ) = typ { - Span::from(name.span().start()..typ.span.end()) + Span::from(name.span().start()..typ.location.span.end()) } else { name.span() }; @@ -114,11 +114,11 @@ impl<'a> DocumentSymbolCollector<'a> { let mut span = name.span(); // If there's a type span, extend the span to include it - span = Span::from(span.start()..typ.span.end()); + span = Span::from(span.start()..typ.location.span.end()); // If there's a default value, extend the span to include it if let Some(default_value) = default_value { - span = Span::from(span.start()..default_value.span.end()); + span = Span::from(span.start()..default_value.location.span.end()); } let Some(location) = self.to_lsp_location(span) else { @@ -143,7 +143,7 @@ impl<'a> DocumentSymbolCollector<'a> { } } -impl<'a> Visitor for DocumentSymbolCollector<'a> { +impl Visitor for DocumentSymbolCollector<'_> { fn visit_noir_function(&mut self, noir_function: &NoirFunction, span: Span) -> bool { if noir_function.def.name.0.contents.is_empty() { return false; @@ -190,7 +190,7 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { for field in &noir_struct.fields { let field_name = &field.item.name; let typ = &field.item.typ; - let span = Span::from(field_name.span().start()..typ.span.end()); + let span = Span::from(field_name.span().start()..typ.location.span.end()); let Some(field_location) = self.to_lsp_location(span) else { continue; @@ -292,18 +292,18 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { // If there's a return type, extend the span to include it match return_type { - FunctionReturnType::Default(return_type_span) => { - span = Span::from(span.start()..return_type_span.end()); + FunctionReturnType::Default(return_type_location) => { + span = Span::from(span.start()..return_type_location.span.end()); } FunctionReturnType::Ty(typ) => { - span = Span::from(span.start()..typ.span.end()); + span = Span::from(span.start()..typ.location.span.end()); } } // If there's a body, extend the span to include it if let Some(body) = body { if let Some(statement) = body.statements.last() { - span = Span::from(span.start()..statement.span.end()); + span = Span::from(span.start()..statement.location.span.end()); } } @@ -349,14 +349,14 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { return false; }; - let name_span = + let name_location = if let UnresolvedTypeData::Named(trait_name, _, _) = &noir_trait_impl.r#trait.typ { - trait_name.span + trait_name.location } else { - noir_trait_impl.r#trait.span + noir_trait_impl.r#trait.location }; - let Some(name_location) = self.to_lsp_location(name_span) else { + let Some(name_location) = self.to_lsp_location(name_location.span) else { return false; }; @@ -418,15 +418,15 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { return false; } - let Some(name_location) = self.to_lsp_location(type_impl.object_type.span) else { + let Some(name_location) = self.to_lsp_location(type_impl.object_type.location.span) else { return false; }; let old_symbols = std::mem::take(&mut self.symbols); self.symbols = Vec::new(); - for (noir_function, noir_function_span) in &type_impl.methods { - noir_function.item.accept(*noir_function_span, self); + for (noir_function, noir_function_location) in &type_impl.methods { + noir_function.item.accept(noir_function_location.span, self); } let children = std::mem::take(&mut self.symbols); @@ -660,7 +660,7 @@ mod document_symbol_tests { deprecated: None, range: Range { start: Position { line: 15, character: 7 }, - end: Position { line: 15, character: 24 }, + end: Position { line: 15, character: 25 }, }, selection_range: Range { start: Position { line: 15, character: 7 }, diff --git a/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs b/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs index bd0f0afb827c..9df6a25f8cf1 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs @@ -1,7 +1,7 @@ use std::future::{self, Future}; -use crate::types::GotoDeclarationResult; use crate::LspState; +use crate::types::GotoDeclarationResult; use async_lsp::ResponseError; use lsp_types::request::{GotoDeclarationParams, GotoDeclarationResponse}; @@ -11,7 +11,7 @@ use super::{process_request, to_lsp_location}; pub(crate) fn on_goto_declaration_request( state: &mut LspState, params: GotoDeclarationParams, -) -> impl Future> { +) -> impl Future> + use<> { let result = on_goto_definition_inner(state, params); future::ready(result) } diff --git a/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs b/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs index a2443ea165dc..4e015e948610 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs @@ -2,7 +2,7 @@ use std::future::{self, Future}; use crate::attribute_reference_finder::AttributeReferenceFinder; use crate::utils; -use crate::{types::GotoDefinitionResult, LspState}; +use crate::{LspState, types::GotoDefinitionResult}; use async_lsp::ResponseError; use fm::PathString; @@ -14,7 +14,7 @@ use super::{process_request, to_lsp_location}; pub(crate) fn on_goto_definition_request( state: &mut LspState, params: GotoDefinitionParams, -) -> impl Future> { +) -> impl Future> + use<> { let result = on_goto_definition_inner(state, params, false); future::ready(result) } @@ -22,7 +22,7 @@ pub(crate) fn on_goto_definition_request( pub(crate) fn on_goto_type_definition_request( state: &mut LspState, params: GotoTypeDefinitionParams, -) -> impl Future> { +) -> impl Future> + use<> { let result = on_goto_definition_inner(state, params, true); future::ready(result) } @@ -40,7 +40,7 @@ fn on_goto_definition_inner( utils::position_to_byte_index(args.files, file_id, &position).and_then(|byte_index| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let mut finder = AttributeReferenceFinder::new( file_id, diff --git a/noir/noir-repo/tooling/lsp/src/requests/hover.rs b/noir/noir-repo/tooling/lsp/src/requests/hover.rs index cbb7dafd3c51..0b53b56eaf91 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/hover.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/hover.rs @@ -16,7 +16,7 @@ mod from_visitor; pub(crate) fn on_hover_request( state: &mut LspState, params: HoverParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let uri = params.text_document_position_params.text_document.uri.clone(); let position = params.text_document_position_params.position; let result = process_request(state, params.text_document_position_params, |args| { diff --git a/noir/noir-repo/tooling/lsp/src/requests/hover/from_reference.rs b/noir/noir-repo/tooling/lsp/src/requests/hover/from_reference.rs index 7f589b9df70a..3bc3b3bded70 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/hover/from_reference.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/hover/from_reference.rs @@ -1,6 +1,8 @@ use fm::{FileId, FileMap}; use lsp_types::{Hover, HoverContents, MarkupContent, MarkupKind, Position}; use noirc_frontend::{ + DataType, EnumVariant, Generics, Shared, StructField, Type, TypeAlias, TypeBinding, + TypeVariable, ast::{ItemVisibility, Visibility}, hir::def_map::ModuleId, hir_def::{ @@ -13,14 +15,12 @@ use noirc_frontend::{ DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, NodeInterner, ReferenceId, TraitId, TraitImplKind, TypeAliasId, TypeId, }, - DataType, EnumVariant, Generics, Shared, StructField, Type, TypeAlias, TypeBinding, - TypeVariable, }; use crate::{ attribute_reference_finder::AttributeReferenceFinder, modules::module_full_path, - requests::{to_lsp_location, ProcessRequestCallbackArgs}, + requests::{ProcessRequestCallbackArgs, to_lsp_location}, utils, }; @@ -34,7 +34,7 @@ pub(super) fn hover_from_reference( utils::position_to_byte_index(args.files, file_id, &position).and_then(|byte_index| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let mut finder = AttributeReferenceFinder::new( file_id, @@ -318,7 +318,7 @@ fn get_global_value(interner: &NodeInterner, expr: ExprId) -> Option { get_global_array_value(interner, hir_array_literal, true) } HirLiteral::Bool(value) => Some(value.to_string()), - HirLiteral::Integer(field_element, _) => Some(field_element.to_string()), + HirLiteral::Integer(value) => Some(value.to_string()), HirLiteral::Str(string) => Some(format!("{:?}", string)), HirLiteral::FmtStr(..) => None, HirLiteral::Unit => Some("()".to_string()), @@ -338,11 +338,7 @@ fn get_global_array_value( match literal { HirArrayLiteral::Standard(values) => { get_exprs_global_value(interner, &values).map(|value| { - if is_slice { - format!("&[{}]", value) - } else { - format!("[{}]", value) - } + if is_slice { format!("&[{}]", value) } else { format!("[{}]", value) } }) } HirArrayLiteral::Repeated { repeated_element, length } => { @@ -360,11 +356,7 @@ fn get_global_array_value( fn get_exprs_global_value(interner: &NodeInterner, exprs: &[ExprId]) -> Option { let strings: Vec = exprs.iter().filter_map(|value| get_global_value(interner, *value)).collect(); - if strings.len() == exprs.len() { - Some(strings.join(", ")) - } else { - None - } + if strings.len() == exprs.len() { Some(strings.join(", ")) } else { None } } fn format_function(id: FuncId, args: &ProcessRequestCallbackArgs) -> String { @@ -403,11 +395,7 @@ fn format_function(id: FuncId, args: &ProcessRequestCallbackArgs) -> String { .trait_generics .iter() .filter_map(|generic| { - if let Type::NamedGeneric(_, name) = generic { - Some(name) - } else { - None - } + if let Type::NamedGeneric(_, name) = generic { Some(name) } else { None } }) .collect(); @@ -781,7 +769,7 @@ struct TypeLinksGatherer<'a> { links: Vec, } -impl<'a> TypeLinksGatherer<'a> { +impl TypeLinksGatherer<'_> { fn gather_type_links(&mut self, typ: &Type) { match typ { Type::Array(typ, _) => self.gather_type_links(typ), diff --git a/noir/noir-repo/tooling/lsp/src/requests/hover/from_visitor.rs b/noir/noir-repo/tooling/lsp/src/requests/hover/from_visitor.rs index 2099d98a93f6..97ead183cd21 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/hover/from_visitor.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/hover/from_visitor.rs @@ -1,14 +1,15 @@ use std::str::FromStr; -use acvm::FieldElement; use fm::{FileId, FileMap}; use lsp_types::{Hover, HoverContents, MarkupContent, MarkupKind, Position}; use noirc_errors::{Location, Span}; -use noirc_frontend::{ast::Visitor, node_interner::NodeInterner, parse_program, Type}; +use noirc_frontend::{ + Type, ast::Visitor, node_interner::NodeInterner, parse_program, signed_field::SignedField, +}; use num_bigint::BigInt; use crate::{ - requests::{to_lsp_location, ProcessRequestCallbackArgs}, + requests::{ProcessRequestCallbackArgs, to_lsp_location}, utils, }; @@ -20,7 +21,7 @@ pub(super) fn hover_from_visitor( let file_id = file_id?; let file = args.files.get_file(file_id)?; let source = file.source(); - let (parsed_module, _errors) = parse_program(source); + let (parsed_module, _errors) = parse_program(source, file_id); let byte_index = utils::position_to_byte_index(args.files, file_id, &position)?; let mut finder = HoverFinder::new(args.files, file_id, args.interner, byte_index); @@ -50,8 +51,8 @@ impl<'a> HoverFinder<'a> { } } -impl<'a> Visitor for HoverFinder<'a> { - fn visit_literal_integer(&mut self, value: FieldElement, negative: bool, span: Span) { +impl Visitor for HoverFinder<'_> { + fn visit_literal_integer(&mut self, value: SignedField, span: Span) { if !self.intersects_span(span) { return; } @@ -63,28 +64,31 @@ impl<'a> Visitor for HoverFinder<'a> { return; }; - let value = format_integer(typ, value, negative); + let value = format_integer(typ, value); let contents = HoverContents::Markup(MarkupContent { kind: MarkupKind::Markdown, value }); self.hover = Some(Hover { contents, range }); } } -fn format_integer(typ: Type, value: FieldElement, negative: bool) -> String { - let value_base_10 = value.to_string(); +fn format_integer(typ: Type, value: SignedField) -> String { + let value_base_10 = value.field.to_string(); // For simplicity we parse the value as a BigInt to convert it to hex // because `FieldElement::to_hex` will include many leading zeros. let value_big_int = BigInt::from_str(&value_base_10).unwrap(); - let negative = if negative { "-" } else { "" }; + let negative = if value.is_negative { "-" } else { "" }; - format!(" {typ}\n---\nvalue of literal: `{negative}{value_base_10} ({negative}0x{value_big_int:02x})`") + format!( + " {typ}\n---\nvalue of literal: `{negative}{value_base_10} ({negative}0x{value_big_int:02x})`" + ) } #[cfg(test)] mod tests { use noirc_frontend::{ - ast::{IntegerBitSize, Signedness}, Type, + ast::{IntegerBitSize, Signedness}, + signed_field::SignedField, }; use super::format_integer; @@ -92,27 +96,24 @@ mod tests { #[test] fn format_integer_zero() { let typ = Type::FieldElement; - let value = 0_u128.into(); - let negative = false; + let value = SignedField::positive(0_u128); let expected = " Field\n---\nvalue of literal: `0 (0x00)`"; - assert_eq!(format_integer(typ, value, negative), expected); + assert_eq!(format_integer(typ, value), expected); } #[test] fn format_positive_integer() { let typ = Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo); - let value = 123456_u128.into(); - let negative = false; + let value = SignedField::positive(123456_u128); let expected = " u32\n---\nvalue of literal: `123456 (0x1e240)`"; - assert_eq!(format_integer(typ, value, negative), expected); + assert_eq!(format_integer(typ, value), expected); } #[test] fn format_negative_integer() { let typ = Type::Integer(Signedness::Signed, IntegerBitSize::SixtyFour); - let value = 987654_u128.into(); - let negative = true; + let value = SignedField::new(987654_u128.into(), true); let expected = " i64\n---\nvalue of literal: `-987654 (-0xf1206)`"; - assert_eq!(format_integer(typ, value, negative), expected); + assert_eq!(format_integer(typ, value), expected); } } 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 e2f793f06da8..be7722e744d2 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs @@ -8,7 +8,7 @@ use lsp_types::{ }; use noirc_errors::{Location, Span}; use noirc_frontend::{ - self, + self, Kind, Type, TypeBinding, TypeVariable, ast::{ CallExpression, Expression, ExpressionKind, ForLoopStatement, Ident, Lambda, LetStatement, MethodCallExpression, NoirFunction, NoirTraitImpl, Pattern, Statement, TypeImpl, @@ -17,17 +17,16 @@ use noirc_frontend::{ hir_def::stmt::HirPattern, node_interner::{NodeInterner, ReferenceId}, parser::{Item, ParsedSubModule}, - Kind, Type, TypeBinding, TypeVariable, }; -use crate::{utils, LspState}; +use crate::{LspState, utils}; -use super::{process_request, to_lsp_location, InlayHintsOptions}; +use super::{InlayHintsOptions, process_request, to_lsp_location}; pub(crate) fn on_inlay_hint_request( state: &mut LspState, params: InlayHintParams, -) -> impl Future>, ResponseError>> { +) -> impl Future>, ResponseError>> + use<> { let text_document_position_params = TextDocumentPositionParams { text_document: params.text_document.clone(), position: Position { line: 0, character: 0 }, @@ -40,7 +39,7 @@ pub(crate) fn on_inlay_hint_request( args.files.get_file_id(&path).map(|file_id| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let span = utils::range_to_byte_span(args.files, file_id, ¶ms.range) .map(|range| Span::from(range.start as u32..range.end as u32)); @@ -191,7 +190,7 @@ impl<'a> InlayHintCollector<'a> { for (call_argument, (pattern, _, _)) in arguments.iter().zip(parameters) { let Some(lsp_location) = - to_lsp_location(self.files, self.file_id, call_argument.span) + to_lsp_location(self.files, self.file_id, call_argument.location.span) else { continue; }; @@ -234,7 +233,7 @@ impl<'a> InlayHintCollector<'a> { fn collect_method_call_chain_hints(&mut self, method: &MethodCallExpression) { let Some(object_lsp_location) = - to_lsp_location(self.files, self.file_id, method.object.span) + to_lsp_location(self.files, self.file_id, method.object.location.span) else { return; }; @@ -249,7 +248,7 @@ impl<'a> InlayHintCollector<'a> { return; } - let object_location = Location::new(method.object.span, self.file_id); + let object_location = method.object.location; let Some(typ) = self.interner.type_at_location(object_location) else { return; }; @@ -302,7 +301,7 @@ impl<'a> InlayHintCollector<'a> { } fn intersects_span(&self, other_span: Span) -> bool { - self.span.map_or(true, |span| span.intersects(&other_span)) + self.span.is_none_or(|span| span.intersects(&other_span)) } fn show_closing_brace_hint(&mut self, span: Span, f: F) @@ -320,9 +319,9 @@ impl<'a> InlayHintCollector<'a> { } } -impl<'a> Visitor for InlayHintCollector<'a> { +impl Visitor for InlayHintCollector<'_> { fn visit_item(&mut self, item: &Item) -> bool { - self.intersects_span(item.span) + self.intersects_span(item.location.span) } fn visit_noir_trait_impl(&mut self, noir_trait_impl: &NoirTraitImpl, span: Span) -> bool { @@ -358,7 +357,7 @@ impl<'a> Visitor for InlayHintCollector<'a> { } fn visit_statement(&mut self, statement: &Statement) -> bool { - self.intersects_span(statement.span) + self.intersects_span(statement.location.span) } fn visit_let_statement(&mut self, let_statement: &LetStatement) -> bool { @@ -378,13 +377,13 @@ impl<'a> Visitor for InlayHintCollector<'a> { } fn visit_expression(&mut self, expression: &Expression) -> bool { - self.intersects_span(expression.span) + self.intersects_span(expression.location.span) } fn visit_call_expression(&mut self, call_expression: &CallExpression, _: Span) -> bool { self.collect_call_parameter_names( get_expression_name(&call_expression.func), - call_expression.func.span, + call_expression.func.location.span, &call_expression.arguments, ); @@ -516,18 +515,17 @@ fn push_type_parts(typ: &Type, parts: &mut Vec, files: &File parts.push(string_part("&mut ")); push_type_parts(typ, parts, files); } - Type::TypeVariable(binding) => { - if let TypeBinding::Unbound(_, kind) = &*binding.borrow() { - match kind { - Kind::Any | Kind::Normal => push_type_variable_parts(binding, parts, files), - Kind::Integer => push_type_parts(&Type::default_int_type(), parts, files), - Kind::IntegerOrField => parts.push(string_part("Field")), - Kind::Numeric(ref typ) => push_type_parts(typ, parts, files), - } - } else { + Type::TypeVariable(binding) => match &*binding.borrow() { + TypeBinding::Unbound(_, kind) => match kind { + Kind::Any | Kind::Normal => push_type_variable_parts(binding, parts, files), + Kind::Integer => push_type_parts(&Type::default_int_type(), parts, files), + Kind::IntegerOrField => parts.push(string_part("Field")), + Kind::Numeric(typ) => push_type_parts(typ, parts, files), + }, + _ => { push_type_variable_parts(binding, parts, files); } - } + }, Type::CheckedCast { to, .. } => push_type_parts(to, parts, files), Type::FieldElement @@ -919,8 +917,8 @@ mod inlay_hints_tests { } #[test] - async fn test_do_not_show_parameter_inlay_hints_if_single_param_name_is_suffix_of_function_name( - ) { + async fn test_do_not_show_parameter_inlay_hints_if_single_param_name_is_suffix_of_function_name() + { let inlay_hints = get_inlay_hints(64, 67, parameter_hints()).await; assert!(inlay_hints.is_empty()); } diff --git a/noir/noir-repo/tooling/lsp/src/requests/mod.rs b/noir/noir-repo/tooling/lsp/src/requests/mod.rs index 9bfe47bdaa55..9126ab38e103 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/mod.rs @@ -2,13 +2,13 @@ use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use std::{collections::HashMap, future::Future}; -use crate::{insert_all_files_for_workspace_into_file_manager, parse_diff, PackageCacheData}; +use crate::{PackageCacheData, insert_all_files_for_workspace_into_file_manager, parse_diff}; use crate::{ resolve_workspace_for_source_path, types::{CodeLensOptions, InitializeParams}, }; use async_lsp::{ErrorCode, ResponseError}; -use fm::{codespan_files::Error, FileMap, PathString}; +use fm::{FileMap, PathString, codespan_files::Error}; use lsp_types::{ CodeActionKind, DeclarationCapability, Location, Position, TextDocumentPositionParams, TextDocumentSyncCapability, TextDocumentSyncKind, TypeDefinitionProviderCapability, Url, @@ -25,8 +25,8 @@ use noirc_frontend::{graph::Dependency, node_interner::NodeInterner}; use serde::{Deserialize, Serialize}; use crate::{ - types::{InitializeResult, NargoCapability, NargoTestsOptions, ServerCapabilities}, LspState, + types::{InitializeResult, NargoCapability, NargoTestsOptions, ServerCapabilities}, }; // Handlers @@ -191,7 +191,7 @@ impl Default for LspInitializationOptions { pub(crate) fn on_initialize( state: &mut LspState, params: InitializeParams, -) -> impl Future> { +) -> impl Future> + use<> { state.root_path = params.root_uri.and_then(|root_uri| root_uri.to_file_path().ok()); let initialization_options: LspInitializationOptions = params .initialization_options @@ -293,7 +293,7 @@ pub(crate) fn on_initialize( pub(crate) fn on_formatting( state: &mut LspState, params: lsp_types::DocumentFormattingParams, -) -> impl Future>, ResponseError>> { +) -> impl Future>, ResponseError>> + use<> { std::future::ready(on_formatting_inner(state, params)) } @@ -308,7 +308,7 @@ fn on_formatting_inner( let path = params.text_document.uri.to_string(); if let Some(source) = state.input_files.get(&path) { - let (module, errors) = noirc_frontend::parse_program(source); + let (module, errors) = noirc_frontend::parse_program_with_dummy_file(source); let is_all_warnings = errors.iter().all(ParserError::is_warning); if !is_all_warnings { return Ok(None); @@ -441,7 +441,7 @@ where pub(crate) fn on_shutdown( _state: &mut LspState, _params: (), -) -> impl Future> { +) -> impl Future> + use<> { async { Ok(()) } } @@ -628,11 +628,7 @@ pub(crate) fn find_all_references_in_workspace( }); locations.dedup(); - if locations.is_empty() { - None - } else { - Some(locations) - } + if locations.is_empty() { None } else { Some(locations) } } else { None } @@ -671,7 +667,7 @@ mod initialization { }; use tokio::test; - use crate::{requests::on_initialize, types::ServerCapabilities, LspState}; + use crate::{LspState, requests::on_initialize, types::ServerCapabilities}; #[test] async fn test_on_initialize() { diff --git a/noir/noir-repo/tooling/lsp/src/requests/references.rs b/noir/noir-repo/tooling/lsp/src/requests/references.rs index 3b4ef12b5b74..fbe69c99871d 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/references.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/references.rs @@ -10,7 +10,7 @@ use super::{find_all_references_in_workspace, process_request}; pub(crate) fn on_references_request( state: &mut LspState, params: ReferenceParams, -) -> impl Future>, ResponseError>> { +) -> impl Future>, ResponseError>> + use<> { let include_declaration = params.context.include_declaration; let result = process_request(state, params.text_document_position, |args| { find_all_references_in_workspace( diff --git a/noir/noir-repo/tooling/lsp/src/requests/rename.rs b/noir/noir-repo/tooling/lsp/src/requests/rename.rs index 95dd6b506be2..00e39451caea 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/rename.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/rename.rs @@ -16,7 +16,7 @@ use super::{find_all_references_in_workspace, process_request}; pub(crate) fn on_prepare_rename_request( state: &mut LspState, params: TextDocumentPositionParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let result = process_request(state, params, |args| { let reference_id = args.interner.reference_at_location(args.location); let rename_possible = match reference_id { @@ -33,7 +33,7 @@ pub(crate) fn on_prepare_rename_request( pub(crate) fn on_rename_request( state: &mut LspState, params: RenameParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let result = process_request(state, params.text_document_position, |args| { let rename_changes = find_all_references_in_workspace( args.location, @@ -111,7 +111,10 @@ mod rename_tests { changes.iter().filter(|range| !ranges.contains(range)).collect(); let extra_in_ranges: Vec<_> = ranges.iter().filter(|range| !changes.contains(range)).collect(); - panic!("Rename locations did not match.\nThese renames were not found: {:?}\nThese renames should not have been found: {:?}", extra_in_ranges, extra_in_changes); + panic!( + "Rename locations did not match.\nThese renames were not found: {:?}\nThese renames should not have been found: {:?}", + extra_in_ranges, extra_in_changes + ); } assert_eq!(changes, ranges); } diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs index 4a2609d7ae38..1709106e1218 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs @@ -7,6 +7,7 @@ use lsp_types::{ }; use noirc_errors::{Location, Span}; use noirc_frontend::{ + ParsedModule, Type, ast::{ CallExpression, ConstrainExpression, ConstrainKind, Expression, FunctionReturnType, MethodCallExpression, Statement, Visitor, @@ -14,10 +15,9 @@ use noirc_frontend::{ hir_def::{function::FuncMeta, stmt::HirPattern}, node_interner::{NodeInterner, ReferenceId}, parser::Item, - ParsedModule, Type, }; -use crate::{utils, LspState}; +use crate::{LspState, utils}; use super::process_request; @@ -26,7 +26,7 @@ mod tests; pub(crate) fn on_signature_help_request( state: &mut LspState, params: SignatureHelpParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let uri = params.text_document_position_params.clone().text_document.uri; let result = process_request(state, params.text_document_position_params.clone(), |args| { @@ -40,7 +40,7 @@ pub(crate) fn on_signature_help_request( .and_then(|byte_index| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let mut finder = SignatureFinder::new(file_id, byte_index, args.interner); finder.find(&parsed_module) @@ -312,7 +312,9 @@ impl<'a> SignatureFinder<'a> { fn compute_active_parameter(&self, arguments: &[Expression]) -> Option { let mut active_parameter = None; for (index, arg) in arguments.iter().enumerate() { - if self.includes_span(arg.span) || arg.span.start() as usize >= self.byte_index { + if self.includes_span(arg.location.span) + || arg.location.span.start() as usize >= self.byte_index + { active_parameter = Some(index as u32); break; } @@ -330,24 +332,25 @@ impl<'a> SignatureFinder<'a> { } } -impl<'a> Visitor for SignatureFinder<'a> { +impl Visitor for SignatureFinder<'_> { fn visit_item(&mut self, item: &Item) -> bool { - self.includes_span(item.span) + self.includes_span(item.location.span) } fn visit_statement(&mut self, statement: &Statement) -> bool { - self.includes_span(statement.span) + self.includes_span(statement.location.span) } fn visit_expression(&mut self, expression: &Expression) -> bool { - self.includes_span(expression.span) + self.includes_span(expression.location.span) } fn visit_call_expression(&mut self, call_expression: &CallExpression, span: Span) -> bool { call_expression.accept_children(self); - let arguments_span = Span::from(call_expression.func.span.end() + 1..span.end() - 1); - let span = call_expression.func.span; + let arguments_span = + Span::from(call_expression.func.location.span.end() + 1..span.end() - 1); + let span = call_expression.func.location.span; let name_span = Span::from(span.end() - 1..span.end()); let has_self = false; @@ -391,7 +394,7 @@ impl<'a> Visitor for SignatureFinder<'a> { } let kind_len = constrain_statement.kind.to_string().len() as u32; - let span = constrain_statement.span; + let span = constrain_statement.location.span; let arguments_span = Span::from(span.start() + kind_len + 1..span.end() - 1); if !self.includes_span(arguments_span) { diff --git a/noir/noir-repo/tooling/lsp/src/requests/test_run.rs b/noir/noir-repo/tooling/lsp/src/requests/test_run.rs index e2d8edd46c84..6b7a53e4596e 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/test_run.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/test_run.rs @@ -3,24 +3,23 @@ use std::future::{self, Future}; use crate::insert_all_files_for_workspace_into_file_manager; use async_lsp::{ErrorCode, ResponseError}; use nargo::{ - foreign_calls::DefaultForeignCallBuilder, - ops::{run_test, TestStatus}, PrintOutput, + foreign_calls::DefaultForeignCallBuilder, + ops::{TestStatus, run_test}, }; -use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection}; -use noirc_driver::{check_crate, CompileOptions, NOIR_ARTIFACT_VERSION_STRING}; +use nargo_toml::{PackageSelection, find_package_manifest, resolve_workspace_from_toml}; +use noirc_driver::{CompileOptions, NOIR_ARTIFACT_VERSION_STRING, check_crate}; use noirc_frontend::hir::FunctionNameMatch; use crate::{ - parse_diff, + LspState, parse_diff, types::{NargoTestRunParams, NargoTestRunResult}, - LspState, }; pub(crate) fn on_test_run_request( state: &mut LspState, params: NargoTestRunParams, -) -> impl Future> { +) -> impl Future> + use<> { future::ready(on_test_run_request_inner(state, params)) } @@ -120,7 +119,7 @@ fn on_test_run_request_inner( TestStatus::CompileError(diag) => NargoTestRunResult { id: params.id.clone(), result: "error".to_string(), - message: Some(diag.diagnostic.message), + message: Some(diag.message), }, }; Ok(result) diff --git a/noir/noir-repo/tooling/lsp/src/requests/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/tests.rs index 81910bebedba..234bd971eb5d 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/tests.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/tests.rs @@ -3,19 +3,18 @@ use std::future::{self, Future}; use crate::insert_all_files_for_workspace_into_file_manager; use async_lsp::{ErrorCode, LanguageClient, ResponseError}; use lsp_types::{LogMessageParams, MessageType}; -use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection}; -use noirc_driver::{check_crate, NOIR_ARTIFACT_VERSION_STRING}; +use nargo_toml::{PackageSelection, find_package_manifest, resolve_workspace_from_toml}; +use noirc_driver::{NOIR_ARTIFACT_VERSION_STRING, check_crate}; use crate::{ - get_package_tests_in_crate, parse_diff, + LspState, get_package_tests_in_crate, parse_diff, types::{NargoPackageTests, NargoTestsParams, NargoTestsResult}, - LspState, }; pub(crate) fn on_tests_request( state: &mut LspState, params: NargoTestsParams, -) -> impl Future> { +) -> impl Future> + use<> { future::ready(on_tests_request_inner(state, params)) } @@ -73,9 +72,5 @@ fn on_tests_request_inner( }) .collect(); - if package_tests.is_empty() { - Ok(None) - } else { - Ok(Some(package_tests)) - } + if package_tests.is_empty() { Ok(None) } else { Ok(Some(package_tests)) } } diff --git a/noir/noir-repo/tooling/lsp/src/trait_impl_method_stub_generator.rs b/noir/noir-repo/tooling/lsp/src/trait_impl_method_stub_generator.rs index a7444f819935..3a60a882aeab 100644 --- a/noir/noir-repo/tooling/lsp/src/trait_impl_method_stub_generator.rs +++ b/noir/noir-repo/tooling/lsp/src/trait_impl_method_stub_generator.rs @@ -1,6 +1,7 @@ use std::collections::BTreeMap; use noirc_frontend::{ + Kind, ResolvedGeneric, Type, ast::{NoirTraitImpl, UnresolvedTypeData}, graph::CrateId, hir::{ @@ -9,7 +10,6 @@ use noirc_frontend::{ }, hir_def::{function::FuncMeta, stmt::HirPattern, traits::Trait}, node_interner::{FunctionModifiers, NodeInterner, ReferenceId}, - Kind, ResolvedGeneric, Type, }; use crate::modules::relative_module_id_path; @@ -447,7 +447,7 @@ impl<'a> TraitImplMethodStubGenerator<'a> { Kind::Any | Kind::Normal | Kind::Integer | Kind::IntegerOrField => { self.string.push_str(&generic.name); } - Kind::Numeric(ref typ) => { + Kind::Numeric(typ) => { self.string.push_str("let "); self.string.push_str(&generic.name); self.string.push_str(": "); diff --git a/noir/noir-repo/tooling/lsp/src/types.rs b/noir/noir-repo/tooling/lsp/src/types.rs index b49377787e85..21d274a37764 100644 --- a/noir/noir-repo/tooling/lsp/src/types.rs +++ b/noir/noir-repo/tooling/lsp/src/types.rs @@ -15,7 +15,7 @@ pub(crate) use lsp_types::{ }; pub(crate) mod request { - use lsp_types::{request::Request, InitializeParams}; + use lsp_types::{InitializeParams, request::Request}; use super::{ InitializeResult, NargoTestRunParams, NargoTestRunResult, NargoTestsParams, diff --git a/noir/noir-repo/tooling/lsp/src/use_segment_positions.rs b/noir/noir-repo/tooling/lsp/src/use_segment_positions.rs index 2cd406ee7734..7ff83aecdc2a 100644 --- a/noir/noir-repo/tooling/lsp/src/use_segment_positions.rs +++ b/noir/noir-repo/tooling/lsp/src/use_segment_positions.rs @@ -96,7 +96,7 @@ impl UseSegmentPositions { kind_string, UseSegmentPosition::BeforeSegment { segment_span_until_end: Span::from( - segment.ident.span().start()..use_tree.span.end() - 1, + segment.ident.span().start()..use_tree.location.span.end() - 1, ), }, ); @@ -119,7 +119,7 @@ impl UseSegmentPositions { UseSegmentPosition::BeforeSegment { segment_span_until_end: Span::from( use_tree.prefix.segments[index + 1].ident.span().start() - ..use_tree.span.end() - 1, + ..use_tree.location.span.end() - 1, ), }, ); @@ -163,7 +163,7 @@ impl UseSegmentPositions { prefix, UseSegmentPosition::BeforeSegment { segment_span_until_end: Span::from( - ident.span().start()..use_tree.span.end() - 1, + ident.span().start()..use_tree.location.span.end() - 1, ), }, ); @@ -182,7 +182,7 @@ impl UseSegmentPositions { prefix, UseSegmentPosition::BeforeList { first_entry_span: Span::from( - use_tree.span.end() - 1..use_tree.span.end() - 1, + use_tree.location.span.end() - 1..use_tree.location.span.end() - 1, ), list_is_empty: true, }, diff --git a/noir/noir-repo/tooling/lsp/src/utils.rs b/noir/noir-repo/tooling/lsp/src/utils.rs index 96db1f7bfa23..ca607128bf2e 100644 --- a/noir/noir-repo/tooling/lsp/src/utils.rs +++ b/noir/noir-repo/tooling/lsp/src/utils.rs @@ -51,9 +51,5 @@ pub(crate) fn character_to_line_offset(line: &str, character: u32) -> Option ParsedModule { + ParsedModule { + items: parsed_module.items.into_iter().map(|item| item_with_file(item, file)).collect(), + inner_doc_comments: parsed_module.inner_doc_comments, + } +} + +fn item_with_file(item: Item, file: FileId) -> Item { + Item { + kind: item_kind_with_file(item.kind, file), + location: item.location, + doc_comments: item.doc_comments, + } +} + +fn item_kind_with_file(item_kind: ItemKind, file: FileId) -> ItemKind { + match item_kind { + ItemKind::Import(use_tree, item_visibility) => { + ItemKind::Import(use_tree_with_file(use_tree, file), item_visibility) + } + ItemKind::Function(noir_function) => { + ItemKind::Function(noir_function_with_file(noir_function, file)) + } + ItemKind::Struct(noir_struct) => ItemKind::Struct(noir_struct_with_file(noir_struct, file)), + ItemKind::Enum(noir_enumeration) => { + ItemKind::Enum(noir_enumeration_with_file(noir_enumeration, file)) + } + ItemKind::Trait(noir_trait) => ItemKind::Trait(noir_trait_with_file(noir_trait, file)), + ItemKind::TraitImpl(noir_trait_impl) => { + ItemKind::TraitImpl(noir_trait_impl_with_file(noir_trait_impl, file)) + } + ItemKind::Impl(type_impl) => ItemKind::Impl(type_impl_with_file(type_impl, file)), + ItemKind::TypeAlias(noir_type_alias) => { + ItemKind::TypeAlias(noir_type_alias_with_file(noir_type_alias, file)) + } + ItemKind::Global(let_statement, item_visibility) => { + ItemKind::Global(let_statement_with_file(let_statement, file), item_visibility) + } + ItemKind::ModuleDecl(module_declaration) => { + ItemKind::ModuleDecl(module_declaration_with_file(module_declaration, file)) + } + ItemKind::Submodules(parsed_sub_module) => { + ItemKind::Submodules(parsed_sub_module_with_file(parsed_sub_module, file)) + } + ItemKind::InnerAttribute(secondary_attribute) => { + ItemKind::InnerAttribute(secondary_attribute_with_file(secondary_attribute, file)) + } + } +} + +fn parsed_sub_module_with_file(module: ParsedSubModule, file: FileId) -> ParsedSubModule { + ParsedSubModule { + visibility: module.visibility, + name: ident_with_file(module.name, file), + contents: parsed_module_with_file(module.contents, file), + outer_attributes: secondary_attributes_with_file(module.outer_attributes, file), + is_contract: module.is_contract, + } +} + +fn module_declaration_with_file(module: ModuleDeclaration, file: FileId) -> ModuleDeclaration { + ModuleDeclaration { + visibility: module.visibility, + ident: ident_with_file(module.ident, file), + outer_attributes: secondary_attributes_with_file(module.outer_attributes, file), + has_semicolon: module.has_semicolon, + } +} + +fn let_statement_with_file(let_statement: LetStatement, file: FileId) -> LetStatement { + LetStatement { + pattern: pattern_with_file(let_statement.pattern, file), + r#type: unresolved_type_with_file(let_statement.r#type, file), + expression: expression_with_file(let_statement.expression, file), + attributes: secondary_attributes_with_file(let_statement.attributes, file), + comptime: let_statement.comptime, + is_global_let: let_statement.is_global_let, + } +} + +fn patterns_with_file(patterns: Vec, file: FileId) -> Vec { + vecmap(patterns, |pattern| pattern_with_file(pattern, file)) +} + +fn pattern_with_file(pattern: Pattern, file: FileId) -> Pattern { + match pattern { + Pattern::Identifier(ident) => Pattern::Identifier(ident_with_file(ident, file)), + Pattern::Mutable(pattern, location, synthesized) => Pattern::Mutable( + Box::new(pattern_with_file(*pattern, file)), + location_with_file(location, file), + synthesized, + ), + Pattern::Tuple(patterns, location) => { + Pattern::Tuple(patterns_with_file(patterns, file), location_with_file(location, file)) + } + Pattern::Struct(path, items, location) => Pattern::Struct( + path_with_file(path, file), + vecmap(items, |(ident, pattern)| { + (ident_with_file(ident, file), pattern_with_file(pattern, file)) + }), + location_with_file(location, file), + ), + Pattern::Interned(interned_pattern, location) => { + Pattern::Interned(interned_pattern, location_with_file(location, file)) + } + } +} + +fn noir_type_alias_with_file(noir_type_alias: NoirTypeAlias, file: FileId) -> NoirTypeAlias { + NoirTypeAlias { + name: ident_with_file(noir_type_alias.name, file), + generics: unresolved_generics_with_file(noir_type_alias.generics, file), + typ: unresolved_type_with_file(noir_type_alias.typ, file), + visibility: noir_type_alias.visibility, + location: location_with_file(noir_type_alias.location, file), + } +} + +fn type_impl_with_file(type_impl: TypeImpl, file: FileId) -> TypeImpl { + TypeImpl { + object_type: unresolved_type_with_file(type_impl.object_type, file), + type_location: location_with_file(type_impl.type_location, file), + generics: unresolved_generics_with_file(type_impl.generics, file), + where_clause: unresolved_trait_constraints_with_file(type_impl.where_clause, file), + methods: documented_noir_functions_with_file(type_impl.methods, file), + } +} + +fn documented_noir_functions_with_file( + methods: Vec<(Documented, Location)>, + file: FileId, +) -> Vec<(Documented, Location)> { + vecmap(methods, |(method, location)| { + (documented_noir_function_with_file(method, file), location_with_file(location, file)) + }) +} + +fn documented_noir_function_with_file( + function: Documented, + file: FileId, +) -> Documented { + Documented { + item: noir_function_with_file(function.item, file), + doc_comments: function.doc_comments, + } +} + +fn noir_trait_impl_with_file(noir_trait_impl: NoirTraitImpl, file: FileId) -> NoirTraitImpl { + NoirTraitImpl { + impl_generics: unresolved_generics_with_file(noir_trait_impl.impl_generics, file), + r#trait: unresolved_type_with_file(noir_trait_impl.r#trait, file), + object_type: unresolved_type_with_file(noir_trait_impl.object_type, file), + where_clause: unresolved_trait_constraints_with_file(noir_trait_impl.where_clause, file), + items: documented_trait_impl_items_with_file(noir_trait_impl.items, file), + is_synthetic: noir_trait_impl.is_synthetic, + } +} + +fn documented_trait_impl_items_with_file( + items: Vec>, + file: FileId, +) -> Vec> { + vecmap(items, |item| documented_trait_impl_item_with_file(item, file)) +} + +fn documented_trait_impl_item_with_file( + item: Documented, + file: FileId, +) -> Documented { + Documented { item: trait_impl_item_with_file(item.item, file), doc_comments: item.doc_comments } +} + +fn trait_impl_item_with_file(item: TraitImplItem, file: FileId) -> TraitImplItem { + TraitImplItem { + kind: trait_impl_item_kind_with_file(item.kind, file), + location: location_with_file(item.location, file), + } +} + +fn trait_impl_item_kind_with_file(kind: TraitImplItemKind, file: FileId) -> TraitImplItemKind { + match kind { + TraitImplItemKind::Function(noir_function) => { + TraitImplItemKind::Function(noir_function_with_file(noir_function, file)) + } + TraitImplItemKind::Constant(ident, typ, expression) => TraitImplItemKind::Constant( + ident_with_file(ident, file), + unresolved_type_with_file(typ, file), + expression_with_file(expression, file), + ), + TraitImplItemKind::Type { name, alias } => TraitImplItemKind::Type { + name: ident_with_file(name, file), + alias: unresolved_type_with_file(alias, file), + }, + } +} + +fn noir_trait_with_file(noir_trait: NoirTrait, file: FileId) -> NoirTrait { + NoirTrait { + name: ident_with_file(noir_trait.name, file), + generics: unresolved_generics_with_file(noir_trait.generics, file), + bounds: trait_bounds_with_file(noir_trait.bounds, file), + where_clause: unresolved_trait_constraints_with_file(noir_trait.where_clause, file), + location: location_with_file(noir_trait.location, file), + items: documented_trait_items_with_file(noir_trait.items, file), + attributes: secondary_attributes_with_file(noir_trait.attributes, file), + visibility: noir_trait.visibility, + is_alias: noir_trait.is_alias, + } +} + +fn documented_trait_items_with_file( + items: Vec>, + file: FileId, +) -> Vec> { + vecmap(items, |item| documented_trait_item_with_file(item, file)) +} + +fn documented_trait_item_with_file( + item: Documented, + file: FileId, +) -> Documented { + Documented { item: trait_item_with_file(item.item, file), doc_comments: item.doc_comments } +} + +fn trait_item_with_file(item: TraitItem, file: FileId) -> TraitItem { + match item { + TraitItem::Function { + is_unconstrained, + visibility, + is_comptime, + name, + generics, + parameters, + return_type, + where_clause, + body, + } => TraitItem::Function { + is_unconstrained, + visibility, + is_comptime, + name: ident_with_file(name, file), + generics: unresolved_generics_with_file(generics, file), + parameters: vecmap(parameters, |(ident, typ)| { + (ident_with_file(ident, file), unresolved_type_with_file(typ, file)) + }), + return_type: function_return_type_with_file(return_type, file), + where_clause: unresolved_trait_constraints_with_file(where_clause, file), + body: body.map(|body| block_expression_with_file(body, file)), + }, + TraitItem::Constant { name, typ, default_value } => TraitItem::Constant { + name: ident_with_file(name, file), + typ: unresolved_type_with_file(typ, file), + default_value: default_value.map(|value| expression_with_file(value, file)), + }, + TraitItem::Type { name } => TraitItem::Type { name: ident_with_file(name, file) }, + } +} + +fn function_return_type_with_file(typ: FunctionReturnType, file: FileId) -> FunctionReturnType { + match typ { + FunctionReturnType::Default(location) => { + FunctionReturnType::Default(location_with_file(location, file)) + } + FunctionReturnType::Ty(typ) => FunctionReturnType::Ty(unresolved_type_with_file(typ, file)), + } +} + +fn noir_function_with_file(noir_function: NoirFunction, file: FileId) -> NoirFunction { + NoirFunction { + kind: noir_function.kind, + def: function_definition_with_file(noir_function.def, file), + } +} + +fn noir_struct_with_file(noir_struct: NoirStruct, file: FileId) -> NoirStruct { + NoirStruct { + name: ident_with_file(noir_struct.name, file), + attributes: secondary_attributes_with_file(noir_struct.attributes, file), + visibility: noir_struct.visibility, + generics: unresolved_generics_with_file(noir_struct.generics, file), + fields: documented_struct_fields_with_file(noir_struct.fields, file), + location: location_with_file(noir_struct.location, file), + } +} + +fn documented_struct_fields_with_file( + fields: Vec>, + file: FileId, +) -> Vec> { + vecmap(fields, |field| documented_struct_field_with_file(field, file)) +} + +fn documented_struct_field_with_file( + field: Documented, + file: FileId, +) -> Documented { + Documented { item: struct_field_with_file(field.item, file), doc_comments: field.doc_comments } +} + +fn struct_field_with_file(field: StructField, file: FileId) -> StructField { + StructField { + visibility: field.visibility, + name: ident_with_file(field.name, file), + typ: unresolved_type_with_file(field.typ, file), + } +} + +fn noir_enumeration_with_file(noir_enumeration: NoirEnumeration, file: FileId) -> NoirEnumeration { + NoirEnumeration { + name: ident_with_file(noir_enumeration.name, file), + attributes: secondary_attributes_with_file(noir_enumeration.attributes, file), + visibility: noir_enumeration.visibility, + generics: unresolved_generics_with_file(noir_enumeration.generics, file), + variants: documented_enum_variants_with_file(noir_enumeration.variants, file), + location: location_with_file(noir_enumeration.location, file), + } +} + +fn documented_enum_variants_with_file( + variants: Vec>, + file: FileId, +) -> Vec> { + vecmap(variants, |variant| documented_enum_variant_with_file(variant, file)) +} + +fn documented_enum_variant_with_file( + variant: Documented, + file: FileId, +) -> Documented { + Documented { + item: enum_variant_with_file(variant.item, file), + doc_comments: variant.doc_comments, + } +} + +fn enum_variant_with_file(variant: EnumVariant, file: FileId) -> EnumVariant { + EnumVariant { + name: ident_with_file(variant.name, file), + parameters: variant.parameters.map(|params| unresolved_types_with_file(params, file)), + } +} + +fn function_definition_with_file(func: FunctionDefinition, file: FileId) -> FunctionDefinition { + FunctionDefinition { + name: ident_with_file(func.name, file), + attributes: attributes_with_file(func.attributes, file), + is_unconstrained: func.is_unconstrained, + is_comptime: func.is_comptime, + visibility: func.visibility, + generics: unresolved_generics_with_file(func.generics, file), + parameters: params_with_file(func.parameters, file), + body: block_expression_with_file(func.body, file), + location: location_with_file(func.location, file), + where_clause: unresolved_trait_constraints_with_file(func.where_clause, file), + return_type: function_return_type_with_file(func.return_type, file), + return_visibility: func.return_visibility, + } +} + +fn params_with_file(params: Vec, file: FileId) -> Vec { + vecmap(params, |param| param_with_file(param, file)) +} + +fn param_with_file(param: Param, file: FileId) -> Param { + Param { + visibility: param.visibility, + pattern: pattern_with_file(param.pattern, file), + typ: unresolved_type_with_file(param.typ, file), + location: location_with_file(param.location, file), + } +} + +fn attributes_with_file(attributes: Attributes, file: FileId) -> Attributes { + Attributes { + function: attributes.function, + secondary: secondary_attributes_with_file(attributes.secondary, file), + } +} + +fn use_tree_with_file(use_tree: UseTree, file: FileId) -> UseTree { + UseTree { + prefix: path_with_file(use_tree.prefix, file), + kind: use_tree_kind_with_file(use_tree.kind, file), + location: location_with_file(use_tree.location, file), + } +} + +fn use_tree_kind_with_file(kind: UseTreeKind, file: FileId) -> UseTreeKind { + match kind { + UseTreeKind::Path(ident, alias) => UseTreeKind::Path( + ident_with_file(ident, file), + alias.map(|alias| ident_with_file(alias, file)), + ), + UseTreeKind::List(use_trees) => { + UseTreeKind::List(vecmap(use_trees, |use_tree| use_tree_with_file(use_tree, file))) + } + } +} + +fn path_with_file(path: Path, file: FileId) -> Path { + Path { + segments: vecmap(path.segments, |segment| path_segment_with_file(segment, file)), + kind: path.kind, + kind_location: location_with_file(path.kind_location, file), + location: location_with_file(path.location, file), + } +} + +fn path_segment_with_file(segment: PathSegment, file: FileId) -> PathSegment { + PathSegment { + ident: ident_with_file(segment.ident, file), + generics: segment.generics.map(|generics| unresolved_types_with_file(generics, file)), + location: location_with_file(segment.location, file), + } +} + +fn unresolved_types_with_file(types: Vec, file: FileId) -> Vec { + vecmap(types, |typ| unresolved_type_with_file(typ, file)) +} + +fn unresolved_type_with_file(typ: UnresolvedType, file: FileId) -> UnresolvedType { + UnresolvedType { + typ: unresolved_type_data_with_file(typ.typ, file), + location: location_with_file(typ.location, file), + } +} + +fn unresolved_type_data_with_file(typ: UnresolvedTypeData, file: FileId) -> UnresolvedTypeData { + match typ { + UnresolvedTypeData::Array(length, typ) => UnresolvedTypeData::Array( + unresolved_type_expression_with_file(length, file), + Box::new(unresolved_type_with_file(*typ, file)), + ), + UnresolvedTypeData::Slice(typ) => { + UnresolvedTypeData::Slice(Box::new(unresolved_type_with_file(*typ, file))) + } + UnresolvedTypeData::Expression(expr) => { + UnresolvedTypeData::Expression(unresolved_type_expression_with_file(expr, file)) + } + UnresolvedTypeData::String(expr) => { + UnresolvedTypeData::String(unresolved_type_expression_with_file(expr, file)) + } + UnresolvedTypeData::FormatString(expr, typ) => UnresolvedTypeData::FormatString( + unresolved_type_expression_with_file(expr, file), + Box::new(unresolved_type_with_file(*typ, file)), + ), + UnresolvedTypeData::Parenthesized(typ) => { + UnresolvedTypeData::Parenthesized(Box::new(unresolved_type_with_file(*typ, file))) + } + UnresolvedTypeData::Named(path, generic_type_args, synthesized) => { + UnresolvedTypeData::Named( + path_with_file(path, file), + generic_type_args_with_file(generic_type_args, file), + synthesized, + ) + } + UnresolvedTypeData::TraitAsType(path, generic_type_args) => { + UnresolvedTypeData::TraitAsType( + path_with_file(path, file), + generic_type_args_with_file(generic_type_args, file), + ) + } + UnresolvedTypeData::MutableReference(typ) => { + UnresolvedTypeData::MutableReference(Box::new(unresolved_type_with_file(*typ, file))) + } + UnresolvedTypeData::Tuple(types) => { + UnresolvedTypeData::Tuple(unresolved_types_with_file(types, file)) + } + UnresolvedTypeData::Function(args, ret, env, unconstrained) => { + UnresolvedTypeData::Function( + unresolved_types_with_file(args, file), + Box::new(unresolved_type_with_file(*ret, file)), + Box::new(unresolved_type_with_file(*env, file)), + unconstrained, + ) + } + UnresolvedTypeData::AsTraitPath(as_trait_path) => { + UnresolvedTypeData::AsTraitPath(Box::new(as_trait_path_with_file(*as_trait_path, file))) + } + UnresolvedTypeData::Quoted(..) + | UnresolvedTypeData::Resolved(..) + | UnresolvedTypeData::Interned(..) + | UnresolvedTypeData::Unit + | UnresolvedTypeData::Bool + | UnresolvedTypeData::Integer(..) + | UnresolvedTypeData::FieldElement + | UnresolvedTypeData::Unspecified + | UnresolvedTypeData::Error => typ, + } +} + +fn unresolved_type_expression_with_file( + type_expr: UnresolvedTypeExpression, + file: FileId, +) -> UnresolvedTypeExpression { + match type_expr { + UnresolvedTypeExpression::Variable(path) => { + UnresolvedTypeExpression::Variable(path_with_file(path, file)) + } + UnresolvedTypeExpression::Constant(field_element, location) => { + UnresolvedTypeExpression::Constant(field_element, location_with_file(location, file)) + } + UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, location) => { + UnresolvedTypeExpression::BinaryOperation( + Box::new(unresolved_type_expression_with_file(*lhs, file)), + op, + Box::new(unresolved_type_expression_with_file(*rhs, file)), + location_with_file(location, file), + ) + } + UnresolvedTypeExpression::AsTraitPath(as_trait_path) => { + UnresolvedTypeExpression::AsTraitPath(Box::new(as_trait_path_with_file( + *as_trait_path, + file, + ))) + } + } +} + +fn as_trait_path_with_file(as_trait_path: AsTraitPath, file: FileId) -> AsTraitPath { + AsTraitPath { + typ: unresolved_type_with_file(as_trait_path.typ, file), + trait_path: path_with_file(as_trait_path.trait_path, file), + trait_generics: generic_type_args_with_file(as_trait_path.trait_generics, file), + impl_item: ident_with_file(as_trait_path.impl_item, file), + } +} + +fn generic_type_args_with_file(generics: GenericTypeArgs, file: FileId) -> GenericTypeArgs { + GenericTypeArgs { + ordered_args: unresolved_types_with_file(generics.ordered_args, file), + named_args: vecmap(generics.named_args, |(ident, typ)| { + (ident_with_file(ident, file), unresolved_type_with_file(typ, file)) + }), + kinds: generics.kinds, + } +} + +fn ident_with_file(ident: Ident, file: FileId) -> Ident { + let location = location_with_file(ident.location(), file); + Ident::new(ident.0.contents, location) +} + +fn secondary_attributes_with_file( + attributes: Vec, + file: FileId, +) -> Vec { + vecmap(attributes, |attribute| secondary_attribute_with_file(attribute, file)) +} + +fn secondary_attribute_with_file( + secondary_attribute: SecondaryAttribute, + file: FileId, +) -> SecondaryAttribute { + match secondary_attribute { + SecondaryAttribute::Meta(meta_attribute) => { + SecondaryAttribute::Meta(meta_attribute_with_file(meta_attribute, file)) + } + SecondaryAttribute::Deprecated(_) + | SecondaryAttribute::ContractLibraryMethod + | SecondaryAttribute::Export + | SecondaryAttribute::Field(_) + | SecondaryAttribute::Tag(..) + | SecondaryAttribute::Abi(_) + | SecondaryAttribute::Varargs + | SecondaryAttribute::UseCallersScope + | SecondaryAttribute::Allow(_) => secondary_attribute, + } +} + +fn meta_attribute_with_file(meta_attribute: MetaAttribute, file: FileId) -> MetaAttribute { + MetaAttribute { + name: path_with_file(meta_attribute.name, file), + arguments: expressions_with_file(meta_attribute.arguments, file), + location: location_with_file(meta_attribute.location, file), + } +} + +fn expressions_with_file(expressions: Vec, file: FileId) -> Vec { + vecmap(expressions, |expr| expression_with_file(expr, file)) +} + +fn expression_with_file(expr: Expression, file: FileId) -> Expression { + Expression { + kind: expression_kind_with_file(expr.kind, file), + location: location_with_file(expr.location, file), + } +} + +fn expression_kind_with_file(kind: ExpressionKind, file: FileId) -> ExpressionKind { + match kind { + ExpressionKind::Literal(literal) => { + ExpressionKind::Literal(literal_with_file(literal, file)) + } + ExpressionKind::Block(block_expression) => { + ExpressionKind::Block(block_expression_with_file(block_expression, file)) + } + ExpressionKind::Prefix(prefix_expression) => { + ExpressionKind::Prefix(Box::new(prefix_expression_with_file(*prefix_expression, file))) + } + ExpressionKind::Index(index_expression) => { + ExpressionKind::Index(Box::new(index_expression_with_file(*index_expression, file))) + } + ExpressionKind::Call(call_expression) => { + ExpressionKind::Call(Box::new(call_expression_with_file(*call_expression, file))) + } + ExpressionKind::MethodCall(method_call_expression) => ExpressionKind::MethodCall(Box::new( + method_call_expression_with_file(*method_call_expression, file), + )), + ExpressionKind::Constrain(constrain_expression) => { + ExpressionKind::Constrain(constrain_expression_with_file(constrain_expression, file)) + } + ExpressionKind::Constructor(constructor_expression) => ExpressionKind::Constructor( + Box::new(constructor_expression_with_file(*constructor_expression, file)), + ), + ExpressionKind::MemberAccess(member_access_expression) => ExpressionKind::MemberAccess( + Box::new(member_access_expression_with_file(*member_access_expression, file)), + ), + ExpressionKind::Cast(cast_expression) => { + ExpressionKind::Cast(Box::new(cast_expression_with_file(*cast_expression, file))) + } + ExpressionKind::Infix(infix_expression) => { + ExpressionKind::Infix(Box::new(infix_expression_with_file(*infix_expression, file))) + } + ExpressionKind::If(if_expression) => { + ExpressionKind::If(Box::new(if_expression_with_file(*if_expression, file))) + } + ExpressionKind::Match(match_expression) => { + ExpressionKind::Match(Box::new(match_expression_with_file(*match_expression, file))) + } + ExpressionKind::Variable(path) => ExpressionKind::Variable(path_with_file(path, file)), + ExpressionKind::Tuple(expressions) => { + ExpressionKind::Tuple(expressions_with_file(expressions, file)) + } + ExpressionKind::Lambda(lambda) => { + ExpressionKind::Lambda(Box::new(lambda_with_file(*lambda, file))) + } + ExpressionKind::Parenthesized(expression) => { + ExpressionKind::Parenthesized(Box::new(expression_with_file(*expression, file))) + } + ExpressionKind::Quote(tokens) => ExpressionKind::Quote(tokens_with_file(tokens, file)), + ExpressionKind::Unquote(expression) => { + ExpressionKind::Unquote(Box::new(expression_with_file(*expression, file))) + } + ExpressionKind::Comptime(block_expression, location) => ExpressionKind::Comptime( + block_expression_with_file(block_expression, file), + location_with_file(location, file), + ), + ExpressionKind::Unsafe(UnsafeExpression { block, unsafe_keyword_location }) => { + ExpressionKind::Unsafe(UnsafeExpression { + block: block_expression_with_file(block, file), + unsafe_keyword_location: location_with_file(unsafe_keyword_location, file), + }) + } + ExpressionKind::AsTraitPath(as_trait_path) => { + ExpressionKind::AsTraitPath(as_trait_path_with_file(as_trait_path, file)) + } + ExpressionKind::TypePath(type_path) => { + ExpressionKind::TypePath(type_path_with_file(type_path, file)) + } + ExpressionKind::Resolved(..) + | ExpressionKind::Interned(..) + | ExpressionKind::InternedStatement(..) + | ExpressionKind::Error => kind, + } +} + +fn type_path_with_file(type_path: TypePath, file: FileId) -> TypePath { + TypePath { + typ: unresolved_type_with_file(type_path.typ, file), + item: ident_with_file(type_path.item, file), + turbofish: type_path.turbofish.map(|args| generic_type_args_with_file(args, file)), + } +} + +fn tokens_with_file(tokens: Tokens, file: FileId) -> Tokens { + Tokens(vecmap(tokens.0, |token| { + let location = location_with_file(token.location(), file); + LocatedToken::new(token_with_location(token.into_token(), file), location) + })) +} + +fn token_with_location(token: Token, file: FileId) -> Token { + if let Token::FmtStr(fragments, length) = token { + Token::FmtStr( + vecmap(fragments, |fragment| fmt_str_fragment_with_file(fragment, file)), + length, + ) + } else { + token + } +} + +fn fmt_str_fragment_with_file(fragment: FmtStrFragment, file: FileId) -> FmtStrFragment { + match fragment { + FmtStrFragment::Interpolation(string, location) => { + FmtStrFragment::Interpolation(string, location_with_file(location, file)) + } + FmtStrFragment::String(_) => fragment, + } +} + +fn lambda_with_file(lambda: Lambda, file: FileId) -> Lambda { + Lambda { + parameters: vecmap(lambda.parameters, |(pattern, typ)| { + (pattern_with_file(pattern, file), unresolved_type_with_file(typ, file)) + }), + return_type: unresolved_type_with_file(lambda.return_type, file), + body: expression_with_file(lambda.body, file), + } +} + +fn match_expression_with_file(expr: MatchExpression, file: FileId) -> MatchExpression { + MatchExpression { + expression: expression_with_file(expr.expression, file), + rules: vecmap(expr.rules, |(condition, body)| { + (expression_with_file(condition, file), expression_with_file(body, file)) + }), + } +} + +fn if_expression_with_file(expr: IfExpression, file: FileId) -> IfExpression { + IfExpression { + condition: expression_with_file(expr.condition, file), + consequence: expression_with_file(expr.consequence, file), + alternative: expr.alternative.map(|alternative| expression_with_file(alternative, file)), + } +} + +fn infix_expression_with_file(expr: InfixExpression, file: FileId) -> InfixExpression { + InfixExpression { + lhs: expression_with_file(expr.lhs, file), + rhs: expression_with_file(expr.rhs, file), + operator: expr.operator, + } +} + +fn cast_expression_with_file(expr: CastExpression, file: FileId) -> CastExpression { + CastExpression { + lhs: expression_with_file(expr.lhs, file), + r#type: unresolved_type_with_file(expr.r#type, file), + } +} + +fn member_access_expression_with_file( + expr: MemberAccessExpression, + file: FileId, +) -> MemberAccessExpression { + MemberAccessExpression { + lhs: expression_with_file(expr.lhs, file), + rhs: ident_with_file(expr.rhs, file), + } +} + +fn constructor_expression_with_file( + expr: ConstructorExpression, + file: FileId, +) -> ConstructorExpression { + ConstructorExpression { + typ: unresolved_type_with_file(expr.typ, file), + fields: vecmap(expr.fields, |(ident, expression)| { + (ident_with_file(ident, file), expression_with_file(expression, file)) + }), + } +} + +fn constrain_expression_with_file(expr: ConstrainExpression, file: FileId) -> ConstrainExpression { + ConstrainExpression { + kind: expr.kind, + arguments: expressions_with_file(expr.arguments, file), + location: location_with_file(expr.location, file), + } +} + +fn method_call_expression_with_file( + expr: MethodCallExpression, + file: FileId, +) -> MethodCallExpression { + MethodCallExpression { + object: expression_with_file(expr.object, file), + method_name: ident_with_file(expr.method_name, file), + generics: expr.generics.map(|generics| unresolved_types_with_file(generics, file)), + arguments: expressions_with_file(expr.arguments, file), + is_macro_call: expr.is_macro_call, + } +} + +fn call_expression_with_file(expr: CallExpression, file: FileId) -> CallExpression { + CallExpression { + func: Box::new(expression_with_file(*expr.func, file)), + arguments: expressions_with_file(expr.arguments, file), + is_macro_call: expr.is_macro_call, + } +} + +fn index_expression_with_file(expr: IndexExpression, file: FileId) -> IndexExpression { + IndexExpression { + collection: expression_with_file(expr.collection, file), + index: expression_with_file(expr.index, file), + } +} + +fn prefix_expression_with_file(expr: PrefixExpression, file: FileId) -> PrefixExpression { + PrefixExpression { operator: expr.operator, rhs: expression_with_file(expr.rhs, file) } +} + +fn literal_with_file(literal: Literal, file: FileId) -> Literal { + match literal { + Literal::Array(array_literal) => { + Literal::Array(array_literal_with_file(array_literal, file)) + } + Literal::Slice(array_literal) => { + Literal::Slice(array_literal_with_file(array_literal, file)) + } + Literal::FmtStr(fragments, length) => Literal::FmtStr( + vecmap(fragments, |fragment| fmt_str_fragment_with_file(fragment, file)), + length, + ), + Literal::Bool(..) + | Literal::Integer(..) + | Literal::Str(..) + | Literal::RawStr(..) + | Literal::Unit => literal, + } +} + +fn array_literal_with_file(array_literal: ArrayLiteral, file: FileId) -> ArrayLiteral { + match array_literal { + ArrayLiteral::Standard(expressions) => { + ArrayLiteral::Standard(expressions_with_file(expressions, file)) + } + ArrayLiteral::Repeated { repeated_element, length } => ArrayLiteral::Repeated { + repeated_element: Box::new(expression_with_file(*repeated_element, file)), + length: Box::new(expression_with_file(*length, file)), + }, + } +} + +fn block_expression_with_file(block: BlockExpression, file: FileId) -> BlockExpression { + BlockExpression { statements: statements_with_file(block.statements, file) } +} + +fn statements_with_file(statements: Vec, file: FileId) -> Vec { + vecmap(statements, |statement| statement_with_file(statement, file)) +} + +fn statement_with_file(statement: Statement, file: FileId) -> Statement { + Statement { + kind: statement_kind_with_file(statement.kind, file), + location: location_with_file(statement.location, file), + } +} + +fn statement_kind_with_file(kind: StatementKind, file: FileId) -> StatementKind { + match kind { + StatementKind::Let(let_statement) => { + StatementKind::Let(let_statement_with_file(let_statement, file)) + } + StatementKind::Expression(expression) => { + StatementKind::Expression(expression_with_file(expression, file)) + } + StatementKind::Assign(assign_statement) => { + StatementKind::Assign(assign_statement_with_file(assign_statement, file)) + } + StatementKind::For(for_loop_statement) => { + StatementKind::For(for_loop_statement_with_file(for_loop_statement, file)) + } + StatementKind::While(while_) => StatementKind::While(WhileStatement { + condition: expression_with_file(while_.condition, file), + body: expression_with_file(while_.body, file), + while_keyword_location: while_.while_keyword_location, + }), + StatementKind::Loop(expression, location) => StatementKind::Loop( + expression_with_file(expression, file), + location_with_file(location, file), + ), + StatementKind::Comptime(statement) => { + StatementKind::Comptime(Box::new(statement_with_file(*statement, file))) + } + StatementKind::Semi(expression) => { + StatementKind::Semi(expression_with_file(expression, file)) + } + StatementKind::Interned(..) + | StatementKind::Break + | StatementKind::Continue + | StatementKind::Error => kind, + } +} + +fn for_loop_statement_with_file(for_loop: ForLoopStatement, file: FileId) -> ForLoopStatement { + ForLoopStatement { + identifier: ident_with_file(for_loop.identifier, file), + range: for_range_with_file(for_loop.range, file), + block: expression_with_file(for_loop.block, file), + location: location_with_file(for_loop.location, file), + } +} + +fn for_range_with_file(range: ForRange, file: FileId) -> ForRange { + match range { + ForRange::Range(for_bounds) => ForRange::Range(for_bounds_with_file(for_bounds, file)), + ForRange::Array(expression) => ForRange::Array(expression_with_file(expression, file)), + } +} + +fn for_bounds_with_file(for_bounds: ForBounds, file: FileId) -> ForBounds { + ForBounds { + start: expression_with_file(for_bounds.start, file), + end: expression_with_file(for_bounds.end, file), + inclusive: for_bounds.inclusive, + } +} + +fn assign_statement_with_file(assign: AssignStatement, file: FileId) -> AssignStatement { + AssignStatement { + lvalue: lvalue_with_file(assign.lvalue, file), + expression: expression_with_file(assign.expression, file), + } +} + +fn lvalue_with_file(lvalue: LValue, file: FileId) -> LValue { + match lvalue { + LValue::Ident(ident) => LValue::Ident(ident_with_file(ident, file)), + LValue::MemberAccess { object, field_name, location } => LValue::MemberAccess { + object: Box::new(lvalue_with_file(*object, file)), + field_name: ident_with_file(field_name, file), + location: location_with_file(location, file), + }, + LValue::Index { array, index, location } => LValue::Index { + array: Box::new(lvalue_with_file(*array, file)), + index: expression_with_file(index, file), + location: location_with_file(location, file), + }, + LValue::Dereference(lvalue, location) => LValue::Dereference( + Box::new(lvalue_with_file(*lvalue, file)), + location_with_file(location, file), + ), + LValue::Interned(interned_expression_kind, location) => { + LValue::Interned(interned_expression_kind, location_with_file(location, file)) + } + } +} + +fn unresolved_generics_with_file( + generics: Vec, + file: FileId, +) -> Vec { + vecmap(generics, |generic| unresolved_generic_with_file(generic, file)) +} + +fn unresolved_generic_with_file(generic: UnresolvedGeneric, file: FileId) -> UnresolvedGeneric { + match generic { + UnresolvedGeneric::Variable(ident) => { + UnresolvedGeneric::Variable(ident_with_file(ident, file)) + } + UnresolvedGeneric::Numeric { ident, typ } => UnresolvedGeneric::Numeric { + ident: ident_with_file(ident, file), + typ: unresolved_type_with_file(typ, file), + }, + UnresolvedGeneric::Resolved(quoted_type_id, location) => { + UnresolvedGeneric::Resolved(quoted_type_id, location_with_file(location, file)) + } + } +} + +fn unresolved_trait_constraints_with_file( + constraints: Vec, + file: FileId, +) -> Vec { + vecmap(constraints, |constraint| unresolved_trait_constraint_with_file(constraint, file)) +} + +fn unresolved_trait_constraint_with_file( + constraint: UnresolvedTraitConstraint, + file: FileId, +) -> UnresolvedTraitConstraint { + UnresolvedTraitConstraint { + typ: unresolved_type_with_file(constraint.typ, file), + trait_bound: trait_bound_with_file(constraint.trait_bound, file), + } +} + +fn trait_bounds_with_file(trait_bounds: Vec, file: FileId) -> Vec { + vecmap(trait_bounds, |bound| trait_bound_with_file(bound, file)) +} + +fn trait_bound_with_file(trait_bound: TraitBound, file: FileId) -> TraitBound { + TraitBound { + trait_path: path_with_file(trait_bound.trait_path, file), + trait_id: trait_bound.trait_id, + trait_generics: generic_type_args_with_file(trait_bound.trait_generics, file), + } +} + +fn location_with_file(location: Location, file: FileId) -> Location { + Location { file, ..location } +} diff --git a/noir/noir-repo/tooling/nargo/src/errors.rs b/noir/noir-repo/tooling/nargo/src/errors.rs index 00c411bf7e43..f1743af79ca6 100644 --- a/noir/noir-repo/tooling/nargo/src/errors.rs +++ b/noir/noir-repo/tooling/nargo/src/errors.rs @@ -1,17 +1,15 @@ use std::collections::BTreeMap; use acvm::{ + AcirField, FieldElement, acir::circuit::{ - brillig::BrilligFunctionId, ErrorSelector, OpcodeLocation, RawAssertionPayload, - ResolvedAssertionPayload, ResolvedOpcodeLocation, + ErrorSelector, OpcodeLocation, RawAssertionPayload, ResolvedAssertionPayload, + ResolvedOpcodeLocation, brillig::BrilligFunctionId, }, pwg::{ErrorLocation, OpcodeResolutionError}, - AcirField, FieldElement, -}; -use noirc_abi::{display_abi_error, Abi, AbiErrorType}; -use noirc_errors::{ - debug_info::DebugInfo, reporter::ReportedErrors, CustomDiagnostic, FileDiagnostic, }; +use noirc_abi::{Abi, AbiErrorType, display_abi_error}; +use noirc_errors::{CustomDiagnostic, debug_info::DebugInfo, reporter::ReportedErrors}; pub use noirc_errors::Location; @@ -230,7 +228,7 @@ pub fn try_to_diagnose_runtime_error( nargo_err: &NargoError, abi: &Abi, debug: &[DebugInfo], -) -> Option { +) -> Option { let source_locations = match nargo_err { NargoError::ExecutionError(execution_error) => { extract_locations_from_error(execution_error, debug)? @@ -241,6 +239,6 @@ pub fn try_to_diagnose_runtime_error( // of the call stack (the last item in the Vec). let location = *source_locations.last()?; let message = extract_message_from_error(&abi.error_types, nargo_err); - let error = CustomDiagnostic::simple_error(message, String::new(), location.span); - Some(error.with_call_stack(source_locations).in_file(location.file)) + let error = CustomDiagnostic::simple_error(message, String::new(), location); + Some(error.with_call_stack(source_locations)) } diff --git a/noir/noir-repo/tooling/nargo/src/foreign_calls/default.rs b/noir/noir-repo/tooling/nargo/src/foreign_calls/default.rs index 19928e895630..c6053a1175e1 100644 --- a/noir/noir-repo/tooling/nargo/src/foreign_calls/default.rs +++ b/noir/noir-repo/tooling/nargo/src/foreign_calls/default.rs @@ -4,10 +4,10 @@ use serde::{Deserialize, Serialize}; use crate::PrintOutput; use super::{ + ForeignCallExecutor, layers::{self, Either, Layer, Layering}, mocker::{DisabledMockForeignCallExecutor, MockForeignCallExecutor}, print::PrintForeignCallExecutor, - ForeignCallExecutor, }; #[cfg(feature = "rpc")] @@ -29,7 +29,7 @@ pub struct DefaultForeignCallBuilder<'a> { pub package_name: Option, } -impl<'a> Default for DefaultForeignCallBuilder<'a> { +impl Default for DefaultForeignCallBuilder<'_> { fn default() -> Self { Self { output: PrintOutput::default(), @@ -80,7 +80,7 @@ impl<'a> DefaultForeignCallBuilder<'a> { use rand::Rng; base.add_layer(self.resolver_url.map(|resolver_url| { - let id = rand::thread_rng().gen(); + let id = rand::thread_rng().r#gen(); RPCForeignCallExecutor::new( &resolver_url, id, @@ -136,7 +136,7 @@ impl DefaultForeignCallExecutor { resolver_url: Option<&str>, root_path: Option, package_name: Option, - ) -> impl ForeignCallExecutor + 'a + ) -> impl ForeignCallExecutor + 'a + use<'a, F> where F: AcirField + Serialize + for<'de> Deserialize<'de> + 'a, { diff --git a/noir/noir-repo/tooling/nargo/src/foreign_calls/layers.rs b/noir/noir-repo/tooling/nargo/src/foreign_calls/layers.rs index 83145cacb441..4b1d17bdf65c 100644 --- a/noir/noir-repo/tooling/nargo/src/foreign_calls/layers.rs +++ b/noir/noir-repo/tooling/nargo/src/foreign_calls/layers.rs @@ -1,4 +1,4 @@ -use acvm::{acir::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo, AcirField}; +use acvm::{AcirField, acir::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo}; use super::{ForeignCallError, ForeignCallExecutor}; diff --git a/noir/noir-repo/tooling/nargo/src/foreign_calls/mocker.rs b/noir/noir-repo/tooling/nargo/src/foreign_calls/mocker.rs index 41fac610052b..3d432b18ff9e 100644 --- a/noir/noir-repo/tooling/nargo/src/foreign_calls/mocker.rs +++ b/noir/noir-repo/tooling/nargo/src/foreign_calls/mocker.rs @@ -1,7 +1,7 @@ use acvm::{ + AcirField, acir::brillig::{ForeignCallParam, ForeignCallResult}, pwg::ForeignCallWaitInfo, - AcirField, }; use noirc_abi::decode_string_value; diff --git a/noir/noir-repo/tooling/nargo/src/foreign_calls/print.rs b/noir/noir-repo/tooling/nargo/src/foreign_calls/print.rs index fb5621da9424..a225fe31dcbe 100644 --- a/noir/noir-repo/tooling/nargo/src/foreign_calls/print.rs +++ b/noir/noir-repo/tooling/nargo/src/foreign_calls/print.rs @@ -1,7 +1,7 @@ use acvm::{ + AcirField, acir::brillig::{ForeignCallParam, ForeignCallResult}, pwg::ForeignCallWaitInfo, - AcirField, }; use noirc_abi::{decode_printable_value, decode_string_value}; use noirc_printable_type::{PrintableType, PrintableValueDisplay}; diff --git a/noir/noir-repo/tooling/nargo/src/foreign_calls/rpc.rs b/noir/noir-repo/tooling/nargo/src/foreign_calls/rpc.rs index 89a748b6c455..6e4858128856 100644 --- a/noir/noir-repo/tooling/nargo/src/foreign_calls/rpc.rs +++ b/noir/noir-repo/tooling/nargo/src/foreign_calls/rpc.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use acvm::{acir::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo, AcirField}; +use acvm::{AcirField, acir::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo}; use jsonrpsee::{ core::client::ClientT, http_client::{HttpClient, HttpClientBuilder}, @@ -115,8 +115,8 @@ where #[cfg(test)] mod tests { use acvm::{ - acir::brillig::ForeignCallParam, brillig_vm::brillig::ForeignCallResult, - pwg::ForeignCallWaitInfo, FieldElement, + FieldElement, acir::brillig::ForeignCallParam, brillig_vm::brillig::ForeignCallResult, + pwg::ForeignCallWaitInfo, }; use jsonrpsee::proc_macros::rpc; use jsonrpsee::server::Server; diff --git a/noir/noir-repo/tooling/nargo/src/lib.rs b/noir/noir-repo/tooling/nargo/src/lib.rs index 30f25356e413..11615b3de729 100644 --- a/noir/noir-repo/tooling/nargo/src/lib.rs +++ b/noir/noir-repo/tooling/nargo/src/lib.rs @@ -22,11 +22,11 @@ use std::{ path::PathBuf, }; -use fm::{FileManager, FILE_EXTENSION}; +use fm::{FILE_EXTENSION, FileManager}; use noirc_driver::{add_dep, prepare_crate, prepare_dependency}; use noirc_frontend::{ graph::{CrateId, CrateName}, - hir::{def_map::parse_file, Context, ParsedFiles}, + hir::{Context, ParsedFiles, def_map::parse_file}, }; use package::{Dependency, Package}; use rayon::prelude::*; @@ -106,7 +106,7 @@ fn insert_all_files_for_package_into_file_manager( continue; } - if !entry.path().extension().map_or(false, |ext| ext == FILE_EXTENSION) { + if entry.path().extension().is_none_or(|ext| ext != FILE_EXTENSION) { continue; }; diff --git a/noir/noir-repo/tooling/nargo/src/ops/check.rs b/noir/noir-repo/tooling/nargo/src/ops/check.rs index f22def8bd91b..129aa1bd7885 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/check.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/check.rs @@ -1,6 +1,6 @@ use acvm::compiler::CircuitSimulator; use noirc_driver::{CompiledProgram, ErrorsAndWarnings}; -use noirc_errors::{CustomDiagnostic, FileDiagnostic}; +use noirc_errors::CustomDiagnostic; /// Run each function through a circuit simulator to check that they are solvable. #[tracing::instrument(level = "trace", skip_all)] @@ -8,13 +8,10 @@ pub fn check_program(compiled_program: &CompiledProgram) -> Result<(), ErrorsAnd for (i, circuit) in compiled_program.program.functions.iter().enumerate() { let mut simulator = CircuitSimulator::default(); if !simulator.check_circuit(circuit) { - let diag = FileDiagnostic { - file_id: fm::FileId::dummy(), - diagnostic: CustomDiagnostic::from_message(&format!( - "Circuit \"{}\" is not solvable", - compiled_program.names[i] - )), - }; + let diag = CustomDiagnostic::from_message( + &format!("Circuit \"{}\" is not solvable", compiled_program.names[i]), + fm::FileId::dummy(), + ); return Err(vec![diag]); } } diff --git a/noir/noir-repo/tooling/nargo/src/ops/compile.rs b/noir/noir-repo/tooling/nargo/src/ops/compile.rs index 8c44bf352435..7a3ddbfe3bc6 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/compile.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/compile.rs @@ -1,6 +1,6 @@ use fm::FileManager; use noirc_driver::{ - link_to_debug_crate, CompilationResult, CompileOptions, CompiledContract, CompiledProgram, + CompilationResult, CompileOptions, CompiledContract, CompiledProgram, link_to_debug_crate, }; use noirc_frontend::debug::DebugInstrumenter; use noirc_frontend::hir::ParsedFiles; @@ -120,11 +120,7 @@ pub fn collect_errors(results: Vec>) -> CompilationResul } } - if errors.is_empty() { - Ok((artifacts, warnings)) - } else { - Err(errors) - } + if errors.is_empty() { Ok((artifacts, warnings)) } else { Err(errors) } } pub fn report_errors( diff --git a/noir/noir-repo/tooling/nargo/src/ops/execute.rs b/noir/noir-repo/tooling/nargo/src/ops/execute.rs index 57116ec2efd9..699c54e3f528 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/execute.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/execute.rs @@ -4,14 +4,14 @@ use acvm::acir::circuit::{ }; use acvm::acir::native_types::WitnessStack; use acvm::pwg::{ - ACVMStatus, ErrorLocation, OpcodeNotSolvable, OpcodeResolutionError, ProfilingSamples, ACVM, + ACVM, ACVMStatus, ErrorLocation, OpcodeNotSolvable, OpcodeResolutionError, ProfilingSamples, }; -use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; use acvm::{AcirField, BlackBoxFunctionSolver}; +use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; +use crate::NargoError; use crate::errors::ExecutionError; use crate::foreign_calls::ForeignCallExecutor; -use crate::NargoError; struct ProgramExecutor<'a, F, B: BlackBoxFunctionSolver, E: ForeignCallExecutor> { functions: &'a [Circuit], diff --git a/noir/noir-repo/tooling/nargo/src/ops/mod.rs b/noir/noir-repo/tooling/nargo/src/ops/mod.rs index 7a52a829be36..7ce34b1acd25 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/mod.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/mod.rs @@ -7,7 +7,7 @@ pub use self::optimize::{optimize_contract, optimize_program}; pub use self::transform::{transform_contract, transform_program}; pub use self::execute::{execute_program, execute_program_with_profiling}; -pub use self::test::{run_test, TestStatus}; +pub use self::test::{TestStatus, run_test}; mod check; mod compile; diff --git a/noir/noir-repo/tooling/nargo/src/ops/optimize.rs b/noir/noir-repo/tooling/nargo/src/ops/optimize.rs index 07adfb57df4f..906309e36f8a 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/optimize.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/optimize.rs @@ -1,4 +1,4 @@ -use acvm::{acir::circuit::Program, FieldElement}; +use acvm::{FieldElement, acir::circuit::Program}; use iter_extended::vecmap; use noirc_driver::{CompiledContract, CompiledProgram}; use noirc_errors::debug_info::DebugInfo; diff --git a/noir/noir-repo/tooling/nargo/src/ops/test.rs b/noir/noir-repo/tooling/nargo/src/ops/test.rs index a2f94cd61eba..4f0e63564e2d 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/test.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/test.rs @@ -1,20 +1,20 @@ use acvm::{ + AcirField, BlackBoxFunctionSolver, FieldElement, acir::{ brillig::ForeignCallResult, native_types::{WitnessMap, WitnessStack}, }, pwg::ForeignCallWaitInfo, - AcirField, BlackBoxFunctionSolver, FieldElement, }; use noirc_abi::Abi; -use noirc_driver::{compile_no_check, CompileError, CompileOptions, DEFAULT_EXPRESSION_WIDTH}; -use noirc_errors::{debug_info::DebugInfo, FileDiagnostic}; -use noirc_frontend::hir::{def_map::TestFunction, Context}; +use noirc_driver::{CompileError, CompileOptions, DEFAULT_EXPRESSION_WIDTH, compile_no_check}; +use noirc_errors::{CustomDiagnostic, debug_info::DebugInfo}; +use noirc_frontend::hir::{Context, def_map::TestFunction}; use crate::{ - errors::try_to_diagnose_runtime_error, - foreign_calls::{layers, print::PrintOutput, ForeignCallError, ForeignCallExecutor}, NargoError, + errors::try_to_diagnose_runtime_error, + foreign_calls::{ForeignCallError, ForeignCallExecutor, layers, print::PrintOutput}, }; use super::execute_program; @@ -22,9 +22,9 @@ use super::execute_program; #[derive(Debug)] pub enum TestStatus { Pass, - Fail { message: String, error_diagnostic: Option }, + Fail { message: String, error_diagnostic: Option }, Skipped, - CompileError(FileDiagnostic), + CompileError(CustomDiagnostic), } impl TestStatus { @@ -218,7 +218,7 @@ fn test_status_program_compile_pass( fn check_expected_failure_message( test_function: &TestFunction, failed_assertion: Option, - error_diagnostic: Option, + error_diagnostic: Option, ) -> TestStatus { // Extract the expected failure message, if there was one // @@ -235,9 +235,7 @@ fn check_expected_failure_message( // expected_failure_message let expected_failure_message_matches = failed_assertion .as_ref() - .or_else(|| { - error_diagnostic.as_ref().map(|file_diagnostic| &file_diagnostic.diagnostic.message) - }) + .or_else(|| error_diagnostic.as_ref().map(|file_diagnostic| &file_diagnostic.message)) .map(|message| message.contains(expected_failure_message)) .unwrap_or(false); if expected_failure_message_matches { diff --git a/noir/noir-repo/tooling/nargo/src/ops/transform.rs b/noir/noir-repo/tooling/nargo/src/ops/transform.rs index fdda368d150c..14aa74591174 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/transform.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/transform.rs @@ -1,6 +1,6 @@ use acvm::{ - acir::circuit::{ExpressionWidth, Program}, FieldElement, + acir::circuit::{ExpressionWidth, Program}, }; use iter_extended::vecmap; use noirc_driver::{CompiledContract, CompiledProgram}; diff --git a/noir/noir-repo/tooling/nargo/src/workspace.rs b/noir/noir-repo/tooling/nargo/src/workspace.rs index 2a6669aabdca..f7edc5057f6d 100644 --- a/noir/noir-repo/tooling/nargo/src/workspace.rs +++ b/noir/noir-repo/tooling/nargo/src/workspace.rs @@ -4,7 +4,7 @@ // - library will be default use std::{ - iter::{once, Once}, + iter::{Once, once}, path::PathBuf, slice, }; diff --git a/noir/noir-repo/tooling/nargo_cli/Cargo.toml b/noir/noir-repo/tooling/nargo_cli/Cargo.toml index 92eeed1b3911..5aa37cffcda0 100644 --- a/noir/noir-repo/tooling/nargo_cli/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_cli/Cargo.toml @@ -37,6 +37,7 @@ nargo = { workspace = true, features = ["rpc"] } nargo_fmt.workspace = true nargo_toml.workspace = true noir_lsp.workspace = true +noir_artifact_cli.workspace = true noir_debugger.workspace = true noirc_driver = { workspace = true, features = ["bn254"] } noirc_frontend = { workspace = true, features = ["bn254"] } diff --git a/noir/noir-repo/tooling/nargo_cli/benches/criterion.rs b/noir/noir-repo/tooling/nargo_cli/benches/criterion.rs index 0722e957833e..22c1107c71ca 100644 --- a/noir/noir-repo/tooling/nargo_cli/benches/criterion.rs +++ b/noir/noir-repo/tooling/nargo_cli/benches/criterion.rs @@ -1,18 +1,14 @@ //! Select representative tests to bench with criterion -use acvm::{acir::native_types::WitnessMap, FieldElement}; +use acvm::{FieldElement, acir::native_types::WitnessMap}; use assert_cmd::prelude::{CommandCargoExt, OutputAssertExt}; -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{Criterion, criterion_group, criterion_main}; -use noirc_abi::{ - input_parser::{Format, InputValue}, - Abi, InputMap, -}; -use noirc_artifacts::program::ProgramArtifact; +use noir_artifact_cli::fs::{artifact::read_program_from_file, inputs::read_inputs_from_file}; use noirc_driver::CompiledProgram; use pprof::criterion::{Output, PProfProfiler}; +use std::cell::RefCell; use std::hint::black_box; use std::path::Path; -use std::{cell::RefCell, collections::BTreeMap}; use std::{process::Command, time::Duration}; include!("./utils.rs"); @@ -50,14 +46,14 @@ fn read_compiled_programs_and_inputs( for package in binary_packages { let program_artifact_path = workspace.package_build_path(package); - let program: CompiledProgram = read_program_from_file(&program_artifact_path).into(); + let program: CompiledProgram = + read_program_from_file(&program_artifact_path).unwrap().into(); let (inputs, _) = read_inputs_from_file( - &package.root_dir, - nargo::constants::PROVER_INPUT_FILE, - Format::Toml, + &package.root_dir.join(nargo::constants::PROVER_INPUT_FILE).with_extension("toml"), &program.abi, - ); + ) + .expect("failed to read input"); let initial_witness = program.abi.encode(&inputs, None).expect("failed to encode input witness"); @@ -67,40 +63,6 @@ fn read_compiled_programs_and_inputs( programs } -/// Read the bytecode and ABI from the compilation output -fn read_program_from_file(circuit_path: &Path) -> ProgramArtifact { - let file_path = circuit_path.with_extension("json"); - let input_string = std::fs::read(file_path).expect("failed to read artifact file"); - serde_json::from_slice(&input_string).expect("failed to deserialize artifact") -} - -/// Read the inputs from Prover.toml -fn read_inputs_from_file( - path: &Path, - file_name: &str, - format: Format, - abi: &Abi, -) -> (InputMap, Option) { - if abi.is_empty() { - return (BTreeMap::new(), None); - } - - let file_path = path.join(file_name).with_extension(format.ext()); - if !file_path.exists() { - if abi.parameters.is_empty() { - return (BTreeMap::new(), None); - } else { - panic!("input file doesn't exist: {}", file_path.display()); - } - } - - let input_string = std::fs::read_to_string(file_path).expect("failed to read input file"); - let mut input_map = format.parse(&input_string, abi).expect("failed to parse input"); - let return_value = input_map.remove(noirc_abi::MAIN_RETURN_NAME); - - (input_map, return_value) -} - /// Use the nargo CLI to compile a test program, then benchmark its execution /// by executing the command directly from the benchmark, so that we can have /// meaningful flamegraphs about the ACVM. diff --git a/noir/noir-repo/tooling/nargo_cli/benches/iai.rs b/noir/noir-repo/tooling/nargo_cli/benches/iai.rs index bcd60111ccf6..c0526e9a7e2f 100644 --- a/noir/noir-repo/tooling/nargo_cli/benches/iai.rs +++ b/noir/noir-repo/tooling/nargo_cli/benches/iai.rs @@ -6,7 +6,7 @@ use std::process::Command; include!("./utils.rs"); macro_rules! iai_command { - ($command_name:tt, $command_string:expr) => { + ($command_name:tt, $command_string:expr_2021) => { paste! { fn []() { let test_program_dirs = get_selected_tests(); diff --git a/noir/noir-repo/tooling/nargo_cli/build.rs b/noir/noir-repo/tooling/nargo_cli/build.rs index afc6994823bd..b9faf018dfda 100644 --- a/noir/noir-repo/tooling/nargo_cli/build.rs +++ b/noir/noir-repo/tooling/nargo_cli/build.rs @@ -198,6 +198,9 @@ fn test_{test_name}(force_brillig: ForceBrillig, inliner_aggressiveness: Inliner // Allow more bytecode in exchange to catch illegal states. nargo.arg("--enable-brillig-debug-assertions"); + // Enable enums as an unstable feature + nargo.arg("-Zenums"); + if force_brillig.0 {{ nargo.arg("--force-brillig"); @@ -435,7 +438,6 @@ fn generate_compile_success_no_bug_tests(test_file: &mut File, test_data_dir: &P &test_dir, "compile", r#" - nargo.arg("--enable-brillig-constraints-check"); nargo.assert().success().stderr(predicate::str::contains("bug:").not()); "#, &MatrixConfig::default(), @@ -465,7 +467,6 @@ fn generate_compile_success_with_bug_tests(test_file: &mut File, test_data_dir: &test_dir, "compile", r#" - nargo.arg("--enable-brillig-constraints-check"); nargo.assert().success().stderr(predicate::str::contains("bug:")); "#, &MatrixConfig::default(), diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs index bb7f06692a66..2247f40f1817 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs @@ -8,15 +8,15 @@ use nargo::{ package::Package, parse_all, prepare_package, workspace::Workspace, }; use nargo_toml::PackageSelection; +use noir_artifact_cli::fs::artifact::write_to_file; use noirc_abi::{AbiParameter, AbiType, MAIN_RETURN_NAME}; -use noirc_driver::{check_crate, compute_function_abi, CompileOptions, CrateId}; +use noirc_driver::{CompileOptions, CrateId, check_crate, compute_function_abi}; use noirc_frontend::{ hir::{Context, ParsedFiles}, monomorphization::monomorphize, }; -use super::{fs::write_to_file, PackageOptions}; -use super::{LockType, WorkspaceCommand}; +use super::{LockType, PackageOptions, WorkspaceCommand}; /// Check a local package and all of its dependencies for errors #[derive(Debug, Clone, Args)] @@ -103,7 +103,8 @@ fn check_package( if should_write_prover { let prover_toml = create_input_toml_template(parameters.clone(), None); - write_to_file(prover_toml.as_bytes(), &path_to_prover_input); + write_to_file(prover_toml.as_bytes(), &path_to_prover_input) + .expect("failed to write template"); } else { eprintln!("Note: Prover.toml already exists. Use --overwrite to force overwrite."); } 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 bb08d2675cb8..2ee19f5edc01 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,6 +9,9 @@ use nargo::package::Package; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::PackageSelection; +use noir_artifact_cli::fs::artifact::{ + read_program_from_file, save_contract_to_file, save_program_to_file, +}; use noirc_driver::DEFAULT_EXPRESSION_WIDTH; use noirc_driver::NOIR_ARTIFACT_VERSION_STRING; use noirc_driver::{CompilationResult, CompileOptions, CompiledContract}; @@ -20,7 +23,6 @@ use notify_debouncer_full::new_debouncer; use crate::errors::CliError; -use super::fs::program::{read_program_from_file, save_contract_to_file, save_program_to_file}; use super::{LockType, PackageOptions, WorkspaceCommand}; use rayon::prelude::*; @@ -80,7 +82,7 @@ fn watch_workspace(workspace: &Workspace, compile_options: &CompileOptions) -> n let noir_files_modified = debounced_events.iter().any(|event| { let mut event_paths = event.event.paths.iter(); let event_affects_noir_file = - event_paths.any(|path| path.extension().map_or(false, |ext| ext == "nr")); + event_paths.any(|path| path.extension().is_some_and(|ext| ext == "nr")); let is_relevant_event_kind = matches!( event.kind, @@ -181,7 +183,7 @@ fn compile_programs( // The loaded circuit includes backend specific transformations, which might be different from the current target. let load_cached_program = |package| { let program_artifact_path = workspace.package_build_path(package); - read_program_from_file(program_artifact_path) + read_program_from_file(&program_artifact_path) .ok() .filter(|p| p.noir_version == NOIR_ARTIFACT_VERSION_STRING) .map(|p| p.into()) @@ -241,7 +243,8 @@ fn compile_programs( // Check solvability. nargo::ops::check_program(&program)?; // Overwrite the build artifacts with the final circuit, which includes the backend specific transformations. - save_program_to_file(&program.into(), &package.name, workspace.target_directory_path()); + save_program_to_file(&program.into(), &package.name, &workspace.target_directory_path()) + .expect("failed to save program"); Ok(((), warnings)) }; @@ -292,7 +295,8 @@ fn save_contract( &contract.into(), &format!("{}-{}", package.name, contract_name), target_dir, - ); + ) + .expect("failed to save contract"); if show_artifact_paths { println!("Saved contract artifact to: {}", artifact_path.display()); } @@ -321,6 +325,7 @@ mod tests { use nargo::ops::compile_program; use nargo_toml::PackageSelection; use noirc_driver::{CompileOptions, CrateName}; + use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use crate::cli::test_cmd::formatters::diagnostic_to_string; use crate::cli::{ @@ -343,7 +348,7 @@ mod tests { fn read_test_program_dirs( test_programs_dir: &Path, test_sub_dir: &str, - ) -> impl Iterator { + ) -> impl Iterator + use<> { let test_case_dir = test_programs_dir.join(test_sub_dir); std::fs::read_dir(test_case_dir) .unwrap() @@ -393,7 +398,7 @@ mod tests { assert!(!test_workspaces.is_empty(), "should find some test workspaces"); - test_workspaces.iter().for_each(|workspace| { + test_workspaces.par_iter().for_each(|workspace| { let (file_manager, parsed_files) = parse_workspace(workspace); let binary_packages = workspace.into_iter().filter(|package| package.is_binary()); diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/dap_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/dap_cmd.rs index 91db844ead34..8987ed80d3e6 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/dap_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/dap_cmd.rs @@ -1,12 +1,12 @@ +use acvm::FieldElement; use acvm::acir::circuit::ExpressionWidth; use acvm::acir::native_types::WitnessMap; -use acvm::FieldElement; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; use nargo::constants::PROVER_INPUT_FILE; use nargo::workspace::Workspace; -use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; -use noirc_abi::input_parser::Format; +use nargo_toml::{PackageSelection, get_package_manifest, resolve_workspace_from_toml}; +use noir_artifact_cli::fs::inputs::read_inputs_from_file; use noirc_driver::{CompileOptions, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING}; use noirc_frontend::graph::CrateName; @@ -20,7 +20,6 @@ use dap::types::Capabilities; use serde_json::Value; use super::debug_cmd::compile_bin_package_for_debugging; -use super::fs::inputs::read_inputs_from_file; use crate::errors::CliError; use noir_debugger::errors::{DapError, LoadError}; @@ -125,11 +124,13 @@ fn load_and_compile_project( let compiled_program = nargo::ops::transform_program(compiled_program, expression_width); - let (inputs_map, _) = - read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &compiled_program.abi) - .map_err(|_| { - LoadError::Generic(format!("Failed to read program inputs from {}", prover_name)) - })?; + let (inputs_map, _) = read_inputs_from_file( + &package.root_dir.join(prover_name).with_extension("toml"), + &compiled_program.abi, + ) + .map_err(|e| { + LoadError::Generic(format!("Failed to read program inputs from {prover_name}: {e}")) + })?; let initial_witness = compiled_program .abi .encode(&inputs_map, None) @@ -143,12 +144,7 @@ fn loop_uninitialized_dap( expression_width: ExpressionWidth, pedantic_solving: bool, ) -> Result<(), DapError> { - loop { - let req = match server.poll_request()? { - Some(req) => req, - None => break, - }; - + while let Some(req) = server.poll_request()? { match req.command { Command::Initialize(_) => { let rsp = req.success(ResponseBody::Initialize(Capabilities { @@ -165,7 +161,7 @@ fn loop_uninitialized_dap( server.respond(req.error("Missing launch arguments"))?; continue; }; - let Some(Value::String(ref project_folder)) = additional_data.get("projectFolder") + let Some(Value::String(project_folder)) = additional_data.get("projectFolder") else { server.respond(req.error("Missing project folder argument"))?; continue; diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs index 259abbbbfd03..f9303180fc0d 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -1,7 +1,7 @@ -use std::path::PathBuf; +use std::path::Path; -use acvm::acir::native_types::WitnessStack; use acvm::FieldElement; +use acvm::acir::native_types::WitnessStack; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; @@ -13,14 +13,15 @@ use nargo::package::{CrateName, Package}; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::PackageSelection; -use noirc_abi::input_parser::{Format, InputValue}; +use noir_artifact_cli::fs::inputs::read_inputs_from_file; +use noir_artifact_cli::fs::witness::save_witness_to_dir; use noirc_abi::InputMap; -use noirc_driver::{file_manager_with_stdlib, CompileOptions, CompiledProgram}; +use noirc_abi::input_parser::InputValue; +use noirc_driver::{CompileOptions, CompiledProgram, file_manager_with_stdlib}; use noirc_frontend::debug::DebugInstrumenter; use noirc_frontend::hir::ParsedFiles; use super::compile_cmd::get_target_width; -use super::fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}; use super::{LockType, WorkspaceCommand}; use crate::errors::CliError; @@ -48,6 +49,10 @@ pub(crate) struct DebugCommand { /// Disable vars debug instrumentation (enabled by default) #[clap(long)] skip_instrumentation: Option, + + /// Raw string printing of source for testing + #[clap(long, hide = true)] + raw_source_printing: Option, } impl WorkspaceCommand for DebugCommand { @@ -97,6 +102,7 @@ pub(crate) fn run(args: DebugCommand, workspace: Workspace) -> Result<(), CliErr &args.witness_name, target_dir, args.compile_options.pedantic_solving, + args.raw_source_printing.unwrap_or(false), ) } @@ -107,7 +113,7 @@ pub(crate) fn compile_bin_package_for_debugging( skip_instrumentation: bool, compile_options: CompileOptions, ) -> Result { - let mut workspace_file_manager = file_manager_with_stdlib(std::path::Path::new("")); + let mut workspace_file_manager = file_manager_with_stdlib(Path::new("")); insert_all_files_for_workspace_into_file_manager(workspace, &mut workspace_file_manager); let mut parsed_files = parse_all(&workspace_file_manager); @@ -170,7 +176,7 @@ fn instrument_package_files( for ancestor in file_path.ancestors() { if ancestor == entry_path_parent { // file is in package - debug_instrumenter.instrument_module(&mut parsed_file.0); + debug_instrumenter.instrument_module(&mut parsed_file.0, *file_id); } } } @@ -183,16 +189,22 @@ fn run_async( program: CompiledProgram, prover_name: &str, witness_name: &Option, - target_dir: &PathBuf, + target_dir: &Path, pedantic_solving: bool, + raw_source_printing: bool, ) -> Result<(), CliError> { use tokio::runtime::Builder; let runtime = Builder::new_current_thread().enable_all().build().unwrap(); runtime.block_on(async { println!("[{}] Starting debugger", package.name); - let (return_value, witness_stack) = - debug_program_and_decode(program, package, prover_name, pedantic_solving)?; + let (return_value, witness_stack) = debug_program_and_decode( + program, + package, + prover_name, + pedantic_solving, + raw_source_printing, + )?; if let Some(solved_witness_stack) = witness_stack { println!("[{}] Circuit witness successfully solved", package.name); @@ -203,7 +215,7 @@ fn run_async( if let Some(witness_name) = witness_name { let witness_path = - save_witness_to_dir(solved_witness_stack, witness_name, target_dir)?; + save_witness_to_dir(&solved_witness_stack, witness_name, target_dir)?; println!("[{}] Witness saved to {}", package.name, witness_path.display()); } @@ -220,12 +232,15 @@ fn debug_program_and_decode( package: &Package, prover_name: &str, pedantic_solving: bool, + raw_source_printing: bool, ) -> Result<(Option, Option>), CliError> { // Parse the initial witness values from Prover.toml - let (inputs_map, _) = - read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &program.abi)?; + let (inputs_map, _) = read_inputs_from_file( + &package.root_dir.join(prover_name).with_extension("toml"), + &program.abi, + )?; let program_abi = program.abi.clone(); - let witness_stack = debug_program(program, &inputs_map, pedantic_solving)?; + let witness_stack = debug_program(program, &inputs_map, pedantic_solving, raw_source_printing)?; match witness_stack { Some(witness_stack) => { @@ -244,6 +259,7 @@ pub(crate) fn debug_program( compiled_program: CompiledProgram, inputs_map: &InputMap, pedantic_solving: bool, + raw_source_printing: bool, ) -> Result>, CliError> { let initial_witness = compiled_program.abi.encode(inputs_map, None)?; @@ -251,6 +267,7 @@ pub(crate) fn debug_program( &Bn254BlackBoxSolver(pedantic_solving), compiled_program, initial_witness, + raw_source_printing, ) .map_err(CliError::from) } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs index 884ba06cdcbf..07a7c1195af7 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -1,26 +1,12 @@ -use std::path::PathBuf; - -use acvm::acir::native_types::WitnessStack; -use acvm::FieldElement; -use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; use nargo::constants::PROVER_INPUT_FILE; -use nargo::errors::try_to_diagnose_runtime_error; -use nargo::foreign_calls::DefaultForeignCallBuilder; -use nargo::package::Package; use nargo::workspace::Workspace; -use nargo::PrintOutput; use nargo_toml::PackageSelection; -use noirc_abi::input_parser::{Format, InputValue}; -use noirc_abi::InputMap; -use noirc_artifacts::debug::DebugArtifact; -use noirc_driver::{CompileOptions, CompiledProgram}; +use noirc_driver::CompileOptions; use super::compile_cmd::compile_workspace_full; -use super::fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}; use super::{LockType, PackageOptions, WorkspaceCommand}; -use crate::cli::fs::program::read_program_from_file; use crate::errors::CliError; /// Executes a circuit to calculate its return value @@ -59,126 +45,27 @@ impl WorkspaceCommand for ExecuteCommand { } pub(crate) fn run(args: ExecuteCommand, workspace: Workspace) -> Result<(), CliError> { - let target_dir = &workspace.target_directory_path(); - // Compile the full workspace in order to generate any build artifacts. compile_workspace_full(&workspace, &args.compile_options)?; let binary_packages = workspace.into_iter().filter(|package| package.is_binary()); for package in binary_packages { let program_artifact_path = workspace.package_build_path(package); - let program: CompiledProgram = - read_program_from_file(program_artifact_path.clone())?.into(); - let abi = program.abi.clone(); - - let results = execute_program_and_decode( - program, - package, - &args.prover_name, - args.oracle_resolver.as_deref(), - Some(workspace.root_dir.clone()), - Some(package.name.to_string()), - args.compile_options.pedantic_solving, - )?; - - println!("[{}] Circuit witness successfully solved", package.name); - if let Some(ref return_value) = results.actual_return { - println!("[{}] Circuit output: {return_value:?}", package.name); - } - - let package_name = package.name.clone().into(); - let witness_name = args.witness_name.as_ref().unwrap_or(&package_name); - let witness_path = save_witness_to_dir(results.witness_stack, witness_name, target_dir)?; - println!("[{}] Witness saved to {}", package.name, witness_path.display()); - - // Sanity checks on the return value after the witness has been saved, so it can be inspected if necessary. - if let Some(expected) = results.expected_return { - if results.actual_return.as_ref() != Some(&expected) { - return Err(CliError::UnexpectedReturn { expected, actual: results.actual_return }); - } - } - // We can expect that if the circuit returns something, it should be non-empty after execution. - if let Some(ref expected) = abi.return_type { - if results.actual_return.is_none() { - return Err(CliError::MissingReturn { expected: expected.clone() }); - } - } + let prover_file = package.root_dir.join(&args.prover_name).with_extension("toml"); + + let cmd = noir_artifact_cli::commands::execute_cmd::ExecuteCommand { + artifact_path: program_artifact_path, + prover_file, + output_dir: Some(workspace.target_directory_path()), + witness_name: Some( + args.witness_name.clone().unwrap_or_else(|| package.name.to_string()), + ), + contract_fn: None, + oracle_resolver: args.oracle_resolver.clone(), + pedantic_solving: args.compile_options.pedantic_solving, + }; + + noir_artifact_cli::commands::execute_cmd::run(cmd)?; } Ok(()) } - -fn execute_program_and_decode( - program: CompiledProgram, - package: &Package, - prover_name: &str, - foreign_call_resolver_url: Option<&str>, - root_path: Option, - package_name: Option, - pedantic_solving: bool, -) -> Result { - // Parse the initial witness values from Prover.toml - let (inputs_map, expected_return) = - read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &program.abi)?; - let witness_stack = execute_program( - &program, - &inputs_map, - foreign_call_resolver_url, - root_path, - package_name, - pedantic_solving, - )?; - // Get the entry point witness for the ABI - let main_witness = - &witness_stack.peek().expect("Should have at least one witness on the stack").witness; - let (_, actual_return) = program.abi.decode(main_witness)?; - - Ok(ExecutionResults { expected_return, actual_return, witness_stack }) -} - -struct ExecutionResults { - expected_return: Option, - actual_return: Option, - witness_stack: WitnessStack, -} - -pub(crate) fn execute_program( - compiled_program: &CompiledProgram, - inputs_map: &InputMap, - foreign_call_resolver_url: Option<&str>, - root_path: Option, - package_name: Option, - pedantic_solving: bool, -) -> Result, CliError> { - let initial_witness = compiled_program.abi.encode(inputs_map, None)?; - - let solved_witness_stack_err = nargo::ops::execute_program( - &compiled_program.program, - initial_witness, - &Bn254BlackBoxSolver(pedantic_solving), - &mut DefaultForeignCallBuilder { - output: PrintOutput::Stdout, - enable_mocks: false, - resolver_url: foreign_call_resolver_url.map(|s| s.to_string()), - root_path, - package_name, - } - .build(), - ); - match solved_witness_stack_err { - Ok(solved_witness_stack) => Ok(solved_witness_stack), - Err(err) => { - let debug_artifact = DebugArtifact { - debug_symbols: compiled_program.debug.clone(), - file_map: compiled_program.file_map.clone(), - }; - - if let Some(diagnostic) = - try_to_diagnose_runtime_error(&err, &compiled_program.abi, &compiled_program.debug) - { - diagnostic.report(&debug_artifact, false); - } - - Err(CliError::NargoError(err)) - } - } -} diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs index 09fe11fdb749..373dfce86a91 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs @@ -1,6 +1,7 @@ use nargo::errors::CompileError; use nargo::ops::report_errors; -use noirc_errors::FileDiagnostic; +use noir_artifact_cli::fs::artifact::save_program_to_file; +use noirc_errors::CustomDiagnostic; use noirc_frontend::hir::ParsedFiles; use rayon::prelude::*; @@ -11,7 +12,7 @@ use nargo::prepare_package; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::PackageSelection; -use noirc_driver::{compile_no_check, CompileOptions, CompiledProgram}; +use noirc_driver::{CompileOptions, CompiledProgram, compile_no_check}; use clap::Args; @@ -19,7 +20,6 @@ use crate::errors::CliError; use super::check_cmd::check_crate_and_report_errors; -use super::fs::program::save_program_to_file; use super::{LockType, PackageOptions, WorkspaceCommand}; /// Exports functions marked with #[export] attribute @@ -82,7 +82,7 @@ fn compile_exported_functions( |(function_name, function_id)| -> Result<(String, CompiledProgram), CompileError> { // TODO: We should to refactor how to deal with compilation errors to avoid this. let program = compile_no_check(&mut context, compile_options, function_id, None, false) - .map_err(|error| vec![FileDiagnostic::from(error)]); + .map_err(|error| vec![CustomDiagnostic::from(error)]); let program = report_errors( program.map(|program| (program, Vec::new())), @@ -97,7 +97,7 @@ fn compile_exported_functions( let export_dir = workspace.export_directory_path(); for (function_name, program) in exported_programs { - save_program_to_file(&program.into(), &function_name.parse().unwrap(), &export_dir); + save_program_to_file(&program.into(), &function_name.parse().unwrap(), &export_dir)?; } Ok(()) } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs index 1cdfb1e0c4fb..53551c139508 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs @@ -56,11 +56,8 @@ pub(crate) fn run(args: FormatCommand, workspace: Workspace) -> Result<(), CliEr let is_all_warnings = errors.iter().all(ParserError::is_warning); if !is_all_warnings { let errors = errors - .into_iter() - .map(|error| { - let error = CustomDiagnostic::from(&error); - error.in_file(file_id) - }) + .iter() + .map(CustomDiagnostic::from) .collect(); let _ = report_errors::<()>( @@ -119,7 +116,7 @@ fn visit_noir_files( let path = entry.path(); if path.is_dir() { visit_noir_files(&path, cb)?; - } else if entry.path().extension().map_or(false, |extension| extension == "nr") { + } else if entry.path().extension().is_some_and(|extension| extension == "nr") { cb(&entry)?; } } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/inputs.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/fs/inputs.rs deleted file mode 100644 index 4a7a81431bbc..000000000000 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/inputs.rs +++ /dev/null @@ -1,42 +0,0 @@ -use noirc_abi::{ - input_parser::{Format, InputValue}, - Abi, InputMap, MAIN_RETURN_NAME, -}; -use std::{collections::BTreeMap, path::Path}; - -use crate::errors::FilesystemError; - -/// Returns the circuit's parameters and its return value, if one exists. -/// # Examples -/// -/// ```ignore -/// let (input_map, return_value): (InputMap, Option) = -/// read_inputs_from_file(path, "Verifier", Format::Toml, &abi)?; -/// ``` -pub(crate) fn read_inputs_from_file>( - path: P, - file_name: &str, - format: Format, - abi: &Abi, -) -> Result<(InputMap, Option), FilesystemError> { - if abi.is_empty() { - return Ok((BTreeMap::new(), None)); - } - - let file_path = path.as_ref().join(file_name).with_extension(format.ext()); - if !file_path.exists() { - if abi.parameters.is_empty() { - // Reading a return value from the `Prover.toml` is optional, - // so if the ABI has no parameters we can skip reading the file if it doesn't exist. - return Ok((BTreeMap::new(), None)); - } else { - return Err(FilesystemError::MissingTomlFile(file_name.to_owned(), file_path)); - } - } - - let input_string = std::fs::read_to_string(file_path).unwrap(); - let mut input_map = format.parse(&input_string, abi)?; - let return_value = input_map.remove(MAIN_RETURN_NAME); - - Ok((input_map, return_value)) -} diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/mod.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/fs/mod.rs deleted file mode 100644 index 8658bd5b2482..000000000000 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/mod.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::{ - fs::File, - io::Write, - path::{Path, PathBuf}, -}; - -pub(super) mod inputs; -pub(super) mod program; -pub(super) mod witness; - -pub(super) fn create_named_dir(named_dir: &Path, name: &str) -> PathBuf { - std::fs::create_dir_all(named_dir) - .unwrap_or_else(|_| panic!("could not create the `{name}` directory")); - - PathBuf::from(named_dir) -} - -pub(super) fn write_to_file(bytes: &[u8], path: &Path) -> String { - let display = path.display(); - - let mut file = match File::create(path) { - Err(why) => panic!("couldn't create {display}: {why}"), - Ok(file) => file, - }; - - match file.write_all(bytes) { - Err(why) => panic!("couldn't write to {display}: {why}"), - Ok(_) => display.to_string(), - } -} diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/program.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/fs/program.rs deleted file mode 100644 index 87783e4573a9..000000000000 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/program.rs +++ /dev/null @@ -1,51 +0,0 @@ -use std::path::{Path, PathBuf}; - -use nargo::package::CrateName; -use noirc_artifacts::{contract::ContractArtifact, program::ProgramArtifact}; - -use crate::errors::FilesystemError; - -use super::{create_named_dir, write_to_file}; - -pub(crate) fn save_program_to_file>( - program_artifact: &ProgramArtifact, - crate_name: &CrateName, - circuit_dir: P, -) -> PathBuf { - let circuit_name: String = crate_name.into(); - save_build_artifact_to_file(program_artifact, &circuit_name, circuit_dir) -} - -pub(crate) fn save_contract_to_file>( - compiled_contract: &ContractArtifact, - circuit_name: &str, - circuit_dir: P, -) -> PathBuf { - save_build_artifact_to_file(compiled_contract, circuit_name, circuit_dir) -} - -fn save_build_artifact_to_file, T: ?Sized + serde::Serialize>( - build_artifact: &T, - artifact_name: &str, - circuit_dir: P, -) -> PathBuf { - create_named_dir(circuit_dir.as_ref(), "target"); - let circuit_path = circuit_dir.as_ref().join(artifact_name).with_extension("json"); - write_to_file(&serde_json::to_vec(build_artifact).unwrap(), &circuit_path); - - circuit_path -} - -#[tracing::instrument(level = "trace", skip_all)] -pub(crate) fn read_program_from_file>( - circuit_path: P, -) -> Result { - let file_path = circuit_path.as_ref().with_extension("json"); - - let input_string = - std::fs::read(&file_path).map_err(|_| FilesystemError::PathNotValid(file_path))?; - let program = serde_json::from_slice(&input_string) - .map_err(|err| FilesystemError::ProgramSerializationError(err.to_string()))?; - - Ok(program) -} diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/witness.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/fs/witness.rs deleted file mode 100644 index f95eb3d7a4c0..000000000000 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/witness.rs +++ /dev/null @@ -1,22 +0,0 @@ -use std::path::{Path, PathBuf}; - -use acvm::{acir::native_types::WitnessStack, FieldElement}; -use nargo::constants::WITNESS_EXT; - -use super::{create_named_dir, write_to_file}; -use crate::errors::FilesystemError; - -pub(crate) fn save_witness_to_dir>( - witness_stack: WitnessStack, - witness_name: &str, - witness_dir: P, -) -> Result { - create_named_dir(witness_dir.as_ref(), "witness"); - let witness_path = witness_dir.as_ref().join(witness_name).with_extension(WITNESS_EXT); - - let buf: Vec = witness_stack.try_into()?; - - write_to_file(buf.as_slice(), &witness_path); - - Ok(witness_path) -} diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/generate_completion_script_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/generate_completion_script_cmd.rs index 0b8fa1ee3e70..5f7329e14ba2 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/generate_completion_script_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/generate_completion_script_cmd.rs @@ -23,7 +23,7 @@ pub(crate) fn run(command: GenerateCompletionScriptCommand) -> Result<(), CliErr return Err(CliError::Generic( "Invalid shell. Supported shells are: bash, elvish, fish, powershell, zsh" .to_string(), - )) + )); } }; diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs index 4561a1221f66..c8a9c289fc0e 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs @@ -7,22 +7,21 @@ use nargo::{ workspace::Workspace, }; use nargo_toml::PackageSelection; -use noirc_abi::input_parser::Format; +use noir_artifact_cli::fs::{artifact::read_program_from_file, inputs::read_inputs_from_file}; use noirc_artifacts::program::ProgramArtifact; use noirc_artifacts_info::{ - count_opcodes_and_gates_in_program, show_info_report, FunctionInfo, InfoReport, ProgramInfo, + FunctionInfo, InfoReport, ProgramInfo, count_opcodes_and_gates_in_program, show_info_report, }; use noirc_driver::CompileOptions; -use prettytable::{row, Row}; +use prettytable::{Row, row}; use rayon::prelude::*; use serde::Serialize; use crate::errors::CliError; use super::{ - compile_cmd::{compile_workspace_full, get_target_width}, - fs::{inputs::read_inputs_from_file, program::read_program_from_file}, LockType, PackageOptions, WorkspaceCommand, + compile_cmd::{compile_workspace_full, get_target_width}, }; /// Provides detailed information on each of a program's function (represented by a single circuit) @@ -75,7 +74,7 @@ pub(crate) fn run(mut args: InfoCommand, workspace: Workspace) -> Result<(), Cli .filter(|package| package.is_binary()) .map(|package| -> Result<(Package, ProgramArtifact), CliError> { let program_artifact_path = workspace.package_build_path(package); - let program = read_program_from_file(program_artifact_path)?; + let program = read_program_from_file(&program_artifact_path)?; Ok((package.clone(), program)) }) .collect::>()?; @@ -144,9 +143,7 @@ fn profile_brillig_execution( for (package, program_artifact) in binary_packages.iter() { // Parse the initial witness values from Prover.toml or Prover.json let (inputs_map, _) = read_inputs_from_file( - &package.root_dir, - prover_name, - Format::Toml, + &package.root_dir.join(prover_name).with_extension("toml"), &program_artifact.abi, )?; let initial_witness = program_artifact.abi.encode(&inputs_map, None)?; diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/init_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/init_cmd.rs index ffeb5d9ba74b..bc67a9ba3a87 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/init_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/init_cmd.rs @@ -1,10 +1,10 @@ use crate::errors::CliError; -use super::fs::{create_named_dir, write_to_file}; use super::NargoConfig; use clap::Args; use nargo::constants::{PKG_FILE, SRC_DIR}; use nargo::package::{CrateName, PackageType}; +use noir_artifact_cli::fs::artifact::write_to_file; use std::path::PathBuf; /// Create a Noir project in the current directory. @@ -58,7 +58,6 @@ pub(crate) fn initialize_project( package_type: PackageType, ) { let src_dir = package_dir.join(SRC_DIR); - create_named_dir(&src_dir, "src"); let toml_contents = format!( r#"[package] @@ -69,14 +68,18 @@ authors = [""] [dependencies]"# ); - write_to_file(toml_contents.as_bytes(), &package_dir.join(PKG_FILE)); + write_to_file(toml_contents.as_bytes(), &package_dir.join(PKG_FILE)).unwrap(); // This uses the `match` syntax instead of `if` so we get a compile error when we add new package types (which likely need new template files) match package_type { - PackageType::Binary => write_to_file(BIN_EXAMPLE.as_bytes(), &src_dir.join("main.nr")), + PackageType::Binary => { + write_to_file(BIN_EXAMPLE.as_bytes(), &src_dir.join("main.nr")).unwrap(); + } PackageType::Contract => { - write_to_file(CONTRACT_EXAMPLE.as_bytes(), &src_dir.join("main.nr")) + write_to_file(CONTRACT_EXAMPLE.as_bytes(), &src_dir.join("main.nr")).unwrap(); + } + PackageType::Library => { + write_to_file(LIB_EXAMPLE.as_bytes(), &src_dir.join("lib.nr")).unwrap(); } - PackageType::Library => write_to_file(LIB_EXAMPLE.as_bytes(), &src_dir.join("lib.nr")), }; println!("Project successfully created! It is located at {}", package_dir.display()); } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/mod.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/mod.rs index 1d59fdb806fd..0c644ae554c8 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/mod.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/mod.rs @@ -2,7 +2,7 @@ use clap::{Args, Parser, Subcommand}; use const_format::formatcp; use nargo::workspace::Workspace; use nargo_toml::{ - get_package_manifest, resolve_workspace_from_toml, ManifestError, PackageSelection, + ManifestError, PackageSelection, get_package_manifest, resolve_workspace_from_toml, }; use noirc_driver::{CrateName, NOIR_ARTIFACT_VERSION_STRING}; use std::{ @@ -14,8 +14,6 @@ use color_eyre::eyre; use crate::errors::CliError; -mod fs; - mod check_cmd; mod compile_cmd; mod dap_cmd; @@ -213,14 +211,15 @@ where /// Lock the (selected) packages in the workspace. /// The lock taken can be shared for commands that only read the artifacts, /// or exclusive for the ones that (might) write artifacts as well. -fn lock_workspace(workspace: &Workspace, exclusive: bool) -> Result, CliError> { - use fs2::FileExt as _; - +fn lock_workspace( + workspace: &Workspace, + exclusive: bool, +) -> Result>, CliError> { struct LockedFile(File); impl Drop for LockedFile { fn drop(&mut self) { - let _ = self.0.unlock(); + let _ = fs2::FileExt::unlock(&self.0); } } @@ -233,15 +232,17 @@ fn lock_workspace(workspace: &Workspace, exclusive: bool) -> Result TestRunner<'a> { }; } - if all_passed { - Ok(()) - } else { - Err(CliError::Generic(String::new())) - } + if all_passed { Ok(()) } else { Err(CliError::Generic(String::new())) } } /// Runs all tests. Returns `true` if all tests passed, `false` otherwise. @@ -261,21 +257,22 @@ impl<'a> TestRunner<'a> { // Specify a larger-than-default stack size to prevent overflowing stack in large programs. // (the default is 2MB) .stack_size(STACK_SIZE) - .spawn_scoped(scope, move || loop { - // Get next test to process from the iterator. - let Some(test) = iter.lock().unwrap().next() else { - break; - }; - - self.formatter - .test_start_async(&test.name, &test.package_name) - .expect("Could not display test start"); - - let time_before_test = std::time::Instant::now(); - let (status, output) = match catch_unwind(test.runner) { - Ok((status, output)) => (status, output), - Err(err) => ( - TestStatus::Fail { + .spawn_scoped(scope, move || { + loop { + // Get next test to process from the iterator. + let Some(test) = iter.lock().unwrap().next() else { + break; + }; + + self.formatter + .test_start_async(&test.name, &test.package_name) + .expect("Could not display test start"); + + let time_before_test = std::time::Instant::now(); + let (status, output) = match catch_unwind(test.runner) { + Ok((status, output)) => (status, output), + Err(err) => ( + TestStatus::Fail { message: // It seems `panic!("...")` makes the error be `&str`, so we handle this common case if let Some(message) = err.downcast_ref::<&str>() { @@ -285,31 +282,32 @@ impl<'a> TestRunner<'a> { }, error_diagnostic: None, }, - String::new(), - ), - }; - let time_to_run = time_before_test.elapsed(); - - let test_result = TestResult { - name: test.name, - package_name: test.package_name, - status, - output, - time_to_run, - }; - - self.formatter - .test_end_async( - &test_result, - self.file_manager, - self.args.show_output, - self.args.compile_options.deny_warnings, - self.args.compile_options.silence_warnings, - ) - .expect("Could not display test start"); - - if thread_sender.send(test_result).is_err() { - break; + String::new(), + ), + }; + let time_to_run = time_before_test.elapsed(); + + let test_result = TestResult { + name: test.name, + package_name: test.package_name, + status, + output, + time_to_run, + }; + + self.formatter + .test_end_async( + &test_result, + self.file_manager, + self.args.show_output, + self.args.compile_options.deny_warnings, + self.args.compile_options.silence_warnings, + ) + .expect("Could not display test start"); + + if thread_sender.send(test_result).is_err() { + break; + } } }) .unwrap(); @@ -407,19 +405,21 @@ impl<'a> TestRunner<'a> { // Specify a larger-than-default stack size to prevent overflowing stack in large programs. // (the default is 2MB) .stack_size(STACK_SIZE) - .spawn_scoped(scope, move || loop { - // Get next package to process from the iterator. - let Some(package) = iter.lock().unwrap().next() else { - break; - }; - let tests = self.collect_package_tests::( - package, - self.args.oracle_resolver.as_deref(), - Some(self.workspace.root_dir.clone()), - package.name.to_string(), - ); - if thread_sender.send((package, tests)).is_err() { - break; + .spawn_scoped(scope, move || { + loop { + // Get next package to process from the iterator. + let Some(package) = iter.lock().unwrap().next() else { + break; + }; + let tests = self.collect_package_tests::( + package, + self.args.oracle_resolver.as_deref(), + Some(self.workspace.root_dir.clone()), + package.name.to_string(), + ); + if thread_sender.send((package, tests)).is_err() { + break; + } } }) .unwrap(); @@ -440,11 +440,7 @@ impl<'a> TestRunner<'a> { } }); - if let Some(error) = error { - Err(error) - } else { - Ok(package_tests) - } + if let Some(error) = error { Err(error) } else { Ok(package_tests) } } /// Compiles a single package and returns all of its tests diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd/formatters.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd/formatters.rs index 91d6f0066b1c..686281292458 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd/formatters.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd/formatters.rs @@ -2,8 +2,8 @@ use std::{io::Write, panic::RefUnwindSafe, time::Duration}; use fm::FileManager; use nargo::ops::TestStatus; -use noirc_errors::{reporter::stack_trace, FileDiagnostic}; -use serde_json::{json, Map}; +use noirc_errors::{CustomDiagnostic, reporter::stack_trace}; +use serde_json::{Map, json}; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, StandardStreamLock, WriteColor}; use super::TestResult; @@ -438,7 +438,7 @@ impl Formatter for JsonFormatter { stdout.push_str(message.trim()); if let Some(diagnostic) = error_diagnostic { - if !(diagnostic.diagnostic.is_warning() && silence_warnings) { + if !(diagnostic.is_warning() && silence_warnings) { stdout.push('\n'); stdout.push_str(&diagnostic_to_string(diagnostic, file_manager)); } @@ -450,7 +450,7 @@ impl Formatter for JsonFormatter { TestStatus::CompileError(diagnostic) => { json.insert("event".to_string(), json!("failed")); - if !(diagnostic.diagnostic.is_warning() && silence_warnings) { + if !(diagnostic.is_warning() && silence_warnings) { if !stdout.is_empty() { stdout.push('\n'); } @@ -515,12 +515,11 @@ fn package_start(package_name: &str, test_count: usize) -> std::io::Result<()> { } pub(crate) fn diagnostic_to_string( - file_diagnostic: &FileDiagnostic, + custom_diagnostic: &CustomDiagnostic, file_manager: &FileManager, ) -> String { let file_map = file_manager.as_file_map(); - let custom_diagnostic = &file_diagnostic.diagnostic; let mut message = String::new(); message.push_str(custom_diagnostic.message.trim()); @@ -529,7 +528,7 @@ pub(crate) fn diagnostic_to_string( message.push_str(note.trim()); } - if let Ok(name) = file_map.get_name(file_diagnostic.file_id) { + if let Ok(name) = file_map.get_name(custom_diagnostic.file) { message.push('\n'); message.push_str(&format!("at {name}")); } diff --git a/noir/noir-repo/tooling/nargo_cli/src/errors.rs b/noir/noir-repo/tooling/nargo_cli/src/errors.rs index 9255d6fc6a69..fb241bcbd293 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/errors.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/errors.rs @@ -1,37 +1,11 @@ -use acvm::{acir::native_types::WitnessStackError, FieldElement}; -use nargo::{errors::CompileError, NargoError}; +use acvm::FieldElement; +use nargo::{NargoError, errors::CompileError}; use nargo_toml::ManifestError; use noir_debugger::errors::DapError; -use noirc_abi::{ - errors::{AbiError, InputParserError}, - input_parser::InputValue, - AbiReturnType, -}; +use noirc_abi::errors::AbiError; use std::path::PathBuf; use thiserror::Error; -#[derive(Debug, Error)] -pub(crate) enum FilesystemError { - #[error("Error: {} is not a valid path\nRun either `nargo compile` to generate missing build artifacts or `nargo prove` to construct a proof", .0.display())] - PathNotValid(PathBuf), - - #[error( - " Error: cannot find {0}.toml file.\n Expected location: {1:?} \n Please generate this file at the expected location." - )] - MissingTomlFile(String, PathBuf), - - /// Input parsing error - #[error(transparent)] - InputParserError(#[from] InputParserError), - - /// WitnessStack serialization error - #[error(transparent)] - WitnessStackSerialization(#[from] WitnessStackError), - - #[error("Error: could not deserialize build program: {0}")] - ProgramSerializationError(String), -} - #[derive(Debug, Error)] pub(crate) enum CliError { #[error("{0}")] @@ -43,13 +17,13 @@ pub(crate) enum CliError { #[error("Invalid package name {0}. Did you mean to use `--name`?")] InvalidPackageName(String), - /// ABI encoding/decoding error + /// Artifact CLI error #[error(transparent)] - AbiError(#[from] AbiError), + ArtifactError(#[from] noir_artifact_cli::errors::CliError), - /// Filesystem errors + /// ABI encoding/decoding error #[error(transparent)] - FilesystemError(#[from] FilesystemError), + AbiError(#[from] AbiError), #[error(transparent)] LspError(#[from] async_lsp::Error), @@ -68,10 +42,4 @@ pub(crate) enum CliError { /// Error from the compilation pipeline #[error(transparent)] CompileError(#[from] CompileError), - - #[error("Unexpected return value: expected {expected:?}; got {actual:?}")] - UnexpectedReturn { expected: InputValue, actual: Option }, - - #[error("Missing return witnesses; expected {expected:?}")] - MissingReturn { expected: AbiReturnType }, } diff --git a/noir/noir-repo/tooling/nargo_cli/src/main.rs b/noir/noir-repo/tooling/nargo_cli/src/main.rs index 3ea167b7ffad..f4cc74447bc6 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/main.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/main.rs @@ -15,7 +15,7 @@ use std::env; use color_eyre::config::HookBuilder; use tracing_appender::rolling; -use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter}; +use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan}; const PANIC_MESSAGE: &str = "This is a bug. We may have already fixed this in newer versions of Nargo so try searching for similar issues at https://github.com/noir-lang/noir/issues/.\nIf there isn't an open issue for this bug, consider opening one at https://github.com/noir-lang/noir/issues/new?labels=bug&template=bug_report.yml"; diff --git a/noir/noir-repo/tooling/nargo_cli/tests/stdlib-props.rs b/noir/noir-repo/tooling/nargo_cli/tests/stdlib-props.rs index 780b34bf0c34..bafcc13d855b 100644 --- a/noir/noir-repo/tooling/nargo_cli/tests/stdlib-props.rs +++ b/noir/noir-repo/tooling/nargo_cli/tests/stdlib-props.rs @@ -1,16 +1,15 @@ use std::{cell::RefCell, collections::BTreeMap, path::Path}; -use acvm::{acir::native_types::WitnessStack, AcirField, FieldElement}; +use acvm::{AcirField, FieldElement, acir::native_types::WitnessStack}; use iter_extended::vecmap; use nargo::{foreign_calls::DefaultForeignCallBuilder, ops::execute_program, parse_all}; use noirc_abi::input_parser::InputValue; use noirc_driver::{ - compile_main, file_manager_with_stdlib, prepare_crate, CompilationResult, CompileOptions, - CompiledProgram, CrateId, + CompilationResult, CompileOptions, CompiledProgram, CrateId, compile_main, + file_manager_with_stdlib, prepare_crate, }; use noirc_frontend::hir::Context; use proptest::prelude::*; -use sha3::Digest; /// Inputs and expected output of a snippet encoded in ABI format. #[derive(Debug)] @@ -105,64 +104,6 @@ fn run_snippet_proptest( }); } -/// Run property tests on a code snippet which is assumed to execute a hashing function with the following signature: -/// -/// ```ignore -/// fn main(input: [u8; {max_len}], message_size: u32) -> pub [u8; 32] -/// ``` -/// -/// The calls are executed with and without forcing brillig, because it seems common for hash functions to run different -/// code paths based on `runtime::is_unconstrained()`. -fn run_hash_proptest( - // Different generic maximum input sizes to try. - max_lengths: &[usize], - // Some hash functions allow inputs which are less than the generic parameters, others don't. - variable_length: bool, - // Make the source code specialized for a given expected input size. - source: impl Fn(usize) -> String, - // Rust implementation of the hash function. - hash: fn(&[u8]) -> [u8; N], -) { - for max_len in max_lengths { - let max_len = *max_len; - // The maximum length is used to pick the generic version of the method. - let source = source(max_len); - // Hash functions runs differently depending on whether the code is unconstrained or not. - for force_brillig in [false, true] { - let length_strategy = - if variable_length { (0..=max_len).boxed() } else { Just(max_len).boxed() }; - // The actual input length can be up to the maximum. - let strategy = length_strategy - .prop_flat_map(|len| prop::collection::vec(any::(), len)) - .prop_map(move |mut msg| { - // The output is the hash of the data as it is. - let output = hash(&msg); - - // The input has to be padded to the maximum length. - let msg_size = msg.len(); - msg.resize(max_len, 0u8); - - let mut inputs = vec![("input", bytes_input(&msg))]; - - // Omit the `message_size` if the hash function doesn't support it. - if variable_length { - inputs.push(( - "message_size", - InputValue::Field(FieldElement::from(msg_size)), - )); - } - - SnippetInputOutput::new(inputs, bytes_input(&output)).with_description(format!( - "force_brillig = {force_brillig}, max_len = {max_len}" - )) - }) - .boxed(); - - run_snippet_proptest(source.clone(), force_brillig, strategy); - } - } -} - /// This is just a simple test to check that property testing works. #[test] fn fuzz_basic() { @@ -187,72 +128,6 @@ fn fuzz_basic() { run_snippet_proptest(program.to_string(), false, strategy); } -#[test] -fn fuzz_keccak256_equivalence() { - run_hash_proptest( - // XXX: Currently it fails with inputs >= 135 bytes - &[0, 1, 100, 134], - true, - |max_len| { - format!( - "fn main(input: [u8; {max_len}], message_size: u32) -> pub [u8; 32] {{ - std::hash::keccak256(input, message_size) - }}" - ) - }, - |data| sha3::Keccak256::digest(data).into(), - ); -} - -#[test] -#[should_panic] // Remove once fixed -fn fuzz_keccak256_equivalence_over_135() { - run_hash_proptest( - &[135, 150], - true, - |max_len| { - format!( - "fn main(input: [u8; {max_len}], message_size: u32) -> pub [u8; 32] {{ - std::hash::keccak256(input, message_size) - }}" - ) - }, - |data| sha3::Keccak256::digest(data).into(), - ); -} - -#[test] -fn fuzz_sha256_equivalence() { - run_hash_proptest( - &[0, 1, 200, 511, 512], - true, - |max_len| { - format!( - "fn main(input: [u8; {max_len}], message_size: u64) -> pub [u8; 32] {{ - std::hash::sha256_var(input, message_size) - }}" - ) - }, - |data| sha2::Sha256::digest(data).into(), - ); -} - -#[test] -fn fuzz_sha512_equivalence() { - run_hash_proptest( - &[0, 1, 200], - false, - |max_len| { - format!( - "fn main(input: [u8; {max_len}]) -> pub [u8; 64] {{ - std::hash::sha512::digest(input) - }}" - ) - }, - |data| sha2::Sha512::digest(data).into(), - ); -} - #[test] fn fuzz_poseidon2_equivalence() { use bn254_blackbox_solver::poseidon_hash; @@ -332,12 +207,6 @@ fn fuzz_poseidon_equivalence() { } } -fn bytes_input(bytes: &[u8]) -> InputValue { - InputValue::Vec( - bytes.iter().map(|b| InputValue::Field(FieldElement::from(*b as u32))).collect(), - ) -} - fn field_vec_strategy(len: usize) -> impl Strategy> { // Generate Field elements from random 32 byte vectors. let field = prop::collection::vec(any::(), 32) diff --git a/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs b/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs index 2e3e3e2ae5af..6096b619e576 100644 --- a/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs +++ b/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs @@ -2,15 +2,15 @@ #![allow(clippy::items_after_test_module)] use clap::Parser; use fm::FileManager; -use nargo::foreign_calls::DefaultForeignCallBuilder; use nargo::PrintOutput; -use noirc_driver::{check_crate, file_manager_with_stdlib, CompileOptions}; +use nargo::foreign_calls::DefaultForeignCallBuilder; +use noirc_driver::{CompileOptions, check_crate, file_manager_with_stdlib}; use noirc_frontend::hir::FunctionNameMatch; use std::io::Write; use std::{collections::BTreeMap, path::PathBuf}; use nargo::{ - ops::{report_errors, run_test, TestStatus}, + ops::{TestStatus, report_errors, run_test}, package::{Package, PackageType}, parse_all, prepare_package, }; diff --git a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml index 710519712a2c..46dc61d30157 100644 --- a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml @@ -11,6 +11,7 @@ workspace = true [dependencies] noirc_frontend.workspace = true +noirc_errors.workspace = true serde.workspace = true toml.workspace = true thiserror.workspace = true diff --git a/noir/noir-repo/tooling/nargo_fmt/build.rs b/noir/noir-repo/tooling/nargo_fmt/build.rs index 988a7dcc94d5..f0dde56b0ca0 100644 --- a/noir/noir-repo/tooling/nargo_fmt/build.rs +++ b/noir/noir-repo/tooling/nargo_fmt/build.rs @@ -60,7 +60,7 @@ fn generate_formatter_tests(test_file: &mut File, test_data_dir: &Path) { let expected_output = r#"{output_source}"#; - let (parsed_module, _errors) = noirc_frontend::parse_program(input); + let (parsed_module, _errors) = noirc_frontend::parse_program_with_dummy_file(input); let config = nargo_fmt::Config::of("{config}", &std::path::PathBuf::new()).unwrap(); let fmt_text = nargo_fmt::format(input, parsed_module, &config); @@ -82,7 +82,7 @@ fn generate_formatter_tests(test_file: &mut File, test_data_dir: &Path) { fn format_idempotent_{test_name}() {{ let expected_output = r#"{output_source}"#; - let (parsed_module, _errors) = noirc_frontend::parse_program(expected_output); + let (parsed_module, _errors) = noirc_frontend::parse_program_with_dummy_file(expected_output); let config = nargo_fmt::Config::of("{config}", &std::path::PathBuf::new()).unwrap(); let fmt_text = nargo_fmt::format(expected_output, parsed_module, &config); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/chunks.rs b/noir/noir-repo/tooling/nargo_fmt/src/chunks.rs index fcef261284df..28e6d22f2c8e 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/chunks.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/chunks.rs @@ -113,11 +113,7 @@ impl Chunk { /// Returns the current chunk as a Group, if it is one. Otherwise returns None. pub(crate) fn group(self) -> Option { - if let Chunk::Group(group) = self { - Some(group) - } else { - None - } + if let Chunk::Group(group) = self { Some(group) } else { None } } } @@ -546,7 +542,7 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { /// Treating a `ChunkFormatter` as a `Formatter` in read-only mode is always fine, /// and reduces some boilerplate. -impl<'a, 'b> Deref for ChunkFormatter<'a, 'b> { +impl<'b> Deref for ChunkFormatter<'_, 'b> { type Target = Formatter<'b>; fn deref(&self) -> &Self::Target { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/config.rs b/noir/noir-repo/tooling/nargo_fmt/src/config.rs index f01afc87af2e..3950d267c5ea 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/config.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/config.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use crate::errors::ConfigError; macro_rules! config { - ($($field_name:ident: $field_ty:ty, $default_value:expr, $description:expr );+ $(;)*) => ( + ($($field_name:ident: $field_ty:ty, $default_value:expr_2021, $description:expr_2021 );+ $(;)*) => ( pub struct Config { $( #[doc = $description] diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter.rs index 2a8adb3fb28a..cff0b2c0619b 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter.rs @@ -1,10 +1,10 @@ use buffer::Buffer; use noirc_frontend::{ + ParsedModule, ast::Ident, hir::resolution::errors::Span, lexer::Lexer, token::{Keyword, SpannedToken, Token}, - ParsedModule, }; use crate::Config; @@ -80,7 +80,7 @@ pub(crate) struct Formatter<'a> { impl<'a> Formatter<'a> { pub(crate) fn new(source: &'a str, config: &'a Config) -> Self { - let lexer = Lexer::new(source).skip_comments(false).skip_whitespaces(false); + let lexer = Lexer::new_with_dummy_file(source).skip_comments(false).skip_whitespaces(false); let mut formatter = Self { config, source, @@ -107,6 +107,7 @@ impl<'a> Formatter<'a> { ); self.format_parsed_module(parsed_module, self.ignore_next); + self.buffer.trim_multiple_newlines(); } pub(crate) fn format_parsed_module(&mut self, parsed_module: ParsedModule, ignore_next: bool) { @@ -295,7 +296,7 @@ impl<'a> Formatter<'a> { self.ignore_next = false; let next_token = self.read_token_internal(); - self.token_span = next_token.to_span(); + self.token_span = next_token.span(); std::mem::replace(&mut self.token, next_token.into_token()) } @@ -303,7 +304,7 @@ impl<'a> Formatter<'a> { let token = self.lexer.next(); if let Some(token) = token { match token { - Ok(token) => token, + Ok(token) => token.into_spanned_token(), Err(err) => panic!("Expected lexer not to error, but got: {:?}", err), } } else { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/alias.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/alias.rs index d4c63ebdd9e1..96eedfd3d885 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/alias.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/alias.rs @@ -5,7 +5,7 @@ use noirc_frontend::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_type_alias(&mut self, type_alias: NoirTypeAlias) { self.write_indentation(); self.format_item_visibility(type_alias.visibility); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/attribute.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/attribute.rs index 19d5730a546c..d23a788eae4c 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/attribute.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/attribute.rs @@ -6,7 +6,7 @@ use crate::chunks::ChunkGroup; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_attributes(&mut self, attributes: Attributes) { let mut all_attributes = Vec::new(); for attribute in attributes.secondary { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/buffer.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/buffer.rs index 3e4bebef6081..0440f30eb586 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/buffer.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/buffer.rs @@ -56,6 +56,13 @@ impl Buffer { } } + /// Trim multiple newlines from the end of the buffer, keeping at most one. + pub(crate) fn trim_multiple_newlines(&mut self) { + while self.buffer.ends_with("\n\n") { + self.buffer.truncate(self.buffer.len() - 1); + } + } + pub(crate) fn contents(self) -> String { self.buffer } diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/comments_and_whitespace.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/comments_and_whitespace.rs index e20eb4291d18..da988e7039ec 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/comments_and_whitespace.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/comments_and_whitespace.rs @@ -7,7 +7,7 @@ const NEWLINE: &str = "\r\n"; #[cfg(not(windows))] const NEWLINE: &str = "\n"; -impl<'a> Formatter<'a> { +impl Formatter<'_> { /// Writes a single space, skipping any whitespace and comments. /// That is, suppose the next token is a big whitespace, possibly with multiple lines. /// Those are skipped but only one space is written. In this way if we have @@ -858,4 +858,11 @@ global x = 1; "; assert_format(src, expected); } + + #[test] + fn trims_newlines_from_the_end_of_the_file() { + let src = "global x: Field = 1;\n\n\n"; + let expected = "global x: Field = 1;\n"; + assert_format(src, expected); + } } diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/doc_comments.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/doc_comments.rs index f591f09e7299..6d25d7688d0b 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/doc_comments.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/doc_comments.rs @@ -2,7 +2,7 @@ use noirc_frontend::token::{DocStyle, Token}; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_inner_doc_comments(&mut self) { loop { self.skip_comments_and_whitespace(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/enums.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/enums.rs index 2d1182a941ce..beabf11fa46b 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/enums.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/enums.rs @@ -6,7 +6,7 @@ use noirc_frontend::{ use super::Formatter; use crate::chunks::ChunkGroup; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_enum(&mut self, noir_enum: NoirEnumeration) { self.format_secondary_attributes(noir_enum.attributes); self.write_indentation(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/expression.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/expression.rs index 1a6610364f28..6b46a4557a2b 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/expression.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/expression.rs @@ -6,7 +6,7 @@ use noirc_frontend::{ MemberAccessExpression, MethodCallExpression, PrefixExpression, TypePath, UnaryOp, UnresolvedTypeData, }, - token::{Keyword, Token}, + token::{Keyword, Token, TokenKind}, }; use crate::chunks::{Chunk, ChunkFormatter, ChunkGroup, GroupKind, GroupTag, TextChunk}; @@ -19,9 +19,21 @@ struct FormattedLambda { first_line_width: usize, } -impl<'a, 'b> ChunkFormatter<'a, 'b> { +impl ChunkFormatter<'_, '_> { pub(super) fn format_expression(&mut self, expression: Expression, group: &mut ChunkGroup) { - group.leading_comment(self.skip_comments_and_whitespace_chunk()); + group.leading_comment(self.chunk(|formatter| { + // Doc comments for an expression could come before a potential non-doc comment + if formatter.token.kind() == TokenKind::OuterDocComment { + formatter.format_outer_doc_comments_checking_safety(); + } + + formatter.skip_comments_and_whitespace(); + + // Or doc comments could come after a potential non-doc comment + if formatter.token.kind() == TokenKind::OuterDocComment { + formatter.format_outer_doc_comments_checking_safety(); + } + })); match expression.kind { ExpressionKind::Literal(literal) => self.format_literal(literal, group), @@ -86,9 +98,9 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { false, // force multiple lines )); } - ExpressionKind::Unsafe(block_expression, _span) => { + ExpressionKind::Unsafe(unsafe_xpression) => { group.group(self.format_unsafe_expression( - block_expression, + unsafe_xpression.block, false, // force multiple lines )); } @@ -349,7 +361,7 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { formatter.write_space(); formatter.write(&delimiter_start.to_string()); for token in tokens.0 { - formatter.write_source_span(token.to_span()); + formatter.write_source_span(token.span()); } formatter.write(&delimiter_end.to_string()); })); @@ -377,7 +389,6 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { ) -> ChunkGroup { let mut group = ChunkGroup::new(); group.text(self.chunk(|formatter| { - formatter.format_outer_doc_comments_checking_safety(); formatter.write_keyword(Keyword::Unsafe); formatter.write_space(); })); @@ -1156,7 +1167,9 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { ConstrainKind::Assert => Keyword::Assert, ConstrainKind::AssertEq => Keyword::AssertEq, ConstrainKind::Constrain => { - unreachable!("constrain always produces an error, and the formatter doesn't run when there are errors") + unreachable!( + "constrain always produces an error, and the formatter doesn't run when there are errors" + ) } }; @@ -1309,7 +1322,7 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { } } -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_empty_block_contents(&mut self) { if let Some(chunks) = self.chunk_formatter().empty_block_contents_chunk() { self.format_chunk_group(chunks); @@ -1331,7 +1344,7 @@ fn force_if_chunks_to_multiple_lines(group: &mut ChunkGroup, group_tag: GroupTag #[cfg(test)] mod tests { - use crate::{assert_format, assert_format_with_config, assert_format_with_max_width, Config}; + use crate::{Config, assert_format, assert_format_with_config, assert_format_with_max_width}; #[test] fn format_unit() { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/function.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/function.rs index ca905f3dcf88..f37683286d36 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/function.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/function.rs @@ -22,7 +22,7 @@ pub(super) struct FunctionToFormat { pub(super) skip_visibility: bool, } -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_function(&mut self, func: NoirFunction, skip_visibility: bool) { self.format_function_impl(FunctionToFormat { attributes: func.def.attributes, @@ -542,7 +542,6 @@ fn bar() { // noir-fmt:ignore fn baz() { let z = 3 ; } - "; assert_format(src, expected); } diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/generics.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/generics.rs index 4ee5a7439421..c457f4976d5e 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/generics.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/generics.rs @@ -5,7 +5,7 @@ use noirc_frontend::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_generics(&mut self, generics: Vec) { self.skip_comments_and_whitespace(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/global.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/global.rs index c351e15e3b66..4d6a43d0674f 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/global.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/global.rs @@ -6,7 +6,7 @@ use noirc_frontend::{ use super::Formatter; use crate::chunks::{ChunkFormatter, ChunkGroup}; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_global( &mut self, let_statement: LetStatement, @@ -20,7 +20,7 @@ impl<'a> Formatter<'a> { } } -impl<'a, 'b> ChunkFormatter<'a, 'b> { +impl ChunkFormatter<'_, '_> { pub(super) fn format_global( &mut self, let_statement: LetStatement, diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/impls.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/impls.rs index 71548dd5efac..b58b9381d172 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/impls.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/impls.rs @@ -2,7 +2,7 @@ use noirc_frontend::{ast::TypeImpl, token::Keyword}; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_impl(&mut self, type_impl: TypeImpl) { let has_where_clause = !type_impl.where_clause.is_empty(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/item.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/item.rs index 499acb8415c8..fa07478cee12 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/item.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/item.rs @@ -8,7 +8,7 @@ use crate::config::ImportsGranularity; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_items(&mut self, mut items: Vec, mut ignore_next: bool) { // Reverse the items because we'll be processing them one by one, and it's a bit // more efficient to pop than to shift. @@ -50,7 +50,7 @@ impl<'a> Formatter<'a> { ignore_next |= self.ignore_next; if ignore_next { - self.write_and_skip_span_without_formatting(item.span); + self.write_and_skip_span_without_formatting(item.location.span); return; } @@ -98,7 +98,7 @@ impl<'a> Formatter<'a> { let mut imports = Vec::new(); let item = items.last()?; - if self.span_has_comments(item.span) { + if self.span_has_comments(item.location.span) { return None; } @@ -112,16 +112,17 @@ impl<'a> Formatter<'a> { }; imports.push(use_tree); - let mut span_end = item.span.end(); + let mut span_end = item.location.span.end(); while let Some(item) = items.last() { - if self.span_is_import_group_separator(Span::from(span_end..item.span.start())) { + if self.span_is_import_group_separator(Span::from(span_end..item.location.span.start())) + { break; } let next_item_start = if items.len() > 1 { if let Some(next_item) = items.get(items.len() - 2) { - next_item.span.start() + next_item.location.span.start() } else { self.source.len() as u32 } @@ -129,8 +130,9 @@ impl<'a> Formatter<'a> { self.source.len() as u32 }; - if self.span_starts_with_trailing_comment(Span::from(item.span.end()..next_item_start)) - { + if self.span_starts_with_trailing_comment(Span::from( + item.location.span.end()..next_item_start, + )) { break; } @@ -147,7 +149,7 @@ impl<'a> Formatter<'a> { panic!("Expected import, got {:?}", item.kind); }; imports.push(use_tree); - span_end = item.span.end(); + span_end = item.location.span.end(); } Some(ImportGroup { imports, visibility, span_end }) diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/lvalue.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/lvalue.rs index 15fb7f5fa265..8dd1c76ab938 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/lvalue.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/lvalue.rs @@ -3,7 +3,7 @@ use noirc_frontend::{ast::LValue, token::Token}; use super::Formatter; use crate::chunks::ChunkGroup; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_lvalue(&mut self, lvalue: LValue) { // Parenthesized l-values exist but are not represented in the AST while let Token::LeftParen = self.token { @@ -12,12 +12,12 @@ impl<'a> Formatter<'a> { match lvalue { LValue::Ident(ident) => self.write_identifier(ident), - LValue::MemberAccess { object, field_name, span: _ } => { + LValue::MemberAccess { object, field_name, location: _ } => { self.format_lvalue(*object); self.write_token(Token::Dot); self.write_identifier_or_integer(field_name); } - LValue::Index { array, index, span: _ } => { + LValue::Index { array, index, location: _ } => { self.format_lvalue(*array); self.write_left_bracket(); let mut group = ChunkGroup::new(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/module.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/module.rs index e07d22c75863..b2cb250a90a6 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/module.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/module.rs @@ -1,10 +1,10 @@ use noirc_frontend::{ - ast::ModuleDeclaration, parser::ParsedSubModule, token::Keyword, ParsedModule, + ParsedModule, ast::ModuleDeclaration, parser::ParsedSubModule, token::Keyword, }; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_module_declaration(&mut self, module_declaration: ModuleDeclaration) { self.format_secondary_attributes(module_declaration.outer_attributes); self.write_indentation(); @@ -47,7 +47,7 @@ fn parsed_module_is_empty(parsed_module: &ParsedModule) -> bool { #[cfg(test)] mod tests { - use crate::{assert_format, assert_format_with_config, Config}; + use crate::{Config, assert_format, assert_format_with_config}; #[test] fn format_module_declaration() { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/path.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/path.rs index 2a46467bf72f..f79bb72eff16 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/path.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/path.rs @@ -5,7 +5,7 @@ use noirc_frontend::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_path(&mut self, path: Path) { self.skip_comments_and_whitespace(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/pattern.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/pattern.rs index 9a76612109bd..f0f94dfa7678 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/pattern.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/pattern.rs @@ -6,7 +6,7 @@ use noirc_frontend::{ use super::Formatter; use crate::chunks::ChunkGroup; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_pattern(&mut self, pattern: Pattern) { self.skip_comments_and_whitespace(); @@ -93,11 +93,7 @@ impl<'a> Formatter<'a> { } fn is_identifier_pattern(pattern: &Pattern, ident: &Ident) -> bool { - if let Pattern::Identifier(pattern_ident) = pattern { - pattern_ident == ident - } else { - false - } + if let Pattern::Identifier(pattern_ident) = pattern { pattern_ident == ident } else { false } } #[cfg(test)] diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/statement.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/statement.rs index f31cb80b9fdc..d70778ae5d11 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/statement.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/statement.rs @@ -8,7 +8,7 @@ use noirc_frontend::{ use crate::chunks::{ChunkFormatter, ChunkGroup, GroupKind}; -impl<'a, 'b> ChunkFormatter<'a, 'b> { +impl ChunkFormatter<'_, '_> { pub(super) fn format_statement( &mut self, statement: Statement, @@ -39,7 +39,7 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { if ignore_next { group.text(self.chunk(|formatter| { - formatter.write_and_skip_span_without_formatting(statement.span); + formatter.write_and_skip_span_without_formatting(statement.location.span); })); return; } @@ -52,9 +52,10 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { ExpressionKind::Block(block) => group.group(self.format_block_expression( block, true, // force multiple lines )), - ExpressionKind::Unsafe(block, _) => { + ExpressionKind::Unsafe(unsafe_expression) => { group.group(self.format_unsafe_expression( - block, true, // force multiple lines + unsafe_expression.block, + true, // force multiple lines )); } ExpressionKind::If(if_expression) => { @@ -408,7 +409,7 @@ mod tests { #[test] fn format_let_statement_with_unsafe_comment() { - let src = " fn foo() { + let src = " fn foo() { // Safety: some comment let x = unsafe { 1 } ; } "; let expected = "fn foo() { @@ -421,7 +422,7 @@ mod tests { #[test] fn format_let_statement_with_unsafe_doc_comment() { - let src = " fn foo() { + let src = " fn foo() { /// Safety: some comment let x = unsafe { 1 } ; } "; let expected = "fn foo() { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/structs.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/structs.rs index c26ab552f30c..9af10ff505c3 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/structs.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/structs.rs @@ -6,7 +6,7 @@ use noirc_frontend::{ use super::Formatter; use crate::chunks::ChunkGroup; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_struct(&mut self, noir_struct: NoirStruct) { self.format_secondary_attributes(noir_struct.attributes); self.write_indentation(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/trait_impl.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/trait_impl.rs index 896620c3bf80..9252082b26dd 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/trait_impl.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/trait_impl.rs @@ -5,7 +5,7 @@ use noirc_frontend::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_trait_impl(&mut self, trait_impl: NoirTraitImpl) { // skip synthetic trait impl's, e.g. generated from trait aliases if trait_impl.is_synthetic { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/traits.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/traits.rs index 175dcad61701..8195a30c2962 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/traits.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/traits.rs @@ -1,11 +1,12 @@ +use noirc_errors::Location; use noirc_frontend::{ ast::{NoirTrait, Param, Pattern, TraitItem, Visibility}, token::{Attributes, Keyword, Token}, }; -use super::{function::FunctionToFormat, Formatter}; +use super::{Formatter, function::FunctionToFormat}; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_trait(&mut self, noir_trait: NoirTrait) { self.format_secondary_attributes(noir_trait.attributes); self.write_indentation(); @@ -99,7 +100,7 @@ impl<'a> Formatter<'a> { visibility: Visibility::Private, pattern: Pattern::Identifier(name), typ, - span: Default::default(), // Doesn't matter + location: Location::dummy(), // Doesn't matter }) .collect(); @@ -299,6 +300,23 @@ mod tests { assert_format(src, expected); } + #[test] + fn format_trait_with_function_with_multiple_where_clauses() { + let src = " mod moo { trait Foo { + fn foo () where A: B, C: D; + } }"; + let expected = "mod moo { + trait Foo { + fn foo() + where + A: B, + C: D; + } +} +"; + assert_format(src, expected); + } + #[test] fn format_trait_with_function_with_visibility() { let src = " mod moo { trait Foo { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/type_expression.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/type_expression.rs index 95b0c0451566..8bebfd42f0cf 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/type_expression.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/type_expression.rs @@ -2,7 +2,7 @@ use noirc_frontend::{ast::UnresolvedTypeExpression, token::Token}; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_type_expression(&mut self, type_expr: UnresolvedTypeExpression) { self.skip_comments_and_whitespace(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/types.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/types.rs index e52704ddaa74..6a0e66bc1f98 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/types.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/types.rs @@ -5,7 +5,7 @@ use noirc_frontend::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_type(&mut self, typ: UnresolvedType) { self.skip_comments_and_whitespace(); @@ -178,7 +178,7 @@ mod tests { fn assert_format_type(src: &str, expected: &str) { let module_src = format!("type X = {};", src); - let (parsed_module, errors) = parser::parse_program(&module_src); + let (parsed_module, errors) = parser::parse_program_with_dummy_file(&module_src); if !errors.is_empty() { panic!("Expected no errors, got: {:?}", errors); } @@ -187,7 +187,7 @@ mod tests { let type_result = &type_result[..type_result.len() - 2]; similar_asserts::assert_eq!(type_result, expected); - let (parsed_module, errors) = parser::parse_program(&result); + let (parsed_module, errors) = parser::parse_program_with_dummy_file(&result); if !errors.is_empty() { panic!("Expected no errors in idempotent check, got: {:?}", errors); } diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree.rs index 98d63ef6611b..471dd00ca301 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree.rs @@ -7,7 +7,7 @@ use crate::chunks::{Chunk, ChunkFormatter, ChunkGroup}; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_import(&mut self, use_tree: UseTree, visibility: ItemVisibility) { let group = self.chunk_formatter().format_import(use_tree, visibility); @@ -16,7 +16,7 @@ impl<'a> Formatter<'a> { } } -impl<'a, 'b> ChunkFormatter<'a, 'b> { +impl ChunkFormatter<'_, '_> { pub(super) fn format_import( &mut self, use_tree: UseTree, @@ -121,7 +121,7 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { #[cfg(test)] mod tests { - use crate::{assert_format_with_config, config::ImportsGranularity, Config}; + use crate::{Config, assert_format_with_config, config::ImportsGranularity}; fn assert_format(src: &str, expected: &str) { let config = Config { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree_merge.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree_merge.rs index a679e026435b..f64b0cb128ee 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree_merge.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree_merge.rs @@ -9,7 +9,7 @@ use crate::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn merge_and_format_imports( &mut self, imports: Vec, @@ -180,11 +180,7 @@ impl Ord for Segment { if let (Segment::Plain(self_string), Segment::Plain(other_string)) = (self, other) { // Case-insensitive comparison for plain segments let ordering = self_string.to_lowercase().cmp(&other_string.to_lowercase()); - if ordering == Ordering::Equal { - self_string.cmp(other_string) - } else { - ordering - } + if ordering == Ordering::Equal { self_string.cmp(other_string) } else { ordering } } else { order_number_ordering } @@ -299,7 +295,7 @@ fn merge_imports_in_tree(imports: Vec, mut tree: &mut ImportTree) { #[cfg(test)] mod tests { - use crate::{assert_format_with_config, config::ImportsGranularity, Config}; + use crate::{Config, assert_format_with_config, config::ImportsGranularity}; fn assert_format(src: &str, expected: &str) { let config = Config { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/visibility.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/visibility.rs index 27441b977bb2..2c2279ecb48f 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/visibility.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/visibility.rs @@ -4,7 +4,7 @@ use noirc_frontend::{ token::Keyword, }; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_item_visibility(&mut self, visibility: ItemVisibility) { self.skip_comments_and_whitespace(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/where_clause.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/where_clause.rs index 538d2ba8c011..c5ecb178bbb1 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/where_clause.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/where_clause.rs @@ -5,7 +5,7 @@ use noirc_frontend::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_where_clause( &mut self, constraints: Vec, @@ -23,7 +23,8 @@ impl<'a> Formatter<'a> { // To format it we'll have to skip the second type `F` if we find a `+` token. let mut write_type = true; - for constraint in constraints { + let constrains_len = constraints.len(); + for (index, constraint) in constraints.into_iter().enumerate() { if write_type { self.write_line(); self.write_indentation(); @@ -45,7 +46,9 @@ impl<'a> Formatter<'a> { write_type = true; - if self.is_at(Token::Comma) { + if index < constrains_len - 1 { + self.write_token(Token::Comma); + } else if self.is_at(Token::Comma) { if write_trailing_comma_and_new_line { self.write_token(Token::Comma); } else { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/lib.rs b/noir/noir-repo/tooling/nargo_fmt/src/lib.rs index 05bfb0e7d570..bf3cf48184df 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/lib.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/lib.rs @@ -65,7 +65,7 @@ pub(crate) fn assert_format_with_max_width(src: &str, expected: &str, max_width: pub(crate) fn assert_format_with_config(src: &str, expected: &str, config: Config) { use noirc_frontend::parser; - let (parsed_module, errors) = parser::parse_program(src); + let (parsed_module, errors) = parser::parse_program_with_dummy_file(src); let errors: Vec<_> = errors.into_iter().filter(|error| !error.is_warning()).collect(); if !errors.is_empty() { panic!("Expected no errors, got: {:?}", errors); @@ -78,7 +78,7 @@ pub(crate) fn assert_format_with_config(src: &str, expected: &str, config: Confi similar_asserts::assert_eq!(result, expected); let src = &result; - let (parsed_module, errors) = parser::parse_program(src); + let (parsed_module, errors) = parser::parse_program_with_dummy_file(src); let errors: Vec<_> = errors.into_iter().filter(|error| !error.is_warning()).collect(); if !errors.is_empty() { panic!("Expected no errors in idempotent check, got: {:?}", errors); diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/databus.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/databus.nr index 0e9761ed52dc..15ba3fc12729 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/databus.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/databus.nr @@ -1,2 +1 @@ fn main(x: pub u8, y: call_data(0) u8) -> return_data u32 {} - diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr index afdb8883e158..571ec33f5d36 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr @@ -95,4 +95,3 @@ fn four() {} #[test(should_fail_with = "oops")] fn five() {} - diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/ignore.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/ignore.nr index 6e957539405b..1c84e178f34b 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/ignore.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/ignore.nr @@ -24,4 +24,3 @@ fn mk_array() { ]; let array = [1]; } - diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait.nr index 0467585fac39..8b64273a1a21 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait.nr @@ -1,2 +1 @@ trait Foo: Bar + Baz {} - diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait_alias.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait_alias.nr index 926f31602790..5b2eb0ce5523 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait_alias.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait_alias.nr @@ -83,4 +83,3 @@ fn main() { assert(0.foo_3().bar_3() == baz_3(0)); } - diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr index 265cba9604f6..ca32b3954e0f 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr @@ -12,4 +12,3 @@ fn main(x: pub u8, y: u8) { assert_eq(x, y); } } - diff --git a/noir/noir-repo/tooling/nargo_toml/src/errors.rs b/noir/noir-repo/tooling/nargo_toml/src/errors.rs index 7e1003d04f76..5aeb6a135f1b 100644 --- a/noir/noir-repo/tooling/nargo_toml/src/errors.rs +++ b/noir/noir-repo/tooling/nargo_toml/src/errors.rs @@ -28,7 +28,9 @@ pub enum ManifestError { #[error("Nargo.toml is badly formed, could not parse.\n\n {0}")] MalformedFile(#[from] toml::de::Error), - #[error("Unexpected workspace definition found in {0}. If you're attempting to load this as a dependency, you may need to add a `directory` field to your `Nargo.toml` to show which package within the workspace to use")] + #[error( + "Unexpected workspace definition found in {0}. If you're attempting to load this as a dependency, you may need to add a `directory` field to your `Nargo.toml` to show which package within the workspace to use" + )] UnexpectedWorkspace(PathBuf), #[error("Cannot find file {entry} which was specified as the `entry` field in {toml}")] @@ -80,16 +82,24 @@ pub enum ManifestError { #[allow(clippy::enum_variant_names)] #[derive(Error, Debug, PartialEq, Eq, Clone)] pub enum SemverError { - #[error("Invalid value for `compiler_version` in package {package_name}. Requirements may only refer to full releases")] + #[error( + "Invalid value for `compiler_version` in package {package_name}. Requirements may only refer to full releases" + )] InvalidCompilerVersionRequirement { package_name: CrateName, required_compiler_version: String }, - #[error("Incompatible compiler version in package {package_name}. Required compiler version is {required_compiler_version} but the compiler version is {compiler_version_found}.\n Update the compiler_version field in Nargo.toml to >={required_compiler_version} or compile this project with version {required_compiler_version}")] + #[error( + "Incompatible compiler version in package {package_name}. Required compiler version is {required_compiler_version} but the compiler version is {compiler_version_found}.\n Update the compiler_version field in Nargo.toml to >={required_compiler_version} or compile this project with version {required_compiler_version}" + )] IncompatibleVersion { package_name: CrateName, required_compiler_version: String, compiler_version_found: String, }, - #[error("Could not parse the required compiler version for package {package_name} in Nargo.toml. Error: {error}")] + #[error( + "Could not parse the required compiler version for package {package_name} in Nargo.toml. Error: {error}" + )] CouldNotParseRequiredVersion { package_name: String, error: String }, - #[error("Could not parse the package version for package {package_name} in Nargo.toml. Error: {error}")] + #[error( + "Could not parse the package version for package {package_name} in Nargo.toml. Error: {error}" + )] CouldNotParsePackageVersion { package_name: String, error: String }, } diff --git a/noir/noir-repo/tooling/nargo_toml/src/flock.rs b/noir/noir-repo/tooling/nargo_toml/src/flock.rs index 031dbcff647e..1433827c3fa9 100644 --- a/noir/noir-repo/tooling/nargo_toml/src/flock.rs +++ b/noir/noir-repo/tooling/nargo_toml/src/flock.rs @@ -1,4 +1,3 @@ -use fs2::FileExt; use std::{ fs::{File, OpenOptions}, path::Path, @@ -14,11 +13,11 @@ impl FileLock { pub(crate) fn new(file_path: &Path, lock_name: &str) -> std::io::Result { std::fs::create_dir_all(file_path.parent().expect("can't create lock on filesystem root"))?; let file = OpenOptions::new().create(true).truncate(false).write(true).open(file_path)?; - if file.try_lock_exclusive().is_err() { + if fs2::FileExt::try_lock_exclusive(&file).is_err() { eprintln!("Waiting for lock on {lock_name}..."); } - file.lock_exclusive()?; + fs2::FileExt::lock_exclusive(&file)?; Ok(Self { file }) } @@ -26,7 +25,7 @@ impl FileLock { impl Drop for FileLock { fn drop(&mut self) { - if let Err(e) = self.file.unlock() { + if let Err(e) = fs2::FileExt::unlock(&self.file) { tracing::warn!("failed to release lock: {e:?}"); } } diff --git a/noir/noir-repo/tooling/nargo_toml/src/lib.rs b/noir/noir-repo/tooling/nargo_toml/src/lib.rs index edf26411cf5a..b62884f28c4c 100644 --- a/noir/noir-repo/tooling/nargo_toml/src/lib.rs +++ b/noir/noir-repo/tooling/nargo_toml/src/lib.rs @@ -9,7 +9,7 @@ use std::{ }; use errors::SemverError; -use fm::{NormalizePath, FILE_EXTENSION}; +use fm::{FILE_EXTENSION, NormalizePath}; use nargo::{ package::{Dependency, Package, PackageType}, workspace::Workspace, @@ -52,11 +52,7 @@ pub fn find_file_manifest(current_path: &Path) -> Option { /// /// Returns a [ManifestError] if no parent directories of `current_path` contain a manifest file. pub fn find_root(current_path: &Path, workspace: bool) -> Result { - if workspace { - find_package_root(current_path) - } else { - find_file_root(current_path) - } + if workspace { find_package_root(current_path) } else { find_file_root(current_path) } } /// Returns the [PathBuf] of the directory containing the `Nargo.toml` by searching from `current_path` to the root of its [Path], @@ -190,7 +186,7 @@ impl PackageConfig { return Err(ManifestError::InvalidPackageType( root_dir.join("Nargo.toml"), invalid.to_string(), - )) + )); } None => return Err(ManifestError::MissingPackageType(root_dir.join("Nargo.toml"))), }; @@ -389,7 +385,7 @@ fn toml_to_workspace( let member = package_config.resolve_to_package(&nargo_toml.root_dir, &mut resolved)?; match &package_selection { PackageSelection::Selected(selected_name) if selected_name != &member.name => { - return Err(ManifestError::MissingSelectedPackage(member.name)) + return Err(ManifestError::MissingSelectedPackage(member.name)); } _ => Workspace { root_dir: nargo_toml.root_dir, @@ -540,7 +536,7 @@ mod tests { use test_case::test_matrix; - use crate::{find_root, Config, ManifestError}; + use crate::{Config, ManifestError, find_root}; #[test] fn parse_standard_toml() { @@ -649,7 +645,8 @@ mod tests { assert!( indent <= current_indent + 1, - "cannot increase indent by more than {INDENT_SIZE}; item = {item}, current_dir={}", current_dir.display() + "cannot increase indent by more than {INDENT_SIZE}; item = {item}, current_dir={}", + current_dir.display() ); // Go into the last created directory diff --git a/noir/noir-repo/tooling/nargo_toml/src/semver.rs b/noir/noir-repo/tooling/nargo_toml/src/semver.rs index ececa1b30dd0..83f31c71a5e2 100644 --- a/noir/noir-repo/tooling/nargo_toml/src/semver.rs +++ b/noir/noir-repo/tooling/nargo_toml/src/semver.rs @@ -1,4 +1,4 @@ -use crate::{errors::SemverError, ManifestError}; +use crate::{ManifestError, errors::SemverError}; use nargo::{ package::{Dependency, Package}, workspace::Workspace, @@ -37,7 +37,7 @@ fn semver_check_package(package: &Package, compiler_version: &Version) -> Result return Err(SemverError::CouldNotParseRequiredVersion { package_name: package.name.clone().into(), error: err.to_string(), - }) + }); } }; @@ -109,12 +109,16 @@ mod tests { expression_width: None, }; if let Err(err) = semver_check_package(&package, &compiler_version) { - panic!("semver check should have passed. compiler version is 0.1.0 and required version from the package is 0.1.0\n error: {err:?}") + panic!( + "semver check should have passed. compiler version is 0.1.0 and required version from the package is 0.1.0\n error: {err:?}" + ) }; package.compiler_required_version = Some("0.2.0".to_string()); let got_err = match semver_check_package(&package, &compiler_version) { - Ok(_) => panic!("semver check should have failed. compiler version is 0.1.0 and required version from the package is 0.2.0"), + Ok(_) => panic!( + "semver check should have failed. compiler version is 0.1.0 and required version from the package is 0.2.0" + ), Err(err) => err, }; @@ -168,15 +172,19 @@ mod tests { ); if let Err(err) = semver_check_package(&package, &compiler_version) { - panic!("semver check should have passed. compiler version is 0.1.0 and required version from the package is 0.1.0\n error: {err:?}") + panic!( + "semver check should have passed. compiler version is 0.1.0 and required version from the package is 0.1.0\n error: {err:?}" + ) }; package.dependencies.insert( CrateName::from_str("test_dep_invalid").unwrap(), Dependency::Local { package: invalid_dependency.clone() }, ); - let got_err = match semver_check_package(&package,&compiler_version) { - Ok(_) => panic!("semver check should have failed. compiler version is 0.1.0 and required version from the package is 0.2.0"), + let got_err = match semver_check_package(&package, &compiler_version) { + Ok(_) => panic!( + "semver check should have failed. compiler version is 0.1.0 and required version from the package is 0.2.0" + ), Err(err) => err, }; @@ -204,7 +212,9 @@ mod tests { }; if let Err(err) = semver_check_package(&package, &compiler_version) { - panic!("semver check should have passed. compiler version is 0.2.0 and required version from the package is >=0.1.0\n error: {err:?}") + panic!( + "semver check should have passed. compiler version is 0.2.0 and required version from the package is >=0.1.0\n error: {err:?}" + ) }; } @@ -244,7 +254,9 @@ mod tests { }; if let Err(err) = semver_check_package(&package, &compiler_version) { - panic!("semver check should have passed. compiler version is 0.1.0+build_data and required version from the package is 0.1.0\n The build data should be ignored\n error: {err:?}") + panic!( + "semver check should have passed. compiler version is 0.1.0+build_data and required version from the package is 0.1.0\n The build data should be ignored\n error: {err:?}" + ) }; } } diff --git a/noir/noir-repo/tooling/noir_codegen/package.json b/noir/noir-repo/tooling/noir_codegen/package.json index 1297658b14eb..5f818f694d18 100644 --- a/noir/noir-repo/tooling/noir_codegen/package.json +++ b/noir/noir-repo/tooling/noir_codegen/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "1.0.0-beta.2", + "version": "1.0.0-beta.3", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/noir/noir-repo/tooling/noir_js/package.json b/noir/noir-repo/tooling/noir_js/package.json index fee79c680de2..5269867708f0 100644 --- a/noir/noir-repo/tooling/noir_js/package.json +++ b/noir/noir-repo/tooling/noir_js/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "1.0.0-beta.2", + "version": "1.0.0-beta.3", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/noir/noir-repo/tooling/noir_js_types/package.json b/noir/noir-repo/tooling/noir_js_types/package.json index 4cf637db0db8..31a1e6fdb79a 100644 --- a/noir/noir-repo/tooling/noir_js_types/package.json +++ b/noir/noir-repo/tooling/noir_js_types/package.json @@ -4,7 +4,7 @@ "The Noir Team " ], "packageManager": "yarn@3.5.1", - "version": "1.0.0-beta.2", + "version": "1.0.0-beta.3", "license": "(MIT OR Apache-2.0)", "homepage": "https://noir-lang.org/", "repository": { diff --git a/noir/noir-repo/tooling/noirc_abi/proptest-regressions/input_parser/json.txt b/noir/noir-repo/tooling/noirc_abi/proptest-regressions/input_parser/json.txt index 19de8eeaf489..66aeed8f1202 100644 --- a/noir/noir-repo/tooling/noirc_abi/proptest-regressions/input_parser/json.txt +++ b/noir/noir-repo/tooling/noirc_abi/proptest-regressions/input_parser/json.txt @@ -5,3 +5,4 @@ # It is recommended to check this file in to source control so that # everyone who runs the test benefits from these saved cases. cc b3f9ae88d54944ca274764f4d99a2023d4b0ac09beb89bc599cbba1e45dd3620 # shrinks to (typ, value) = (Integer { sign: Signed, width: 1 }, -1) +cc afc4c7b26dcb04c8bb68af61dd8422745802d5a722b2200a9cb69a8425154b0a # shrinks to (abi, input_map) = (Abi { parameters: [AbiParameter { name: "A", typ: Struct { path: "\u{8}\u{6d174}\0:.Ѩ**¥\u{feff}`\u{4}o\u{55a3b}2=\u{6}:<", fields: [("\u{10bba4}Y&\u{83c23}?${\u{dc4b5}Z𬰫'º\u{1b}\u{37592}\u{feff}Ѩ.\u{4b753}c\u{736c6}", Integer { sign: Signed, width: 128 })] }, visibility: Public }], return_type: Some(AbiReturnType { abi_type: Tuple { fields: [Array { length: 1, typ: Field }, Tuple { fields: [Boolean] }, Array { length: 5, typ: Struct { path: "|\u{99332}?\u{7}\u{5131a}{q", fields: [("*🕴\u{d41d7}?\r*&O\\\u{c01a6}5{\t\u{9a8f1}\"\u{464e9}hM\\", Integer { sign: Signed, width: 83 }), ("ä:$\u{d4b96}\u{911ce}\u{1cc1c}EL{.¥🕴\u{36eb8}\u{feff}\u{a0}\u{40267}.🕴🕴{\u{97de2}\u{df645}\u{b}B}\u{ba0d1}(iter: impl Iterator) { proptest::prop_compose! { pub(super) fn arb_field_from_integer(bit_size: u32)(value: u128)-> FieldElement { - let width = (bit_size % 128).clamp(1, 127); - let max_value = 2u128.pow(width) - 1; + let width = (bit_size % 129).clamp(1, 128); + let max_value = if bit_size == 128 { u128::MAX } else { (1u128 << width) - 1 }; FieldElement::from(value.clamp(0, max_value)) } } @@ -86,7 +86,7 @@ fn arb_primitive_abi_type() -> SBoxedStrategy { Just(AbiType::Field), Just(AbiType::Boolean), any::<(Sign, u32)>().prop_map(|(sign, width)| { - let width = (width % 128).clamp(1, 127); + let width = (width % 129).clamp(1, 128); AbiType::Integer { sign, width } }), // restrict length of strings to avoid running out of memory diff --git a/noir/noir-repo/tooling/noirc_abi/src/errors.rs b/noir/noir-repo/tooling/noirc_abi/src/errors.rs index c46945d8ff22..f08f7b217219 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/errors.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/errors.rs @@ -1,8 +1,8 @@ use crate::{ - input_parser::{InputTypecheckingError, InputValue}, AbiType, + input_parser::{InputTypecheckingError, InputValue}, }; -use acvm::{acir::native_types::Witness, AcirField, FieldElement}; +use acvm::{AcirField, FieldElement, acir::native_types::Witness}; use thiserror::Error; #[derive(Debug, Error)] @@ -13,9 +13,13 @@ pub enum InputParserError { "The value passed for parameter `{arg_name}` is invalid:\nExpected witness values to be integers, but `{value}` failed with `{error}`" )] ParseStr { arg_name: String, value: String, error: String }, - #[error("The value passed for parameter `{arg_name}` is invalid:\nValue {value} is less than minimum allowed value of {min}")] + #[error( + "The value passed for parameter `{arg_name}` is invalid:\nValue {value} is less than minimum allowed value of {min}" + )] InputUnderflowsMinimum { arg_name: String, value: String, min: String }, - #[error("The value passed for parameter `{arg_name}` is invalid:\nValue {value} exceeds maximum allowed value of {max}")] + #[error( + "The value passed for parameter `{arg_name}` is invalid:\nValue {value} exceeds maximum allowed value of {max}" + )] InputOverflowsMaximum { arg_name: String, value: String, max: String }, #[error( "The value passed for parameter `{arg_name}` is invalid:\nValue {value} exceeds field modulus. Values must fall within [0, {})", @@ -58,9 +62,13 @@ pub enum AbiError { "Could not read witness value at index {witness_index:?} (required for parameter \"{name}\")" )] MissingParamWitnessValue { name: String, witness_index: Witness }, - #[error("Attempted to write to witness index {0:?} but it is already initialized to a different value")] + #[error( + "Attempted to write to witness index {0:?} but it is already initialized to a different value" + )] InconsistentWitnessAssignment(Witness), - #[error("The return value is expected to be a {return_type:?} but found incompatible value {value:?}")] + #[error( + "The return value is expected to be a {return_type:?} but found incompatible value {value:?}" + )] ReturnTypeMismatch { return_type: AbiType, value: InputValue }, #[error("No return value is expected but received {0:?}")] UnexpectedReturnValue(InputValue), diff --git a/noir/noir-repo/tooling/noirc_abi/src/input_parser/json.rs b/noir/noir-repo/tooling/noirc_abi/src/input_parser/json.rs index 7b2e8be454bf..f8b73ad05a5c 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/input_parser/json.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/input_parser/json.rs @@ -1,8 +1,8 @@ use super::{ - field_to_signed_hex, parse_integer_to_signed, parse_str_to_field, parse_str_to_signed, - InputValue, + InputValue, field_to_signed_hex, parse_integer_to_signed, parse_str_to_field, + parse_str_to_signed, }; -use crate::{errors::InputParserError, Abi, AbiType, MAIN_RETURN_NAME}; +use crate::{Abi, AbiType, MAIN_RETURN_NAME, errors::InputParserError}; use acvm::{AcirField, FieldElement}; use iter_extended::{try_btree_map, try_vecmap}; use serde::{Deserialize, Serialize}; @@ -225,9 +225,9 @@ mod test { use proptest::prelude::*; use crate::{ - arbitrary::arb_abi_and_input_map, - input_parser::{arbitrary::arb_signed_integer_type_and_value, json::JsonTypes, InputValue}, AbiType, + arbitrary::arb_abi_and_input_map, + input_parser::{InputValue, arbitrary::arb_signed_integer_type_and_value, json::JsonTypes}, }; use super::{parse_json, serialize_to_json}; diff --git a/noir/noir-repo/tooling/noirc_abi/src/input_parser/mod.rs b/noir/noir-repo/tooling/noirc_abi/src/input_parser/mod.rs index fce627c8d313..72aef1f48bb3 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/input_parser/mod.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/input_parser/mod.rs @@ -27,7 +27,9 @@ pub enum InputValue { pub enum InputTypecheckingError { #[error("Value {value:?} does not fall within range of allowable values for a {typ:?}")] OutsideOfValidRange { path: String, typ: AbiType, value: InputValue }, - #[error("Type {typ:?} is expected to have length {expected_length} but value {value:?} has length {actual_length}")] + #[error( + "Type {typ:?} is expected to have length {expected_length} but value {value:?} has length {actual_length}" + )] LengthMismatch { path: String, typ: AbiType, @@ -35,9 +37,13 @@ pub enum InputTypecheckingError { expected_length: usize, actual_length: usize, }, - #[error("Could not find value for required field `{expected_field}`. Found values for fields {found_fields:?}")] + #[error( + "Could not find value for required field `{expected_field}`. Found values for fields {found_fields:?}" + )] MissingField { path: String, expected_field: String, found_fields: Vec }, - #[error("Additional unexpected field was provided for type {typ:?}. Found field named `{extra_field}`")] + #[error( + "Additional unexpected field was provided for type {typ:?}. Found field named `{extra_field}`" + )] UnexpectedField { path: String, typ: AbiType, extra_field: String }, #[error("Type {typ:?} and value {value:?} do not match")] IncompatibleTypes { path: String, typ: AbiType, value: InputValue }, @@ -242,8 +248,8 @@ mod serialization_tests { use strum::IntoEnumIterator; use crate::{ - input_parser::InputValue, Abi, AbiParameter, AbiReturnType, AbiType, AbiVisibility, Sign, - MAIN_RETURN_NAME, + Abi, AbiParameter, AbiReturnType, AbiType, AbiVisibility, MAIN_RETURN_NAME, Sign, + input_parser::InputValue, }; use super::Format; @@ -357,8 +363,11 @@ fn parse_str_to_signed( error: err_msg.to_string(), }) .and_then(|bigint| { - let max = BigInt::from(2_u128.pow(width - 1) - 1); - let min = BigInt::from(-(2_i128.pow(width - 1))); + let min = if width == 128 { i128::MIN } else { -(1 << (width - 1)) }; + let max = if width == 128 { i128::MAX } else { (1 << (width - 1)) - 1 }; + + let max = BigInt::from(max); + let min = BigInt::from(min); if bigint < min { return Err(InputParserError::InputUnderflowsMinimum { @@ -398,8 +407,8 @@ fn parse_integer_to_signed( width: u32, arg_name: &str, ) -> Result { - let min = -(1 << (width - 1)); - let max = (1 << (width - 1)) - 1; + let min = if width == 128 { i128::MIN } else { -(1 << (width - 1)) }; + let max = if width == 128 { i128::MAX } else { (1 << (width - 1)) - 1 }; if integer < min { return Err(InputParserError::InputUnderflowsMinimum { @@ -417,8 +426,12 @@ fn parse_integer_to_signed( }); } - let integer = if integer < 0 { (1 << width) + integer } else { integer }; - Ok(FieldElement::from(integer as u128)) + let integer = if integer < 0 { + FieldElement::from(2u32).pow(&width.into()) + FieldElement::from(integer) + } else { + FieldElement::from(integer) + }; + Ok(integer) } fn field_from_big_uint(bigint: BigUint) -> FieldElement { @@ -439,9 +452,9 @@ fn field_from_big_int(bigint: BigInt) -> FieldElement { fn field_to_signed_hex(f: FieldElement, bit_size: u32) -> String { let f_u128 = f.to_u128(); - let max = 2_u128.pow(bit_size - 1) - 1; + let max = if bit_size == 128 { i128::MAX as u128 } else { (1 << (bit_size - 1)) - 1 }; if f_u128 > max { - let f = FieldElement::from(2_u128.pow(bit_size) - f_u128); + let f = FieldElement::from(2u32).pow(&bit_size.into()) - f; format!("-0x{}", f.to_hex()) } else { format!("0x{}", f.to_hex()) @@ -454,7 +467,7 @@ mod test { use num_bigint::BigUint; use strum::IntoEnumIterator; - use super::{parse_str_to_field, parse_str_to_signed, Format}; + use super::{Format, parse_str_to_field, parse_str_to_signed}; fn big_uint_from_field(field: FieldElement) -> BigUint { BigUint::from_bytes_be(&field.to_be_bytes()) diff --git a/noir/noir-repo/tooling/noirc_abi/src/input_parser/toml.rs b/noir/noir-repo/tooling/noirc_abi/src/input_parser/toml.rs index aaa3358f4fca..2e2d12f853c5 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/input_parser/toml.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/input_parser/toml.rs @@ -1,8 +1,8 @@ use super::{ - field_to_signed_hex, parse_integer_to_signed, parse_str_to_field, parse_str_to_signed, - InputValue, + InputValue, field_to_signed_hex, parse_integer_to_signed, parse_str_to_field, + parse_str_to_signed, }; -use crate::{errors::InputParserError, Abi, AbiType, MAIN_RETURN_NAME}; +use crate::{Abi, AbiType, MAIN_RETURN_NAME, errors::InputParserError}; use acvm::{AcirField, FieldElement}; use iter_extended::{try_btree_map, try_vecmap}; use serde::{Deserialize, Serialize}; @@ -210,9 +210,9 @@ mod test { use proptest::prelude::*; use crate::{ - arbitrary::arb_abi_and_input_map, - input_parser::{arbitrary::arb_signed_integer_type_and_value, toml::TomlTypes, InputValue}, AbiType, + arbitrary::arb_abi_and_input_map, + input_parser::{InputValue, arbitrary::arb_signed_integer_type_and_value, toml::TomlTypes}, }; use super::{parse_toml, serialize_to_toml}; diff --git a/noir/noir-repo/tooling/noirc_abi/src/lib.rs b/noir/noir-repo/tooling/noirc_abi/src/lib.rs index 5f5f3748bc46..17f9a07c2217 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/lib.rs @@ -4,11 +4,11 @@ #![warn(clippy::semicolon_if_nothing_returned)] use acvm::{ + AcirField, FieldElement, acir::{ circuit::ErrorSelector, native_types::{Witness, WitnessMap}, }, - AcirField, FieldElement, }; use errors::AbiError; use input_parser::InputValue; @@ -202,7 +202,7 @@ impl Abi { let has_public_return = self .return_type .as_ref() - .map_or(false, |typ| matches!(typ.visibility, AbiVisibility::Public)); + .is_some_and(|typ| matches!(typ.visibility, AbiVisibility::Public)); has_public_args || has_public_return } @@ -263,7 +263,7 @@ impl Abi { encoded_inputs.push(encoded_return_fields); } (None, Some(return_value)) => { - return Err(AbiError::UnexpectedReturnValue(return_value)) + return Err(AbiError::UnexpectedReturnValue(return_value)); } // We allow not passing a return value despite the circuit defining one // in order to generate the initial partial witness. diff --git a/noir/noir-repo/tooling/noirc_abi_wasm/Cargo.toml b/noir/noir-repo/tooling/noirc_abi_wasm/Cargo.toml index b00d580515ef..1d9dc02939fc 100644 --- a/noir/noir-repo/tooling/noirc_abi_wasm/Cargo.toml +++ b/noir/noir-repo/tooling/noirc_abi_wasm/Cargo.toml @@ -31,5 +31,5 @@ getrandom = { workspace = true, features = ["js"] } [build-dependencies] build-data.workspace = true -[dev-dependencies] +[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dev-dependencies] wasm-bindgen-test.workspace = true 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/tooling/noirc_abi_wasm/package.json b/noir/noir-repo/tooling/noirc_abi_wasm/package.json index 769efe1d92a1..0a0941be34a8 100644 --- a/noir/noir-repo/tooling/noirc_abi_wasm/package.json +++ b/noir/noir-repo/tooling/noirc_abi_wasm/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "1.0.0-beta.2", + "version": "1.0.0-beta.3", "license": "(MIT OR Apache-2.0)", "homepage": "https://noir-lang.org/", "repository": { diff --git a/noir/noir-repo/tooling/noirc_abi_wasm/src/js_witness_map.rs b/noir/noir-repo/tooling/noirc_abi_wasm/src/js_witness_map.rs index a82621822e46..8b751426ac20 100644 --- a/noir/noir-repo/tooling/noirc_abi_wasm/src/js_witness_map.rs +++ b/noir/noir-repo/tooling/noirc_abi_wasm/src/js_witness_map.rs @@ -1,11 +1,11 @@ //! This can most likely be imported from acvm_js to avoid redefining it here. use acvm::{ - acir::native_types::{Witness, WitnessMap}, AcirField, FieldElement, + acir::native_types::{Witness, WitnessMap}, }; use js_sys::{JsString, Map}; -use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; +use wasm_bindgen::prelude::{JsValue, wasm_bindgen}; // WitnessMap #[wasm_bindgen] @@ -65,21 +65,21 @@ pub(crate) fn field_element_to_js_string(field_element: &FieldElement) -> JsStri format!("0x{}", field_element.to_hex()).into() } -#[cfg(test)] +#[cfg(all(test, any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))] mod test { - use wasm_bindgen_test::wasm_bindgen_test as test; + use wasm_bindgen_test::*; use std::collections::BTreeMap; use acvm::{ - acir::native_types::{Witness, WitnessMap}, AcirField, FieldElement, + acir::native_types::{Witness, WitnessMap}, }; use wasm_bindgen::JsValue; use crate::JsWitnessMap; - #[test] + #[wasm_bindgen_test] fn test_witness_map_to_js() { let witness_map = BTreeMap::from([ (Witness(1), FieldElement::one()), diff --git a/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs b/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs index 79aecafa7426..390f4353bb2d 100644 --- a/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs @@ -6,24 +6,23 @@ use getrandom as _; use acvm::{ + FieldElement, acir::{ circuit::RawAssertionPayload, native_types::{WitnessMap, WitnessStack}, }, - FieldElement, }; use iter_extended::try_btree_map; use noirc_abi::{ - decode_value, display_abi_error, + Abi, AbiErrorType, MAIN_RETURN_NAME, decode_value, display_abi_error, errors::InputParserError, - input_parser::{json::JsonTypes, InputValue}, - Abi, AbiErrorType, MAIN_RETURN_NAME, + input_parser::{InputValue, json::JsonTypes}, }; use serde::Serialize; use std::collections::BTreeMap; use gloo_utils::format::JsValueSerdeExt; -use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; +use wasm_bindgen::{JsValue, prelude::wasm_bindgen}; mod errors; mod js_witness_map; diff --git a/noir/noir-repo/tooling/noirc_artifacts/src/contract.rs b/noir/noir-repo/tooling/noirc_artifacts/src/contract.rs index 0649a34e6255..9f8f7019ff13 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/src/contract.rs +++ b/noir/noir-repo/tooling/noirc_artifacts/src/contract.rs @@ -1,6 +1,6 @@ -use acvm::{acir::circuit::Program, FieldElement}; +use acvm::{FieldElement, acir::circuit::Program}; use noirc_abi::{Abi, AbiType, AbiValue}; -use noirc_driver::{CompiledContract, CompiledContractOutputs, ContractFunction}; +use noirc_driver::{CompiledContract, CompiledContractOutputs, CompiledProgram, ContractFunction}; use serde::{Deserialize, Serialize}; use noirc_driver::DebugFile; @@ -9,6 +9,8 @@ use std::collections::{BTreeMap, HashMap}; use fm::FileId; +use super::{deserialize_hash, serialize_hash}; + #[derive(Clone, Serialize, Deserialize, Debug)] pub struct ContractOutputsArtifact { pub structs: HashMap>, @@ -47,6 +49,14 @@ impl From for ContractArtifact { } } +impl ContractArtifact { + pub fn function_as_compiled_program(&self, function_name: &str) -> Option { + self.functions.iter().find(|f| f.name == function_name).map(|f| { + f.clone().into_compiled_program(self.noir_version.clone(), self.file_map.clone()) + }) + } +} + /// Each function in the contract will be compiled as a separate noir program. /// /// A contract function unlike a regular Noir program however can have additional properties. @@ -55,6 +65,12 @@ impl From for ContractArtifact { pub struct ContractFunctionArtifact { pub name: String, + /// Hash of the [`Program`][noirc_frontend::monomorphization::ast::Program] from which the [`ContractFunction`] + /// was compiled. + #[serde(default)] // For backwards compatibility (it was missing). + #[serde(serialize_with = "serialize_hash", deserialize_with = "deserialize_hash")] + pub hash: u64, + pub is_unconstrained: bool, pub custom_attributes: Vec, @@ -72,18 +88,41 @@ pub struct ContractFunctionArtifact { deserialize_with = "ProgramDebugInfo::deserialize_compressed_base64_json" )] pub debug_symbols: ProgramDebugInfo, - + #[serde(default)] // For backwards compatibility (it was missing). + pub names: Vec, pub brillig_names: Vec, } +impl ContractFunctionArtifact { + pub fn into_compiled_program( + self, + noir_version: String, + file_map: BTreeMap, + ) -> CompiledProgram { + CompiledProgram { + noir_version, + hash: self.hash, + program: self.bytecode, + abi: self.abi, + debug: self.debug_symbols.debug_infos, + file_map, + warnings: Vec::new(), + names: self.names, + brillig_names: self.brillig_names, + } + } +} + impl From for ContractFunctionArtifact { fn from(func: ContractFunction) -> Self { ContractFunctionArtifact { name: func.name, + hash: func.hash, is_unconstrained: func.is_unconstrained, custom_attributes: func.custom_attributes, abi: func.abi, bytecode: func.bytecode, + names: func.names, brillig_names: func.brillig_names, debug_symbols: ProgramDebugInfo { debug_infos: func.debug }, } diff --git a/noir/noir-repo/tooling/noirc_artifacts/src/debug.rs b/noir/noir-repo/tooling/noirc_artifacts/src/debug.rs index 5c47f1f2652c..4924a9f1ca19 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/src/debug.rs +++ b/noir/noir-repo/tooling/noirc_artifacts/src/debug.rs @@ -1,6 +1,6 @@ use codespan_reporting::files::{Error, Files, SimpleFile}; use noirc_driver::{CompiledContract, CompiledProgram, DebugFile}; -use noirc_errors::{debug_info::DebugInfo, Location}; +use noirc_errors::{Location, debug_info::DebugInfo}; use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeMap, BTreeSet}, @@ -204,12 +204,12 @@ mod tests { use crate::debug::DebugArtifact; use acvm::acir::circuit::OpcodeLocation; use fm::FileManager; - use noirc_errors::{debug_info::DebugInfo, Location, Span}; + use noirc_errors::{Location, Span, debug_info::DebugInfo}; use std::collections::BTreeMap; use std::ops::Range; use std::path::Path; use std::path::PathBuf; - use tempfile::{tempdir, TempDir}; + use tempfile::{TempDir, tempdir}; // Returns the absolute path to the file fn create_dummy_file(dir: &TempDir, file_name: &Path) -> PathBuf { diff --git a/noir/noir-repo/tooling/noirc_artifacts/src/lib.rs b/noir/noir-repo/tooling/noirc_artifacts/src/lib.rs index 77873ed94098..337d5d8550da 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_artifacts/src/lib.rs @@ -9,7 +9,48 @@ //! Should any projects require/desire a different artifact format, it's expected that they will write a transformer //! to generate them using these artifacts as a starting point. +use serde::{Deserializer, Serializer, de::Visitor}; + pub mod contract; pub mod debug; mod debug_vars; pub mod program; + +/// Serialize `hash` as `String`, so that it doesn't get truncated in Javascript. +fn serialize_hash(hash: &u64, s: S) -> Result +where + S: Serializer, +{ + s.serialize_str(&hash.to_string()) +} + +/// Deserialize `hash` from `String` in JSON. +fn deserialize_hash<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + use serde::de::Error; + + // Backwards compatible with `hash` serialized as a number. + struct StringOrU64; + + impl Visitor<'_> for StringOrU64 { + type Value = u64; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("String or u64") + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + value.parse().map_err(E::custom) + } + + fn visit_u64(self, value: u64) -> Result { + Ok(value) + } + } + deserializer.deserialize_any(StringOrU64) +} diff --git a/noir/noir-repo/tooling/noirc_artifacts/src/program.rs b/noir/noir-repo/tooling/noirc_artifacts/src/program.rs index ffffc6345d58..fc452fcb2afd 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/src/program.rs +++ b/noir/noir-repo/tooling/noirc_artifacts/src/program.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; -use acvm::acir::circuit::Program; use acvm::FieldElement; +use acvm::acir::circuit::Program; use fm::FileId; use noirc_abi::Abi; use noirc_driver::CompiledProgram; @@ -9,6 +9,8 @@ use noirc_driver::DebugFile; use noirc_errors::debug_info::ProgramDebugInfo; use serde::{Deserialize, Serialize}; +use super::{deserialize_hash, serialize_hash}; + #[derive(Clone, Serialize, Deserialize, Debug)] pub struct ProgramArtifact { pub noir_version: String, @@ -17,6 +19,7 @@ pub struct ProgramArtifact { /// was compiled. /// /// Used to short-circuit compilation in the case of the source code not changing since the last compilation. + #[serde(serialize_with = "serialize_hash", deserialize_with = "deserialize_hash")] pub hash: u64, pub abi: Abi, diff --git a/noir/noir-repo/tooling/noirc_artifacts_info/src/lib.rs b/noir/noir-repo/tooling/noirc_artifacts_info/src/lib.rs index 6f4c80accbdd..b1db98f21579 100644 --- a/noir/noir-repo/tooling/noirc_artifacts_info/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_artifacts_info/src/lib.rs @@ -1,7 +1,7 @@ use acvm::acir::circuit::ExpressionWidth; use iter_extended::vecmap; use noirc_artifacts::program::ProgramArtifact; -use prettytable::{row, table, Row}; +use prettytable::{Row, row, table}; use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; use serde::Serialize; diff --git a/noir/noir-repo/tooling/profiler/Cargo.toml b/noir/noir-repo/tooling/profiler/Cargo.toml index 798a21ea0d6c..b61271e03ef9 100644 --- a/noir/noir-repo/tooling/profiler/Cargo.toml +++ b/noir/noir-repo/tooling/profiler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "noir_profiler" -description = "Profiler for noir circuits" +description = "Profiler for Noir circuits" version.workspace = true authors.workspace = true edition.workspace = true @@ -32,8 +32,9 @@ im.workspace = true acir.workspace = true nargo.workspace = true noirc_errors.workspace = true -noirc_abi.workspace = true noirc_evaluator.workspace = true +noir_artifact_cli.workspace = true +thiserror.workspace = true # Logs tracing-subscriber.workspace = true @@ -43,4 +44,3 @@ tracing-appender = "0.2.3" noirc_abi.workspace = true noirc_driver.workspace = true tempfile.workspace = true - diff --git a/noir/noir-repo/tooling/profiler/src/cli/execution_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/execution_flamegraph_cmd.rs index dbb2a2c38bcd..c9d7eca50f59 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/execution_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/execution_flamegraph_cmd.rs @@ -1,18 +1,23 @@ use std::path::{Path, PathBuf}; +use acir::circuit::Opcode; use acir::circuit::OpcodeLocation; use clap::Args; use color_eyre::eyre::{self, Context}; -use nargo::foreign_calls::DefaultForeignCallBuilder; use nargo::PrintOutput; +use nargo::errors::try_to_diagnose_runtime_error; +use nargo::foreign_calls::DefaultForeignCallBuilder; +use noir_artifact_cli::fs::artifact::read_program_from_file; +use noir_artifact_cli::fs::inputs::read_inputs_from_file; +use noirc_artifacts::program::ProgramArtifact; +use crate::errors::{CliError, report_error}; use crate::flamegraph::{BrilligExecutionSample, FlamegraphGenerator, InfernoFlamegraphGenerator}; -use crate::fs::{read_inputs_from_file, read_program_from_file}; use crate::opcode_formatter::format_brillig_opcode; use bn254_blackbox_solver::Bn254BlackBoxSolver; -use noirc_abi::input_parser::Format; use noirc_artifacts::debug::DebugArtifact; +/// Generates a flamegraph mapping unconstrained Noir execution to source code. #[derive(Debug, Clone, Args)] pub(crate) struct ExecutionFlamegraphCommand { /// The path to the artifact JSON file @@ -54,17 +59,41 @@ fn run_with_generator( let program = read_program_from_file(artifact_path).context("Error reading program from file")?; - let (inputs_map, _) = read_inputs_from_file(prover_toml_path, Format::Toml, &program.abi)?; + ensure_brillig_entry_point(&program)?; + + let (inputs_map, _) = + read_inputs_from_file(&prover_toml_path.with_extension("toml"), &program.abi)?; let initial_witness = program.abi.encode(&inputs_map, None)?; - println!("Executing"); - let (_, mut profiling_samples) = nargo::ops::execute_program_with_profiling( + println!("Executing..."); + + let solved_witness_stack_err = nargo::ops::execute_program_with_profiling( &program.bytecode, initial_witness, &Bn254BlackBoxSolver(pedantic_solving), &mut DefaultForeignCallBuilder::default().with_output(PrintOutput::Stdout).build(), - )?; + ); + let mut profiling_samples = match solved_witness_stack_err { + Ok((_, profiling_samples)) => profiling_samples, + Err(err) => { + let debug_artifact = DebugArtifact { + debug_symbols: program.debug_symbols.debug_infos.clone(), + file_map: program.file_map.clone(), + }; + + if let Some(diagnostic) = try_to_diagnose_runtime_error( + &err, + &program.abi, + &program.debug_symbols.debug_infos, + ) { + diagnostic.report(&debug_artifact, false); + } + + return Err(CliError::Generic.into()); + } + }; + println!("Executed"); println!("Collecting {} samples", profiling_samples.len()); @@ -99,8 +128,27 @@ fn run_with_generator( &debug_artifact, artifact_path.to_str().unwrap(), "main", - &Path::new(&output_path).join(Path::new(&format!("{}.svg", "main"))), + &Path::new(&output_path).join(Path::new(&format!("{}_brillig_trace.svg", "main"))), )?; Ok(()) } + +fn ensure_brillig_entry_point(artifact: &ProgramArtifact) -> Result<(), CliError> { + let err_msg = "Command only supports fully unconstrained Noir programs e.g. `unconstrained fn main() { .. }".to_owned(); + let program = &artifact.bytecode; + if program.functions.len() != 1 || program.unconstrained_functions.len() != 1 { + return report_error(err_msg); + } + + let main_function = &program.functions[0]; + let Opcode::BrilligCall { id, .. } = main_function.opcodes[0] else { + return report_error(err_msg); + }; + + if id.as_usize() != 0 { + return report_error(err_msg); + } + + Ok(()) +} diff --git a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs index e68a8cd5bd27..736cddf7cf4a 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs @@ -4,33 +4,35 @@ use acir::circuit::OpcodeLocation; use clap::Args; use color_eyre::eyre::{self, Context}; +use noir_artifact_cli::fs::artifact::read_program_from_file; use noirc_artifacts::debug::DebugArtifact; use crate::flamegraph::{CompilationSample, FlamegraphGenerator, InfernoFlamegraphGenerator}; -use crate::fs::read_program_from_file; use crate::gates_provider::{BackendGatesProvider, GatesProvider}; use crate::opcode_formatter::format_acir_opcode; +/// Generates a flamegraph mapping backend opcodes to their associated locations in the source code. #[derive(Debug, Clone, Args)] pub(crate) struct GatesFlamegraphCommand { /// The path to the artifact JSON file #[clap(long, short)] - artifact_path: String, + artifact_path: PathBuf, - /// Path to the noir backend binary + /// Path to the Noir backend binary #[clap(long, short)] - backend_path: String, + backend_path: PathBuf, /// Command to get a gates report from the backend. Defaults to "gates" #[clap(long, short = 'g', default_value = "gates")] backend_gates_command: String, + /// Optional arguments for the backend gates command #[arg(trailing_var_arg = true, allow_hyphen_values = true)] backend_extra_args: Vec, /// The output folder for the flamegraph svg files #[clap(long, short)] - output: String, + output: PathBuf, /// The output name for the flamegraph svg files #[clap(long, short = 'f')] @@ -39,14 +41,14 @@ pub(crate) struct GatesFlamegraphCommand { pub(crate) fn run(args: GatesFlamegraphCommand) -> eyre::Result<()> { run_with_provider( - &PathBuf::from(args.artifact_path), + &args.artifact_path, &BackendGatesProvider { - backend_path: PathBuf::from(args.backend_path), + backend_path: args.backend_path, gates_command: args.backend_gates_command, extra_args: args.backend_extra_args, }, &InfernoFlamegraphGenerator { count_name: "gates".to_string() }, - &PathBuf::from(args.output), + &args.output, args.output_filename, ) } @@ -64,19 +66,24 @@ fn run_with_provider( let backend_gates_response = gates_provider.get_gates(artifact_path).context("Error querying backend for gates")?; - let function_names = program.names.clone(); + let function_names = std::mem::take(&mut program.names); let bytecode = std::mem::take(&mut program.bytecode); let debug_artifact: DebugArtifact = program.into(); - for (func_idx, ((func_gates, func_name), bytecode)) in backend_gates_response - .functions - .into_iter() - .zip(function_names) - .zip(bytecode.functions) - .enumerate() + let num_functions = bytecode.functions.len(); + for (func_idx, (func_gates, circuit)) in + backend_gates_response.functions.into_iter().zip(bytecode.functions).enumerate() { + // We can have repeated names if there are functions with the same name in different + // modules or functions that use generics. Thus, add the unique function index as a suffix. + let function_name = if num_functions > 1 { + format!("{}_{}", function_names[func_idx].as_str(), func_idx) + } else { + function_names[func_idx].to_owned() + }; + println!( "Opcode count: {}, Total gates by opcodes: {}, Circuit size: {}", func_gates.acir_opcodes, @@ -87,7 +94,7 @@ fn run_with_provider( let samples = func_gates .gates_per_opcode .into_iter() - .zip(bytecode.opcodes) + .zip(circuit.opcodes) .enumerate() .map(|(index, (gates, opcode))| CompilationSample { opcode: Some(format_acir_opcode(&opcode)), @@ -98,16 +105,16 @@ fn run_with_provider( .collect(); let output_filename = if let Some(output_filename) = &output_filename { - format!("{}::{}::gates.svg", output_filename, func_name) + format!("{}_{}_gates.svg", output_filename, function_name) } else { - format!("{}::gates.svg", func_name) + format!("{}_gates.svg", function_name) }; flamegraph_generator.generate_flamegraph( samples, &debug_artifact.debug_symbols[func_idx], &debug_artifact, artifact_path.to_str().unwrap(), - &func_name, + &function_name, &Path::new(&output_path).join(Path::new(&output_filename)), )?; } @@ -118,7 +125,7 @@ fn run_with_provider( #[cfg(test)] mod tests { use acir::circuit::{Circuit, Program}; - use color_eyre::eyre::{self}; + use color_eyre::eyre; use fm::codespan_files::Files; use noirc_artifacts::program::ProgramArtifact; use noirc_errors::debug_info::{DebugInfo, ProgramDebugInfo}; @@ -210,7 +217,7 @@ mod tests { .expect("should run without errors"); // Check that the output file was written to - let output_file = temp_dir.path().join("test_filename::main::gates.svg"); + let output_file = temp_dir.path().join("test_filename_main_gates.svg"); assert!(output_file.exists()); } } diff --git a/noir/noir-repo/tooling/profiler/src/cli/mod.rs b/noir/noir-repo/tooling/profiler/src/cli/mod.rs index 80c6bceb3ce2..b91dd6990aa0 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/mod.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/mod.rs @@ -32,5 +32,7 @@ pub(crate) fn start_cli() -> eyre::Result<()> { ProfilerCommand::Gates(args) => gates_flamegraph_cmd::run(args), ProfilerCommand::Opcodes(args) => opcodes_flamegraph_cmd::run(args), ProfilerCommand::ExecutionOpcodes(args) => execution_flamegraph_cmd::run(args), - } + }?; + + Ok(()) } diff --git a/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs index 4e271c9f8385..8ce9ba1de398 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs @@ -5,21 +5,22 @@ use acir::circuit::{Circuit, Opcode, OpcodeLocation}; use clap::Args; use color_eyre::eyre::{self, Context}; +use noir_artifact_cli::fs::artifact::read_program_from_file; use noirc_artifacts::debug::DebugArtifact; use crate::flamegraph::{CompilationSample, FlamegraphGenerator, InfernoFlamegraphGenerator}; -use crate::fs::read_program_from_file; use crate::opcode_formatter::{format_acir_opcode, format_brillig_opcode}; +/// Generates a flamegraph mapping ACIR opcodes to their associated locations in the source code. #[derive(Debug, Clone, Args)] pub(crate) struct OpcodesFlamegraphCommand { /// The path to the artifact JSON file #[clap(long, short)] - artifact_path: String, + artifact_path: PathBuf, /// The output folder for the flamegraph svg files #[clap(long, short)] - output: String, + output: PathBuf, /// Whether to skip brillig functions #[clap(long, short, action)] @@ -28,9 +29,9 @@ pub(crate) struct OpcodesFlamegraphCommand { pub(crate) fn run(args: OpcodesFlamegraphCommand) -> eyre::Result<()> { run_with_generator( - &PathBuf::from(args.artifact_path), + &args.artifact_path, &InfernoFlamegraphGenerator { count_name: "opcodes".to_string() }, - &PathBuf::from(args.output), + &args.output, args.skip_brillig, ) } @@ -44,18 +45,25 @@ fn run_with_generator( let mut program = read_program_from_file(artifact_path).context("Error reading program from file")?; - let function_names = program.names.clone(); + let acir_names = std::mem::take(&mut program.names); + let brillig_names = std::mem::take(&mut program.brillig_names); let bytecode = std::mem::take(&mut program.bytecode); let debug_artifact: DebugArtifact = program.into(); - for (func_idx, (func_name, bytecode)) in - function_names.into_iter().zip(bytecode.functions.iter()).enumerate() - { - println!("Opcode count for {}: {}", func_name, bytecode.opcodes.len()); + for (func_idx, circuit) in bytecode.functions.iter().enumerate() { + // We can have repeated names if there are functions with the same name in different + // modules or functions that use generics. Thus, add the unique function index as a suffix. + let function_name = if bytecode.functions.len() > 1 { + format!("{}_{}", acir_names[func_idx].as_str(), func_idx) + } else { + acir_names[func_idx].to_owned() + }; - let samples = bytecode + println!("Opcode count for {}: {}", function_name, circuit.opcodes.len()); + + let samples = circuit .opcodes .iter() .enumerate() @@ -72,51 +80,55 @@ fn run_with_generator( &debug_artifact.debug_symbols[func_idx], &debug_artifact, artifact_path.to_str().unwrap(), - &func_name, - &Path::new(&output_path).join(Path::new(&format!("{}_acir_opcodes.svg", &func_name))), + &function_name, + &Path::new(&output_path) + .join(Path::new(&format!("{}_acir_opcodes.svg", &function_name))), )?; } - if !skip_brillig { - for (brillig_fn_index, brillig_bytecode) in - bytecode.unconstrained_functions.into_iter().enumerate() - { - let acir_location = locate_brillig_call(brillig_fn_index, &bytecode.functions); - let Some((acir_fn_index, acir_opcode_index)) = acir_location else { - continue; - }; - - println!( - "Opcode count for brillig_{}: {}", - brillig_fn_index, - brillig_bytecode.bytecode.len() - ); - - let samples = brillig_bytecode - .bytecode - .into_iter() - .enumerate() - .map(|(brillig_index, opcode)| CompilationSample { - opcode: Some(format_brillig_opcode(&opcode)), - call_stack: vec![OpcodeLocation::Brillig { - acir_index: acir_opcode_index, - brillig_index, - }], - count: 1, - brillig_function_id: Some(BrilligFunctionId(brillig_fn_index as u32)), - }) - .collect(); - - flamegraph_generator.generate_flamegraph( - samples, - &debug_artifact.debug_symbols[acir_fn_index], - &debug_artifact, - artifact_path.to_str().unwrap(), - &format!("brillig_{}", brillig_fn_index), - &Path::new(&output_path) - .join(Path::new(&format!("{}_brillig_opcodes.svg", &brillig_fn_index))), - )?; - } + if skip_brillig { + return Ok(()); + } + + for (brillig_fn_index, brillig_bytecode) in + bytecode.unconstrained_functions.into_iter().enumerate() + { + let acir_location = locate_brillig_call(brillig_fn_index, &bytecode.functions); + let Some((acir_fn_index, acir_opcode_index)) = acir_location else { + continue; + }; + + // We can have repeated names if there are functions with the same name in different + // modules or functions that use generics. Thus, add the unique function index as a suffix. + let function_name = + format!("{}_{}", brillig_names[brillig_fn_index].as_str(), brillig_fn_index); + + println!("Opcode count for {}_brillig: {}", function_name, brillig_bytecode.bytecode.len()); + + let samples = brillig_bytecode + .bytecode + .into_iter() + .enumerate() + .map(|(brillig_index, opcode)| CompilationSample { + opcode: Some(format_brillig_opcode(&opcode)), + call_stack: vec![OpcodeLocation::Brillig { + acir_index: acir_opcode_index, + brillig_index, + }], + count: 1, + brillig_function_id: Some(BrilligFunctionId(brillig_fn_index as u32)), + }) + .collect(); + + flamegraph_generator.generate_flamegraph( + samples, + &debug_artifact.debug_symbols[acir_fn_index], + &debug_artifact, + artifact_path.to_str().unwrap(), + &function_name, + &Path::new(&output_path) + .join(Path::new(&format!("{}_brillig_opcodes.svg", function_name))), + )?; } Ok(()) @@ -130,7 +142,7 @@ fn locate_brillig_call( for (acir_opcode_index, acir_opcode) in acir_fn.opcodes.iter().enumerate() { match acir_opcode { Opcode::BrilligCall { id, .. } if id.as_usize() == brillig_fn_index => { - return Some((acir_fn_index, acir_opcode_index)) + return Some((acir_fn_index, acir_opcode_index)); } _ => {} } @@ -142,13 +154,13 @@ fn locate_brillig_call( #[cfg(test)] mod tests { use acir::{ + FieldElement, circuit::{ - brillig::{BrilligBytecode, BrilligFunctionId}, Circuit, Opcode, Program, + brillig::{BrilligBytecode, BrilligFunctionId}, }, - FieldElement, }; - use color_eyre::eyre::{self}; + use color_eyre::eyre; use fm::codespan_files::Files; use noirc_artifacts::program::ProgramArtifact; use noirc_errors::debug_info::{DebugInfo, ProgramDebugInfo}; @@ -214,12 +226,26 @@ mod tests { let artifact_path = temp_dir.path().join("test.json"); - let acir: Vec> = vec![Opcode::BrilligCall { - id: BrilligFunctionId(0), - inputs: vec![], - outputs: vec![], - predicate: None, - }]; + let acir: Vec> = vec![ + Opcode::BrilligCall { + id: BrilligFunctionId(0), + inputs: vec![], + outputs: vec![], + predicate: None, + }, + Opcode::BrilligCall { + id: BrilligFunctionId(1), + inputs: vec![], + outputs: vec![], + predicate: None, + }, + Opcode::BrilligCall { + id: BrilligFunctionId(2), + inputs: vec![], + outputs: vec![], + predicate: None, + }, + ]; let artifact = ProgramArtifact { noir_version: "0.0.0".to_string(), @@ -227,12 +253,16 @@ mod tests { abi: noirc_abi::Abi::default(), bytecode: Program { functions: vec![Circuit { opcodes: acir, ..Circuit::default() }], - unconstrained_functions: vec![BrilligBytecode::default()], + unconstrained_functions: vec![ + BrilligBytecode::default(), + BrilligBytecode::default(), + BrilligBytecode::default(), + ], }, debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], - brillig_names: vec!["main".to_string()], + brillig_names: vec!["main".to_string(), "main".to_string(), "main_1".to_string()], }; // Write the artifact to a file @@ -244,11 +274,17 @@ mod tests { super::run_with_generator(&artifact_path, &flamegraph_generator, temp_dir.path(), false) .expect("should run without errors"); - // Check that the output files ware written to + // Check that the output files were written let output_file = temp_dir.path().join("main_acir_opcodes.svg"); assert!(output_file.exists()); - let output_file = temp_dir.path().join("0_brillig_opcodes.svg"); + let output_file = temp_dir.path().join("main_0_brillig_opcodes.svg"); + assert!(output_file.exists()); + + let output_file = temp_dir.path().join("main_1_brillig_opcodes.svg"); + assert!(output_file.exists()); + + let output_file = temp_dir.path().join("main_1_2_brillig_opcodes.svg"); assert!(output_file.exists()); } } diff --git a/noir/noir-repo/tooling/profiler/src/errors.rs b/noir/noir-repo/tooling/profiler/src/errors.rs new file mode 100644 index 000000000000..6a028931f5e4 --- /dev/null +++ b/noir/noir-repo/tooling/profiler/src/errors.rs @@ -0,0 +1,16 @@ +use fm::FileMap; +use noirc_errors::{CustomDiagnostic, Location}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub(crate) enum CliError { + #[error("Failed to run profiler command")] + Generic, +} + +/// Report an error from the CLI that is not reliant on a stack trace. +pub(crate) fn report_error(message: String) -> Result<(), CliError> { + let error = CustomDiagnostic::simple_error(message.clone(), String::new(), Location::dummy()); + noirc_errors::reporter::report(&FileMap::default(), &error, false); + Err(CliError::Generic) +} diff --git a/noir/noir-repo/tooling/profiler/src/flamegraph.rs b/noir/noir-repo/tooling/profiler/src/flamegraph.rs index 38966902314e..16857eb2454c 100644 --- a/noir/noir-repo/tooling/profiler/src/flamegraph.rs +++ b/noir/noir-repo/tooling/profiler/src/flamegraph.rs @@ -1,15 +1,15 @@ use std::path::Path; use std::{collections::BTreeMap, io::BufWriter}; -use acir::circuit::brillig::BrilligFunctionId; use acir::circuit::OpcodeLocation; -use color_eyre::eyre::{self}; +use acir::circuit::brillig::BrilligFunctionId; +use color_eyre::eyre; use fm::codespan_files::Files; use fxhash::FxHashMap as HashMap; -use inferno::flamegraph::{from_lines, Options, TextTruncateDirection}; +use inferno::flamegraph::{Options, TextTruncateDirection, from_lines}; +use noirc_errors::Location; use noirc_errors::debug_info::DebugInfo; use noirc_errors::reporter::line_and_column_from_span; -use noirc_errors::Location; use noirc_evaluator::brillig::ProcedureId; pub(crate) trait Sample { @@ -112,7 +112,7 @@ impl FlamegraphGenerator for InfernoFlamegraphGenerator { let mut options = Options::default(); options.hash = true; options.deterministic = true; - options.title = format!("{}-{}", artifact_name, function_name); + options.title = format!("Artifact: {}, Function: {}", artifact_name, function_name); options.frame_height = 24; options.color_diffusion = true; options.min_width = 0.0; @@ -293,12 +293,12 @@ fn to_folded_sorted_lines( #[cfg(test)] mod tests { use acir::{ - circuit::{opcodes::BlockId, Opcode as AcirOpcode, OpcodeLocation}, - native_types::Expression, FieldElement, + circuit::{Opcode as AcirOpcode, OpcodeLocation, opcodes::BlockId}, + native_types::Expression, }; use fm::FileManager; - use noirc_errors::{debug_info::DebugInfo, Location, Span}; + use noirc_errors::{Location, Span, debug_info::DebugInfo}; use std::{collections::BTreeMap, path::Path}; use crate::{flamegraph::CompilationSample, opcode_formatter::format_acir_opcode}; diff --git a/noir/noir-repo/tooling/profiler/src/fs.rs b/noir/noir-repo/tooling/profiler/src/fs.rs deleted file mode 100644 index 43848504a7ff..000000000000 --- a/noir/noir-repo/tooling/profiler/src/fs.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::{collections::BTreeMap, path::Path}; - -use color_eyre::eyre; -use noirc_abi::{ - input_parser::{Format, InputValue}, - Abi, InputMap, MAIN_RETURN_NAME, -}; -use noirc_artifacts::program::ProgramArtifact; - -pub(crate) fn read_program_from_file>( - circuit_path: P, -) -> eyre::Result { - let file_path = circuit_path.as_ref().with_extension("json"); - - let input_string = std::fs::read(file_path)?; - let program = serde_json::from_slice(&input_string)?; - - Ok(program) -} - -/// Returns the circuit's parameters and its return value, if one exists. -/// # Examples -/// -/// ```ignore -/// let (input_map, return_value): (InputMap, Option) = -/// read_inputs_from_file(path, "Verifier", Format::Toml, &abi)?; -/// ``` -pub(crate) fn read_inputs_from_file( - file_path: &Path, - format: Format, - abi: &Abi, -) -> eyre::Result<(InputMap, Option)> { - if abi.is_empty() { - return Ok((BTreeMap::new(), None)); - } - - let input_string = std::fs::read_to_string(file_path)?; - let mut input_map = format.parse(&input_string, abi)?; - let return_value = input_map.remove(MAIN_RETURN_NAME); - - Ok((input_map, return_value)) -} diff --git a/noir/noir-repo/tooling/profiler/src/gates_provider.rs b/noir/noir-repo/tooling/profiler/src/gates_provider.rs index 3f07f3e4be6f..044e2a3642cd 100644 --- a/noir/noir-repo/tooling/profiler/src/gates_provider.rs +++ b/noir/noir-repo/tooling/profiler/src/gates_provider.rs @@ -1,7 +1,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; -use color_eyre::eyre::{self}; +use color_eyre::eyre; use serde::{Deserialize, Serialize}; pub(crate) trait GatesProvider { diff --git a/noir/noir-repo/tooling/profiler/src/main.rs b/noir/noir-repo/tooling/profiler/src/main.rs index b0b42e6ee41a..e4a5bc153d2e 100644 --- a/noir/noir-repo/tooling/profiler/src/main.rs +++ b/noir/noir-repo/tooling/profiler/src/main.rs @@ -4,15 +4,15 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies, unused_extern_crates))] mod cli; +mod errors; mod flamegraph; -mod fs; mod gates_provider; mod opcode_formatter; use std::env; use tracing_appender::rolling; -use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter}; +use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan}; fn main() { // Setup tracing @@ -33,7 +33,7 @@ fn main() { } if let Err(report) = cli::start_cli() { - eprintln!("{report:?}"); + eprintln!("{report}"); std::process::exit(1); } } diff --git a/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs b/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs index d1081de6c8f2..2276bcb4403f 100644 --- a/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs +++ b/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs @@ -1,6 +1,6 @@ -use acir::brillig::{BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpcode}; -use acir::circuit::{opcodes::BlackBoxFuncCall, Opcode as AcirOpcode}; use acir::AcirField; +use acir::brillig::{BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpcode}; +use acir::circuit::{Opcode as AcirOpcode, opcodes::BlackBoxFuncCall}; fn format_blackbox_function(call: &BlackBoxFuncCall) -> String { match call { diff --git a/noir/noir-repo/yarn.lock b/noir/noir-repo/yarn.lock index 56088fedd94b..568e7e487d1e 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,9 +221,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@portal:../../../../barretenberg/ts::locator=integration-tests%40workspace%3Acompiler%2Fintegration-tests": - version: 0.0.0-use.local - resolution: "@aztec/bb.js@portal:../../../../barretenberg/ts::locator=integration-tests%40workspace%3Acompiler%2Fintegration-tests" +"@aztec/bb.js@npm:0.72.1": + version: 0.72.1 + resolution: "@aztec/bb.js@npm:0.72.1" dependencies: comlink: ^4.4.1 commander: ^12.1.0 @@ -233,8 +233,9 @@ __metadata: tslib: ^2.4.0 bin: bb.js: dest/node/main.js + checksum: 143f0062a31e262ceff6e454d31e2a2672afd8dcbff0dafb17d5308f8c5312048e5d3503d80e7ad9ff4b17b6c3d36e8ffbff8d2deda2cdb684f19fa64d5a04ab 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 @@ -16608,7 +16609,7 @@ __metadata: version: 0.0.0-use.local resolution: "integration-tests@workspace:compiler/integration-tests" dependencies: - "@aztec/bb.js": "portal:../../../../barretenberg/ts" + "@aztec/bb.js": 0.72.1 "@noir-lang/noir_js": "workspace:*" "@noir-lang/noir_wasm": "workspace:*" "@nomicfoundation/hardhat-chai-matchers": ^2.0.0 From 9f874fb66d9e57feff3456a3fadf2d0e94a01316 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Wed, 5 Mar 2025 08:09:41 +0000 Subject: [PATCH 08/13] chore: apply sync fixes --- noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../noir-repo/compiler/integration-tests/package.json | 2 +- noir/noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- noir/noir-repo/yarn.lock | 11 +++++------ 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/noir/noir-repo/acvm-repo/acvm_js/build.sh b/noir/noir-repo/acvm-repo/acvm_js/build.sh index 16fb26e55db1..c07d2d8a4c1d 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/compiler/integration-tests/package.json b/noir/noir-repo/compiler/integration-tests/package.json index 053e9efeed20..e33179f31e7f 100644 --- a/noir/noir-repo/compiler/integration-tests/package.json +++ b/noir/noir-repo/compiler/integration-tests/package.json @@ -13,7 +13,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "0.72.1", + "@aztec/bb.js": "portal:../../../../barretenberg/ts", "@noir-lang/noir_js": "workspace:*", "@noir-lang/noir_wasm": "workspace:*", "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", diff --git a/noir/noir-repo/tooling/noirc_abi_wasm/build.sh b/noir/noir-repo/tooling/noirc_abi_wasm/build.sh index 16fb26e55db1..c07d2d8a4c1d 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 568e7e487d1e..56088fedd94b 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,9 +221,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.72.1": - version: 0.72.1 - resolution: "@aztec/bb.js@npm:0.72.1" +"@aztec/bb.js@portal:../../../../barretenberg/ts::locator=integration-tests%40workspace%3Acompiler%2Fintegration-tests": + version: 0.0.0-use.local + resolution: "@aztec/bb.js@portal:../../../../barretenberg/ts::locator=integration-tests%40workspace%3Acompiler%2Fintegration-tests" dependencies: comlink: ^4.4.1 commander: ^12.1.0 @@ -233,9 +233,8 @@ __metadata: tslib: ^2.4.0 bin: bb.js: dest/node/main.js - checksum: 143f0062a31e262ceff6e454d31e2a2672afd8dcbff0dafb17d5308f8c5312048e5d3503d80e7ad9ff4b17b6c3d36e8ffbff8d2deda2cdb684f19fa64d5a04ab languageName: node - linkType: hard + linkType: soft "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.11, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5, @babel/code-frame@npm:^7.8.3": version: 7.23.5 @@ -16609,7 +16608,7 @@ __metadata: version: 0.0.0-use.local resolution: "integration-tests@workspace:compiler/integration-tests" dependencies: - "@aztec/bb.js": 0.72.1 + "@aztec/bb.js": "portal:../../../../barretenberg/ts" "@noir-lang/noir_js": "workspace:*" "@noir-lang/noir_wasm": "workspace:*" "@nomicfoundation/hardhat-chai-matchers": ^2.0.0 From 1ea350c6a1ad7c7ffb6e6e2ea3216691c2ee6e37 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Thu, 6 Mar 2025 08:08:36 +0000 Subject: [PATCH 09/13] [1 changes] chore: track more critical libraries (https://github.com/noir-lang/noir/pull/7604) chore!: bump bb version to v0.77.0 (https://github.com/noir-lang/noir/pull/7599) chore: bump external pinned commits (https://github.com/noir-lang/noir/pull/7601) feat(cli): Log and replay oracle transcript (https://github.com/noir-lang/noir/pull/7417) chore: some SSA improvements (https://github.com/noir-lang/noir/pull/7588) chore(profiler): Add option to only get the total sample count for the `execution-opcodes` command (https://github.com/noir-lang/noir/pull/7578) feat(experimental): Issue errors for unreachable match branches (https://github.com/noir-lang/noir/pull/7556) fix: Log to `stderr` (https://github.com/noir-lang/noir/pull/7585) chore!: remove merkle module from stdlib (https://github.com/noir-lang/noir/pull/7582) fix: Display causes but not stack trace in CLI error report (https://github.com/noir-lang/noir/pull/7584) chore: bump `light-poseidon` (https://github.com/noir-lang/noir/pull/7568) chore: bump external pinned commits (https://github.com/noir-lang/noir/pull/7581) chore!: remove deprecated hash functions from stdlib (https://github.com/noir-lang/noir/pull/7477) fix(frontend)!: Restrict capturing mutable variable in lambdas (https://github.com/noir-lang/noir/pull/7488) feat: perform constant sha256 compressions at compile-time (https://github.com/noir-lang/noir/pull/7566) chore: bump external pinned commits (https://github.com/noir-lang/noir/pull/7565) chore(ssa): Turn the Brillig constraints check back on by default (https://github.com/noir-lang/noir/pull/7404) chore: bump external pinned commits (https://github.com/noir-lang/noir/pull/7561) chore: address some frontend tests TODOs (https://github.com/noir-lang/noir/pull/7554) fix: shift right overflow in ACIR with unknown var now returns zero (https://github.com/noir-lang/noir/pull/7509) chore(cli): Forward `nargo execute` to `noir_artifact_cli` (https://github.com/noir-lang/noir/pull/7406) feat: Support `::method` in expressions (https://github.com/noir-lang/noir/pull/7551) chore: remove FileDiagnostic (https://github.com/noir-lang/noir/pull/7546) chore: add some extra tests (https://github.com/noir-lang/noir/pull/7544) fix: fix a few cases where safety comment wasn't correctly identified (https://github.com/noir-lang/noir/pull/7548) chore!: remove U128 struct from stdlib (https://github.com/noir-lang/noir/pull/7529) feat: simplify simple conditionals for brillig (https://github.com/noir-lang/noir/pull/7205) chore: put RcTracker as part of the DIE context (https://github.com/noir-lang/noir/pull/7309) --- .noir-sync-commit | 2 +- .../actions/download-noir-execute/action.yml | 18 + noir/noir-repo/.github/benchmark_projects.yml | 30 +- .../noir-lang/keccak256/.failures.jsonl | 0 .../noir-lang/sha512/.failures.jsonl | 0 .../.github/scripts/integration-test-node.sh | 2 +- .../.github/scripts/wasm-bindgen-install.sh | 4 +- .../workflows/bump-aztec-packages-commit.yml | 50 + noir/noir-repo/.github/workflows/deny.yml | 4 +- noir/noir-repo/.github/workflows/docs-pr.yml | 10 +- .../.github/workflows/formatting.yml | 8 +- .../.github/workflows/publish-acvm.yml | 2 +- .../.github/workflows/publish-docs.yml | 4 +- .../.github/workflows/publish-es-packages.yml | 16 +- .../.github/workflows/publish-nargo.yml | 101 +- noir/noir-repo/.github/workflows/release.yml | 16 +- noir/noir-repo/.github/workflows/reports.yml | 7 +- .../.github/workflows/test-js-packages.yml | 56 +- .../workflows/test-rust-workspace-msrv.yml | 20 +- .../.github/workflows/test-rust-workspace.yml | 14 +- noir/noir-repo/.gitignore | 8 +- noir/noir-repo/.release-please-manifest.json | 2 +- noir/noir-repo/.vscode/settings.json | 6 + noir/noir-repo/CHANGELOG.md | 57 + noir/noir-repo/CRITICAL_NOIR_LIBRARIES | 3 + noir/noir-repo/Cargo.lock | 280 +-- noir/noir-repo/Cargo.toml | 26 +- noir/noir-repo/EXTERNAL_NOIR_LIBRARIES.yml | 14 +- noir/noir-repo/README.md | 2 +- noir/noir-repo/acvm-repo/acir/Cargo.toml | 2 +- .../acvm-repo/acir/benches/serialization.rs | 4 +- .../acvm-repo/acir/src/circuit/mod.rs | 8 +- noir/noir-repo/acvm-repo/acir/src/lib.rs | 4 +- .../src/native_types/expression/ordering.rs | 6 +- .../acir/src/native_types/witness_map.rs | 4 +- .../acir/src/native_types/witness_stack.rs | 22 +- .../acir/tests/test_program_serialization.rs | 2 +- .../noir-repo/acvm-repo/acir_field/Cargo.toml | 8 +- .../acir_field/benches/field_element.rs | 11 + .../acvm-repo/acir_field/src/field_element.rs | 82 +- noir/noir-repo/acvm-repo/acvm/Cargo.toml | 2 +- .../acvm-repo/acvm/src/compiler/mod.rs | 4 +- .../acvm/src/compiler/optimizers/general.rs | 2 +- .../compiler/optimizers/merge_expressions.rs | 10 +- .../acvm/src/compiler/optimizers/mod.rs | 4 +- .../compiler/optimizers/redundant_range.rs | 8 +- .../src/compiler/optimizers/unused_memory.rs | 2 +- .../acvm-repo/acvm/src/compiler/simulator.rs | 6 +- .../acvm/src/compiler/transformers/csat.rs | 4 +- .../acvm/src/compiler/transformers/mod.rs | 8 +- .../acvm-repo/acvm/src/pwg/arithmetic.rs | 51 +- .../acvm-repo/acvm/src/pwg/blackbox/aes128.rs | 4 +- .../acvm-repo/acvm/src/pwg/blackbox/bigint.rs | 2 +- .../src/pwg/blackbox/embedded_curve_ops.rs | 4 +- .../acvm-repo/acvm/src/pwg/blackbox/hash.rs | 25 +- .../acvm-repo/acvm/src/pwg/blackbox/logic.rs | 4 +- .../acvm-repo/acvm/src/pwg/blackbox/mod.rs | 14 +- .../acvm-repo/acvm/src/pwg/blackbox/range.rs | 37 +- .../acvm/src/pwg/blackbox/signature/ecdsa.rs | 4 +- .../acvm-repo/acvm/src/pwg/blackbox/utils.rs | 4 +- .../acvm-repo/acvm/src/pwg/brillig.rs | 12 +- .../acvm-repo/acvm/src/pwg/memory_op.rs | 6 +- noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs | 17 +- noir/noir-repo/acvm-repo/acvm/tests/solver.rs | 8 +- noir/noir-repo/acvm-repo/acvm_js/Cargo.toml | 4 +- noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- noir/noir-repo/acvm-repo/acvm_js/package.json | 2 +- .../acvm_js/src/black_box_solvers.rs | 2 +- .../acvm-repo/acvm_js/src/execute.rs | 10 +- .../acvm_js/src/foreign_call/inputs.rs | 2 +- .../acvm-repo/acvm_js/src/foreign_call/mod.rs | 4 +- .../acvm_js/src/foreign_call/outputs.rs | 2 +- .../acvm_js/src/js_execution_error.rs | 4 +- .../acvm-repo/acvm_js/src/js_witness_map.rs | 14 +- .../acvm-repo/acvm_js/src/js_witness_stack.rs | 4 +- .../acvm-repo/acvm_js/src/logging.rs | 2 +- .../acvm-repo/acvm_js/src/public_witness.rs | 20 +- .../acvm-repo/blackbox_solver/Cargo.toml | 2 +- .../acvm-repo/blackbox_solver/src/bigint.rs | 7 +- .../src/curve_specific_solver.rs | 4 +- .../blackbox_solver/src/ecdsa/secp256k1.rs | 8 +- .../blackbox_solver/src/ecdsa/secp256r1.rs | 8 +- .../bn254_blackbox_solver/Cargo.toml | 2 +- .../benches/criterion.rs | 2 +- .../src/generator/hash_to_curve.rs | 6 +- .../bn254_blackbox_solver/src/lib.rs | 4 +- .../bn254_blackbox_solver/src/poseidon2.rs | 4 +- noir/noir-repo/acvm-repo/brillig/Cargo.toml | 2 +- .../acvm-repo/brillig/src/black_box.rs | 2 +- .../noir-repo/acvm-repo/brillig_vm/Cargo.toml | 2 +- .../acvm-repo/brillig_vm/src/arithmetic.rs | 16 +- .../acvm-repo/brillig_vm/src/black_box.rs | 6 +- .../noir-repo/acvm-repo/brillig_vm/src/lib.rs | 144 +- .../acvm-repo/brillig_vm/src/memory.rs | 6 +- .../compiler/integration-tests/package.json | 2 +- .../scripts/codegen-verifiers.sh | 14 +- .../compiler/noirc_driver/src/abi_gen.rs | 22 +- .../compiler/noirc_driver/src/contract.rs | 4 +- .../compiler/noirc_driver/src/lib.rs | 97 +- .../compiler/noirc_driver/src/program.rs | 2 +- .../compiler/noirc_driver/tests/contracts.rs | 10 +- .../noirc_driver/tests/stdlib_warnings.rs | 4 +- .../compiler/noirc_errors/src/debug_info.rs | 8 +- .../compiler/noirc_errors/src/lib.rs | 20 +- .../compiler/noirc_errors/src/position.rs | 85 +- .../compiler/noirc_errors/src/reporter.rs | 86 +- .../compiler/noirc_evaluator/Cargo.toml | 1 + .../noirc_evaluator/src/acir/acir_variable.rs | 66 +- .../noirc_evaluator/src/acir/black_box.rs | 14 +- .../noirc_evaluator/src/acir/brillig_call.rs | 6 +- .../src/acir/brillig_directive.rs | 2 +- .../src/acir/generated_acir.rs | 16 +- .../compiler/noirc_evaluator/src/acir/mod.rs | 83 +- .../src/brillig/brillig_gen.rs | 6 +- .../brillig/brillig_gen/brillig_black_box.rs | 68 +- .../src/brillig/brillig_gen/brillig_block.rs | 120 +- .../brillig_gen/brillig_block_variables.rs | 6 +- .../src/brillig/brillig_gen/brillig_fn.rs | 2 +- .../brillig/brillig_gen/brillig_globals.rs | 14 +- .../brillig/brillig_gen/brillig_slice_ops.rs | 8 +- .../noirc_evaluator/src/brillig/brillig_ir.rs | 6 +- .../src/brillig/brillig_ir/artifact.rs | 22 +- .../brillig/brillig_ir/brillig_variable.rs | 4 +- .../src/brillig/brillig_ir/codegen_binary.rs | 6 +- .../src/brillig/brillig_ir/codegen_calls.rs | 4 +- .../brillig_ir/codegen_control_flow.rs | 4 +- .../brillig/brillig_ir/codegen_intrinsic.rs | 4 +- .../src/brillig/brillig_ir/codegen_memory.rs | 4 +- .../src/brillig/brillig_ir/codegen_stack.rs | 8 +- .../src/brillig/brillig_ir/debug_show.rs | 20 +- .../src/brillig/brillig_ir/entry_point.rs | 4 +- .../src/brillig/brillig_ir/instructions.rs | 28 +- .../brillig_ir/procedures/array_copy.rs | 4 +- .../brillig_ir/procedures/array_reverse.rs | 4 +- .../procedures/check_max_stack_depth.rs | 2 +- .../brillig/brillig_ir/procedures/mem_copy.rs | 4 +- .../src/brillig/brillig_ir/procedures/mod.rs | 4 +- .../procedures/prepare_vector_insert.rs | 6 +- .../procedures/prepare_vector_push.rs | 4 +- .../procedures/revert_with_string.rs | 2 +- .../brillig_ir/procedures/vector_copy.rs | 4 +- .../brillig_ir/procedures/vector_pop_back.rs | 4 +- .../brillig_ir/procedures/vector_pop_front.rs | 4 +- .../brillig_ir/procedures/vector_remove.rs | 4 +- .../src/brillig/brillig_ir/registers.rs | 2 +- .../compiler/noirc_evaluator/src/errors.rs | 48 +- .../compiler/noirc_evaluator/src/lib.rs | 12 +- .../compiler/noirc_evaluator/src/ssa.rs | 25 +- .../check_for_underconstrained_values.rs | 68 +- .../src/ssa/function_builder/mod.rs | 10 +- .../noirc_evaluator/src/ssa/ir/dfg.rs | 47 +- .../noirc_evaluator/src/ssa/ir/dom.rs | 9 +- .../src/ssa/ir/function_inserter.rs | 25 + .../noirc_evaluator/src/ssa/ir/instruction.rs | 53 +- .../src/ssa/ir/instruction/binary.rs | 54 +- .../src/ssa/ir/instruction/call.rs | 20 +- .../src/ssa/ir/instruction/call/blackbox.rs | 97 +- .../src/ssa/ir/instruction/cast.rs | 2 +- .../src/ssa/ir/instruction/constrain.rs | 24 +- .../noirc_evaluator/src/ssa/ir/printer.rs | 8 +- .../noirc_evaluator/src/ssa/ir/types.rs | 41 +- .../noirc_evaluator/src/ssa/opt/array_set.rs | 8 +- .../src/ssa/opt/basic_conditional.rs | 526 +++++ .../src/ssa/opt/brillig_array_gets.rs | 168 ++ .../src/ssa/opt/brillig_entry_points.rs | 6 +- .../src/ssa/opt/check_u128_mul_overflow.rs | 285 +++ .../src/ssa/opt/constant_folding.rs | 15 +- .../noirc_evaluator/src/ssa/opt/die.rs | 113 +- .../src/ssa/opt/flatten_cfg.rs | 81 +- .../ssa/opt/flatten_cfg/branch_analysis.rs | 4 +- .../src/ssa/opt/flatten_cfg/value_merger.rs | 2 +- .../noirc_evaluator/src/ssa/opt/hint.rs | 6 +- .../noirc_evaluator/src/ssa/opt/inlining.rs | 74 +- .../src/ssa/opt/inlining/inline_info.rs | 6 +- .../src/ssa/opt/loop_invariant.rs | 20 +- .../src/ssa/opt/make_constrain_not_equal.rs | 2 +- .../noirc_evaluator/src/ssa/opt/mem2reg.rs | 6 +- .../src/ssa/opt/mem2reg/block.rs | 6 +- .../noirc_evaluator/src/ssa/opt/mod.rs | 3 + .../src/ssa/opt/preprocess_fns.rs | 4 +- .../noirc_evaluator/src/ssa/opt/pure.rs | 2 +- .../src/ssa/opt/remove_bit_shifts.rs | 50 +- .../src/ssa/opt/remove_enable_side_effects.rs | 4 +- .../src/ssa/opt/remove_if_else.rs | 4 +- .../src/ssa/opt/simplify_cfg.rs | 6 +- .../noirc_evaluator/src/ssa/opt/unrolling.rs | 24 +- .../src/ssa/parser/into_ssa.rs | 4 +- .../noirc_evaluator/src/ssa/parser/lexer.rs | 2 +- .../noirc_evaluator/src/ssa/parser/mod.rs | 51 +- .../noirc_evaluator/src/ssa/parser/tests.rs | 2 +- .../noirc_evaluator/src/ssa/parser/token.rs | 2 +- .../src/ssa/ssa_gen/context.rs | 32 +- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 12 +- .../compiler/noirc_frontend/Cargo.toml | 1 - .../noirc_frontend/src/ast/enumeration.rs | 4 +- .../noirc_frontend/src/ast/expression.rs | 118 +- .../noirc_frontend/src/ast/function.rs | 11 +- .../compiler/noirc_frontend/src/ast/mod.rs | 63 +- .../noirc_frontend/src/ast/statement.rs | 285 +-- .../noirc_frontend/src/ast/structure.rs | 4 +- .../compiler/noirc_frontend/src/ast/traits.rs | 16 +- .../noirc_frontend/src/ast/type_alias.rs | 4 +- .../noirc_frontend/src/ast/visitor.rs | 192 +- .../compiler/noirc_frontend/src/debug/mod.rs | 243 +- .../noirc_frontend/src/elaborator/comptime.rs | 157 +- .../noirc_frontend/src/elaborator/enums.rs | 771 +++++-- .../src/elaborator/expressions.rs | 525 +++-- .../noirc_frontend/src/elaborator/lints.rs | 70 +- .../noirc_frontend/src/elaborator/mod.rs | 433 ++-- .../noirc_frontend/src/elaborator/options.rs | 62 + .../src/elaborator/path_resolution.rs | 18 +- .../noirc_frontend/src/elaborator/patterns.rs | 158 +- .../noirc_frontend/src/elaborator/scope.rs | 28 +- .../src/elaborator/statements.rs | 175 +- .../src/elaborator/trait_impls.rs | 39 +- .../noirc_frontend/src/elaborator/traits.rs | 52 +- .../noirc_frontend/src/elaborator/types.rs | 525 +++-- .../noirc_frontend/src/elaborator/unquote.rs | 13 +- .../compiler/noirc_frontend/src/graph/mod.rs | 4 +- .../src/hir/comptime/display.rs | 75 +- .../noirc_frontend/src/hir/comptime/errors.rs | 208 +- .../src/hir/comptime/hir_to_display_ast.rs | 161 +- .../src/hir/comptime/interpreter.rs | 151 +- .../src/hir/comptime/interpreter/builtin.rs | 225 +- .../interpreter/builtin/builtin_helpers.rs | 46 +- .../src/hir/comptime/interpreter/foreign.rs | 22 +- .../src/hir/comptime/interpreter/unquote.rs | 12 +- .../noirc_frontend/src/hir/comptime/mod.rs | 2 +- .../noirc_frontend/src/hir/comptime/tests.rs | 12 +- .../noirc_frontend/src/hir/comptime/value.rs | 224 +- .../src/hir/def_collector/dc_crate.rs | 130 +- .../src/hir/def_collector/dc_mod.rs | 184 +- .../src/hir/def_collector/errors.rs | 191 +- .../src/hir/def_map/item_scope.rs | 10 +- .../noirc_frontend/src/hir/def_map/mod.rs | 19 +- .../compiler/noirc_frontend/src/hir/mod.rs | 17 +- .../src/hir/resolution/errors.rs | 529 +++-- .../src/hir/resolution/import.rs | 81 +- .../src/hir/resolution/visibility.rs | 4 +- .../src/hir/type_check/errors.rs | 596 +++-- .../src/hir/type_check/generics.rs | 2 +- .../noirc_frontend/src/hir/type_check/mod.rs | 2 +- .../noirc_frontend/src/hir_def/expr.rs | 106 +- .../noirc_frontend/src/hir_def/function.rs | 2 +- .../noirc_frontend/src/hir_def/stmt.rs | 2 +- .../noirc_frontend/src/hir_def/traits.rs | 16 +- .../noirc_frontend/src/hir_def/types.rs | 193 +- .../src/hir_def/types/arithmetic.rs | 46 +- .../noirc_frontend/src/lexer/errors.rs | 136 +- .../noirc_frontend/src/lexer/lexer.rs | 220 +- .../noirc_frontend/src/lexer/token.rs | 117 +- .../compiler/noirc_frontend/src/lib.rs | 5 +- .../compiler/noirc_frontend/src/locations.rs | 10 +- .../src/monomorphization/ast.rs | 6 +- .../src/monomorphization/debug.rs | 39 +- .../src/monomorphization/errors.rs | 31 +- .../src/monomorphization/mod.rs | 89 +- .../src/monomorphization/printer.rs | 2 +- .../noirc_frontend/src/node_interner.rs | 139 +- .../noirc_frontend/src/parser/errors.rs | 82 +- .../compiler/noirc_frontend/src/parser/mod.rs | 8 +- .../noirc_frontend/src/parser/parser.rs | 163 +- .../src/parser/parser/arguments.rs | 4 +- .../src/parser/parser/attributes.rs | 144 +- .../src/parser/parser/doc_comments.rs | 27 +- .../noirc_frontend/src/parser/parser/enums.rs | 47 +- .../src/parser/parser/expression.rs | 287 +-- .../src/parser/parser/function.rs | 157 +- .../src/parser/parser/generics.rs | 22 +- .../src/parser/parser/global.rs | 44 +- .../noirc_frontend/src/parser/parser/impls.rs | 59 +- .../noirc_frontend/src/parser/parser/infix.rs | 18 +- .../noirc_frontend/src/parser/parser/item.rs | 30 +- .../src/parser/parser/item_visibility.rs | 16 +- .../src/parser/parser/lambda.rs | 4 +- .../src/parser/parser/modifiers.rs | 31 +- .../src/parser/parser/module.rs | 18 +- .../src/parser/parser/parse_many.rs | 10 +- .../noirc_frontend/src/parser/parser/path.rs | 65 +- .../src/parser/parser/pattern.rs | 60 +- .../src/parser/parser/statement.rs | 205 +- .../statement_or_expression_or_lvalue.rs | 8 +- .../src/parser/parser/structs.rs | 48 +- .../src/parser/parser/traits.rs | 58 +- .../src/parser/parser/type_alias.rs | 28 +- .../src/parser/parser/type_expression.rs | 111 +- .../noirc_frontend/src/parser/parser/types.rs | 109 +- .../src/parser/parser/use_tree.rs | 59 +- .../src/parser/parser/where_clause.rs | 20 +- .../noirc_frontend/src/resolve_locations.rs | 21 +- .../noirc_frontend/src/signed_field.rs | 207 ++ .../compiler/noirc_frontend/src/tests.rs | 2048 +++++++---------- .../src/tests/arithmetic_generics.rs | 24 +- .../noirc_frontend/src/tests/bound_checks.rs | 68 +- .../noirc_frontend/src/tests/enums.rs | 368 +++ .../noirc_frontend/src/tests/imports.rs | 66 +- .../src/tests/metaprogramming.rs | 78 +- .../noirc_frontend/src/tests/references.rs | 58 +- .../noirc_frontend/src/tests/traits.rs | 345 +-- .../noirc_frontend/src/tests/turbofish.rs | 155 +- .../noirc_frontend/src/tests/unused_items.rs | 109 +- .../noirc_frontend/src/tests/visibility.rs | 170 +- noir/noir-repo/compiler/wasm/package.json | 2 +- noir/noir-repo/compiler/wasm/src/compile.rs | 49 +- .../compiler/wasm/src/compile_new.rs | 14 +- noir/noir-repo/compiler/wasm/src/errors.rs | 17 +- noir/noir-repo/compiler/wasm/src/lib.rs | 6 +- .../compiler/wasm/src/types/noir_artifact.ts | 4 +- .../wasm/test/compiler/shared/compile.test.ts | 18 +- noir/noir-repo/cspell.json | 5 +- .../docs/how_to/how-to-solidity-verifier.mdx | 17 +- .../docs/noir/concepts/data_types/integers.md | 48 - .../docs/docs/noir/concepts/traits.md | 31 + .../modules_packages_crates/dependencies.md | 4 +- .../noir/standard_library/black_box_fns.md | 2 +- .../cryptographic_primitives/hashes.mdx | 33 +- .../docs/docs/noir/standard_library/traits.md | 2 +- noir/noir-repo/docs/docs/tooling/profiler.md | 135 ++ noir/noir-repo/docs/docs/tooling/security.md | 2 +- .../profiler/acir-flamegraph-optimized.png | Bin 0 -> 90387 bytes .../profiler/acir-flamegraph-unoptimized.png | Bin 0 -> 90830 bytes .../profiler/brillig-trace-initial-32.png | Bin 0 -> 98463 bytes .../tooling/profiler/brillig-trace-opt-32.png | Bin 0 -> 114318 bytes .../gates-flamegraph-optimized-2048.png | Bin 0 -> 85893 bytes .../profiler/gates-flamegraph-optimized.png | Bin 0 -> 34504 bytes .../gates-flamegraph-unoptimized-2048.png | Bin 0 -> 75614 bytes .../profiler/gates-flamegraph-unoptimized.png | Bin 0 -> 67875 bytes .../how_to/how-to-solidity-verifier.mdx | 15 +- .../how_to/using-devcontainers.mdx | 10 +- .../noir/concepts/traits.md | 56 +- .../explainers}/cspell.json | 2 +- .../explainers/explainer-oracle.md | 57 + .../explainers/explainer-recursion.md | 176 ++ .../explainers/explainer-writing-noir.md | 208 ++ .../getting_started/noir_installation.md | 106 + .../getting_started/project_breakdown.md | 159 ++ .../getting_started/quick_start.md | 127 + .../setting_up_shell_completions.md | 87 + .../how_to/_category_.json | 5 + .../how_to/debugger/_category_.json | 6 + .../debugger/debugging_with_the_repl.md | 164 ++ .../how_to/debugger/debugging_with_vs_code.md | 68 + .../how_to/how-to-oracles.md | 275 +++ .../how_to/how-to-recursion.md | 172 ++ .../how_to/how-to-solidity-verifier.mdx | 305 +++ .../how_to/merkle-proof.mdx | 0 .../how_to/using-devcontainers.mdx | 91 + .../version-v1.0.0-beta.3/index.mdx | 76 + .../version-v1.0.0-beta.3/migration_notes.md | 105 + .../noir/concepts/_category_.json | 6 + .../noir/concepts/assert.md | 79 + .../noir/concepts/comments.md | 33 + .../noir/concepts/comptime.md | 565 +++++ .../noir/concepts/control_flow.md | 111 + .../noir/concepts/data_bus.mdx | 23 + .../noir/concepts/data_types/_category_.json | 5 + .../noir/concepts/data_types/arrays.md | 276 +++ .../noir/concepts/data_types/booleans.md | 28 + .../noir/concepts/data_types/fields.md | 260 +++ .../concepts/data_types/function_types.md | 26 + .../noir/concepts/data_types/index.md | 126 + .../noir/concepts/data_types/integers.md | 178 ++ .../noir/concepts/data_types/references.md | 23 + .../noir/concepts/data_types/slices.mdx | 358 +++ .../noir/concepts/data_types/strings.md | 114 + .../noir/concepts/data_types/structs.md | 96 + .../noir/concepts/data_types/tuples.md | 48 + .../noir/concepts/functions.md | 226 ++ .../noir/concepts/generics.md | 262 +++ .../noir/concepts/globals.md | 82 + .../noir/concepts/lambdas.md | 81 + .../noir/concepts/mutability.md | 121 + .../noir/concepts/ops.md | 98 + .../noir/concepts/oracles.mdx | 29 + .../noir/concepts/shadowing.md | 44 + .../noir/concepts/traits.md | 611 +++++ .../noir/concepts/unconstrained.md | 104 + .../modules_packages_crates/_category_.json | 6 + .../crates_and_packages.md | 43 + .../modules_packages_crates/dependencies.md | 122 + .../noir/modules_packages_crates/modules.md | 221 ++ .../modules_packages_crates/workspaces.md | 46 + .../noir/standard_library/_category_.json | 6 + .../noir/standard_library/black_box_fns.md | 31 + .../noir/standard_library/bn254.md | 46 + .../standard_library/containers/boundedvec.md | 479 ++++ .../standard_library/containers/hashmap.md | 587 +++++ .../noir/standard_library/containers/index.md | 5 + .../noir/standard_library/containers/vec.mdx | 170 ++ .../cryptographic_primitives/_category_.json | 5 + .../cryptographic_primitives/ciphers.mdx | 36 + .../ecdsa_sig_verification.mdx | 98 + .../embedded_curve_ops.mdx | 95 + .../cryptographic_primitives/hashes.mdx | 228 ++ .../cryptographic_primitives/index.md | 14 + .../noir/standard_library/fmtstr.md | 17 + .../noir/standard_library/is_unconstrained.md | 69 + .../noir/standard_library/logging.md | 78 + .../noir/standard_library/mem.md | 82 + .../noir/standard_library/merkle_trees.md | 0 .../noir/standard_library/meta/ctstring.md | 100 + .../noir/standard_library/meta/expr.md | 380 +++ .../standard_library/meta/function_def.md | 181 ++ .../noir/standard_library/meta/index.md | 224 ++ .../noir/standard_library/meta/module.md | 82 + .../noir/standard_library/meta/op.md | 244 ++ .../noir/standard_library/meta/quoted.md | 140 ++ .../noir/standard_library/meta/struct_def.md | 199 ++ .../standard_library/meta/trait_constraint.md | 17 + .../noir/standard_library/meta/trait_def.md | 26 + .../noir/standard_library/meta/trait_impl.md | 62 + .../noir/standard_library/meta/typ.md | 264 +++ .../noir/standard_library/meta/typed_expr.md | 27 + .../standard_library/meta/unresolved_type.md | 57 + .../noir/standard_library/options.md | 101 + .../noir/standard_library/recursion.mdx | 67 + .../noir/standard_library/traits.md | 655 ++++++ .../reference/NoirJS/noir_js/.nojekyll | 1 + .../reference/NoirJS/noir_js/classes/Noir.md | 52 + .../reference/NoirJS/noir_js/functions/and.md | 22 + .../NoirJS/noir_js/functions/blake2s256.md | 21 + .../functions/ecdsa_secp256k1_verify.md | 28 + .../functions/ecdsa_secp256r1_verify.md | 28 + .../reference/NoirJS/noir_js/functions/xor.md | 22 + .../reference/NoirJS/noir_js/index.md | 47 + .../noir_js/type-aliases/ErrorWithPayload.md | 15 + .../type-aliases/ForeignCallHandler.md | 24 + .../noir_js/type-aliases/ForeignCallInput.md | 9 + .../noir_js/type-aliases/ForeignCallOutput.md | 9 + .../NoirJS/noir_js/type-aliases/WitnessMap.md | 9 + .../NoirJS/noir_js/typedoc-sidebar.cjs | 4 + .../reference/NoirJS/noir_wasm/.nojekyll | 1 + .../NoirJS/noir_wasm/functions/compile.md | 51 + .../noir_wasm/functions/compile_contract.md | 51 + .../noir_wasm/functions/createFileManager.md | 21 + .../functions/inflateDebugSymbols.md | 21 + .../reference/NoirJS/noir_wasm/index.md | 49 + .../NoirJS/noir_wasm/typedoc-sidebar.cjs | 4 + .../reference/_category_.json | 5 + .../reference/debugger/_category_.json | 6 + .../debugger/debugger_known_limitations.md | 59 + .../reference/debugger/debugger_repl.md | 360 +++ .../reference/debugger/debugger_vscode.md | 82 + .../reference/nargo_commands.md | 580 +++++ .../reference/noir_codegen.md | 116 + .../version-v1.0.0-beta.3/tooling/cspell.json | 8 + .../version-v1.0.0-beta.3/tooling/debugger.md | 26 + .../tooling/language_server.md | 43 + .../version-v1.0.0-beta.3/tooling/profiler.md | 115 + .../version-v1.0.0-beta.3/tooling/security.md | 61 + .../version-v1.0.0-beta.3/tooling/testing.md | 79 + .../tutorials/noirjs_app.md | 304 +++ .../version-v1.0.0-beta.3-sidebars.json | 93 + .../codegen_verifier/codegen_verifier.sh | 8 +- .../examples/oracle_transcript/Nargo.toml | 7 + .../examples/oracle_transcript/Oracle.jsonl | 5 + .../oracle_transcript/Oracle.test.jsonl | 20 + .../examples/oracle_transcript/Prover.toml | 6 + .../log_and_exec_transcript.sh | 22 + .../examples/oracle_transcript/src/main.nr | 63 + .../examples/oracle_transcript/test.sh | 10 + noir/noir-repo/noir_stdlib/src/cmp.nr | 16 + noir/noir-repo/noir_stdlib/src/convert.nr | 23 + noir/noir-repo/noir_stdlib/src/default.nr | 6 + .../noir_stdlib/src/ecdsa_secp256k1.nr | 1 - noir/noir-repo/noir_stdlib/src/hash/keccak.nr | 155 -- noir/noir-repo/noir_stdlib/src/hash/mod.nr | 48 +- noir/noir-repo/noir_stdlib/src/hash/sha256.nr | 845 ------- noir/noir-repo/noir_stdlib/src/hash/sha512.nr | 165 -- noir/noir-repo/noir_stdlib/src/lib.nr | 42 +- noir/noir-repo/noir_stdlib/src/merkle.nr | 19 - noir/noir-repo/noir_stdlib/src/meta/op.nr | 1 - noir/noir-repo/noir_stdlib/src/ops/arith.nr | 26 +- noir/noir-repo/noir_stdlib/src/ops/bit.nr | 35 +- noir/noir-repo/noir_stdlib/src/prelude.nr | 1 - noir/noir-repo/noir_stdlib/src/sha256.nr | 10 - noir/noir-repo/noir_stdlib/src/sha512.nr | 5 - noir/noir-repo/noir_stdlib/src/uint128.nr | 572 ----- noir/noir-repo/rust-toolchain.toml | 4 +- ...ommit.sh => bump-aztec-packages-commit.sh} | 0 noir/noir-repo/scripts/install_bb.sh | 4 +- .../benchmarks/bench_sha256/Prover.toml | 1 - .../benchmarks/bench_sha256/src/main.nr | 4 - .../benchmarks/bench_sha256_100/Prover.toml | 102 - .../benchmarks/bench_sha256_100/src/main.nr | 10 - .../benchmarks/bench_sha256_30/Nargo.toml | 7 - .../benchmarks/bench_sha256_30/Prover.toml | 32 - .../benchmarks/bench_sha256_30/src/main.nr | 10 - .../benchmarks/bench_sha256_long/Prover.toml | 191 -- .../benchmarks/bench_sha256_long/src/main.nr | 7 - .../test_programs/compilation_report.sh | 2 +- .../broken_impl}/Nargo.toml | 4 +- .../compile_failure/broken_impl/src/main.nr | 2 + .../src/main.nr | 1 - .../comptime_as_field/src/main.nr | 6 - .../Nargo.toml | 0 .../comptime_as_primitive/src/main.nr | 7 + .../comptime_fmt_strings/src/main.nr | 1 - .../comptime_from_field/Nargo.toml | 6 - .../comptime_from_field/src/main.nr | 6 - .../comptime_function_definition/src/main.nr | 6 +- .../comptime_keccak/Nargo.toml | 7 - .../comptime_keccak/src/main.nr | 32 - .../comptime_traits/src/main.nr | 1 - .../src/main.nr | 1 - .../compile_success_empty/enums/src/main.nr | 39 +- .../src/main.nr | 1 - .../trait_override_implementation/src/main.nr | 1 - .../unquote_struct/src/main.nr | 15 + .../src/main.nr | 1 - .../u128_multiplication_overflow/Nargo.toml | 7 + .../u128_multiplication_overflow/Prover.toml | 2 + .../u128_multiplication_overflow/src/main.nr | 7 + .../test_programs/execution_report.sh | 2 +- .../execution_success/6/Prover.toml | 35 +- .../execution_success/6/src/main.nr | 8 +- .../array_dynamic_blackbox_input/Prover.toml | 2 +- .../array_dynamic_blackbox_input/src/main.nr | 2 +- .../Prover.toml | 4 +- .../src/main.nr | 2 +- .../execution_success/bit_and/src/main.nr | 1 - .../execution_success/bool_not/src/main.nr | 1 - .../execution_success/bool_or/src/main.nr | 1 - .../brillig_calls_conditionals/src/main.nr | 1 - .../brillig_cow_regression/src/main.nr | 4 +- .../brillig_nested_arrays/src/main.nr | 1 - .../brillig_pedersen/src/main.nr | 1 - .../execution_success/cast_bool/src/main.nr | 1 - .../conditional_1/Prover.toml | 2 +- .../conditional_1/src/main.nr | 2 +- .../conditional_regression_661/src/main.nr | 1 - .../Prover.toml | 35 +- .../src/main.nr | 4 +- .../execution_success/databus/src/main.nr | 1 - .../ecdsa_secp256k1/Prover.toml | 40 - .../ecdsa_secp256k1/src/main.nr | 12 +- .../fold_complex_outputs/src/main.nr | 1 - .../src/main.nr | 1 - .../if_else_chain/src/main.nr | 1 - .../execution_success/keccak256/Prover.toml | 35 - .../execution_success/keccak256/src/main.nr | 20 - .../merkle_insert/src/main.nr | 19 +- .../nested_array_dynamic/src/main.nr | 1 - .../nested_arrays_from_brillig/src/main.nr | 1 - .../pedersen_check/src/main.nr | 1 - .../pedersen_commitment/src/main.nr | 1 - .../pedersen_hash/src/main.nr | 1 - .../ram_blowup_regression/src/main.nr | 10 +- .../reference_only_used_as_alias/src/main.nr | 1 - .../regression_4449/Prover.toml | 2 +- .../regression_4449/src/main.nr | 2 +- .../{sha256 => regression_7195}/Nargo.toml | 4 +- .../regression_7195/Prover.toml | 2 + .../regression_7195/src/main.nr | 21 + .../{sha2_byte => regression_7451}/Nargo.toml | 2 +- .../regression_7451/Prover.toml | 1 + .../regression_7451/src/main.nr | 12 + .../execution_success/sha256/Prover.toml | 38 - .../execution_success/sha256/src/main.nr | 27 - .../Nargo.toml | 7 - .../Prover.toml | 16 - .../src/main.nr | 104 - .../sha256_regression/Nargo.toml | 7 - .../sha256_regression/Prover.toml | 14 - .../sha256_regression/src/main.nr | 39 - .../sha256_var_padding_regression/Nargo.toml | 7 - .../sha256_var_padding_regression/Prover.toml | 2 - .../sha256_var_padding_regression/src/main.nr | 29 - .../sha256_var_size_regression/Nargo.toml | 7 - .../sha256_var_size_regression/Prover.toml | 3 - .../sha256_var_size_regression/src/main.nr | 17 - .../Nargo.toml | 7 - .../Prover.toml | 2 - .../src/main.nr | 9 - .../execution_success/sha2_byte/Prover.toml | 5 - .../execution_success/sha2_byte/src/main.nr | 8 - .../Nargo.toml | 3 +- .../shift_right_overflow/Prover.toml | 1 + .../shift_right_overflow/src/main.nr | 5 + .../simple_shield/src/main.nr | 17 +- .../slice_dynamic_index/src/main.nr | 1 - .../execution_success/slice_regex/src/main.nr | 1 - .../struct_fields_ordering/src/main.nr | 1 - .../execution_success/submodules/src/main.nr | 1 - .../execution_success/u128/Nargo.toml | 6 - .../execution_success/u128/Prover.toml | 7 - .../execution_success/u128/src/main.nr | 42 - .../u128_type}/Nargo.toml | 4 +- .../execution_success/u128_type/Prover.toml | 3 + .../execution_success/u128_type/src/main.nr | 22 + .../wrapping_operations/src/main.nr | 1 - noir/noir-repo/test_programs/memory_report.sh | 2 +- .../noir_test_success/bounded_vec/src/main.nr | 1 - .../brillig_overflow_checks/src/main.nr | 1 - .../comptime_blackbox/src/main.nr | 13 +- .../noir_test_success/global_eval/src/main.nr | 11 - .../noir_test_success/mock_oracle/src/main.nr | 1 - .../u128_type}/Nargo.toml | 4 +- .../noir_test_success/u128_type/src/main.nr | 49 + .../tooling/acvm_cli/src/cli/execute_cmd.rs | 9 +- .../tooling/acvm_cli/src/fs/witness.rs | 4 +- noir/noir-repo/tooling/acvm_cli/src/main.rs | 7 +- .../tooling/artifact_cli/src/bin/execute.rs | 5 +- .../artifact_cli/src/commands/execute_cmd.rs | 205 +- .../tooling/artifact_cli/src/commands/mod.rs | 1 + .../tooling/artifact_cli/src/errors.rs | 24 +- .../tooling/artifact_cli/src/execution.rs | 135 ++ .../tooling/artifact_cli/src/fs/artifact.rs | 60 +- .../tooling/artifact_cli/src/fs/inputs.rs | 2 +- .../tooling/artifact_cli/src/fs/witness.rs | 8 +- .../noir-repo/tooling/artifact_cli/src/lib.rs | 1 + .../noir-repo/tooling/debugger/src/context.rs | 20 +- noir/noir-repo/tooling/debugger/src/dap.rs | 6 +- .../tooling/debugger/src/foreign_calls.rs | 8 +- noir/noir-repo/tooling/debugger/src/lib.rs | 3 +- noir/noir-repo/tooling/debugger/src/repl.rs | 50 +- .../debugger/src/source_code_printer.rs | 40 +- .../noir-repo/tooling/debugger/tests/debug.rs | 111 + .../tooling/fuzzer/src/dictionary/mod.rs | 4 +- noir/noir-repo/tooling/fuzzer/src/lib.rs | 2 +- .../tooling/fuzzer/src/strategies/int.rs | 12 +- .../tooling/fuzzer/src/strategies/mod.rs | 2 +- .../tooling/fuzzer/src/strategies/uint.rs | 6 +- noir/noir-repo/tooling/inspector/Cargo.toml | 1 + .../tooling/inspector/src/cli/info_cmd.rs | 50 +- .../tooling/inspector/src/cli/mod.rs | 2 +- .../inspector/src/cli/print_acir_cmd.rs | 28 +- noir/noir-repo/tooling/inspector/src/main.rs | 2 +- noir/noir-repo/tooling/lsp/Cargo.toml | 1 + .../lsp/src/attribute_reference_finder.rs | 6 +- noir/noir-repo/tooling/lsp/src/lib.rs | 41 +- noir/noir-repo/tooling/lsp/src/modules.rs | 7 +- .../tooling/lsp/src/notifications/mod.rs | 44 +- .../tooling/lsp/src/requests/code_action.rs | 24 +- .../code_action/fill_struct_fields.rs | 6 +- .../code_action/implement_missing_members.rs | 2 +- .../requests/code_action/import_or_qualify.rs | 4 +- .../src/requests/code_action/import_trait.rs | 8 +- .../code_action/remove_bang_from_call.rs | 4 +- .../code_action/remove_unused_import.rs | 19 +- .../lsp/src/requests/code_action/tests.rs | 6 +- .../lsp/src/requests/code_lens_request.rs | 5 +- .../tooling/lsp/src/requests/completion.rs | 89 +- .../src/requests/completion/auto_import.rs | 6 +- .../lsp/src/requests/completion/builtins.rs | 9 +- .../requests/completion/completion_items.rs | 10 +- .../lsp/src/requests/completion/kinds.rs | 2 +- .../lsp/src/requests/completion/tests.rs | 24 +- .../lsp/src/requests/document_symbol.rs | 40 +- .../lsp/src/requests/goto_declaration.rs | 4 +- .../lsp/src/requests/goto_definition.rs | 8 +- .../tooling/lsp/src/requests/hover.rs | 2 +- .../lsp/src/requests/hover/from_reference.rs | 30 +- .../lsp/src/requests/hover/from_visitor.rs | 43 +- .../tooling/lsp/src/requests/inlay_hint.rs | 54 +- .../noir-repo/tooling/lsp/src/requests/mod.rs | 22 +- .../tooling/lsp/src/requests/references.rs | 2 +- .../tooling/lsp/src/requests/rename.rs | 9 +- .../lsp/src/requests/signature_help.rs | 31 +- .../tooling/lsp/src/requests/test_run.rs | 15 +- .../tooling/lsp/src/requests/tests.rs | 15 +- .../src/trait_impl_method_stub_generator.rs | 4 +- noir/noir-repo/tooling/lsp/src/types.rs | 2 +- .../tooling/lsp/src/use_segment_positions.rs | 8 +- noir/noir-repo/tooling/lsp/src/utils.rs | 6 +- noir/noir-repo/tooling/lsp/src/with_file.rs | 1022 ++++++++ noir/noir-repo/tooling/nargo/src/errors.rs | 18 +- .../nargo/src/foreign_calls/default.rs | 8 +- .../tooling/nargo/src/foreign_calls/layers.rs | 2 +- .../tooling/nargo/src/foreign_calls/mocker.rs | 2 +- .../tooling/nargo/src/foreign_calls/mod.rs | 4 + .../tooling/nargo/src/foreign_calls/print.rs | 3 +- .../tooling/nargo/src/foreign_calls/rpc.rs | 6 +- .../nargo/src/foreign_calls/transcript.rs | 156 ++ noir/noir-repo/tooling/nargo/src/lib.rs | 6 +- noir/noir-repo/tooling/nargo/src/ops/check.rs | 13 +- .../tooling/nargo/src/ops/compile.rs | 8 +- .../tooling/nargo/src/ops/execute.rs | 6 +- noir/noir-repo/tooling/nargo/src/ops/mod.rs | 2 +- .../tooling/nargo/src/ops/optimize.rs | 2 +- noir/noir-repo/tooling/nargo/src/ops/test.rs | 45 +- .../tooling/nargo/src/ops/transform.rs | 2 +- noir/noir-repo/tooling/nargo/src/workspace.rs | 2 +- noir/noir-repo/tooling/nargo_cli/Cargo.toml | 8 +- .../tooling/nargo_cli/benches/criterion.rs | 56 +- .../tooling/nargo_cli/benches/iai.rs | 2 +- noir/noir-repo/tooling/nargo_cli/build.rs | 5 +- .../tooling/nargo_cli/src/cli/check_cmd.rs | 9 +- .../tooling/nargo_cli/src/cli/compile_cmd.rs | 19 +- .../tooling/nargo_cli/src/cli/dap_cmd.rs | 28 +- .../tooling/nargo_cli/src/cli/debug_cmd.rs | 45 +- .../tooling/nargo_cli/src/cli/execute_cmd.rs | 152 +- .../tooling/nargo_cli/src/cli/export_cmd.rs | 10 +- .../tooling/nargo_cli/src/cli/fmt_cmd.rs | 9 +- .../tooling/nargo_cli/src/cli/fs/inputs.rs | 42 - .../tooling/nargo_cli/src/cli/fs/mod.rs | 30 - .../tooling/nargo_cli/src/cli/fs/program.rs | 51 - .../tooling/nargo_cli/src/cli/fs/witness.rs | 22 - .../src/cli/generate_completion_script_cmd.rs | 2 +- .../tooling/nargo_cli/src/cli/info_cmd.rs | 15 +- .../tooling/nargo_cli/src/cli/init_cmd.rs | 15 +- .../tooling/nargo_cli/src/cli/mod.rs | 29 +- .../tooling/nargo_cli/src/cli/new_cmd.rs | 2 +- .../tooling/nargo_cli/src/cli/test_cmd.rs | 134 +- .../nargo_cli/src/cli/test_cmd/formatters.rs | 13 +- .../noir-repo/tooling/nargo_cli/src/errors.rs | 46 +- noir/noir-repo/tooling/nargo_cli/src/main.rs | 9 +- .../tooling/nargo_cli/tests/stdlib-props.rs | 153 +- .../tooling/nargo_cli/tests/stdlib-tests.rs | 6 +- noir/noir-repo/tooling/nargo_fmt/Cargo.toml | 1 + noir/noir-repo/tooling/nargo_fmt/build.rs | 4 +- .../noir-repo/tooling/nargo_fmt/src/chunks.rs | 8 +- .../noir-repo/tooling/nargo_fmt/src/config.rs | 2 +- .../tooling/nargo_fmt/src/formatter.rs | 9 +- .../tooling/nargo_fmt/src/formatter/alias.rs | 2 +- .../nargo_fmt/src/formatter/attribute.rs | 2 +- .../tooling/nargo_fmt/src/formatter/buffer.rs | 7 + .../src/formatter/comments_and_whitespace.rs | 9 +- .../nargo_fmt/src/formatter/doc_comments.rs | 2 +- .../tooling/nargo_fmt/src/formatter/enums.rs | 2 +- .../nargo_fmt/src/formatter/expression.rs | 33 +- .../nargo_fmt/src/formatter/function.rs | 3 +- .../nargo_fmt/src/formatter/generics.rs | 2 +- .../tooling/nargo_fmt/src/formatter/global.rs | 4 +- .../tooling/nargo_fmt/src/formatter/impls.rs | 2 +- .../tooling/nargo_fmt/src/formatter/item.rs | 20 +- .../tooling/nargo_fmt/src/formatter/lvalue.rs | 6 +- .../tooling/nargo_fmt/src/formatter/module.rs | 6 +- .../tooling/nargo_fmt/src/formatter/path.rs | 2 +- .../nargo_fmt/src/formatter/pattern.rs | 8 +- .../nargo_fmt/src/formatter/statement.rs | 13 +- .../nargo_fmt/src/formatter/structs.rs | 2 +- .../nargo_fmt/src/formatter/trait_impl.rs | 2 +- .../tooling/nargo_fmt/src/formatter/traits.rs | 24 +- .../src/formatter/type_expression.rs | 2 +- .../tooling/nargo_fmt/src/formatter/types.rs | 6 +- .../nargo_fmt/src/formatter/use_tree.rs | 6 +- .../nargo_fmt/src/formatter/use_tree_merge.rs | 10 +- .../nargo_fmt/src/formatter/visibility.rs | 2 +- .../nargo_fmt/src/formatter/where_clause.rs | 9 +- noir/noir-repo/tooling/nargo_fmt/src/lib.rs | 4 +- .../nargo_fmt/tests/expected/databus.nr | 1 - .../tooling/nargo_fmt/tests/expected/fn.nr | 1 - .../nargo_fmt/tests/expected/ignore.nr | 1 - .../tooling/nargo_fmt/tests/expected/trait.nr | 1 - .../nargo_fmt/tests/expected/trait_alias.nr | 1 - .../nargo_fmt/tests/expected/unsafe.nr | 1 - .../tooling/nargo_toml/src/errors.rs | 20 +- .../noir-repo/tooling/nargo_toml/src/flock.rs | 7 +- noir/noir-repo/tooling/nargo_toml/src/lib.rs | 17 +- .../tooling/nargo_toml/src/semver.rs | 30 +- .../tooling/noir_codegen/package.json | 2 +- noir/noir-repo/tooling/noir_js/package.json | 2 +- .../tooling/noir_js_types/package.json | 2 +- .../input_parser/json.txt | 1 + .../tooling/noirc_abi/src/arbitrary.rs | 10 +- .../noir-repo/tooling/noirc_abi/src/errors.rs | 20 +- .../noirc_abi/src/input_parser/json.rs | 10 +- .../tooling/noirc_abi/src/input_parser/mod.rs | 41 +- .../noirc_abi/src/input_parser/toml.rs | 10 +- noir/noir-repo/tooling/noirc_abi/src/lib.rs | 6 +- .../tooling/noirc_abi_wasm/Cargo.toml | 2 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- .../tooling/noirc_abi_wasm/package.json | 2 +- .../noirc_abi_wasm/src/js_witness_map.rs | 12 +- .../tooling/noirc_abi_wasm/src/lib.rs | 9 +- .../tooling/noirc_artifacts/src/contract.rs | 45 +- .../tooling/noirc_artifacts/src/debug.rs | 6 +- .../tooling/noirc_artifacts/src/lib.rs | 41 + .../tooling/noirc_artifacts/src/program.rs | 5 +- .../tooling/noirc_artifacts_info/src/lib.rs | 2 +- noir/noir-repo/tooling/profiler/Cargo.toml | 6 +- .../src/cli/execution_flamegraph_cmd.rs | 197 +- .../profiler/src/cli/gates_flamegraph_cmd.rs | 49 +- .../noir-repo/tooling/profiler/src/cli/mod.rs | 6 +- .../src/cli/opcodes_flamegraph_cmd.rs | 170 +- noir/noir-repo/tooling/profiler/src/errors.rs | 16 + .../tooling/profiler/src/flamegraph.rs | 16 +- noir/noir-repo/tooling/profiler/src/fs.rs | 42 - .../tooling/profiler/src/gates_provider.rs | 2 +- noir/noir-repo/tooling/profiler/src/main.rs | 9 +- .../tooling/profiler/src/opcode_formatter.rs | 4 +- noir/noir-repo/yarn.lock | 11 +- 783 files changed, 28528 insertions(+), 12203 deletions(-) create mode 100644 noir/noir-repo/.github/actions/download-noir-execute/action.yml create mode 100644 noir/noir-repo/.github/critical_libraries_status/noir-lang/keccak256/.failures.jsonl create mode 100644 noir/noir-repo/.github/critical_libraries_status/noir-lang/sha512/.failures.jsonl create mode 100644 noir/noir-repo/.github/workflows/bump-aztec-packages-commit.yml create mode 100644 noir/noir-repo/acvm-repo/acir_field/benches/field_element.rs create mode 100644 noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/basic_conditional.rs create mode 100644 noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_array_gets.rs create mode 100644 noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/check_u128_mul_overflow.rs create mode 100644 noir/noir-repo/compiler/noirc_frontend/src/elaborator/options.rs create mode 100644 noir/noir-repo/compiler/noirc_frontend/src/signed_field.rs create mode 100644 noir/noir-repo/compiler/noirc_frontend/src/tests/enums.rs create mode 100644 noir/noir-repo/docs/docs/tooling/profiler.md create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/acir-flamegraph-optimized.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/acir-flamegraph-unoptimized.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/brillig-trace-initial-32.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/brillig-trace-opt-32.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/gates-flamegraph-optimized-2048.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/gates-flamegraph-optimized.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/gates-flamegraph-unoptimized-2048.png create mode 100644 noir/noir-repo/docs/static/img/tooling/profiler/gates-flamegraph-unoptimized.png rename noir/noir-repo/docs/{docs/tooling => versioned_docs/version-v1.0.0-beta.3/explainers}/cspell.json (55%) create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-oracle.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-recursion.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-writing-noir.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/noir_installation.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/project_breakdown.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/quick_start.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/setting_up_shell_completions.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_the_repl.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_vs_code.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-oracles.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-recursion.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-solidity-verifier.mdx rename noir/noir-repo/docs/{docs => versioned_docs/version-v1.0.0-beta.3}/how_to/merkle-proof.mdx (100%) create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/using-devcontainers.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/index.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/migration_notes.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/assert.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comments.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comptime.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/control_flow.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_bus.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/arrays.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/booleans.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/fields.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/function_types.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/index.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/integers.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/references.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/slices.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/strings.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/structs.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/tuples.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/functions.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/generics.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/globals.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/lambdas.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/mutability.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/ops.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/oracles.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/shadowing.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/traits.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/unconstrained.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/crates_and_packages.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/dependencies.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/modules.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/workspaces.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/black_box_fns.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/bn254.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/boundedvec.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/hashmap.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/index.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/vec.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ciphers.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/hashes.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/index.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/fmtstr.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/is_unconstrained.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/logging.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/mem.md rename noir/noir-repo/docs/{docs => versioned_docs/version-v1.0.0-beta.3}/noir/standard_library/merkle_trees.md (100%) create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/ctstring.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/expr.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/function_def.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/index.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/module.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/op.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/quoted.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/struct_def.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_constraint.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_def.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_impl.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typ.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typed_expr.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/unresolved_type.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/options.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/recursion.mdx create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/traits.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/.nojekyll create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/classes/Noir.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/and.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/blake2s256.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/xor.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/index.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/WitnessMap.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/typedoc-sidebar.cjs create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/.nojekyll create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile_contract.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/createFileManager.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/index.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/_category_.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_known_limitations.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_repl.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_vscode.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/nargo_commands.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/noir_codegen.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/cspell.json create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/debugger.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/language_server.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/profiler.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/security.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/testing.md create mode 100644 noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tutorials/noirjs_app.md create mode 100644 noir/noir-repo/docs/versioned_sidebars/version-v1.0.0-beta.3-sidebars.json create mode 100644 noir/noir-repo/examples/oracle_transcript/Nargo.toml create mode 100644 noir/noir-repo/examples/oracle_transcript/Oracle.jsonl create mode 100644 noir/noir-repo/examples/oracle_transcript/Oracle.test.jsonl create mode 100644 noir/noir-repo/examples/oracle_transcript/Prover.toml create mode 100755 noir/noir-repo/examples/oracle_transcript/log_and_exec_transcript.sh create mode 100644 noir/noir-repo/examples/oracle_transcript/src/main.nr create mode 100755 noir/noir-repo/examples/oracle_transcript/test.sh delete mode 100644 noir/noir-repo/noir_stdlib/src/hash/keccak.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/hash/sha256.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/hash/sha512.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/merkle.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/sha256.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/sha512.nr delete mode 100644 noir/noir-repo/noir_stdlib/src/uint128.nr rename noir/noir-repo/scripts/{bump-aztec-packges-commit.sh => bump-aztec-packages-commit.sh} (100%) delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256/Prover.toml delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256/src/main.nr delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Prover.toml delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_100/src/main.nr delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Prover.toml delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_30/src/main.nr delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Prover.toml delete mode 100644 noir/noir-repo/test_programs/benchmarks/bench_sha256_long/src/main.nr rename noir/noir-repo/test_programs/{benchmarks/bench_sha256_100 => compile_failure/broken_impl}/Nargo.toml (51%) create mode 100644 noir/noir-repo/test_programs/compile_failure/broken_impl/src/main.nr delete mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_as_field/src/main.nr rename noir/noir-repo/test_programs/compile_success_empty/{comptime_as_field => comptime_as_primitive}/Nargo.toml (100%) create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_as_primitive/src/main.nr delete mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/src/main.nr delete mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/keccak256/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/keccak256/src/main.nr rename noir/noir-repo/test_programs/execution_success/{sha256 => regression_7195}/Nargo.toml (50%) create mode 100644 noir/noir-repo/test_programs/execution_success/regression_7195/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/regression_7195/src/main.nr rename noir/noir-repo/test_programs/execution_success/{sha2_byte => regression_7451}/Nargo.toml (68%) create mode 100644 noir/noir-repo/test_programs/execution_success/regression_7451/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/regression_7451/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_regression/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_regression/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_regression/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/sha2_byte/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/sha2_byte/src/main.nr rename noir/noir-repo/test_programs/execution_success/{keccak256 => shift_right_overflow}/Nargo.toml (63%) create mode 100644 noir/noir-repo/test_programs/execution_success/shift_right_overflow/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/shift_right_overflow/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/u128/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/u128/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/u128/src/main.nr rename noir/noir-repo/test_programs/{benchmarks/bench_sha256 => execution_success/u128_type}/Nargo.toml (52%) create mode 100644 noir/noir-repo/test_programs/execution_success/u128_type/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/u128_type/src/main.nr rename noir/noir-repo/test_programs/{benchmarks/bench_sha256_long => noir_test_success/u128_type}/Nargo.toml (52%) create mode 100644 noir/noir-repo/test_programs/noir_test_success/u128_type/src/main.nr create mode 100644 noir/noir-repo/tooling/artifact_cli/src/execution.rs create mode 100644 noir/noir-repo/tooling/lsp/src/with_file.rs create mode 100644 noir/noir-repo/tooling/nargo/src/foreign_calls/transcript.rs delete mode 100644 noir/noir-repo/tooling/nargo_cli/src/cli/fs/inputs.rs delete mode 100644 noir/noir-repo/tooling/nargo_cli/src/cli/fs/mod.rs delete mode 100644 noir/noir-repo/tooling/nargo_cli/src/cli/fs/program.rs delete mode 100644 noir/noir-repo/tooling/nargo_cli/src/cli/fs/witness.rs create mode 100644 noir/noir-repo/tooling/profiler/src/errors.rs delete mode 100644 noir/noir-repo/tooling/profiler/src/fs.rs diff --git a/.noir-sync-commit b/.noir-sync-commit index 58cd9057d55a..73dc17fe6af1 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -fdfe2bf752771b9611dc71953d50423b4ae7ec44 +5a3d2bc0e13a12b039c793c73d7817924c13e159 diff --git a/noir/noir-repo/.github/actions/download-noir-execute/action.yml b/noir/noir-repo/.github/actions/download-noir-execute/action.yml new file mode 100644 index 000000000000..470edc045384 --- /dev/null +++ b/noir/noir-repo/.github/actions/download-noir-execute/action.yml @@ -0,0 +1,18 @@ +name: Download noir-execute +description: Downloads the noir-execute binary from an artifact and adds it to the path + +runs: + using: composite + steps: + - name: Download noir-execute binary + uses: actions/download-artifact@v4 + with: + name: noir-execute + path: ./noir-execute + + - name: Set noir-execute on PATH + shell: bash + run: | + noir_binary="${{ github.workspace }}/noir-execute/noir-execute" + chmod +x $noir_binary + echo "$(dirname $noir_binary)" >> $GITHUB_PATH diff --git a/noir/noir-repo/.github/benchmark_projects.yml b/noir/noir-repo/.github/benchmark_projects.yml index e1bcb080829b..5d9266a2d1a5 100644 --- a/noir/noir-repo/.github/benchmark_projects.yml +++ b/noir/noir-repo/.github/benchmark_projects.yml @@ -1,4 +1,4 @@ -define: &AZ_COMMIT 1350f93c3e9af8f601ca67ca3e67d0127c9767b6 +define: &AZ_COMMIT 3b981f9217f9b859bdfbcdba2f5c080392c98da6 projects: private-kernel-inner: repo: AztecProtocol/aztec-packages @@ -7,7 +7,7 @@ projects: num_runs: 5 compilation-timeout: 2.5 execution-timeout: 0.08 - compilation-memory-limit: 300 + compilation-memory-limit: 350 execution-memory-limit: 250 private-kernel-tail: repo: AztecProtocol/aztec-packages @@ -16,9 +16,9 @@ projects: num_runs: 5 timeout: 4 compilation-timeout: 1.2 - execution-timeout: 0.02 + execution-timeout: 0.04 compilation-memory-limit: 250 - execution-memory-limit: 200 + execution-memory-limit: 230 private-kernel-reset: repo: AztecProtocol/aztec-packages ref: *AZ_COMMIT @@ -35,19 +35,19 @@ projects: path: noir-projects/noir-protocol-circuits/crates/rollup-base-private num_runs: 5 timeout: 15 - compilation-timeout: 10 - execution-timeout: 0.5 - compilation-memory-limit: 1100 - execution-memory-limit: 500 + compilation-timeout: 20 + execution-timeout: 1 + compilation-memory-limit: 1500 + execution-memory-limit: 650 rollup-base-public: repo: AztecProtocol/aztec-packages ref: *AZ_COMMIT path: noir-projects/noir-protocol-circuits/crates/rollup-base-public num_runs: 5 timeout: 15 - compilation-timeout: 8 - execution-timeout: 0.4 - compilation-memory-limit: 1000 + compilation-timeout: 15 + execution-timeout: 0.75 + compilation-memory-limit: 1500 execution-memory-limit: 500 rollup-block-root-empty: repo: AztecProtocol/aztec-packages @@ -65,17 +65,17 @@ projects: cannot_execute: true num_runs: 1 timeout: 60 - compilation-timeout: 100 - compilation-memory-limit: 7000 + compilation-timeout: 135 + compilation-memory-limit: 8000 rollup-block-root: repo: AztecProtocol/aztec-packages ref: *AZ_COMMIT path: noir-projects/noir-protocol-circuits/crates/rollup-block-root num_runs: 1 timeout: 60 - compilation-timeout: 110 + compilation-timeout: 135 execution-timeout: 40 - compilation-memory-limit: 7000 + compilation-memory-limit: 8000 execution-memory-limit: 1500 rollup-merge: repo: AztecProtocol/aztec-packages diff --git a/noir/noir-repo/.github/critical_libraries_status/noir-lang/keccak256/.failures.jsonl b/noir/noir-repo/.github/critical_libraries_status/noir-lang/keccak256/.failures.jsonl new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/noir/noir-repo/.github/critical_libraries_status/noir-lang/sha512/.failures.jsonl b/noir/noir-repo/.github/critical_libraries_status/noir-lang/sha512/.failures.jsonl new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/noir/noir-repo/.github/scripts/integration-test-node.sh b/noir/noir-repo/.github/scripts/integration-test-node.sh index b7f00c65620b..0d070b8001dd 100755 --- a/noir/noir-repo/.github/scripts/integration-test-node.sh +++ b/noir/noir-repo/.github/scripts/integration-test-node.sh @@ -1,5 +1,5 @@ #!/bin/bash set -eu -apt-get install libc++-dev -y +apt-get install libc6 libstdc++6 -y yarn workspace integration-tests test:node diff --git a/noir/noir-repo/.github/scripts/wasm-bindgen-install.sh b/noir/noir-repo/.github/scripts/wasm-bindgen-install.sh index 209080036934..bb48062cb43f 100755 --- a/noir/noir-repo/.github/scripts/wasm-bindgen-install.sh +++ b/noir/noir-repo/.github/scripts/wasm-bindgen-install.sh @@ -6,8 +6,8 @@ cd $(dirname "$0") ./cargo-binstall-install.sh # Install wasm-bindgen-cli. -if [ "$(wasm-bindgen --version &> /dev/null | cut -d' ' -f2)" != "0.2.86" ]; then +if [ "$(wasm-bindgen --version &> /dev/null | cut -d' ' -f2)" != "0.2.100" ]; then echo "Building wasm-bindgen..." - cargo binstall wasm-bindgen-cli@0.2.86 --force --no-confirm + cargo binstall wasm-bindgen-cli@0.2.100 --force --no-confirm fi diff --git a/noir/noir-repo/.github/workflows/bump-aztec-packages-commit.yml b/noir/noir-repo/.github/workflows/bump-aztec-packages-commit.yml new file mode 100644 index 000000000000..6b7c1a80a0dd --- /dev/null +++ b/noir/noir-repo/.github/workflows/bump-aztec-packages-commit.yml @@ -0,0 +1,50 @@ +name: Bump external repos pinned commits + +on: + workflow_dispatch: + schedule: + # Trigger at 8am on Mondays + - cron: '0 8 * * 1' + + +jobs: + bump-commit: + name: Update external repo pinned commits + runs-on: ubuntu-22.04 + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + ref: master + + - name: Check for existing PR + id: pr-check + run: | + set -xue # print commands + PR_URL=$(gh pr list --repo noir-lang/noir --head bump-aztec-packages --json url --jq ".[0].url") + echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT + env: + GH_TOKEN: ${{ github.token }} + + - name: Configure git + run: | + git config user.name noirwhal + git config user.email tomfrench@aztecprotocol.com + + - name: Update commit + run: | + git checkout bump-aztec-packages || git checkout -b bump-aztec-packages + ./scripts/bump-aztec-packages-commit.sh + git add . + git commit -m 'chore: Update pinned commit of aztec-packages' + git push --set-upstream origin bump-aztec-packages --force + + - name: Create PR + if: ${{ steps.pr-check.outputs.pr_url == '' }} + run: | + PR_BODY=""" + Automated update of the pinned commit of [aztec-packages](https://github.com/AztecProtocol/aztec-packages) repository against which we run benchmarks. + """ + gh pr create --repo noir-lang/noir --title "chore: bump external pinned commits" --body "$PR_BODY" --base master --head bump-aztec-packages + env: + GH_TOKEN: ${{ secrets.NOIR_REPO_TOKEN }} diff --git a/noir/noir-repo/.github/workflows/deny.yml b/noir/noir-repo/.github/workflows/deny.yml index 11dbc3eef4bc..c1b1da95ea26 100644 --- a/noir/noir-repo/.github/workflows/deny.yml +++ b/noir/noir-repo/.github/workflows/deny.yml @@ -21,6 +21,6 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - - uses: EmbarkStudios/cargo-deny-action@v1 + - uses: EmbarkStudios/cargo-deny-action@8d73959fce1cdc8989f23fdf03bec6ae6a6576ef with: - command: check all \ No newline at end of file + command: check all diff --git a/noir/noir-repo/.github/workflows/docs-pr.yml b/noir/noir-repo/.github/workflows/docs-pr.yml index c123def6ba39..d60bc8b841eb 100644 --- a/noir/noir-repo/.github/workflows/docs-pr.yml +++ b/noir/noir-repo/.github/workflows/docs-pr.yml @@ -33,7 +33,7 @@ jobs: // Check if any file is within the 'docs' folder const docsChanged = files.some(file => file.filename.startsWith('docs/')); return docsChanged; - + - name: Add label if not present if: steps.check-labels.outputs.result == 'true' uses: actions/github-script@v7.0.1 @@ -57,7 +57,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -71,7 +71,7 @@ jobs: - name: Install wasm-bindgen-cli uses: taiki-e/install-action@v2 with: - tool: wasm-bindgen-cli@0.2.86 + tool: wasm-bindgen-cli@0.2.100 - name: Install wasm-opt run: | @@ -102,13 +102,13 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - + - name: Download built docs uses: actions/download-artifact@v4 with: name: docs path: ./docs/build - + - name: Deploy to Netlify uses: nwtgck/actions-netlify@v2.1 with: diff --git a/noir/noir-repo/.github/workflows/formatting.yml b/noir/noir-repo/.github/workflows/formatting.yml index 34216c22e01a..007fd89b0aeb 100644 --- a/noir/noir-repo/.github/workflows/formatting.yml +++ b/noir/noir-repo/.github/workflows/formatting.yml @@ -25,7 +25,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: x86_64-unknown-linux-gnu components: clippy, rustfmt @@ -51,7 +51,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: x86_64-unknown-linux-gnu components: clippy, rustfmt @@ -89,7 +89,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -121,7 +121,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - + - name: Download nargo binary uses: ./.github/actions/download-nargo diff --git a/noir/noir-repo/.github/workflows/publish-acvm.yml b/noir/noir-repo/.github/workflows/publish-acvm.yml index 27d927a67d13..60e523777e80 100644 --- a/noir/noir-repo/.github/workflows/publish-acvm.yml +++ b/noir/noir-repo/.github/workflows/publish-acvm.yml @@ -18,7 +18,7 @@ jobs: ref: ${{ inputs.noir-ref }} - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 # These steps are in a specific order so crate dependencies are updated first - name: Publish acir_field diff --git a/noir/noir-repo/.github/workflows/publish-docs.yml b/noir/noir-repo/.github/workflows/publish-docs.yml index 16959256d2aa..f949576900f2 100644 --- a/noir/noir-repo/.github/workflows/publish-docs.yml +++ b/noir/noir-repo/.github/workflows/publish-docs.yml @@ -22,12 +22,12 @@ jobs: - name: Install wasm-bindgen-cli uses: taiki-e/install-action@v2 with: - tool: wasm-bindgen-cli@0.2.86 + tool: wasm-bindgen-cli@0.2.100 - name: Install wasm-opt run: | npm i wasm-opt -g - + - name: Query active docs versions run: yarn workspace docs version::stables diff --git a/noir/noir-repo/.github/workflows/publish-es-packages.yml b/noir/noir-repo/.github/workflows/publish-es-packages.yml index 76c6fce6d5e9..8186213effda 100644 --- a/noir/noir-repo/.github/workflows/publish-es-packages.yml +++ b/noir/noir-repo/.github/workflows/publish-es-packages.yml @@ -22,9 +22,9 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ inputs.noir-ref }} - + - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -44,7 +44,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: noirc_abi_wasm - path: | + path: | ./tooling/noirc_abi_wasm/nodejs ./tooling/noirc_abi_wasm/web retention-days: 10 @@ -58,7 +58,7 @@ jobs: ref: ${{ inputs.noir-ref }} - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -95,7 +95,7 @@ jobs: ref: ${{ inputs.noir-ref }} - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -119,7 +119,7 @@ jobs: ./acvm-repo/acvm_js/nodejs ./acvm-repo/acvm_js/web retention-days: 3 - + publish-es-packages: runs-on: ubuntu-22.04 needs: [build-acvm_js, build-noirc_abi_wasm, build-noir_wasm] @@ -133,12 +133,12 @@ jobs: with: name: acvm-js path: acvm-repo/acvm_js - + - uses: actions/download-artifact@v4 with: name: noir_wasm path: compiler/wasm - + - uses: actions/download-artifact@v4 with: name: noirc_abi_wasm diff --git a/noir/noir-repo/.github/workflows/publish-nargo.yml b/noir/noir-repo/.github/workflows/publish-nargo.yml index d7d9c1ea03eb..e18dac52ca48 100644 --- a/noir/noir-repo/.github/workflows/publish-nargo.yml +++ b/noir/noir-repo/.github/workflows/publish-nargo.yml @@ -38,15 +38,8 @@ jobs: with: ref: ${{ inputs.tag || env.GITHUB_REF }} - - name: Setup for Apple Silicon - if: matrix.target == 'aarch64-apple-darwin' - run: | - sudo xcode-select -s /Applications/Xcode_15.4.0.app/Contents/Developer/ - echo "SDKROOT=$(xcrun -sdk macosx$(sw_vers -productVersion) --show-sdk-path)" >> $GITHUB_ENV - echo "MACOSX_DEPLOYMENT_TARGET=$(xcrun -sdk macosx$(sw_vers -productVersion) --show-sdk-platform-version)" >> $GITHUB_ENV - - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: ${{ matrix.target }} @@ -59,21 +52,28 @@ jobs: - name: Build environment and Compile run: | cargo build --package nargo_cli --release --target ${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" + cargo build --package noir_profiler --release --target ${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" + cargo build --package noir_inspector --release --target ${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" - name: Package artifacts run: | mkdir dist cp ./target/${{ matrix.target }}/release/nargo ./dist/nargo - 7z a -ttar -so -an ./dist/* | 7z a -si ./nargo-${{ matrix.target }}.tar.gz + cp ./target/${{ matrix.target }}/release/noir-profiler ./dist/noir-profiler + cp ./target/${{ matrix.target }}/release/noir-inspector ./dist/noir-inspector + + # TODO(https://github.com/noir-lang/noir/issues/7445): Remove the separate nargo binary + 7z a -ttar -so -an ./dist/nargo | 7z a -si ./nargo-${{ matrix.target }}.tar.gz + 7z a -ttar -so -an ./dist/* | 7z a -si ./noir-${{ matrix.target }}.tar.gz - - name: Upload artifact + - name: Upload Noir binaries artifact uses: actions/upload-artifact@v4 with: - name: nargo-${{ matrix.target }} + name: noir-${{ matrix.target }} path: ./dist/* retention-days: 3 - - name: Upload binaries to release tag + - name: Upload nargo binary to release tag uses: svenstaro/upload-release-action@v2 if: ${{ inputs.publish || github.event_name == 'schedule' }} with: @@ -84,12 +84,23 @@ jobs: overwrite: true tag: ${{ inputs.tag || 'nightly' }} # This will fail if `inputs.tag` is not a tag (e.g. testing a branch) + - name: Upload Noir binaries to release tag + uses: svenstaro/upload-release-action@v2 + if: ${{ inputs.publish || github.event_name == 'schedule' }} + with: + repo_name: noir-lang/noir + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ./noir-${{ matrix.target }}.tar.gz + asset_name: noir-${{ matrix.target }}.tar.gz + overwrite: true + tag: ${{ inputs.tag || 'nightly' }} # This will fail if `inputs.tag` is not a tag (e.g. testing a branch) + - name: Get formatted date id: date if: ${{ inputs.tag == '' && inputs.publish || github.event_name == 'schedule' }} run: echo "date=$(date '+%Y-%m-%d')" >> $GITHUB_OUTPUT - - name: Upload binaries to release with date tag + - name: Upload nargo binary to release with date tag uses: svenstaro/upload-release-action@v2 if: ${{ inputs.tag == '' && inputs.publish || github.event_name == 'schedule' }} with: @@ -102,6 +113,19 @@ jobs: overwrite: true tag: ${{ format('{0}-{1}', 'nightly', steps.date.outputs.date) }} + - name: Upload Noir binaries to release with date tag + uses: svenstaro/upload-release-action@v2 + if: ${{ inputs.tag == '' && inputs.publish || github.event_name == 'schedule' }} + with: + repo_name: noir-lang/noir + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ./noir-${{ matrix.target }}.tar.gz + asset_name: noir-${{ matrix.target }}.tar.gz + prerelease: true + make_latest: false + overwrite: true + tag: ${{ format('{0}-{1}', 'nightly', steps.date.outputs.date) }} + build-linux: runs-on: ubuntu-22.04 env: @@ -120,7 +144,7 @@ jobs: ref: ${{ inputs.tag || env.GITHUB_REF }} - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: ${{ matrix.target }} @@ -135,23 +159,31 @@ jobs: with: tool: cross@0.2.5 - - name: Build Nargo - run: cross build --package nargo_cli --release --target=${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" + - name: Build binaries + run: | + cross build --package nargo_cli --release --target=${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" + cross build --package noir_profiler --release --target=${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" + cross build --package noir_inspector --release --target ${{ matrix.target }} --no-default-features --features "${{ inputs.features }}" - name: Package artifacts run: | mkdir dist cp ./target/${{ matrix.target }}/release/nargo ./dist/nargo - 7z a -ttar -so -an ./dist/* | 7z a -si ./nargo-${{ matrix.target }}.tar.gz + cp ./target/${{ matrix.target }}/release/noir-profiler ./dist/noir-profiler + cp ./target/${{ matrix.target }}/release/noir-inspector ./dist/noir-inspector - - name: Upload artifact + # TODO(https://github.com/noir-lang/noir/issues/7445): Remove the separate nargo binary + tar -czf nargo-${{ matrix.target }}.tar.gz -C dist nargo + tar -czf noir-${{ matrix.target }}.tar.gz -C dist . + + - name: Upload Noir binaries artifact uses: actions/upload-artifact@v4 with: - name: nargo-${{ matrix.target }} + name: noir-${{ matrix.target }} path: ./dist/* retention-days: 3 - - name: Upload binaries to release tag + - name: Upload nargo binary to release tag uses: svenstaro/upload-release-action@v2 if: ${{ inputs.publish }} with: @@ -163,12 +195,24 @@ jobs: overwrite: true tag: ${{ inputs.tag || 'nightly' }} # This will fail if `inputs.tag` is not a tag (e.g. testing a branch) + - name: Upload Noir binaries to release tag + uses: svenstaro/upload-release-action@v2 + if: ${{ inputs.publish }} + with: + repo_name: noir-lang/noir + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ./noir-${{ matrix.target }}.tar.gz + asset_name: noir-${{ matrix.target }}.tar.gz + prerelease: true + overwrite: true + tag: ${{ inputs.tag || 'nightly' }} # This will fail if `inputs.tag` is not a tag (e.g. testing a branch) + - name: Get formatted date id: date if: ${{ env.NIGHTLY_RELEASE && inputs.publish }} run: echo "date=$(date '+%Y-%m-%d')" >> $GITHUB_OUTPUT - - name: Upload binaries to release with date tag + - name: Upload nargo binary to release with date tag uses: svenstaro/upload-release-action@v2 if: ${{ env.NIGHTLY_RELEASE && inputs.publish }} with: @@ -181,4 +225,17 @@ jobs: overwrite: true tag: ${{ format('{0}-{1}', 'nightly', steps.date.outputs.date) }} - + - name: Upload Noir binaries to release with date tag + uses: svenstaro/upload-release-action@v2 + if: ${{ env.NIGHTLY_RELEASE && inputs.publish }} + with: + repo_name: noir-lang/noir + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ./noir-${{ matrix.target }}.tar.gz + asset_name: noir-${{ matrix.target }}.tar.gz + prerelease: true + make_latest: false + overwrite: true + tag: ${{ format('{0}-{1}', 'nightly', steps.date.outputs.date) }} + + diff --git a/noir/noir-repo/.github/workflows/release.yml b/noir/noir-repo/.github/workflows/release.yml index bbe9a7fff620..ea1f1eba6197 100644 --- a/noir/noir-repo/.github/workflows/release.yml +++ b/noir/noir-repo/.github/workflows/release.yml @@ -47,10 +47,10 @@ jobs: node-version: 18.19.0 cache: 'yarn' cache-dependency-path: 'yarn.lock' - + - name: Update yarn.lock run: yarn - + - name: Configure git run: | git config user.name noirwhal @@ -61,13 +61,13 @@ jobs: git add . git commit -m 'chore: Update root workspace acvm versions and lockfile' git push - + update-docs: name: Update docs needs: [release-please, update-acvm-workspace-package-versions] if: ${{ needs.release-please.outputs.release-pr }} runs-on: ubuntu-22.04 - + steps: - name: Checkout release branch uses: actions/checkout@v4 @@ -81,7 +81,7 @@ jobs: - name: Install wasm-bindgen-cli uses: taiki-e/install-action@v2 with: - tool: wasm-bindgen-cli@0.2.86 + tool: wasm-bindgen-cli@0.2.100 - name: Install wasm-opt run: | @@ -113,7 +113,7 @@ jobs: runs-on: ubuntu-22.04 # We want this job to always run (even if the dependant jobs fail) as we need apply changes to the sticky comment. if: ${{ always() }} - + needs: - release-please - update-acvm-workspace-package-versions @@ -123,7 +123,7 @@ jobs: # We treat any skipped or failing jobs as a failure for the workflow as a whole. FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }} - steps: + steps: - name: Add warning to sticky comment uses: marocchino/sticky-pull-request-comment@v2 with: @@ -175,7 +175,7 @@ jobs: needs: [release-please] if: ${{ needs.release-please.outputs.tag-name }} runs-on: ubuntu-22.04 - + steps: - name: Dispatch to publish-acvm uses: benc-uk/workflow-dispatch@v1 diff --git a/noir/noir-repo/.github/workflows/reports.yml b/noir/noir-repo/.github/workflows/reports.yml index 37f313259ad3..80f1910e3101 100644 --- a/noir/noir-repo/.github/workflows/reports.yml +++ b/noir/noir-repo/.github/workflows/reports.yml @@ -32,7 +32,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -58,7 +58,7 @@ jobs: compare_gates_reports: name: Circuit sizes needs: [build-nargo] - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: pull-requests: write @@ -417,9 +417,6 @@ jobs: - name: Download nargo binary uses: ./scripts/.github/actions/download-nargo - - name: Download nargo binary - uses: ./scripts/.github/actions/download-nargo - - name: Checkout uses: actions/checkout@v4 with: diff --git a/noir/noir-repo/.github/workflows/test-js-packages.yml b/noir/noir-repo/.github/workflows/test-js-packages.yml index 0af0e41d46ae..690575076c75 100644 --- a/noir/noir-repo/.github/workflows/test-js-packages.yml +++ b/noir/noir-repo/.github/workflows/test-js-packages.yml @@ -54,7 +54,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -78,7 +78,7 @@ jobs: path: ./dist/* retention-days: 3 - build-noirc-abi: + build-noir-execute: runs-on: ubuntu-22.04 timeout-minutes: 30 @@ -89,6 +89,39 @@ jobs: - name: Setup toolchain uses: dtolnay/rust-toolchain@1.75.0 + - uses: Swatinem/rust-cache@v2 + with: + key: x86_64-unknown-linux-gnu + cache-on-failure: true + save-if: ${{ github.event_name != 'merge_group' }} + + - name: Build noir-execute + run: cargo build --package noir_artifact_cli --release + + - name: Package artifacts + run: | + mkdir dist + cp ./target/release/noir-execute ./dist/noir-execute + 7z a -ttar -so -an ./dist/* | 7z a -si ./noir-execute-x86_64-unknown-linux-gnu.tar.gz + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: noir-execute + path: ./dist/* + retention-days: 3 + + build-noirc-abi: + runs-on: ubuntu-22.04 + timeout-minutes: 30 + + steps: + - name: Checkout Noir repo + uses: actions/checkout@v4 + + - name: Setup toolchain + uses: dtolnay/rust-toolchain@1.85.0 + - uses: Swatinem/rust-cache@v2 with: key: noirc-abi @@ -119,7 +152,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -154,7 +187,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 - uses: Swatinem/rust-cache@v2 with: @@ -361,7 +394,7 @@ jobs: test-integration-node: name: Integration Tests (Node) - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: [build-acvm-js, build-noir-wasm, build-nargo, build-noirc-abi] timeout-minutes: 30 @@ -373,6 +406,7 @@ jobs: run: | ./scripts/install_bb.sh echo "$HOME/.bb/" >> $GITHUB_PATH + sudo apt-get install libc6 libstdc++6 -y - name: Download nargo binary uses: ./.github/actions/download-nargo @@ -456,8 +490,8 @@ jobs: test-examples: name: Example scripts - runs-on: ubuntu-22.04 - needs: [build-nargo] + runs-on: ubuntu-24.04 + needs: [build-nargo, build-noir-execute] timeout-minutes: 30 steps: @@ -473,10 +507,14 @@ jobs: run: | ./scripts/install_bb.sh echo "$HOME/.bb/" >> $GITHUB_PATH + sudo apt-get install libc6 libstdc++6 -y - name: Download nargo binary uses: ./.github/actions/download-nargo + - name: Download noir-execute binary + uses: ./.github/actions/download-noir-execute + - name: Run `prove_and_verify` working-directory: ./examples/prove_and_verify run: ./test.sh @@ -485,6 +523,10 @@ jobs: working-directory: ./examples/codegen_verifier run: ./test.sh + - name: Run `oracle_transcript` + working-directory: ./examples/oracle_transcript + run: ./test.sh + external-repo-checks: needs: [build-nargo, critical-library-list] runs-on: ubuntu-22.04 diff --git a/noir/noir-repo/.github/workflows/test-rust-workspace-msrv.yml b/noir/noir-repo/.github/workflows/test-rust-workspace-msrv.yml index 38bc3cba153e..8f061bcad64e 100644 --- a/noir/noir-repo/.github/workflows/test-rust-workspace-msrv.yml +++ b/noir/noir-repo/.github/workflows/test-rust-workspace-msrv.yml @@ -3,7 +3,7 @@ name: Test (MSRV check) # TL;DR https://github.com/noir-lang/noir/issues/4384 # # This workflow acts to ensure that we can publish to crates.io, we need this extra check as libraries don't respect the Cargo.lock file committed in this repository. -# We must then always be able to build the workspace using the latest versions of all of our dependencies, so we explicitly update them and build in this workflow. +# We must then always be able to build the workspace using the latest versions of all of our dependencies, so we explicitly update them and build in this workflow. on: schedule: @@ -29,12 +29,12 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: x86_64-unknown-linux-gnu # We force the ACVM crate and all of its dependencies to update their dependencies - # This ensures that we'll be able to build the crates when they're being published. + # This ensures that we'll be able to build the crates when they're being published. - name: Update Cargo.lock run: | cargo update --package acvm --aggressive @@ -53,7 +53,7 @@ jobs: - name: Build and archive tests run: cargo nextest archive --workspace --archive-file nextest-archive.tar.zst - + - name: Upload archive to workflow uses: actions/upload-artifact@v4 with: @@ -70,9 +70,9 @@ jobs: partition: [1, 2, 3, 4] steps: - uses: actions/checkout@v4 - + - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: x86_64-unknown-linux-gnu @@ -80,7 +80,7 @@ jobs: uses: taiki-e/install-action@v2 with: tool: nextest@0.9.67 - + - name: Download archive uses: actions/download-artifact@v4 with: @@ -99,9 +99,9 @@ jobs: runs-on: ubuntu-22.04 # We want this job to always run (even if the dependant jobs fail) as we want this job to fail rather than skipping. if: ${{ always() }} - needs: + needs: - run-tests - + steps: - name: Report overall success run: | @@ -113,7 +113,7 @@ jobs: env: # We treat any cancelled, skipped or failing jobs as a failure for the workflow as a whole. FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'skipped') }} - + - name: Checkout if: ${{ failure() }} uses: actions/checkout@v4 diff --git a/noir/noir-repo/.github/workflows/test-rust-workspace.yml b/noir/noir-repo/.github/workflows/test-rust-workspace.yml index fe4213610723..91809a98a26a 100644 --- a/noir/noir-repo/.github/workflows/test-rust-workspace.yml +++ b/noir/noir-repo/.github/workflows/test-rust-workspace.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: x86_64-unknown-linux-gnu @@ -40,7 +40,7 @@ jobs: - name: Build and archive tests run: cargo nextest archive --workspace --archive-file nextest-archive.tar.zst - + - name: Upload archive to workflow uses: actions/upload-artifact@v4 with: @@ -57,9 +57,9 @@ jobs: partition: [1, 2, 3, 4] steps: - uses: actions/checkout@v4 - + - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.75.0 + uses: dtolnay/rust-toolchain@1.85.0 with: targets: x86_64-unknown-linux-gnu @@ -67,7 +67,7 @@ jobs: uses: taiki-e/install-action@v2 with: tool: nextest@0.9.67 - + - name: Download archive uses: actions/download-artifact@v4 with: @@ -86,9 +86,9 @@ jobs: runs-on: ubuntu-22.04 # We want this job to always run (even if the dependant jobs fail) as we want this job to fail rather than skipping. if: ${{ always() }} - needs: + needs: - run-tests - + steps: - name: Report overall success run: | diff --git a/noir/noir-repo/.gitignore b/noir/noir-repo/.gitignore index 49d9e2dbb2ff..334901860d91 100644 --- a/noir/noir-repo/.gitignore +++ b/noir/noir-repo/.gitignore @@ -52,4 +52,10 @@ tooling/noir_js/lib # docs autogen build /docs/docs/noir_js/reference/ -codegen \ No newline at end of file +codegen + +**/cspell.json +!./cspell.json + +mutants.out +mutants.out.old diff --git a/noir/noir-repo/.release-please-manifest.json b/noir/noir-repo/.release-please-manifest.json index e3112f8e85e1..52d52e73e5a8 100644 --- a/noir/noir-repo/.release-please-manifest.json +++ b/noir/noir-repo/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.0.0-beta.2" + ".": "1.0.0-beta.3" } diff --git a/noir/noir-repo/.vscode/settings.json b/noir/noir-repo/.vscode/settings.json index cd1c5f886dfa..7568da6a1af5 100644 --- a/noir/noir-repo/.vscode/settings.json +++ b/noir/noir-repo/.vscode/settings.json @@ -8,5 +8,11 @@ "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, + "[markdown]": { + "files.trimTrailingWhitespace": true + }, + "[yaml]": { + "files.trimTrailingWhitespace": true + }, "rust-analyzer.server.extraEnv": { "RUSTUP_TOOLCHAIN": "stable" } } diff --git a/noir/noir-repo/CHANGELOG.md b/noir/noir-repo/CHANGELOG.md index 31d87763a150..0c6662010c2a 100644 --- a/noir/noir-repo/CHANGELOG.md +++ b/noir/noir-repo/CHANGELOG.md @@ -1,5 +1,62 @@ # Changelog +## [1.0.0-beta.3](https://github.com/noir-lang/noir/compare/v1.0.0-beta.2...v1.0.0-beta.3) (2025-02-20) + + +### ⚠ BREAKING CHANGES + +* make `ResolverError::OracleMarkedAsConstrained` into a full error ([#7426](https://github.com/noir-lang/noir/issues/7426)) +* remove bigint from stdlib ([#7411](https://github.com/noir-lang/noir/issues/7411)) +* Only decrement the counter of an array if its address has not changed ([#7297](https://github.com/noir-lang/noir/issues/7297)) + +### Features + +* `FunctionDefinition::as_typed_expr` ([#7358](https://github.com/noir-lang/noir/issues/7358)) ([97afa52](https://github.com/noir-lang/noir/commit/97afa52f5212be2d05af26b9e8dde9c3ea7a1d2e)) +* **acir_field:** Add little-endian byte serialization for FieldElement ([#7258](https://github.com/noir-lang/noir/issues/7258)) ([f37eedc](https://github.com/noir-lang/noir/commit/f37eedca7f286187c71587797e08da189c4eee70)) +* Add native `u128` type ([#7301](https://github.com/noir-lang/noir/issues/7301)) ([8783e48](https://github.com/noir-lang/noir/commit/8783e480a39efb47c7783c68b0e09c1c65b1f33a)) +* Allow unquoting TraitConstraint in trait impl position ([#7395](https://github.com/noir-lang/noir/issues/7395)) ([40d2763](https://github.com/noir-lang/noir/commit/40d276328e568712fcb742c1c2ecdd975e5123b6)) +* **brillig:** Hoist shared constants across functions to the global space ([#7216](https://github.com/noir-lang/noir/issues/7216)) ([8652072](https://github.com/noir-lang/noir/commit/8652072920b3abc5990be5d57db27a7e66221252)) +* **ci:** Publish binaries for noir-profiler ([#7443](https://github.com/noir-lang/noir/issues/7443)) ([af5e4cd](https://github.com/noir-lang/noir/commit/af5e4cd1f200da6f21c543616dea9c188190d833)) +* **ci:** Release noir-inspector in binaries ([#7464](https://github.com/noir-lang/noir/issues/7464)) ([7a9c8c1](https://github.com/noir-lang/noir/commit/7a9c8c13f626575f23d25f9cb8689e44dc1c6116)) +* **cli:** Add `--target-dir` option ([#7350](https://github.com/noir-lang/noir/issues/7350)) ([1b6ba5d](https://github.com/noir-lang/noir/commit/1b6ba5d960239f8fa934d9543699eb86edd3c43b)) +* **cli:** Add noir-execute binary ([#7384](https://github.com/noir-lang/noir/issues/7384)) ([fdfe2bf](https://github.com/noir-lang/noir/commit/fdfe2bf752771b9611dc71953d50423b4ae7ec44)) +* **experimental:** Compile match expressions ([#7312](https://github.com/noir-lang/noir/issues/7312)) ([4c3dee1](https://github.com/noir-lang/noir/commit/4c3dee165f400124ba727a884455d83948f2006e)) +* **experimental:** Show macro errors where they happen ([#7333](https://github.com/noir-lang/noir/issues/7333)) ([04dd6d9](https://github.com/noir-lang/noir/commit/04dd6d9cdc906210e77cf755c711d45d04cb29aa)) +* LSP hover for integer literals ([#7368](https://github.com/noir-lang/noir/issues/7368)) ([967ab5f](https://github.com/noir-lang/noir/commit/967ab5f6e23b7e9f4503fe01d05b8a7a94ca70f6)) +* **LSP:** Auto-import via visible reexport ([#7409](https://github.com/noir-lang/noir/issues/7409)) ([9b03217](https://github.com/noir-lang/noir/commit/9b03217ff4b7f8eb13a43466bf9b2d761944c6f7)) +* Optimize FieldElement::num_bits ([#7147](https://github.com/noir-lang/noir/issues/7147)) ([44c35dc](https://github.com/noir-lang/noir/commit/44c35dc3ed5cceb34131bcb90622b0e3e0156b9c)) +* **performance:** Check sub operations against induction variables ([#7356](https://github.com/noir-lang/noir/issues/7356)) ([7cdce1f](https://github.com/noir-lang/noir/commit/7cdce1fef7e0fd63355fe6dc0993415bbb210ebf)) +* **performance:** Use unchecked ops based upon known induction variables ([#7344](https://github.com/noir-lang/noir/issues/7344)) ([10b377f](https://github.com/noir-lang/noir/commit/10b377fb4eb9284df66f5c0bd830f6d20ab2c003)) +* Remove bigint from stdlib ([#7411](https://github.com/noir-lang/noir/issues/7411)) ([31cc6a1](https://github.com/noir-lang/noir/commit/31cc6a1cf9ea0a02931ef60c71d4d41524f8b84c)) +* Require safety comments instead of safety doc comments ([#7295](https://github.com/noir-lang/noir/issues/7295)) ([e895feb](https://github.com/noir-lang/noir/commit/e895feb4e7b25530a22668bca597dfc78be92584)) +* Simplify assertions that squared values are equal to zero ([#7432](https://github.com/noir-lang/noir/issues/7432)) ([5d19109](https://github.com/noir-lang/noir/commit/5d19109b6a5738a97b745c6117cf0a1e9f1552bb)) +* While statement ([#7280](https://github.com/noir-lang/noir/issues/7280)) ([582f56e](https://github.com/noir-lang/noir/commit/582f56e6b6ea43ab79b08aacfe7f1ba67a097f26)) + + +### Bug Fixes + +* **brillig:** Brillig entry point analysis and function specialization through duplication ([#7277](https://github.com/noir-lang/noir/issues/7277)) ([119bf62](https://github.com/noir-lang/noir/commit/119bf620005b52362e3aca9321b69e96e8a42fc0)) +* **cli:** Only lock the packages selected in the workspace ([#7345](https://github.com/noir-lang/noir/issues/7345)) ([f0ce5c5](https://github.com/noir-lang/noir/commit/f0ce5c5a57bc4cd8b3b482a3b682e8d5c2605d5c)) +* Do not discard negative sign from field literals in comptime interpreter ([#7439](https://github.com/noir-lang/noir/issues/7439)) ([1d04f8b](https://github.com/noir-lang/noir/commit/1d04f8ba0292e493e4e64cf8caf6df15c51b3346)) +* Don't let nargo fmt produce multiple trailing newlines ([#7444](https://github.com/noir-lang/noir/issues/7444)) ([093a8ec](https://github.com/noir-lang/noir/commit/093a8ec60eaacd1b307447545e166a40e037436d)) +* Field zero division in brillig ([#7386](https://github.com/noir-lang/noir/issues/7386)) ([e73f8cd](https://github.com/noir-lang/noir/commit/e73f8cd669c13cdb792313b46dd4aa012c40a0ad)) +* Format global attributes ([#7401](https://github.com/noir-lang/noir/issues/7401)) ([b7ace68](https://github.com/noir-lang/noir/commit/b7ace682af1ab8a43308457302f08b151af342db)) +* Give "correct" error when trying to use AsTraitPath ([#7360](https://github.com/noir-lang/noir/issues/7360)) ([8f20392](https://github.com/noir-lang/noir/commit/8f20392cab7cca4abf0f1811204ce1a4229f827a)) +* Incorrect secondary file in LSP errors ([#7347](https://github.com/noir-lang/noir/issues/7347)) ([5d782f0](https://github.com/noir-lang/noir/commit/5d782f020f6aec6aaa8a445c3a6a5fb9b275e3c6)) +* Let LSP read `noirfmt.toml` for formatting files ([#7355](https://github.com/noir-lang/noir/issues/7355)) ([81b86e2](https://github.com/noir-lang/noir/commit/81b86e2a9bfe991bc0385118094656648a125587)) +* Only decrement the counter of an array if its address has not changed ([#7297](https://github.com/noir-lang/noir/issues/7297)) ([93d1740](https://github.com/noir-lang/noir/commit/93d17407f7170abbab7a6e9c8df6b39fb478ec18)) +* **performance:** Remove redundant slice access check from brillig ([#7434](https://github.com/noir-lang/noir/issues/7434)) ([49a095d](https://github.com/noir-lang/noir/commit/49a095ded5cd33795bcdac60cbd98ce7c5ab9198)) +* Prevent incorrect ACIRgen caused by noop truncations ([#7456](https://github.com/noir-lang/noir/issues/7456)) ([1fa9b33](https://github.com/noir-lang/noir/commit/1fa9b33aab796dbc2a61f3062bf80e120831b462)) +* Require loop/for/while body to be unit ([#7437](https://github.com/noir-lang/noir/issues/7437)) ([13a7309](https://github.com/noir-lang/noir/commit/13a7309e6e2aec58cce3e12d4dc5f9ce8eb08a67)) +* **ssa:** Accurately mark binary ops for hoisting and check Div/Mod against induction variable lower bound ([#7396](https://github.com/noir-lang/noir/issues/7396)) ([64890c0](https://github.com/noir-lang/noir/commit/64890c0d7420adb32d3867e51dd194e48b87bb32)) +* **ssa:** Do not deduplicate division by a zero constant ([#7393](https://github.com/noir-lang/noir/issues/7393)) ([38eeee3](https://github.com/noir-lang/noir/commit/38eeee39a98a62747dcca3b31b409151761d4ef1)) +* **ssa:** Make the lookback feature opt-in ([#7190](https://github.com/noir-lang/noir/issues/7190)) ([31becc6](https://github.com/noir-lang/noir/commit/31becc6863688dc9cadf15d2e9726aab9f2a0150)) + + +### Miscellaneous Chores + +* Make `ResolverError::OracleMarkedAsConstrained` into a full error ([#7426](https://github.com/noir-lang/noir/issues/7426)) ([40184eb](https://github.com/noir-lang/noir/commit/40184eb75d69153fb7849700ad10c53bf19cacf3)) + ## [1.0.0-beta.2](https://github.com/noir-lang/noir/compare/v1.0.0-beta.1...v1.0.0-beta.2) (2025-02-10) diff --git a/noir/noir-repo/CRITICAL_NOIR_LIBRARIES b/noir/noir-repo/CRITICAL_NOIR_LIBRARIES index 7637d9ac6df3..442f51949697 100644 --- a/noir/noir-repo/CRITICAL_NOIR_LIBRARIES +++ b/noir/noir-repo/CRITICAL_NOIR_LIBRARIES @@ -12,3 +12,6 @@ https://github.com/noir-lang/noir_string_search https://github.com/noir-lang/sparse_array https://github.com/noir-lang/noir_rsa https://github.com/noir-lang/noir_json_parser +https://github.com/noir-lang/sha256 +https://github.com/noir-lang/sha512 +https://github.com/noir-lang/keccak256 diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index 8b6912452f04..995f43567fc3 100644 --- a/noir/noir-repo/Cargo.lock +++ b/noir/noir-repo/Cargo.lock @@ -1,10 +1,10 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "acir" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir_field", "base64 0.21.7", @@ -26,12 +26,14 @@ dependencies = [ [[package]] name = "acir_field" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "ark-bls12-381", "ark-bn254 0.5.0", "ark-ff 0.5.0", + "ark-std 0.5.0", "cfg-if", + "criterion", "hex", "num-bigint", "proptest", @@ -40,7 +42,7 @@ dependencies = [ [[package]] name = "acvm" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "acvm_blackbox_solver", @@ -63,7 +65,7 @@ dependencies = [ [[package]] name = "acvm_blackbox_solver" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "blake2", @@ -101,7 +103,7 @@ dependencies = [ [[package]] name = "acvm_js" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "bn254_blackbox_solver", @@ -251,9 +253,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" [[package]] name = "ark-bls12-381" @@ -730,15 +732,16 @@ dependencies = [ [[package]] name = "blake3" -version = "1.5.5" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e" +checksum = "1230237285e3e10cde447185e8975408ae24deaa67205ce684805c25bc0c7937" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", + "memmap2", ] [[package]] @@ -765,7 +768,7 @@ dependencies = [ [[package]] name = "bn254_blackbox_solver" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "acvm_blackbox_solver", @@ -783,7 +786,7 @@ dependencies = [ [[package]] name = "brillig" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir_field", "serde", @@ -791,7 +794,7 @@ dependencies = [ [[package]] name = "brillig_vm" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "acvm_blackbox_solver", @@ -854,9 +857,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.13" +version = "1.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" +checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" dependencies = [ "shlex", ] @@ -875,9 +878,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.39" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" dependencies = [ "android-tzdata", "iana-time-zone", @@ -885,7 +888,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -917,9 +920,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.28" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff" +checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" dependencies = [ "clap_builder", "clap_derive", @@ -935,9 +938,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.27" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" +checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" dependencies = [ "anstream", "anstyle", @@ -947,9 +950,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.44" +version = "4.5.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375f9d8255adeeedd51053574fd8d4ba875ea5fa558e86617b07f09f1680c8b6" +checksum = "f5c5508ea23c5366f77e53f5a0070e5a84e51687ec3ef9e0464c86dc8d13ce98" dependencies = [ "clap", ] @@ -1288,9 +1291,9 @@ dependencies = [ [[package]] name = "csv-core" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" dependencies = [ "memchr", ] @@ -1511,9 +1514,9 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" [[package]] name = "elliptic-curve" @@ -1608,9 +1611,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" @@ -1731,12 +1734,12 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.35" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" dependencies = [ "crc32fast", - "miniz_oxide 0.8.3", + "miniz_oxide 0.8.5", ] [[package]] @@ -1750,7 +1753,7 @@ dependencies = [ [[package]] name = "fm" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "codespan-reporting", "iter-extended", @@ -1998,9 +2001,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" dependencies = [ "atomic-waker", "bytes", @@ -2554,7 +2557,7 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "iter-extended" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" [[package]] name = "itertools" @@ -2602,10 +2605,11 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.63" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -2796,9 +2800,9 @@ checksum = "82903360c009b816f5ab72a9b68158c27c301ee2c3f20655b55c5e589e7d3bb7" [[package]] name = "libc" -version = "0.2.169" +version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" [[package]] name = "libredox" @@ -2819,17 +2823,17 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.8.0", "libc", - "redox_syscall 0.5.8", + "redox_syscall 0.5.9", ] [[package]] name = "light-poseidon" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c9a85a9752c549ceb7578064b4ed891179d20acd85f27318573b64d2d7ee7ee" +checksum = "39e3d87542063daaccbfecd78b60f988079b6ec4e089249658b9455075c78d42" dependencies = [ - "ark-bn254 0.4.0", - "ark-ff 0.4.2", + "ark-bn254 0.5.0", + "ark-ff 0.5.0", "num-bigint", "thiserror", ] @@ -2858,9 +2862,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.25" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "louds-rs" @@ -2945,6 +2949,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d97bbf43eb4f088f8ca469930cde17fa036207c9a5e02ccc5107c4e8b17c964" +[[package]] +name = "minicov" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" +dependencies = [ + "cc", + "walkdir", +] + [[package]] name = "miniz_oxide" version = "0.7.4" @@ -2956,9 +2970,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", ] @@ -2988,7 +3002,7 @@ dependencies = [ [[package]] name = "nargo" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "fm", @@ -3013,12 +3027,10 @@ dependencies = [ [[package]] name = "nargo_cli" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", - "ark-bn254 0.4.0", "ark-bn254 0.5.0", - "ark-ff 0.4.2", "assert_cmd", "assert_fs", "async-lsp", @@ -3042,6 +3054,7 @@ dependencies = [ "nargo", "nargo_fmt", "nargo_toml", + "noir_artifact_cli", "noir_debugger", "noir_lsp", "noirc_abi", @@ -3079,8 +3092,9 @@ dependencies = [ [[package]] name = "nargo_fmt" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ + "noirc_errors", "noirc_frontend", "serde", "similar-asserts", @@ -3090,7 +3104,7 @@ dependencies = [ [[package]] name = "nargo_toml" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "dirs", "fm", @@ -3157,7 +3171,7 @@ dependencies = [ [[package]] name = "noir_artifact_cli" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "acvm", @@ -3181,7 +3195,7 @@ dependencies = [ [[package]] name = "noir_debugger" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "assert_cmd", @@ -3205,7 +3219,7 @@ dependencies = [ [[package]] name = "noir_fuzzer" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "noirc_abi", @@ -3216,12 +3230,13 @@ dependencies = [ [[package]] name = "noir_inspector" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "clap", "color-eyre", "const_format", + "noir_artifact_cli", "noirc_artifacts", "noirc_artifacts_info", "serde", @@ -3230,7 +3245,7 @@ dependencies = [ [[package]] name = "noir_lsp" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "async-lsp", @@ -3238,6 +3253,7 @@ dependencies = [ "convert_case", "fm", "fxhash", + "iter-extended", "lsp-types 0.94.1", "nargo", "nargo_fmt", @@ -3258,7 +3274,7 @@ dependencies = [ [[package]] name = "noir_profiler" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "bn254_blackbox_solver", @@ -3270,6 +3286,7 @@ dependencies = [ "im", "inferno", "nargo", + "noir_artifact_cli", "noirc_abi", "noirc_artifacts", "noirc_driver", @@ -3278,13 +3295,14 @@ dependencies = [ "serde", "serde_json", "tempfile", + "thiserror", "tracing-appender", "tracing-subscriber", ] [[package]] name = "noir_wasm" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "build-data", @@ -3308,7 +3326,7 @@ dependencies = [ [[package]] name = "noirc_abi" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "iter-extended", @@ -3327,7 +3345,7 @@ dependencies = [ [[package]] name = "noirc_abi_wasm" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "build-data", @@ -3344,11 +3362,11 @@ dependencies = [ [[package]] name = "noirc_arena" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" [[package]] name = "noirc_artifacts" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "codespan-reporting", @@ -3363,7 +3381,7 @@ dependencies = [ [[package]] name = "noirc_artifacts_info" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acir", "acvm", @@ -3378,7 +3396,7 @@ dependencies = [ [[package]] name = "noirc_driver" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "build-data", @@ -3397,7 +3415,7 @@ dependencies = [ [[package]] name = "noirc_errors" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "base64 0.21.7", @@ -3414,7 +3432,7 @@ dependencies = [ [[package]] name = "noirc_evaluator" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "bn254_blackbox_solver", @@ -3444,7 +3462,7 @@ dependencies = [ [[package]] name = "noirc_frontend" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "base64 0.21.7", @@ -3476,7 +3494,7 @@ dependencies = [ [[package]] name = "noirc_printable_type" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" dependencies = [ "acvm", "proptest", @@ -3687,7 +3705,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.8", + "redox_syscall 0.5.9", "smallvec", "windows-targets 0.52.6", ] @@ -4137,9 +4155,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" dependencies = [ "bitflags 2.8.0", ] @@ -4240,15 +4258,14 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.8" +version = "0.17.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "da5349ae27d3887ca812fb375b45a4fbb36d8d12d2df394968cd86e35683fe73" dependencies = [ "cc", "cfg-if", "getrandom 0.2.15", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] @@ -4335,9 +4352,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.22" +version = "0.23.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb9263ab4eb695e42321db096e3b8fbd715a59b154d5c88d82db2175b681ba7" +checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" dependencies = [ "log", "once_cell", @@ -4543,12 +4560,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -4601,9 +4612,9 @@ checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" [[package]] name = "serde" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] @@ -4644,9 +4655,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", @@ -4655,9 +4666,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.138" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" dependencies = [ "itoa", "memchr", @@ -4790,9 +4801,9 @@ dependencies = [ [[package]] name = "similar-asserts" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f08357795f0d604ea7d7130f22c74b03838c959bdb14adde3142aab4d18a293" +checksum = "b5b441962c817e33508847a22bd82f03a30cff43642dc2fae8b050566121eb9a" dependencies = [ "console", "similar", @@ -4834,9 +4845,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" dependencies = [ "serde", ] @@ -5023,9 +5034,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.16.0" +version = "3.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" +checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" dependencies = [ "cfg-if", "fastrand", @@ -5313,7 +5324,7 @@ checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap 2.7.1", "toml_datetime", - "winnow 0.7.2", + "winnow 0.7.3", ] [[package]] @@ -5491,9 +5502,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unarray" @@ -5503,9 +5514,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" [[package]] name = "unicode-linebreak" @@ -5569,9 +5580,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" +checksum = "bd8dcafa1ca14750d8d7a05aa05988c17aab20886e1f3ae33a40223c58d92ef7" [[package]] name = "valuable" @@ -5655,11 +5666,13 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.86" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "serde", "serde_json", "wasm-bindgen-macro", @@ -5667,13 +5680,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.86" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn 2.0.98", @@ -5682,21 +5694,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.36" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.86" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5704,9 +5717,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.86" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -5717,19 +5730,21 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.86" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-bindgen-test" -version = "0.3.36" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e636f3a428ff62b3742ebc3c70e254dfe12b8c2b469d688ea59cdd4abcf502" +checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3" dependencies = [ - "console_error_panic_hook", "js-sys", - "scoped-tls", + "minicov", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", @@ -5737,19 +5752,20 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.36" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f18c1fad2f7c4958e7bcce014fa212f59a65d5e3721d0f77e6c0b27ede936ba3" +checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", + "syn 2.0.98", ] [[package]] name = "web-sys" -version = "0.3.63" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -5804,6 +5820,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-link" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" + [[package]] name = "windows-sys" version = "0.48.0" @@ -5963,9 +5985,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" +checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" dependencies = [ "memchr", ] diff --git a/noir/noir-repo/Cargo.toml b/noir/noir-repo/Cargo.toml index c91eb816083f..9c5bf1351d1c 100644 --- a/noir/noir-repo/Cargo.toml +++ b/noir/noir-repo/Cargo.toml @@ -49,11 +49,11 @@ resolver = "2" [workspace.package] # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors = ["The Noir Team "] -edition = "2021" -rust-version = "1.75.0" +edition = "2024" +rust-version = "1.85.0" license = "MIT OR Apache-2.0" repository = "https://github.com/noir-lang/noir/" @@ -66,13 +66,13 @@ unused_qualifications = "warn" [workspace.dependencies] # ACVM workspace dependencies -acir_field = { version = "1.0.0-beta.2", path = "acvm-repo/acir_field", default-features = false } -acir = { version = "1.0.0-beta.2", path = "acvm-repo/acir", default-features = false } -acvm = { version = "1.0.0-beta.2", path = "acvm-repo/acvm" } -brillig = { version = "1.0.0-beta.2", path = "acvm-repo/brillig", default-features = false } -brillig_vm = { version = "1.0.0-beta.2", path = "acvm-repo/brillig_vm", default-features = false } -acvm_blackbox_solver = { version = "1.0.0-beta.2", path = "acvm-repo/blackbox_solver", default-features = false } -bn254_blackbox_solver = { version = "1.0.0-beta.2", path = "acvm-repo/bn254_blackbox_solver", default-features = false } +acir_field = { version = "1.0.0-beta.3", path = "acvm-repo/acir_field", default-features = false } +acir = { version = "1.0.0-beta.3", path = "acvm-repo/acir", default-features = false } +acvm = { version = "1.0.0-beta.3", path = "acvm-repo/acvm" } +brillig = { version = "1.0.0-beta.3", path = "acvm-repo/brillig", default-features = false } +brillig_vm = { version = "1.0.0-beta.3", path = "acvm-repo/brillig_vm", default-features = false } +acvm_blackbox_solver = { version = "1.0.0-beta.3", path = "acvm-repo/blackbox_solver", default-features = false } +bn254_blackbox_solver = { version = "1.0.0-beta.3", path = "acvm-repo/bn254_blackbox_solver", default-features = false } # Noir compiler workspace dependencies fm = { path = "compiler/fm" } @@ -116,9 +116,9 @@ lsp-types = "0.94.1" tower = "0.4" # Wasm -wasm-bindgen = { version = "=0.2.86", features = ["serde-serialize"] } -wasm-bindgen-test = "0.3.36" -wasm-bindgen-futures = "0.4.36" +wasm-bindgen = { version = "=0.2.100", features = ["serde-serialize"] } +wasm-bindgen-test = "0.3.50" +wasm-bindgen-futures = "0.4.50" console_error_panic_hook = "0.1.7" gloo-utils = { version = "0.1", features = ["serde"] } js-sys = "0.3.62" diff --git a/noir/noir-repo/EXTERNAL_NOIR_LIBRARIES.yml b/noir/noir-repo/EXTERNAL_NOIR_LIBRARIES.yml index fcb6885c1659..1d86c3e5d5b1 100644 --- a/noir/noir-repo/EXTERNAL_NOIR_LIBRARIES.yml +++ b/noir/noir-repo/EXTERNAL_NOIR_LIBRARIES.yml @@ -1,4 +1,4 @@ -define: &AZ_COMMIT 1350f93c3e9af8f601ca67ca3e67d0127c9767b6 +define: &AZ_COMMIT 3b981f9217f9b859bdfbcdba2f5c080392c98da6 libraries: noir_check_shuffle: repo: noir-lang/noir_check_shuffle @@ -23,13 +23,13 @@ libraries: timeout: 2 noir-bignum: repo: noir-lang/noir-bignum - timeout: 380 + timeout: 90 noir_bigcurve: repo: noir-lang/noir_bigcurve - timeout: 330 + timeout: 250 noir_base64: repo: noir-lang/noir_base64 - timeout: 2 + timeout: 5 noir_string_search: repo: noir-lang/noir_string_search timeout: 2 @@ -45,6 +45,12 @@ libraries: sha256: repo: noir-lang/sha256 timeout: 3 + sha512: + repo: noir-lang/sha512 + timeout: 30 + keccak256: + repo: noir-lang/keccak256 + timeout: 3 aztec_nr: repo: AztecProtocol/aztec-packages ref: *AZ_COMMIT diff --git a/noir/noir-repo/README.md b/noir/noir-repo/README.md index c2e41435b660..20f1f80d83ae 100644 --- a/noir/noir-repo/README.md +++ b/noir/noir-repo/README.md @@ -34,7 +34,7 @@ The current focus is to gather as much feedback as possible while in the alpha p ## Minimum Rust version -This workspace's minimum supported rustc version is 1.75.0. +This workspace's minimum supported rustc version is 1.85.0. ## License diff --git a/noir/noir-repo/acvm-repo/acir/Cargo.toml b/noir/noir-repo/acvm-repo/acir/Cargo.toml index de55860762e5..2b15c2abf095 100644 --- a/noir/noir-repo/acvm-repo/acir/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir/Cargo.toml @@ -2,7 +2,7 @@ name = "acir" description = "ACIR is the IR that the VM processes, it is analogous to LLVM IR" # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/acir/benches/serialization.rs b/noir/noir-repo/acvm-repo/acir/benches/serialization.rs index dd6a5c8b1cf3..2725b3e1dd0c 100644 --- a/noir/noir-repo/acvm-repo/acir/benches/serialization.rs +++ b/noir/noir-repo/acvm-repo/acir/benches/serialization.rs @@ -1,10 +1,10 @@ -use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; +use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main}; use std::{collections::BTreeSet, time::Duration}; use acir::{ + FieldElement, circuit::{Circuit, ExpressionWidth, Opcode, Program, PublicInputs}, native_types::{Expression, Witness}, - FieldElement, }; use pprof::criterion::{Output, PProfProfiler}; 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 091a3dcb0e52..68c3c832b5cd 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs @@ -11,7 +11,7 @@ use std::{io::prelude::*, num::ParseIntError, str::FromStr}; use base64::Engine; use flate2::Compression; -use serde::{de::Error as DeserializationError, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as DeserializationError}; use std::collections::BTreeSet; @@ -252,7 +252,7 @@ impl Program { program_bytes } - // Serialize and base64 encode program + /// Serialize and base64 encode program pub fn serialize_program_base64(program: &Self, s: S) -> Result where S: Serializer, @@ -277,7 +277,7 @@ impl Deserialize<'a>> Program { Program::read(serialized_circuit) } - // Deserialize and base64 decode program + /// Deserialize and base64 decode program pub fn deserialize_program_base64<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, @@ -375,8 +375,8 @@ mod tests { use std::collections::BTreeSet; use super::{ - opcodes::{BlackBoxFuncCall, FunctionInput}, Circuit, Compression, Opcode, PublicInputs, + opcodes::{BlackBoxFuncCall, FunctionInput}, }; use crate::{ circuit::{ExpressionWidth, Program}, diff --git a/noir/noir-repo/acvm-repo/acir/src/lib.rs b/noir/noir-repo/acvm-repo/acir/src/lib.rs index bb5b50c0daf2..e49ab60f9e06 100644 --- a/noir/noir-repo/acvm-repo/acir/src/lib.rs +++ b/noir/noir-repo/acvm-repo/acir/src/lib.rs @@ -41,10 +41,10 @@ mod reflection { use crate::{ circuit::{ - brillig::{BrilligInputs, BrilligOutputs}, - opcodes::{BlackBoxFuncCall, BlockType, ConstantOrWitnessEnum, FunctionInput}, AssertionPayload, Circuit, ExpressionOrMemory, ExpressionWidth, Opcode, OpcodeLocation, Program, + brillig::{BrilligInputs, BrilligOutputs}, + opcodes::{BlackBoxFuncCall, BlockType, ConstantOrWitnessEnum, FunctionInput}, }, native_types::{Witness, WitnessMap, WitnessStack}, }; diff --git a/noir/noir-repo/acvm-repo/acir/src/native_types/expression/ordering.rs b/noir/noir-repo/acvm-repo/acir/src/native_types/expression/ordering.rs index f1d217b3bd9d..968dc310bc11 100644 --- a/noir/noir-repo/acvm-repo/acir/src/native_types/expression/ordering.rs +++ b/noir/noir-repo/acvm-repo/acir/src/native_types/expression/ordering.rs @@ -85,11 +85,7 @@ impl Expression { fn cmp_max(m1: Option, m2: Option) -> Ordering { if let Some(m1) = m1 { - if let Some(m2) = m2 { - m1.cmp(&m2) - } else { - Ordering::Greater - } + if let Some(m2) = m2 { m1.cmp(&m2) } else { Ordering::Greater } } else if m2.is_some() { Ordering::Less } else { diff --git a/noir/noir-repo/acvm-repo/acir/src/native_types/witness_map.rs b/noir/noir-repo/acvm-repo/acir/src/native_types/witness_map.rs index e508fe5b1861..77745c714a39 100644 --- a/noir/noir-repo/acvm-repo/acir/src/native_types/witness_map.rs +++ b/noir/noir-repo/acvm-repo/acir/src/native_types/witness_map.rs @@ -1,12 +1,12 @@ use std::{ - collections::{btree_map, BTreeMap}, + collections::{BTreeMap, btree_map}, io::Read, ops::Index, }; +use flate2::Compression; use flate2::bufread::GzDecoder; use flate2::bufread::GzEncoder; -use flate2::Compression; use serde::{Deserialize, Serialize}; use thiserror::Error; diff --git a/noir/noir-repo/acvm-repo/acir/src/native_types/witness_stack.rs b/noir/noir-repo/acvm-repo/acir/src/native_types/witness_stack.rs index 8a4fffa15772..6338ad630d6c 100644 --- a/noir/noir-repo/acvm-repo/acir/src/native_types/witness_stack.rs +++ b/noir/noir-repo/acvm-repo/acir/src/native_types/witness_stack.rs @@ -1,8 +1,8 @@ use std::io::Read; +use flate2::Compression; use flate2::bufread::GzDecoder; use flate2::bufread::GzEncoder; -use flate2::Compression; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -12,6 +12,9 @@ use super::WitnessMap; enum SerializationError { #[error(transparent)] Deflate(#[from] std::io::Error), + + #[error(transparent)] + BincodeError(#[from] bincode::Error), } #[derive(Debug, Error)] @@ -57,11 +60,11 @@ impl From> for WitnessStack { } } -impl TryFrom> for Vec { +impl TryFrom<&WitnessStack> for Vec { type Error = WitnessStackError; - fn try_from(val: WitnessStack) -> Result { - let buf = bincode::serialize(&val).unwrap(); + fn try_from(val: &WitnessStack) -> Result { + let buf = bincode::serialize(val).map_err(|e| WitnessStackError(e.into()))?; let mut deflater = GzEncoder::new(buf.as_slice(), Compression::best()); let mut buf_c = Vec::new(); deflater.read_to_end(&mut buf_c).map_err(|err| WitnessStackError(err.into()))?; @@ -69,6 +72,14 @@ impl TryFrom> for Vec { } } +impl TryFrom> for Vec { + type Error = WitnessStackError; + + fn try_from(val: WitnessStack) -> Result { + Self::try_from(&val) + } +} + impl Deserialize<'a>> TryFrom<&[u8]> for WitnessStack { type Error = WitnessStackError; @@ -76,7 +87,8 @@ impl Deserialize<'a>> TryFrom<&[u8]> for WitnessStack { let mut deflater = GzDecoder::new(bytes); let mut buf_d = Vec::new(); deflater.read_to_end(&mut buf_d).map_err(|err| WitnessStackError(err.into()))?; - let witness_stack = bincode::deserialize(&buf_d).unwrap(); + let witness_stack = + bincode::deserialize(&buf_d).map_err(|e| WitnessStackError(e.into()))?; Ok(witness_stack) } } diff --git a/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs b/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs index 305d94abcee2..4ff571106a1b 100644 --- a/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs +++ b/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs @@ -13,9 +13,9 @@ use std::collections::BTreeSet; use acir::{ circuit::{ + Circuit, Opcode, Program, PublicInputs, brillig::{BrilligBytecode, BrilligFunctionId, BrilligInputs, BrilligOutputs}, opcodes::{AcirFunctionId, BlackBoxFuncCall, BlockId, FunctionInput, MemOp}, - Circuit, Opcode, Program, PublicInputs, }, native_types::{Expression, Witness}, }; diff --git a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml index bc4be250cfd4..719d2585485b 100644 --- a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml @@ -2,7 +2,7 @@ name = "acir_field" description = "The field implementation being used by ACIR." # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true @@ -23,12 +23,18 @@ serde.workspace = true ark-bn254.workspace = true ark-bls12-381 = { workspace = true, optional = true } ark-ff.workspace = true +ark-std.workspace = true cfg-if.workspace = true [dev-dependencies] proptest.workspace = true +criterion.workspace = true [features] bn254 = [] bls12_381 = ["dep:ark-bls12-381"] + +[[bench]] +name = "field_element" +harness = false diff --git a/noir/noir-repo/acvm-repo/acir_field/benches/field_element.rs b/noir/noir-repo/acvm-repo/acir_field/benches/field_element.rs new file mode 100644 index 000000000000..8560c10cd040 --- /dev/null +++ b/noir/noir-repo/acvm-repo/acir_field/benches/field_element.rs @@ -0,0 +1,11 @@ +use acir_field::{AcirField, FieldElement}; +use criterion::{Criterion, criterion_group, criterion_main}; +use std::hint::black_box; + +fn criterion_benchmark(c: &mut Criterion) { + let field_element = FieldElement::from(123456789_u128); + c.bench_function("FieldElement::num_bits", |b| b.iter(|| black_box(field_element).num_bits())); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs index 8afc76da9d89..fdac33836ad8 100644 --- a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs +++ b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs @@ -1,5 +1,6 @@ use ark_ff::PrimeField; use ark_ff::Zero; +use ark_std::io::Write; use num_bigint::BigUint; use serde::{Deserialize, Serialize}; use std::borrow::Cow; @@ -111,11 +112,7 @@ impl From for FieldElement { impl From for FieldElement { fn from(boolean: bool) -> FieldElement { - if boolean { - FieldElement::one() - } else { - FieldElement::zero() - } + if boolean { FieldElement::one() } else { FieldElement::zero() } } } @@ -182,11 +179,7 @@ impl AcirField for FieldElement { /// For example, a max bit size of 254 would give a max byte size of 32. fn max_num_bytes() -> u32 { let num_bytes = Self::max_num_bits() / 8; - if Self::max_num_bits() % 8 == 0 { - num_bytes - } else { - num_bytes + 1 - } + if Self::max_num_bits() % 8 == 0 { num_bytes } else { num_bytes + 1 } } fn modulus() -> BigUint { @@ -195,26 +188,9 @@ impl AcirField for FieldElement { /// This is the number of bits required to represent this specific field element fn num_bits(&self) -> u32 { - let bytes = self.to_be_bytes(); - - // Iterate through the byte decomposition and pop off all leading zeroes - let mut iter = bytes.iter().skip_while(|x| (**x) == 0); - - // The first non-zero byte in the decomposition may have some leading zero-bits. - let Some(head_byte) = iter.next() else { - // If we don't have a non-zero byte then the field element is zero, - // which we consider to require a single bit to represent. - return 1; - }; - let num_bits_for_head_byte = head_byte.ilog2(); - - // Each remaining byte in the byte decomposition requires 8 bits. - // - // Note: count will panic if it goes over usize::MAX. - // This may not be suitable for devices whose usize < u16 - let tail_length = iter.count() as u32; - - 8 * tail_length + num_bits_for_head_byte + 1 + let mut bit_counter = BitCounter::default(); + self.0.serialize_uncompressed(&mut bit_counter).unwrap(); + bit_counter.bits() } fn to_u128(self) -> u128 { @@ -363,6 +339,52 @@ impl SubAssign for FieldElement { } } +#[derive(Default, Debug)] +struct BitCounter { + /// Total number of non-zero bytes we found. + count: usize, + /// Total bytes we found. + total: usize, + /// The last non-zero byte we found. + head_byte: u8, +} + +impl BitCounter { + fn bits(&self) -> u32 { + // If we don't have a non-zero byte then the field element is zero, + // which we consider to require a single bit to represent. + if self.count == 0 { + return 1; + } + + let num_bits_for_head_byte = self.head_byte.ilog2(); + + // Each remaining byte in the byte decomposition requires 8 bits. + // + // Note: count will panic if it goes over usize::MAX. + // This may not be suitable for devices whose usize < u16 + let tail_length = (self.count - 1) as u32; + 8 * tail_length + num_bits_for_head_byte + 1 + } +} + +impl Write for BitCounter { + fn write(&mut self, buf: &[u8]) -> ark_std::io::Result { + for byte in buf { + self.total += 1; + if *byte != 0 { + self.count = self.total; + self.head_byte = *byte; + } + } + Ok(buf.len()) + } + + fn flush(&mut self) -> ark_std::io::Result<()> { + Ok(()) + } +} + #[cfg(test)] mod tests { use super::{AcirField, FieldElement}; diff --git a/noir/noir-repo/acvm-repo/acvm/Cargo.toml b/noir/noir-repo/acvm-repo/acvm/Cargo.toml index 902918475138..53e220db23e5 100644 --- a/noir/noir-repo/acvm-repo/acvm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm" description = "The virtual machine that processes ACIR given a backend/proof system." # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/mod.rs index ee84f7bf60bd..ee503e7e0d05 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/mod.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; use acir::{ - circuit::{AssertionPayload, Circuit, ExpressionWidth, OpcodeLocation}, AcirField, + circuit::{AssertionPayload, Circuit, ExpressionWidth, OpcodeLocation}, }; // The various passes that we can use over ACIR @@ -14,7 +14,7 @@ pub use optimizers::optimize; use optimizers::optimize_internal; pub use simulator::CircuitSimulator; use transformers::transform_internal; -pub use transformers::{transform, MIN_EXPRESSION_WIDTH}; +pub use transformers::{MIN_EXPRESSION_WIDTH, transform}; /// This module moves and decomposes acir opcodes. The transformation map allows consumers of this module to map /// metadata they had about the opcodes to the new opcode structure generated after the transformation. diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/general.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/general.rs index 39a01a38cace..0802f33185cc 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/general.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/general.rs @@ -1,6 +1,6 @@ use acir::{ - native_types::{Expression, Witness}, AcirField, + native_types::{Expression, Witness}, }; use indexmap::IndexMap; diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/merge_expressions.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/merge_expressions.rs index e95c6207c3c3..2590c5f208aa 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/merge_expressions.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/merge_expressions.rs @@ -1,13 +1,13 @@ use std::collections::{BTreeMap, BTreeSet, HashMap}; use acir::{ + AcirField, circuit::{ + Circuit, Opcode, brillig::{BrilligInputs, BrilligOutputs}, opcodes::BlockId, - Circuit, Opcode, }, native_types::{Expression, Witness}, - AcirField, }; use crate::compiler::CircuitSimulator; @@ -258,16 +258,16 @@ impl MergeExpressionsOptimizer { #[cfg(test)] mod tests { - use crate::compiler::{optimizers::MergeExpressionsOptimizer, CircuitSimulator}; + use crate::compiler::{CircuitSimulator, optimizers::MergeExpressionsOptimizer}; use acir::{ + FieldElement, acir_field::AcirField, circuit::{ + Circuit, ExpressionWidth, Opcode, PublicInputs, brillig::{BrilligFunctionId, BrilligOutputs}, opcodes::{BlackBoxFuncCall, FunctionInput}, - Circuit, ExpressionWidth, Opcode, PublicInputs, }, native_types::{Expression, Witness}, - FieldElement, }; use std::collections::BTreeSet; diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/mod.rs index 3531825c709d..3e085e4ba9b5 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/mod.rs @@ -1,6 +1,6 @@ use acir::{ - circuit::{Circuit, Opcode}, AcirField, + circuit::{Circuit, Opcode}, }; // mod constant_backpropagation; @@ -17,7 +17,7 @@ use tracing::info; // use self::constant_backpropagation::ConstantBackpropagationOptimizer; use self::unused_memory::UnusedMemoryOptimizer; -use super::{transform_assert_messages, AcirTransformationMap}; +use super::{AcirTransformationMap, transform_assert_messages}; /// Applies [`ProofSystemCompiler`][crate::ProofSystemCompiler] independent optimizations to a [`Circuit`]. pub fn optimize(acir: Circuit) -> (Circuit, AcirTransformationMap) { diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs index f9c715a277f4..67dce75411e8 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs @@ -1,10 +1,10 @@ use acir::{ + AcirField, circuit::{ - opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum}, Circuit, Opcode, + opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum}, }, native_types::Witness, - AcirField, }; use std::collections::{BTreeMap, HashSet}; @@ -163,12 +163,12 @@ mod tests { use crate::compiler::optimizers::redundant_range::RangeOptimizer; use acir::{ + FieldElement, circuit::{ - opcodes::{BlackBoxFuncCall, FunctionInput}, Circuit, ExpressionWidth, Opcode, PublicInputs, + opcodes::{BlackBoxFuncCall, FunctionInput}, }, native_types::{Expression, Witness}, - FieldElement, }; fn test_circuit(ranges: Vec<(Witness, u32)>) -> Circuit { diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/unused_memory.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/unused_memory.rs index 1325a3b03cdf..8b7e52d66f2c 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/unused_memory.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/unused_memory.rs @@ -1,4 +1,4 @@ -use acir::circuit::{brillig::BrilligInputs, opcodes::BlockId, Circuit, Opcode}; +use acir::circuit::{Circuit, Opcode, brillig::BrilligInputs, opcodes::BlockId}; use std::collections::HashSet; /// `UnusedMemoryOptimizer` will remove initializations of memory blocks which are unused. diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/simulator.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/simulator.rs index 893195f342a9..96134926f5e0 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/simulator.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/simulator.rs @@ -1,11 +1,11 @@ use acir::{ + AcirField, circuit::{ + Circuit, Opcode, brillig::{BrilligInputs, BrilligOutputs}, opcodes::{BlockId, FunctionInput}, - Circuit, Opcode, }, native_types::{Expression, Witness}, - AcirField, }; use std::collections::{BTreeSet, HashMap, HashSet}; @@ -215,10 +215,10 @@ mod tests { use crate::compiler::CircuitSimulator; use acir::{ + FieldElement, acir_field::AcirField, circuit::{Circuit, ExpressionWidth, Opcode, PublicInputs}, native_types::{Expression, Witness}, - FieldElement, }; fn test_circuit( 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 bdd6998835a2..8f890e4ca86b 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 @@ -1,8 +1,8 @@ use std::{cmp::Ordering, collections::HashSet}; use acir::{ - native_types::{Expression, Witness}, AcirField, + native_types::{Expression, Witness}, }; use indexmap::IndexMap; @@ -201,7 +201,7 @@ impl CSatTransformer { // Now we have used up 2 spaces in our assert-zero opcode. The width now dictates, how many more we can add let mut remaining_space = self.width - 2 - 1; // We minus 1 because we need an extra space to contain the intermediate variable - // Keep adding terms until we have no more left, or we reach the width + // Keep adding terms until we have no more left, or we reach the width let mut remaining_linear_terms = Vec::with_capacity(opcode.linear_combinations.len()); while remaining_space > 0 { diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/transformers/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/transformers/mod.rs index 77a5d2da34e0..fe9404b075c8 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/transformers/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/transformers/mod.rs @@ -1,12 +1,11 @@ use acir::{ + AcirField, circuit::{ - self, + self, Circuit, ExpressionWidth, Opcode, brillig::{BrilligInputs, BrilligOutputs}, opcodes::{BlackBoxFuncCall, FunctionInput, MemOp}, - Circuit, ExpressionWidth, Opcode, }, native_types::{Expression, Witness}, - AcirField, }; use indexmap::IndexMap; @@ -17,8 +16,9 @@ pub use csat::MIN_EXPRESSION_WIDTH; use tracing::info; use super::{ + AcirTransformationMap, optimizers::{MergeExpressionsOptimizer, RangeOptimizer}, - transform_assert_messages, AcirTransformationMap, + transform_assert_messages, }; /// We need multiple passes to stabilize the output. diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/arithmetic.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/arithmetic.rs index 5eeabd8a8332..a2921bcbc9b1 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/arithmetic.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/arithmetic.rs @@ -1,9 +1,9 @@ use acir::{ - native_types::{Expression, Witness, WitnessMap}, AcirField, + native_types::{Expression, Witness, WitnessMap}, }; -use super::{insert_value, ErrorLocation, OpcodeNotSolvable, OpcodeResolutionError}; +use super::{ErrorLocation, OpcodeNotSolvable, OpcodeResolutionError, insert_value}; /// An Expression solver will take a Circuit's assert-zero opcodes with witness assignments /// and create the other witness variables @@ -254,7 +254,52 @@ mod tests { use acir::FieldElement; #[test] - fn expression_solver_smoke_test() { + fn solves_simple_assignment() { + let a = Witness(0); + + // a - 1 == 0; + let opcode_a = Expression { + mul_terms: vec![], + linear_combinations: vec![(FieldElement::one(), a)], + q_c: -FieldElement::one(), + }; + + let mut values = WitnessMap::new(); + assert_eq!(ExpressionSolver::solve(&mut values, &opcode_a), Ok(())); + + assert_eq!(values.get(&a).unwrap(), &FieldElement::from(1_i128)); + } + + #[test] + fn solves_unknown_in_mul_term() { + let a = Witness(0); + let b = Witness(1); + let c = Witness(2); + let d = Witness(3); + + // a * b - b - c - d == 0; + let opcode_a = Expression { + mul_terms: vec![(FieldElement::one(), a, b)], + linear_combinations: vec![ + (-FieldElement::one(), b), + (-FieldElement::one(), c), + (-FieldElement::one(), d), + ], + q_c: FieldElement::zero(), + }; + + let mut values = WitnessMap::new(); + values.insert(b, FieldElement::from(2_i128)); + values.insert(c, FieldElement::from(1_i128)); + values.insert(d, FieldElement::from(1_i128)); + + assert_eq!(ExpressionSolver::solve(&mut values, &opcode_a), Ok(())); + + assert_eq!(values.get(&a).unwrap(), &FieldElement::from(2_i128)); + } + + #[test] + fn solves_unknown_in_linear_term() { let a = Witness(0); let b = Witness(1); let c = Witness(2); diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/aes128.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/aes128.rs index e3c8dc78aa62..e241a39f5af7 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/aes128.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/aes128.rs @@ -1,11 +1,11 @@ use acir::{ + AcirField, circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, - AcirField, }; use acvm_blackbox_solver::aes128_encrypt; -use crate::{pwg::insert_value, OpcodeResolutionError}; +use crate::{OpcodeResolutionError, pwg::insert_value}; use super::utils::{to_u8_array, to_u8_vec}; diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/bigint.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/bigint.rs index eb5a6f81e7d5..1c5a436fa110 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/bigint.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/bigint.rs @@ -1,8 +1,8 @@ use crate::pwg::input_to_value; use acir::{ + AcirField, BlackBoxFunc, circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, - AcirField, BlackBoxFunc, }; use acvm_blackbox_solver::BigIntSolver; diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs index 9e5115712751..2c728e77304c 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs @@ -1,11 +1,11 @@ use acir::{ + AcirField, circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, - AcirField, }; use acvm_blackbox_solver::BlackBoxFunctionSolver; -use crate::pwg::{input_to_value, insert_value, OpcodeResolutionError}; +use crate::pwg::{OpcodeResolutionError, input_to_value, insert_value}; pub(super) fn multi_scalar_mul( backend: &impl BlackBoxFunctionSolver, diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/hash.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/hash.rs index 7476b0dc2dcd..a4af9de55cfe 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/hash.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/hash.rs @@ -1,12 +1,12 @@ use acir::{ + AcirField, circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, - AcirField, }; -use acvm_blackbox_solver::{sha256_compression, BlackBoxFunctionSolver, BlackBoxResolutionError}; +use acvm_blackbox_solver::{BlackBoxFunctionSolver, BlackBoxResolutionError, sha256_compression}; -use crate::pwg::{input_to_value, insert_value}; use crate::OpcodeResolutionError; +use crate::pwg::{input_to_value, insert_value}; /// Attempts to solve a 256 bit hash function opcode. /// If successful, `initial_witness` will be mutated to contain the new witness assignment. @@ -49,9 +49,13 @@ fn get_hash_input( // in the message, then we error. if num_bytes_to_take > message_input.len() { return Err(OpcodeResolutionError::BlackBoxFunctionFailed( - acir::BlackBoxFunc::Blake2s, - format!("the number of bytes to take from the message is more than the number of bytes in the message. {} > {}", num_bytes_to_take, message_input.len()), - )); + acir::BlackBoxFunc::Blake2s, + format!( + "the number of bytes to take from the message is more than the number of bytes in the message. {} > {}", + num_bytes_to_take, + message_input.len() + ), + )); } let truncated_message = message_input[0..num_bytes_to_take].to_vec(); Ok(truncated_message) @@ -132,11 +136,10 @@ pub(crate) fn solve_poseidon2_permutation_opcode( } // Read witness assignments - let mut state = Vec::new(); - for input in inputs.iter() { - let witness_assignment = input_to_value(initial_witness, *input, false)?; - state.push(witness_assignment); - } + let state: Vec = inputs + .iter() + .map(|input| input_to_value(initial_witness, *input, false)) + .collect::>()?; let state = backend.poseidon2_permutation(&state, len)?; diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/logic.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/logic.rs index 3fa72d9b215b..587add11b810 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/logic.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/logic.rs @@ -1,9 +1,9 @@ -use crate::pwg::{input_to_value, insert_value}; use crate::OpcodeResolutionError; +use crate::pwg::{input_to_value, insert_value}; use acir::{ + AcirField, circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, - AcirField, }; use acvm_blackbox_solver::{bit_and, bit_xor}; diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs index 2f10e333c24b..ad064fefd7b8 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -1,7 +1,7 @@ use acir::{ + AcirField, circuit::opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput}, native_types::{Witness, WitnessMap}, - AcirField, }; use acvm_blackbox_solver::{blake2s, blake3, keccakf1600}; @@ -10,8 +10,8 @@ use self::{ hash::solve_poseidon2_permutation_opcode, }; -use super::{insert_value, OpcodeNotSolvable, OpcodeResolutionError}; -use crate::{pwg::input_to_value, BlackBoxFunctionSolver}; +use super::{OpcodeNotSolvable, OpcodeResolutionError, insert_value}; +use crate::{BlackBoxFunctionSolver, pwg::input_to_value}; mod aes128; pub(crate) mod bigint; @@ -37,12 +37,8 @@ fn first_missing_assignment( inputs: &[FunctionInput], ) -> Option { inputs.iter().find_map(|input| { - if let ConstantOrWitnessEnum::Witness(ref witness) = input.input_ref() { - if witness_assignments.contains_key(witness) { - None - } else { - Some(*witness) - } + if let ConstantOrWitnessEnum::Witness(witness) = input.input_ref() { + if witness_assignments.contains_key(witness) { None } else { Some(*witness) } } else { None } diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/range.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/range.rs index d7083e4b9c05..039a04b9063d 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/range.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/range.rs @@ -1,8 +1,8 @@ use crate::{ - pwg::{input_to_value, ErrorLocation}, OpcodeResolutionError, + pwg::{ErrorLocation, input_to_value}, }; -use acir::{circuit::opcodes::FunctionInput, native_types::WitnessMap, AcirField}; +use acir::{AcirField, circuit::opcodes::FunctionInput, native_types::WitnessMap}; pub(crate) fn solve_range_opcode( initial_witness: &WitnessMap, @@ -21,3 +21,36 @@ pub(crate) fn solve_range_opcode( } Ok(()) } + +#[cfg(test)] +mod tests { + use std::collections::BTreeMap; + + use acir::{ + FieldElement, + circuit::opcodes::FunctionInput, + native_types::{Witness, WitnessMap}, + }; + + use crate::pwg::blackbox::solve_range_opcode; + + #[test] + fn rejects_too_large_inputs() { + let witness_map = + WitnessMap::from(BTreeMap::from([(Witness(0), FieldElement::from(256u32))])); + let input: FunctionInput = FunctionInput::witness(Witness(0), 8); + assert!(solve_range_opcode(&witness_map, &input, false).is_err()); + } + + #[test] + fn accepts_valid_inputs() { + let values: [u32; 4] = [0, 1, 8, 255]; + + for value in values { + let witness_map = + WitnessMap::from(BTreeMap::from([(Witness(0), FieldElement::from(value))])); + let input: FunctionInput = FunctionInput::witness(Witness(0), 8); + assert!(solve_range_opcode(&witness_map, &input, false).is_ok()); + } + } +} diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/signature/ecdsa.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/signature/ecdsa.rs index db92d27b871e..4b53316e88f5 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/signature/ecdsa.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/signature/ecdsa.rs @@ -1,16 +1,16 @@ use acir::{ + AcirField, circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, - AcirField, }; use acvm_blackbox_solver::{ecdsa_secp256k1_verify, ecdsa_secp256r1_verify}; use crate::{ + OpcodeResolutionError, pwg::{ blackbox::utils::{to_u8_array, to_u8_vec}, insert_value, }, - OpcodeResolutionError, }; pub(crate) fn secp256k1_prehashed( diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/utils.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/utils.rs index b966cb0cc5d2..6c9c5a1025ad 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/utils.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/utils.rs @@ -1,6 +1,6 @@ -use acir::{circuit::opcodes::FunctionInput, native_types::WitnessMap, AcirField}; +use acir::{AcirField, circuit::opcodes::FunctionInput, native_types::WitnessMap}; -use crate::pwg::{input_to_value, OpcodeResolutionError}; +use crate::pwg::{OpcodeResolutionError, input_to_value}; pub(crate) fn to_u8_array( initial_witness: &WitnessMap, diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs index a635cd926159..136f358b9bb6 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/brillig.rs @@ -1,20 +1,20 @@ use std::collections::HashMap; use acir::{ + AcirField, brillig::{ForeignCallParam, ForeignCallResult, Opcode as BrilligOpcode}, circuit::{ + ErrorSelector, OpcodeLocation, RawAssertionPayload, ResolvedAssertionPayload, brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}, opcodes::BlockId, - ErrorSelector, OpcodeLocation, RawAssertionPayload, ResolvedAssertionPayload, }, native_types::WitnessMap, - AcirField, }; use acvm_blackbox_solver::BlackBoxFunctionSolver; -use brillig_vm::{BrilligProfilingSamples, FailureReason, MemoryValue, VMStatus, VM}; +use brillig_vm::{BrilligProfilingSamples, FailureReason, MemoryValue, VM, VMStatus}; use serde::{Deserialize, Serialize}; -use crate::{pwg::OpcodeNotSolvable, OpcodeResolutionError}; +use crate::{OpcodeResolutionError, pwg::OpcodeNotSolvable}; use super::{get_value, insert_value, memory_op::MemoryOpSolver}; @@ -100,7 +100,7 @@ impl<'b, B: BlackBoxFunctionSolver, F: AcirField> BrilligSolver<'b, F, B> { Err(_) => { return Err(OpcodeResolutionError::OpcodeNotSolvable( OpcodeNotSolvable::ExpressionHasTooManyUnknowns(expr.clone()), - )) + )); } }, BrilligInputs::Array(expr_arr) => { @@ -111,7 +111,7 @@ impl<'b, B: BlackBoxFunctionSolver, F: AcirField> BrilligSolver<'b, F, B> { Err(_) => { return Err(OpcodeResolutionError::OpcodeNotSolvable( OpcodeNotSolvable::ExpressionHasTooManyUnknowns(expr.clone()), - )) + )); } } } diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/memory_op.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/memory_op.rs index 2a83bf2531ca..aef18d3957ea 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/memory_op.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/memory_op.rs @@ -1,15 +1,15 @@ use std::collections::HashMap; use acir::{ + AcirField, circuit::opcodes::MemOp, native_types::{Expression, Witness, WitnessMap}, - AcirField, }; +use super::{ErrorLocation, OpcodeResolutionError}; use super::{ arithmetic::ExpressionSolver, get_value, insert_value, is_predicate_false, witness_to_value, }; -use super::{ErrorLocation, OpcodeResolutionError}; type MemoryIndex = u32; @@ -140,9 +140,9 @@ mod tests { use std::collections::BTreeMap; use acir::{ + AcirField, FieldElement, circuit::opcodes::MemOp, native_types::{Expression, Witness, WitnessMap}, - AcirField, FieldElement, }; use super::MemoryOpSolver; diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs index 6e0e28cf81d3..f5d56df17c1e 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs @@ -3,17 +3,17 @@ use std::collections::HashMap; use acir::{ + AcirField, BlackBoxFunc, brillig::ForeignCallResult, circuit::{ + AssertionPayload, ErrorSelector, ExpressionOrMemory, Opcode, OpcodeLocation, + RawAssertionPayload, ResolvedAssertionPayload, brillig::{BrilligBytecode, BrilligFunctionId}, opcodes::{ AcirFunctionId, BlockId, ConstantOrWitnessEnum, FunctionInput, InvalidInputBitSize, }, - AssertionPayload, ErrorSelector, ExpressionOrMemory, Opcode, OpcodeLocation, - RawAssertionPayload, ResolvedAssertionPayload, }, native_types::{Expression, Witness, WitnessMap}, - AcirField, BlackBoxFunc, }; use acvm_blackbox_solver::BlackBoxResolutionError; @@ -73,6 +73,7 @@ impl std::fmt::Display for ACVMStatus { } } +#[expect(clippy::large_enum_variant)] pub enum StepResult<'a, F, B: BlackBoxFunctionSolver> { Status(ACVMStatus), IntoBrillig(BrilligSolver<'a, F, B>), @@ -142,7 +143,9 @@ pub enum OpcodeResolutionError { }, #[error("Attempted to call `main` with a `Call` opcode")] AcirMainCallAttempted { opcode_location: ErrorLocation }, - #[error("{results_size:?} result values were provided for {outputs_size:?} call output witnesses, most likely due to bad ACIR codegen")] + #[error( + "{results_size:?} result values were provided for {outputs_size:?} call output witnesses, most likely due to bad ACIR codegen" + )] AcirCallOutputsMismatch { opcode_location: ErrorLocation, results_size: u32, outputs_size: u32 }, #[error("(--pedantic): Predicates are expected to be 0 or 1, but found: {pred_value}")] PredicateLargerThanOne { opcode_location: ErrorLocation, pred_value: F }, @@ -744,11 +747,7 @@ pub fn insert_value( // The function is used during partial witness generation to report unsolved witness fn any_witness_from_expression(expr: &Expression) -> Option { if expr.linear_combinations.is_empty() { - if expr.mul_terms.is_empty() { - None - } else { - Some(expr.mul_terms[0].1) - } + if expr.mul_terms.is_empty() { None } else { Some(expr.mul_terms[0].1) } } else { Some(expr.linear_combinations[0].1) } diff --git a/noir/noir-repo/acvm-repo/acvm/tests/solver.rs b/noir/noir-repo/acvm-repo/acvm/tests/solver.rs index 2fd610503779..ce75a7d20017 100644 --- a/noir/noir-repo/acvm-repo/acvm/tests/solver.rs +++ b/noir/noir-repo/acvm-repo/acvm/tests/solver.rs @@ -3,20 +3,20 @@ use std::sync::Arc; use acir::brillig::{BitSize, HeapVector, IntegerBitSize}; use acir::{ + AcirField, FieldElement, acir_field::GenericFieldElement, brillig::{BinaryFieldOp, MemoryAddress, Opcode as BrilligOpcode, ValueOrArray}, circuit::{ + Opcode, OpcodeLocation, brillig::{BrilligBytecode, BrilligFunctionId, BrilligInputs, BrilligOutputs}, opcodes::{BlackBoxFuncCall, BlockId, BlockType, FunctionInput, MemOp}, - Opcode, OpcodeLocation, }, native_types::{Expression, Witness, WitnessMap}, - AcirField, FieldElement, }; -use acvm::pwg::{ACVMStatus, ErrorLocation, ForeignCallWaitInfo, OpcodeResolutionError, ACVM}; +use acvm::pwg::{ACVM, ACVMStatus, ErrorLocation, ForeignCallWaitInfo, OpcodeResolutionError}; use acvm_blackbox_solver::{BigIntSolver, StubbedBlackBoxSolver}; -use bn254_blackbox_solver::{field_from_hex, Bn254BlackBoxSolver, POSEIDON2_CONFIG}; +use bn254_blackbox_solver::{Bn254BlackBoxSolver, POSEIDON2_CONFIG, field_from_hex}; use brillig_vm::brillig::HeapValueType; use num_bigint::BigUint; diff --git a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml index 308d0ef516bb..d8416b5d89f8 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm_js" description = "Typescript wrapper around the ACVM allowing execution of ACIR code" # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true @@ -40,7 +40,7 @@ getrandom = { workspace = true, features = ["js"] } build-data.workspace = true pkg-config = "0.3" -[dev-dependencies] +[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dev-dependencies] wasm-bindgen-test.workspace = true [features] 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/acvm-repo/acvm_js/package.json b/noir/noir-repo/acvm-repo/acvm_js/package.json index e54a952698b1..fc842da42360 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/package.json +++ b/noir/noir-repo/acvm-repo/acvm_js/package.json @@ -1,6 +1,6 @@ { "name": "@noir-lang/acvm_js", - "version": "1.0.0-beta.2", + "version": "1.0.0-beta.3", "publishConfig": { "access": "public" }, diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/black_box_solvers.rs b/noir/noir-repo/acvm-repo/acvm_js/src/black_box_solvers.rs index 0e35851ee788..02c026cd26fa 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/black_box_solvers.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/black_box_solvers.rs @@ -2,7 +2,7 @@ use js_sys::JsString; use wasm_bindgen::prelude::*; use crate::js_witness_map::{field_element_to_js_string, js_value_to_field_element}; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; /// Performs a bitwise AND operation between `lhs` and `rhs` #[wasm_bindgen] diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/execute.rs b/noir/noir-repo/acvm-repo/acvm_js/src/execute.rs index e4d52063977f..3efa6900f6ec 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/execute.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/execute.rs @@ -1,22 +1,22 @@ use std::{future::Future, pin::Pin}; -use acvm::acir::circuit::brillig::BrilligBytecode; use acvm::acir::circuit::ResolvedAssertionPayload; +use acvm::acir::circuit::brillig::BrilligBytecode; +use acvm::{BlackBoxFunctionSolver, FieldElement}; use acvm::{ acir::circuit::{Circuit, Program}, acir::native_types::{WitnessMap, WitnessStack}, - pwg::{ACVMStatus, ErrorLocation, OpcodeResolutionError, ACVM}, + pwg::{ACVM, ACVMStatus, ErrorLocation, OpcodeResolutionError}, }; -use acvm::{BlackBoxFunctionSolver, FieldElement}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use js_sys::Error; use wasm_bindgen::prelude::wasm_bindgen; use crate::{ - foreign_call::{resolve_brillig, ForeignCallHandler}, - public_witness::extract_indices, JsExecutionError, JsSolvedAndReturnWitness, JsWitnessMap, JsWitnessStack, + foreign_call::{ForeignCallHandler, resolve_brillig}, + public_witness::extract_indices, }; /// Executes an ACIR circuit to generate the solved witness from the initial witness. diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/inputs.rs b/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/inputs.rs index dd12bc639ece..eee36a0f01a9 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/inputs.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/inputs.rs @@ -1,4 +1,4 @@ -use acvm::{brillig_vm::brillig::ForeignCallParam, FieldElement}; +use acvm::{FieldElement, brillig_vm::brillig::ForeignCallParam}; use crate::js_witness_map::field_element_to_js_string; diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/mod.rs b/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/mod.rs index 4884c1173dc4..8b34b699a856 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/mod.rs @@ -1,7 +1,7 @@ -use acvm::{brillig_vm::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo, FieldElement}; +use acvm::{FieldElement, brillig_vm::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo}; use js_sys::{Error, JsString}; -use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; +use wasm_bindgen::{JsValue, prelude::wasm_bindgen}; mod inputs; mod outputs; diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/outputs.rs b/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/outputs.rs index 2b3f44fe98df..75b9c3aa3113 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/outputs.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/foreign_call/outputs.rs @@ -1,6 +1,6 @@ use acvm::{ - brillig_vm::brillig::{ForeignCallParam, ForeignCallResult}, FieldElement, + brillig_vm::brillig::{ForeignCallParam, ForeignCallResult}, }; use wasm_bindgen::JsValue; diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs b/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs index f6a00af79422..6aa7cf756786 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs @@ -1,10 +1,10 @@ use acvm::{ - acir::circuit::{brillig::BrilligFunctionId, OpcodeLocation, RawAssertionPayload}, FieldElement, + acir::circuit::{OpcodeLocation, RawAssertionPayload, brillig::BrilligFunctionId}, }; use gloo_utils::format::JsValueSerdeExt; use js_sys::{Array, Error, JsString, Reflect}; -use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; +use wasm_bindgen::prelude::{JsValue, wasm_bindgen}; #[wasm_bindgen(typescript_custom_section)] const EXECUTION_ERROR: &'static str = r#" diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_map.rs b/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_map.rs index 8316059e21a6..a09e8e1ade7a 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_map.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_map.rs @@ -1,10 +1,10 @@ use acvm::{ - acir::native_types::{Witness, WitnessMap}, - acir::AcirField, FieldElement, + acir::AcirField, + acir::native_types::{Witness, WitnessMap}, }; use js_sys::{JsString, Map, Object}; -use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; +use wasm_bindgen::prelude::{JsValue, wasm_bindgen}; #[wasm_bindgen(typescript_custom_section)] const WITNESS_MAP: &'static str = r#" @@ -106,21 +106,21 @@ pub(crate) fn field_element_to_js_string(field_element: &FieldElement) -> JsStri format!("0x{}", field_element.to_hex()).into() } -#[cfg(test)] +#[cfg(all(test, any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))] mod test { - use wasm_bindgen_test::wasm_bindgen_test as test; + use wasm_bindgen_test::*; use std::collections::BTreeMap; use acvm::{ - acir::native_types::{Witness, WitnessMap}, AcirField, FieldElement, + acir::native_types::{Witness, WitnessMap}, }; use wasm_bindgen::JsValue; use crate::JsWitnessMap; - #[test] + #[wasm_bindgen_test] fn test_witness_map_to_js() { let witness_map = BTreeMap::from([ (Witness(1), FieldElement::one()), diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_stack.rs b/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_stack.rs index d59ee508086d..e1baa4917274 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_stack.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/js_witness_stack.rs @@ -1,6 +1,6 @@ -use acvm::{acir::native_types::WitnessStack, FieldElement}; +use acvm::{FieldElement, acir::native_types::WitnessStack}; use js_sys::{Array, Map, Object}; -use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; +use wasm_bindgen::prelude::{JsValue, wasm_bindgen}; use crate::JsWitnessMap; diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/logging.rs b/noir/noir-repo/acvm-repo/acvm_js/src/logging.rs index 483c23ab624e..f40e2fa1bd34 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/logging.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/logging.rs @@ -1,6 +1,6 @@ use js_sys::{Error, JsString}; -use tracing_subscriber::prelude::*; use tracing_subscriber::EnvFilter; +use tracing_subscriber::prelude::*; use tracing_web::MakeWebConsoleWriter; use wasm_bindgen::prelude::*; diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/public_witness.rs b/noir/noir-repo/acvm-repo/acvm_js/src/public_witness.rs index 245d5b4dd0ad..2ca794ba3a50 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/public_witness.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/public_witness.rs @@ -1,9 +1,9 @@ use acvm::{ + FieldElement, acir::{ circuit::Program, native_types::{Witness, WitnessMap}, }, - FieldElement, }; use js_sys::JsString; use wasm_bindgen::prelude::wasm_bindgen; @@ -44,7 +44,11 @@ pub fn get_return_witness( let circuit = match program.functions.len() { 0 => return Ok(JsWitnessMap::from(WitnessMap::new())), 1 => &program.functions[0], - _ => return Err(JsString::from("Program contains multiple circuits however ACVM currently only supports programs containing a single circuit")) + _ => { + return Err(JsString::from( + "Program contains multiple circuits however ACVM currently only supports programs containing a single circuit", + )); + } }; let witness_map = WitnessMap::from(witness_map); @@ -71,7 +75,11 @@ pub fn get_public_parameters_witness( let circuit = match program.functions.len() { 0 => return Ok(JsWitnessMap::from(WitnessMap::new())), 1 => &program.functions[0], - _ => return Err(JsString::from("Program contains multiple circuits however ACVM currently only supports programs containing a single circuit")) + _ => { + return Err(JsString::from( + "Program contains multiple circuits however ACVM currently only supports programs containing a single circuit", + )); + } }; let witness_map = WitnessMap::from(solved_witness); @@ -98,7 +106,11 @@ pub fn get_public_witness( let circuit = match program.functions.len() { 0 => return Ok(JsWitnessMap::from(WitnessMap::new())), 1 => &program.functions[0], - _ => return Err(JsString::from("Program contains multiple circuits however ACVM currently only supports programs containing a single circuit")) + _ => { + return Err(JsString::from( + "Program contains multiple circuits however ACVM currently only supports programs containing a single circuit", + )); + } }; let witness_map = WitnessMap::from(solved_witness); diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml index bcd5be3f43ab..38292dd1924d 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm_blackbox_solver" description = "A solver for the blackbox functions found in ACIR and Brillig" # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs b/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs index f7be1e80a556..d3531007f092 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs +++ b/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs @@ -220,8 +220,8 @@ impl BigIntSolverWithId { #[test] fn all_allowed_bigint_moduli_are_prime() { - use num_prime::nt_funcs::is_prime; use num_prime::Primality; + use num_prime::nt_funcs::is_prime; for modulus in BigIntSolver::allowed_bigint_moduli() { let modulus = BigUint::from_bytes_le(&modulus); @@ -231,7 +231,10 @@ fn all_allowed_bigint_moduli_are_prime() { Primality::No => panic!("not all allowed_bigint_moduli are prime: {modulus}"), Primality::Probable(probability) => { if probability < 0.90 { - panic!("not all allowed_bigint_moduli are prime within the allowed probability: {} < 0.90", probability); + panic!( + "not all allowed_bigint_moduli are prime within the allowed probability: {} < 0.90", + probability + ); } } } diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs b/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs index 37fe5d053633..af0104b54f0a 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs +++ b/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs @@ -25,8 +25,8 @@ pub trait BlackBoxFunctionSolver { ) -> Result<(F, F, F), BlackBoxResolutionError>; fn poseidon2_permutation( &self, - _inputs: &[F], - _len: u32, + inputs: &[F], + len: u32, ) -> Result, BlackBoxResolutionError>; } diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256k1.rs b/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256k1.rs index 17c51353f7fa..d090029b1cd5 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256k1.rs +++ b/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256k1.rs @@ -1,15 +1,15 @@ -use k256::elliptic_curve::sec1::FromEncodedPoint; use k256::elliptic_curve::PrimeField; +use k256::elliptic_curve::sec1::FromEncodedPoint; use blake2::digest::generic_array::GenericArray; -use k256::{ecdsa::Signature, Scalar}; use k256::{ + AffinePoint, EncodedPoint, ProjectivePoint, PublicKey, elliptic_curve::{ - sec1::{Coordinates, ToEncodedPoint}, IsHigh, + sec1::{Coordinates, ToEncodedPoint}, }, - AffinePoint, EncodedPoint, ProjectivePoint, PublicKey, }; +use k256::{Scalar, ecdsa::Signature}; pub(super) fn verify_signature( hashed_msg: &[u8], diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256r1.rs b/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256r1.rs index 54559d7c7744..7a162aa800ab 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256r1.rs +++ b/noir/noir-repo/acvm-repo/blackbox_solver/src/ecdsa/secp256r1.rs @@ -1,15 +1,15 @@ -use p256::elliptic_curve::sec1::FromEncodedPoint; use p256::elliptic_curve::PrimeField; +use p256::elliptic_curve::sec1::FromEncodedPoint; use blake2::digest::generic_array::GenericArray; -use p256::{ecdsa::Signature, Scalar}; use p256::{ + AffinePoint, EncodedPoint, ProjectivePoint, PublicKey, elliptic_curve::{ - sec1::{Coordinates, ToEncodedPoint}, IsHigh, + sec1::{Coordinates, ToEncodedPoint}, }, - AffinePoint, EncodedPoint, ProjectivePoint, PublicKey, }; +use p256::{Scalar, ecdsa::Signature}; pub(super) fn verify_signature( hashed_msg: &[u8], diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml index 5ac93d9d8f68..d9367f6b52af 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml @@ -2,7 +2,7 @@ name = "bn254_blackbox_solver" description = "Solvers for black box functions which are specific for the bn254 curve" # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/benches/criterion.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/benches/criterion.rs index fc566b70a268..05211264beb6 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/benches/criterion.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/benches/criterion.rs @@ -1,4 +1,4 @@ -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{Criterion, criterion_group, criterion_main}; use std::{hint::black_box, time::Duration}; use acir::{AcirField, FieldElement}; diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/hash_to_curve.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/hash_to_curve.rs index 3c284fa811ca..028ca2e5d1cc 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/hash_to_curve.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/hash_to_curve.rs @@ -62,11 +62,7 @@ pub(crate) fn hash_to_curve(seed: &[u8], attempt_count: u8) -> Affine { if let Some(point) = Affine::get_point_from_x_unchecked(x, false) { let parity_bit = hash_hi[0] > 127; let y_bit_set = point.y().unwrap().into_bigint().get_bit(0); - if (parity_bit && !y_bit_set) || (!parity_bit && y_bit_set) { - -point - } else { - point - } + if (parity_bit && !y_bit_set) || (!parity_bit && y_bit_set) { -point } else { point } } else { hash_to_curve(seed, attempt_count + 1) } diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs index fc031faefa26..d0fece86d6f9 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs @@ -11,8 +11,8 @@ mod poseidon2; pub use embedded_curve_ops::{embedded_curve_add, multi_scalar_mul}; pub use generator::generators::derive_generators; pub use poseidon2::{ - field_from_hex, poseidon2_permutation, poseidon_hash, Poseidon2Config, Poseidon2Sponge, - POSEIDON2_CONFIG, + POSEIDON2_CONFIG, Poseidon2Config, Poseidon2Sponge, field_from_hex, poseidon_hash, + poseidon2_permutation, }; // Temporary hack, this ensure that we always use a bn254 field here diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs index 3aa735388ca8..d756f40a236f 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs @@ -430,7 +430,7 @@ lazy_static! { }; } -impl<'a> Poseidon2<'a> { +impl Poseidon2<'_> { pub(crate) fn new() -> Self { Poseidon2 { config: &POSEIDON2_CONFIG } } @@ -626,7 +626,7 @@ impl<'a> Poseidon2Sponge<'a> { mod test { use acir::AcirField; - use super::{field_from_hex, poseidon2_permutation, FieldElement}; + use super::{FieldElement, field_from_hex, poseidon2_permutation}; #[test] fn smoke_test() { diff --git a/noir/noir-repo/acvm-repo/brillig/Cargo.toml b/noir/noir-repo/acvm-repo/brillig/Cargo.toml index d037531b8ed5..5a9720238aca 100644 --- a/noir/noir-repo/acvm-repo/brillig/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig/Cargo.toml @@ -2,7 +2,7 @@ name = "brillig" description = "Brillig is the bytecode ACIR uses for non-determinism." # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/brillig/src/black_box.rs b/noir/noir-repo/acvm-repo/brillig/src/black_box.rs index be9ba20ed499..eb496d0f826c 100644 --- a/noir/noir-repo/acvm-repo/brillig/src/black_box.rs +++ b/noir/noir-repo/acvm-repo/brillig/src/black_box.rs @@ -1,4 +1,4 @@ -use crate::{opcodes::HeapVector, HeapArray, MemoryAddress}; +use crate::{HeapArray, MemoryAddress, opcodes::HeapVector}; use serde::{Deserialize, Serialize}; /// These opcodes provide an equivalent of ACIR blackbox functions. diff --git a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml index 531179f54d1f..55bfe486c9f3 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml @@ -2,7 +2,7 @@ name = "brillig_vm" description = "The virtual machine that processes Brillig bytecode, used to introduce non-determinism to the ACVM" # x-release-please-start-version -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/acvm-repo/brillig_vm/src/arithmetic.rs b/noir/noir-repo/acvm-repo/brillig_vm/src/arithmetic.rs index c9417faaff30..24013d1fde7a 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/src/arithmetic.rs +++ b/noir/noir-repo/acvm-repo/brillig_vm/src/arithmetic.rs @@ -1,7 +1,7 @@ use std::ops::{BitAnd, BitOr, BitXor, Shl, Shr}; -use acir::brillig::{BinaryFieldOp, BinaryIntOp, BitSize, IntegerBitSize}; use acir::AcirField; +use acir::brillig::{BinaryFieldOp, BinaryIntOp, BitSize, IntegerBitSize}; use num_bigint::BigUint; use num_traits::{CheckedDiv, WrappingAdd, WrappingMul, WrappingSub, Zero}; @@ -231,21 +231,11 @@ fn evaluate_binary_int_op_shifts + Zero + Shl + Shr { let rhs_usize: usize = rhs as usize; - #[allow(unused_qualifications)] - if rhs_usize >= 8 * std::mem::size_of::() { - T::zero() - } else { - lhs << rhs.into() - } + if rhs_usize >= 8 * size_of::() { T::zero() } else { lhs << rhs.into() } } BinaryIntOp::Shr => { let rhs_usize: usize = rhs as usize; - #[allow(unused_qualifications)] - if rhs_usize >= 8 * std::mem::size_of::() { - T::zero() - } else { - lhs >> rhs.into() - } + if rhs_usize >= 8 * size_of::() { T::zero() } else { lhs >> rhs.into() } } _ => unreachable!("Operator not handled by this function: {op:?}"), } diff --git a/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs b/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs index aa3d0de55f47..74ea3568a1a2 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs +++ b/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs @@ -1,14 +1,14 @@ use acir::brillig::{BlackBoxOp, HeapArray, HeapVector}; use acir::{AcirField, BlackBoxFunc}; use acvm_blackbox_solver::{ - aes128_encrypt, blake2s, blake3, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify, keccakf1600, - sha256_compression, BigIntSolverWithId, BlackBoxFunctionSolver, BlackBoxResolutionError, + BigIntSolverWithId, BlackBoxFunctionSolver, BlackBoxResolutionError, aes128_encrypt, blake2s, + blake3, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify, keccakf1600, sha256_compression, }; use num_bigint::BigUint; use num_traits::Zero; -use crate::memory::MemoryValue; use crate::Memory; +use crate::memory::MemoryValue; fn read_heap_vector<'a, F: AcirField>( memory: &'a Memory, diff --git a/noir/noir-repo/acvm-repo/brillig_vm/src/lib.rs b/noir/noir-repo/acvm-repo/brillig_vm/src/lib.rs index 27759012335c..935c296d5ae8 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/src/lib.rs +++ b/noir/noir-repo/acvm-repo/brillig_vm/src/lib.rs @@ -10,19 +10,19 @@ //! [acir]: https://crates.io/crates/acir //! [acvm]: https://crates.io/crates/acvm +use acir::AcirField; use acir::brillig::{ BinaryFieldOp, BinaryIntOp, BitSize, ForeignCallParam, ForeignCallResult, HeapArray, HeapValueType, HeapVector, IntegerBitSize, MemoryAddress, Opcode, ValueOrArray, }; -use acir::AcirField; use acvm_blackbox_solver::BlackBoxFunctionSolver; -use arithmetic::{evaluate_binary_field_op, evaluate_binary_int_op, BrilligArithmeticError}; -use black_box::{evaluate_black_box, BrilligBigIntSolver}; +use arithmetic::{BrilligArithmeticError, evaluate_binary_field_op, evaluate_binary_int_op}; +use black_box::{BrilligBigIntSolver, evaluate_black_box}; // Re-export `brillig`. pub use acir::brillig; use memory::MemoryTypeError; -pub use memory::{Memory, MemoryValue, MEMORY_ADDRESSING_BIT_SIZE}; +pub use memory::{MEMORY_ADDRESSING_BIT_SIZE, Memory, MemoryValue}; mod arithmetic; mod black_box; @@ -548,69 +548,99 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver> VM<'a, F, B> { destinations.iter().zip(destination_value_types).zip(&values) { match (destination, value_type) { - (ValueOrArray::MemoryAddress(value_index), HeapValueType::Simple(bit_size)) => { - match output { - ForeignCallParam::Single(value) => { - self.write_value_to_memory(*value_index, value, *bit_size)?; - } - _ => return Err(format!( - "Function result size does not match brillig bytecode. Expected 1 result but got {output:?}") - ), - } - } - ( - ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }), - HeapValueType::Array { value_types, size: type_size }, - ) if size == type_size => { - if HeapValueType::all_simple(value_types) { + (ValueOrArray::MemoryAddress(value_index), HeapValueType::Simple(bit_size)) => { match output { - ForeignCallParam::Array(values) => { - if values.len() != *size { - // foreign call returning flattened values into a nested type, so the sizes do not match - let destination = self.memory.read_ref(*pointer_index); - let return_type = value_type; - let mut flatten_values_idx = 0; //index of values read from flatten_values - self.write_slice_of_values_to_memory(destination, &output.fields(), &mut flatten_values_idx, return_type)?; - } else { - self.write_values_to_memory_slice(*pointer_index, values, value_types)?; - } + ForeignCallParam::Single(value) => { + self.write_value_to_memory(*value_index, value, *bit_size)?; } _ => { - return Err("Function result size does not match brillig bytecode size".to_string()); + return Err(format!( + "Function result size does not match brillig bytecode. Expected 1 result but got {output:?}" + )); } } - } else { - // foreign call returning flattened values into a nested type, so the sizes do not match - let destination = self.memory.read_ref(*pointer_index); - let return_type = value_type; - let mut flatten_values_idx = 0; //index of values read from flatten_values - self.write_slice_of_values_to_memory(destination, &output.fields(), &mut flatten_values_idx, return_type)?; - } - } - ( - ValueOrArray::HeapVector(HeapVector {pointer: pointer_index, size: size_index }), - HeapValueType::Vector { value_types }, - ) => { - if HeapValueType::all_simple(value_types) { - match output { - ForeignCallParam::Array(values) => { - // Set our size in the size address - self.memory.write(*size_index, values.len().into()); - self.write_values_to_memory_slice(*pointer_index, values, value_types)?; - + } + ( + ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }), + HeapValueType::Array { value_types, size: type_size }, + ) if size == type_size => { + if HeapValueType::all_simple(value_types) { + match output { + ForeignCallParam::Array(values) => { + if values.len() != *size { + // foreign call returning flattened values into a nested type, so the sizes do not match + let destination = self.memory.read_ref(*pointer_index); + let return_type = value_type; + let mut flatten_values_idx = 0; //index of values read from flatten_values + self.write_slice_of_values_to_memory( + destination, + &output.fields(), + &mut flatten_values_idx, + return_type, + )?; + } else { + self.write_values_to_memory_slice( + *pointer_index, + values, + value_types, + )?; + } + } + _ => { + return Err( + "Function result size does not match brillig bytecode size" + .to_string(), + ); + } } - _ => { - return Err("Function result size does not match brillig bytecode size".to_string()); + } else { + // foreign call returning flattened values into a nested type, so the sizes do not match + let destination = self.memory.read_ref(*pointer_index); + let return_type = value_type; + let mut flatten_values_idx = 0; //index of values read from flatten_values + self.write_slice_of_values_to_memory( + destination, + &output.fields(), + &mut flatten_values_idx, + return_type, + )?; + } + } + ( + ValueOrArray::HeapVector(HeapVector { + pointer: pointer_index, + size: size_index, + }), + HeapValueType::Vector { value_types }, + ) => { + if HeapValueType::all_simple(value_types) { + match output { + ForeignCallParam::Array(values) => { + // Set our size in the size address + self.memory.write(*size_index, values.len().into()); + self.write_values_to_memory_slice( + *pointer_index, + values, + value_types, + )?; + } + _ => { + return Err( + "Function result size does not match brillig bytecode size" + .to_string(), + ); + } } + } else { + unimplemented!("deflattening heap vectors from foreign calls"); } - } else { - unimplemented!("deflattening heap vectors from foreign calls"); + } + _ => { + return Err(format!( + "Unexpected value type {value_type:?} for destination {destination:?}" + )); } } - _ => { - return Err(format!("Unexpected value type {value_type:?} for destination {destination:?}")); - } - } } let _ = diff --git a/noir/noir-repo/acvm-repo/brillig_vm/src/memory.rs b/noir/noir-repo/acvm-repo/brillig_vm/src/memory.rs index 73443384efa5..33a760b52f57 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/src/memory.rs +++ b/noir/noir-repo/acvm-repo/brillig_vm/src/memory.rs @@ -1,6 +1,6 @@ use acir::{ - brillig::{BitSize, IntegerBitSize, MemoryAddress}, AcirField, + brillig::{BitSize, IntegerBitSize, MemoryAddress}, }; pub const MEMORY_ADDRESSING_BIT_SIZE: IntegerBitSize = IntegerBitSize::U32; @@ -18,7 +18,9 @@ pub enum MemoryValue { #[derive(Debug, thiserror::Error)] pub enum MemoryTypeError { - #[error("Bit size for value {value_bit_size} does not match the expected bit size {expected_bit_size}")] + #[error( + "Bit size for value {value_bit_size} does not match the expected bit size {expected_bit_size}" + )] MismatchedBitSize { value_bit_size: u32, expected_bit_size: u32 }, } diff --git a/noir/noir-repo/compiler/integration-tests/package.json b/noir/noir-repo/compiler/integration-tests/package.json index e33179f31e7f..87afd208cf7b 100644 --- a/noir/noir-repo/compiler/integration-tests/package.json +++ b/noir/noir-repo/compiler/integration-tests/package.json @@ -13,7 +13,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.77.1", "@noir-lang/noir_js": "workspace:*", "@noir-lang/noir_wasm": "workspace:*", "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", diff --git a/noir/noir-repo/compiler/integration-tests/scripts/codegen-verifiers.sh b/noir/noir-repo/compiler/integration-tests/scripts/codegen-verifiers.sh index de1f71a4cc0b..f1fc321d1ff3 100755 --- a/noir/noir-repo/compiler/integration-tests/scripts/codegen-verifiers.sh +++ b/noir/noir-repo/compiler/integration-tests/scripts/codegen-verifiers.sh @@ -17,19 +17,19 @@ KEYS=$(mktemp -d) # Codegen verifier contract for 1_mul mul_dir=$repo_root/test_programs/execution_success/1_mul nargo --program-dir $mul_dir compile -$NARGO_BACKEND_PATH write_vk -b $mul_dir/target/1_mul.json -o $KEYS/1_mul -$NARGO_BACKEND_PATH contract -k $KEYS/1_mul -o $contracts_dir/1_mul.sol +$NARGO_BACKEND_PATH OLD_API write_vk -b $mul_dir/target/1_mul.json -o $KEYS/1_mul +$NARGO_BACKEND_PATH OLD_API contract -k $KEYS/1_mul -o $contracts_dir/1_mul.sol # Codegen verifier contract for assert_statement assert_statement_dir=$repo_root/test_programs/execution_success/assert_statement nargo --program-dir $assert_statement_dir compile -$NARGO_BACKEND_PATH write_vk -b $assert_statement_dir/target/assert_statement.json -o $KEYS/assert_statement -$NARGO_BACKEND_PATH contract -k $KEYS/assert_statement -o $contracts_dir/assert_statement.sol +$NARGO_BACKEND_PATH OLD_API write_vk -b $assert_statement_dir/target/assert_statement.json -o $KEYS/assert_statement +$NARGO_BACKEND_PATH OLD_API contract -k $KEYS/assert_statement -o $contracts_dir/assert_statement.sol # Codegen verifier contract for recursion recursion_dir=$repo_root/compiler/integration-tests/circuits/recursion nargo --program-dir $recursion_dir compile -$NARGO_BACKEND_PATH write_vk -b $recursion_dir/target/recursion.json -o $KEYS/recursion -$NARGO_BACKEND_PATH contract -k $KEYS/recursion ./ -o $contracts_dir/recursion.sol +$NARGO_BACKEND_PATH OLD_API write_vk -b $recursion_dir/target/recursion.json -o $KEYS/recursion +$NARGO_BACKEND_PATH OLD_API contract -k $KEYS/recursion -o $contracts_dir/recursion.sol -rm -rf $KEYS \ No newline at end of file +rm -rf $KEYS 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 9838a8af2102..3bbe2181798e 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs @@ -1,15 +1,15 @@ use std::collections::BTreeMap; -use acvm::acir::circuit::ErrorSelector; use acvm::AcirField; +use acvm::acir::circuit::ErrorSelector; use iter_extended::vecmap; use noirc_abi::{ Abi, AbiErrorType, AbiParameter, AbiReturnType, AbiType, AbiValue, AbiVisibility, Sign, }; -use noirc_errors::Span; +use noirc_errors::Location; use noirc_evaluator::ErrorType; -use noirc_frontend::ast::{Signedness, Visibility}; use noirc_frontend::TypeBinding; +use noirc_frontend::ast::{Signedness, Visibility}; use noirc_frontend::{ hir::Context, hir_def::{ @@ -42,11 +42,11 @@ pub(super) fn gen_abi( } // Get the Span of the root crate's main function, or else a dummy span if that fails -fn get_main_function_span(context: &Context) -> Span { +fn get_main_function_location(context: &Context) -> Location { if let Some(func_id) = context.get_main_function(context.root_crate_id()) { - context.function_meta(&func_id).location.span + context.function_meta(&func_id).location } else { - Span::default() + Location::dummy() } } @@ -54,7 +54,7 @@ fn build_abi_error_type(context: &Context, typ: ErrorType) -> AbiErrorType { match typ { ErrorType::Dynamic(typ) => { if let Type::FmtString(len, item_types) = typ { - let span = get_main_function_span(context); + let span = get_main_function_location(context); let length = len.evaluate_to_u32(span).expect("Cannot evaluate fmt length"); let Type::Tuple(item_types) = item_types.as_ref() else { unreachable!("FmtString items must be a tuple") @@ -74,7 +74,7 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType { match typ { Type::FieldElement => AbiType::Field, Type::Array(size, typ) => { - let span = get_main_function_span(context); + let span = get_main_function_location(context); let length = size .evaluate_to_u32(span) .expect("Cannot have variable sized arrays as a parameter to main"); @@ -103,7 +103,7 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType { } Type::Bool => AbiType::Boolean, Type::String(size) => { - let span = get_main_function_span(context); + let span = get_main_function_location(context); let size = size .evaluate_to_u32(span) .expect("Cannot have variable sized strings as a parameter to main"); @@ -226,7 +226,9 @@ pub(super) fn value_from_hir_expression(context: &Context, expression: HirExpres }, HirLiteral::Bool(value) => AbiValue::Boolean { value }, HirLiteral::Str(value) => AbiValue::String { value }, - HirLiteral::Integer(field, sign) => AbiValue::Integer { value: field.to_hex(), sign }, + HirLiteral::Integer(value) => { + AbiValue::Integer { value: value.field.to_hex(), sign: value.is_negative } + } _ => unreachable!("Literal cannot be used in the abi"), }, _ => unreachable!("Type cannot be used in the abi {:?}", expression), diff --git a/noir/noir-repo/compiler/noirc_driver/src/contract.rs b/noir/noir-repo/compiler/noirc_driver/src/contract.rs index 6253f3e1814e..b4c5e9a3fcae 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/contract.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/contract.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, HashMap}; -use acvm::{acir::circuit::Program, FieldElement}; +use acvm::{FieldElement, acir::circuit::Program}; use fm::FileId; use noirc_abi::{Abi, AbiType, AbiValue}; use noirc_errors::debug_info::DebugInfo; @@ -41,6 +41,8 @@ pub struct CompiledContract { pub struct ContractFunction { pub name: String, + pub hash: u64, + pub is_unconstrained: bool, pub custom_attributes: Vec, diff --git a/noir/noir-repo/compiler/noirc_driver/src/lib.rs b/noir/noir-repo/compiler/noirc_driver/src/lib.rs index 807ee7385a37..3712634d707f 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/lib.rs @@ -10,14 +10,15 @@ use clap::Args; use fm::{FileId, FileManager}; use iter_extended::vecmap; use noirc_abi::{AbiParameter, AbiType, AbiValue}; -use noirc_errors::{CustomDiagnostic, DiagnosticKind, FileDiagnostic}; +use noirc_errors::{CustomDiagnostic, DiagnosticKind}; use noirc_evaluator::brillig::BrilligOptions; use noirc_evaluator::create_program; use noirc_evaluator::errors::RuntimeError; use noirc_evaluator::ssa::{SsaLogging, SsaProgramArtifact}; use noirc_frontend::debug::build_debug_crate_file; -use noirc_frontend::hir::def_map::{Contract, CrateDefMap}; +use noirc_frontend::elaborator::{FrontendOptions, UnstableFeature}; use noirc_frontend::hir::Context; +use noirc_frontend::hir::def_map::{Contract, CrateDefMap}; use noirc_frontend::monomorphization::{ errors::MonomorphizationError, monomorphize, monomorphize_debug, }; @@ -105,10 +106,6 @@ pub struct CompileOptions { #[arg(long, conflicts_with = "deny_warnings")] pub silence_warnings: bool, - /// Disables the builtin Aztec macros being used in the compiler - #[arg(long, hide = true)] - pub disable_macros: bool, - /// Outputs the monomorphized IR to stdout for debugging #[arg(long, hide = true)] pub show_monomorphized: bool, @@ -136,20 +133,16 @@ pub struct CompileOptions { #[arg(long)] pub skip_underconstrained_check: bool, - /// Flag to turn on the compiler check for missing Brillig call constraints. - /// Warning: This can degrade compilation speed but will also find some correctness errors. + /// Flag to turn off the compiler check for missing Brillig call constraints. + /// Warning: This can improve compilation speed but can also lead to correctness errors. /// This check should always be run on production code. #[arg(long)] - pub enable_brillig_constraints_check: bool, + pub skip_brillig_constraints_check: bool, /// Flag to turn on extra Brillig bytecode to be generated to guard against invalid states in testing. #[arg(long, hide = true)] pub enable_brillig_debug_assertions: bool, - /// Hidden Brillig call check flag to maintain CI compatibility (currently ignored) - #[arg(long, hide = true)] - pub skip_brillig_constraints_check: bool, - /// Flag to turn on the lookback feature of the Brillig call constraints /// check, allowing tracking argument values before the call happens preventing /// certain rare false positives (leads to a slowdown on large rollout functions) @@ -179,6 +172,11 @@ pub struct CompileOptions { /// Used internally to test for non-determinism in the compiler. #[clap(long, hide = true)] pub check_non_determinism: bool, + + /// Unstable features to enable for this current build + #[arg(value_parser = clap::value_parser!(UnstableFeature))] + #[clap(long, short = 'Z', value_delimiter = ',')] + pub unstable_features: Vec, } pub fn parse_expression_width(input: &str) -> Result { @@ -197,6 +195,16 @@ pub fn parse_expression_width(input: &str) -> Result FrontendOptions { + FrontendOptions { + debug_comptime_in_file: self.debug_comptime_in_file.as_deref(), + pedantic_solving: self.pedantic_solving, + enabled_unstable_features: &self.unstable_features, + } + } +} + #[derive(Debug)] pub enum CompileError { MonomorphizationError(MonomorphizationError), @@ -215,8 +223,8 @@ impl From for CompileError { } } -impl From for FileDiagnostic { - fn from(error: CompileError) -> FileDiagnostic { +impl From for CustomDiagnostic { + fn from(error: CompileError) -> CustomDiagnostic { match error { CompileError::RuntimeError(err) => err.into(), CompileError::MonomorphizationError(err) => err.into(), @@ -225,10 +233,10 @@ impl From for FileDiagnostic { } /// Helper type used to signify where only warnings are expected in file diagnostics -pub type Warnings = Vec; +pub type Warnings = Vec; /// Helper type used to signify where errors or warnings are expected in file diagnostics -pub type ErrorsAndWarnings = Vec; +pub type ErrorsAndWarnings = Vec; /// Helper type for connecting a compilation artifact to the errors or warnings which were produced during compilation. pub type CompilationResult = Result<(T, Warnings), ErrorsAndWarnings>; @@ -336,30 +344,18 @@ pub fn check_crate( crate_id: CrateId, options: &CompileOptions, ) -> CompilationResult<()> { - let diagnostics = CrateDefMap::collect_defs( - crate_id, - context, - options.debug_comptime_in_file.as_deref(), - options.pedantic_solving, - ); + let diagnostics = CrateDefMap::collect_defs(crate_id, context, options.frontend_options()); let crate_files = context.crate_files(&crate_id); - let warnings_and_errors: Vec = diagnostics - .into_iter() - .map(|(error, file_id)| { - let diagnostic = CustomDiagnostic::from(&error); - diagnostic.in_file(file_id) - }) + let warnings_and_errors: Vec = diagnostics + .iter() + .map(CustomDiagnostic::from) .filter(|diagnostic| { // We filter out any warnings if they're going to be ignored later on to free up memory. - !options.silence_warnings || diagnostic.diagnostic.kind != DiagnosticKind::Warning + !options.silence_warnings || diagnostic.kind != DiagnosticKind::Warning }) .filter(|error| { // Only keep warnings from the crate we are checking - if error.diagnostic.is_warning() { - crate_files.contains(&error.file_id) - } else { - true - } + if error.is_warning() { crate_files.contains(&error.file) } else { true } }) .collect(); @@ -397,16 +393,16 @@ pub fn compile_main( // TODO(#2155): This error might be a better to exist in Nargo let err = CustomDiagnostic::from_message( "cannot compile crate into a program as it does not contain a `main` function", - ) - .in_file(FileId::default()); + FileId::default(), + ); vec![err] })?; let compiled_program = compile_no_check(context, options, main, cached_program, options.force_compile) - .map_err(FileDiagnostic::from)?; + .map_err(|error| vec![CustomDiagnostic::from(error)])?; - let compilation_warnings = vecmap(compiled_program.warnings.clone(), FileDiagnostic::from); + let compilation_warnings = vecmap(compiled_program.warnings.clone(), CustomDiagnostic::from); if options.deny_warnings && !compilation_warnings.is_empty() { return Err(compilation_warnings); } @@ -435,14 +431,16 @@ pub fn compile_contract( let mut errors = warnings; if contracts.len() > 1 { - let err = CustomDiagnostic::from_message("Packages are limited to a single contract") - .in_file(FileId::default()); + let err = CustomDiagnostic::from_message( + "Packages are limited to a single contract", + FileId::default(), + ); return Err(vec![err]); } else if contracts.is_empty() { let err = CustomDiagnostic::from_message( "cannot compile crate into a contract as it does not contain any contracts", - ) - .in_file(FileId::default()); + FileId::default(), + ); return Err(vec![err]); }; @@ -479,12 +477,8 @@ pub fn compile_contract( } /// True if there are (non-warning) errors present and we should halt compilation -fn has_errors(errors: &[FileDiagnostic], deny_warnings: bool) -> bool { - if deny_warnings { - !errors.is_empty() - } else { - errors.iter().any(|error| error.diagnostic.is_error()) - } +fn has_errors(errors: &[CustomDiagnostic], deny_warnings: bool) -> bool { + if deny_warnings { !errors.is_empty() } else { errors.iter().any(|error| error.is_error()) } } /// Compile all of the functions associated with a Noir contract. @@ -521,7 +515,7 @@ fn compile_contract_inner( let function = match compile_no_check(context, &options, function_id, None, true) { Ok(function) => function, Err(new_error) => { - errors.push(FileDiagnostic::from(new_error)); + errors.push(new_error.into()); continue; } }; @@ -541,6 +535,7 @@ fn compile_contract_inner( functions.push(ContractFunction { name, + hash: function.hash, custom_attributes, abi: function.abi, bytecode: function.program, @@ -699,7 +694,7 @@ pub fn compile_no_check( skip_underconstrained_check: options.skip_underconstrained_check, enable_brillig_constraints_check_lookback: options .enable_brillig_constraints_check_lookback, - enable_brillig_constraints_check: options.enable_brillig_constraints_check, + skip_brillig_constraints_check: options.skip_brillig_constraints_check, inliner_aggressiveness: options.inliner_aggressiveness, max_bytecode_increase_percent: options.max_bytecode_increase_percent, }; diff --git a/noir/noir-repo/compiler/noirc_driver/src/program.rs b/noir/noir-repo/compiler/noirc_driver/src/program.rs index 4b4d6662e8e7..b3d545339fc3 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/program.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/program.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use acvm::{acir::circuit::Program, FieldElement}; +use acvm::{FieldElement, acir::circuit::Program}; use fm::FileId; use noirc_errors::debug_info::DebugInfo; diff --git a/noir/noir-repo/compiler/noirc_driver/tests/contracts.rs b/noir/noir-repo/compiler/noirc_driver/tests/contracts.rs index c30412923527..0732a7728ca5 100644 --- a/noir/noir-repo/compiler/noirc_driver/tests/contracts.rs +++ b/noir/noir-repo/compiler/noirc_driver/tests/contracts.rs @@ -1,9 +1,9 @@ use std::path::Path; use fm::FileId; -use noirc_driver::{file_manager_with_stdlib, prepare_crate, CompileOptions, ErrorsAndWarnings}; +use noirc_driver::{CompileOptions, ErrorsAndWarnings, file_manager_with_stdlib, prepare_crate}; use noirc_errors::CustomDiagnostic; -use noirc_frontend::hir::{def_map::parse_file, Context}; +use noirc_frontend::hir::{Context, def_map::parse_file}; #[test] fn reject_crates_containing_multiple_contracts() -> Result<(), ErrorsAndWarnings> { @@ -33,8 +33,10 @@ contract Bar {}"; assert_eq!( errors, - vec![CustomDiagnostic::from_message("Packages are limited to a single contract") - .in_file(FileId::default())], + vec![CustomDiagnostic::from_message( + "Packages are limited to a single contract", + FileId::default() + )], "stdlib is producing warnings" ); diff --git a/noir/noir-repo/compiler/noirc_driver/tests/stdlib_warnings.rs b/noir/noir-repo/compiler/noirc_driver/tests/stdlib_warnings.rs index e290842480d0..411ea68fea7c 100644 --- a/noir/noir-repo/compiler/noirc_driver/tests/stdlib_warnings.rs +++ b/noir/noir-repo/compiler/noirc_driver/tests/stdlib_warnings.rs @@ -1,7 +1,7 @@ use std::path::Path; -use noirc_driver::{file_manager_with_stdlib, prepare_crate, ErrorsAndWarnings}; -use noirc_frontend::hir::{def_map::parse_file, Context}; +use noirc_driver::{ErrorsAndWarnings, file_manager_with_stdlib, prepare_crate}; +use noirc_frontend::hir::{Context, def_map::parse_file}; #[test] fn stdlib_does_not_produce_constant_warnings() -> Result<(), ErrorsAndWarnings> { diff --git a/noir/noir-repo/compiler/noirc_errors/src/debug_info.rs b/noir/noir-repo/compiler/noirc_errors/src/debug_info.rs index a5e12b37712d..4d25973835db 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/debug_info.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/debug_info.rs @@ -1,16 +1,16 @@ -use acvm::acir::circuit::brillig::BrilligFunctionId; use acvm::acir::circuit::BrilligOpcodeLocation; use acvm::acir::circuit::OpcodeLocation; +use acvm::acir::circuit::brillig::BrilligFunctionId; use acvm::compiler::AcirTransformationMap; use base64::Engine; +use flate2::Compression; use flate2::read::DeflateDecoder; use flate2::write::DeflateEncoder; -use flate2::Compression; use serde::Deserializer; use serde::Serializer; -use serde_with::serde_as; use serde_with::DisplayFromStr; +use serde_with::serde_as; use std::collections::BTreeMap; use std::io::Read; use std::io::Write; @@ -19,7 +19,7 @@ use std::mem; use crate::Location; use noirc_printable_type::PrintableType; use serde::{ - de::Error as DeserializationError, ser::Error as SerializationError, Deserialize, Serialize, + Deserialize, Serialize, de::Error as DeserializationError, ser::Error as SerializationError, }; #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, Deserialize, Serialize)] diff --git a/noir/noir-repo/compiler/noirc_errors/src/lib.rs b/noir/noir-repo/compiler/noirc_errors/src/lib.rs index bcdc0684099c..91d121603baa 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/lib.rs @@ -6,23 +6,5 @@ pub mod debug_info; mod position; pub mod reporter; -pub use position::{Location, Position, Span, Spanned}; +pub use position::{Located, Location, Position, Span, Spanned}; pub use reporter::{CustomDiagnostic, DiagnosticKind}; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct FileDiagnostic { - pub file_id: fm::FileId, - pub diagnostic: CustomDiagnostic, -} - -impl FileDiagnostic { - pub fn new(file_id: fm::FileId, diagnostic: CustomDiagnostic) -> FileDiagnostic { - FileDiagnostic { file_id, diagnostic } - } -} - -impl From for Vec { - fn from(value: FileDiagnostic) -> Self { - vec![value] - } -} diff --git a/noir/noir-repo/compiler/noirc_errors/src/position.rs b/noir/noir-repo/compiler/noirc_errors/src/position.rs index c7a64c4f4224..f87f78e1f309 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/position.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/position.rs @@ -2,12 +2,68 @@ use codespan::Span as ByteSpan; use fm::FileId; use serde::{Deserialize, Serialize}; use std::{ + cmp::Ordering, hash::{Hash, Hasher}, ops::Range, }; pub type Position = u32; +#[derive(Eq, Debug, Clone)] +pub struct Located { + pub contents: T, + location: Location, +} + +/// This is important for tests. Two Located objects are equal if their content is equal +/// They may not have the same location. Use `.location()` to test for Location being equal specifically +impl PartialEq> for Located { + fn eq(&self, other: &Located) -> bool { + self.contents == other.contents + } +} + +impl PartialOrd> for Located { + fn partial_cmp(&self, other: &Located) -> Option { + self.contents.partial_cmp(&other.contents) + } +} + +impl Ord for Located { + fn cmp(&self, other: &Located) -> Ordering { + self.contents.cmp(&other.contents) + } +} + +impl Default for Located { + fn default() -> Self { + Self { contents: Default::default(), location: Location::dummy() } + } +} + +/// Hash-based data structures (HashMap, HashSet) rely on the inverse of Hash +/// being injective, i.e. x.eq(y) => hash(x, H) == hash(y, H), we hence align +/// this with the above +impl Hash for Located { + fn hash(&self, state: &mut H) { + self.contents.hash(state); + } +} + +impl Located { + pub fn from(location: Location, contents: T) -> Located { + Located { location, contents } + } + + pub fn span(&self) -> Span { + self.location.span + } + + pub fn location(&self) -> Location { + self.location + } +} + #[derive(PartialOrd, Eq, Ord, Debug, Clone, Default)] pub struct Spanned { pub contents: T, @@ -36,17 +92,13 @@ impl Spanned { Spanned { span: Span::inclusive(start, end), contents } } - pub const fn from(t_span: Span, contents: T) -> Spanned { + pub fn from(t_span: Span, contents: T) -> Spanned { Spanned { span: t_span, contents } } pub fn span(&self) -> Span { self.span } - - pub fn set_span(&mut self, span: Span) { - self.span = span; - } } impl std::borrow::Borrow for Spanned { @@ -135,12 +187,33 @@ impl Location { } pub fn dummy() -> Self { - Self { span: Span::single_char(0), file: FileId::dummy() } + Self { span: Span::default(), file: FileId::dummy() } } pub fn contains(&self, other: &Location) -> bool { self.file == other.file && self.span.contains(&other.span) } + + #[must_use] + pub fn merge(self, other: Location) -> Location { + if self.file == other.file { + Location::new(self.span.merge(other.span), self.file) + } else { + self + } + } +} + +impl Ord for Location { + fn cmp(&self, other: &Self) -> Ordering { + (self.file, self.span).cmp(&(other.file, other.span)) + } +} + +impl PartialOrd for Location { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } } #[cfg(test)] diff --git a/noir/noir-repo/compiler/noirc_errors/src/reporter.rs b/noir/noir-repo/compiler/noirc_errors/src/reporter.rs index 30bf49348b6a..d406e897d656 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/reporter.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/reporter.rs @@ -1,6 +1,6 @@ use std::io::IsTerminal; -use crate::{FileDiagnostic, Location, Span}; +use crate::{Location, Span}; use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::Files; use codespan_reporting::term; @@ -8,6 +8,7 @@ use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct CustomDiagnostic { + pub file: fm::FileId, pub message: String, pub secondaries: Vec, pub notes: Vec, @@ -35,8 +36,9 @@ pub struct ReportedErrors { } impl CustomDiagnostic { - pub fn from_message(msg: &str) -> CustomDiagnostic { + pub fn from_message(msg: &str, file: fm::FileId) -> CustomDiagnostic { Self { + file, message: msg.to_owned(), secondaries: Vec::new(), notes: Vec::new(), @@ -50,12 +52,13 @@ impl CustomDiagnostic { fn simple_with_kind( primary_message: String, secondary_message: String, - secondary_span: Span, + secondary_location: Location, kind: DiagnosticKind, ) -> CustomDiagnostic { CustomDiagnostic { + file: secondary_location.file, message: primary_message, - secondaries: vec![CustomLabel::new(secondary_message, secondary_span, None)], + secondaries: vec![CustomLabel::new(secondary_message, secondary_location)], notes: Vec::new(), kind, deprecated: false, @@ -67,12 +70,12 @@ impl CustomDiagnostic { pub fn simple_error( primary_message: String, secondary_message: String, - secondary_span: Span, + secondary_location: Location, ) -> CustomDiagnostic { Self::simple_with_kind( primary_message, secondary_message, - secondary_span, + secondary_location, DiagnosticKind::Error, ) } @@ -80,12 +83,12 @@ impl CustomDiagnostic { pub fn simple_warning( primary_message: String, secondary_message: String, - secondary_span: Span, + secondary_location: Location, ) -> CustomDiagnostic { Self::simple_with_kind( primary_message, secondary_message, - secondary_span, + secondary_location, DiagnosticKind::Warning, ) } @@ -93,12 +96,12 @@ impl CustomDiagnostic { pub fn simple_info( primary_message: String, secondary_message: String, - secondary_span: Span, + secondary_location: Location, ) -> CustomDiagnostic { Self::simple_with_kind( primary_message, secondary_message, - secondary_span, + secondary_location, DiagnosticKind::Info, ) } @@ -106,11 +109,12 @@ impl CustomDiagnostic { pub fn simple_bug( primary_message: String, secondary_message: String, - secondary_span: Span, + secondary_location: Location, ) -> CustomDiagnostic { CustomDiagnostic { + file: secondary_location.file, message: primary_message, - secondaries: vec![CustomLabel::new(secondary_message, secondary_span, None)], + secondaries: vec![CustomLabel::new(secondary_message, secondary_location)], notes: Vec::new(), kind: DiagnosticKind::Bug, deprecated: false, @@ -119,10 +123,6 @@ impl CustomDiagnostic { } } - pub fn in_file(self, file_id: fm::FileId) -> FileDiagnostic { - FileDiagnostic::new(file_id, self) - } - pub fn with_call_stack(mut self, call_stack: Vec) -> Self { self.call_stack = call_stack; self @@ -132,12 +132,8 @@ impl CustomDiagnostic { self.notes.push(message); } - pub fn add_secondary(&mut self, message: String, span: Span) { - self.secondaries.push(CustomLabel::new(message, span, None)); - } - - pub fn add_secondary_with_file(&mut self, message: String, span: Span, file: fm::FileId) { - self.secondaries.push(CustomLabel::new(message, span, Some(file))); + pub fn add_secondary(&mut self, message: String, location: Location) { + self.secondaries.push(CustomLabel::new(message, location)); } pub fn is_error(&self) -> bool { @@ -176,13 +172,12 @@ impl std::fmt::Display for CustomDiagnostic { #[derive(Debug, Clone, PartialEq, Eq)] pub struct CustomLabel { pub message: String, - pub span: Span, - pub file: Option, + pub location: Location, } impl CustomLabel { - fn new(message: String, span: Span, file: Option) -> CustomLabel { - CustomLabel { message, span, file } + fn new(message: String, location: Location) -> CustomLabel { + CustomLabel { message, location } } } @@ -190,16 +185,16 @@ impl CustomLabel { /// of diagnostics that were errors. pub fn report_all<'files>( files: &'files impl Files<'files, FileId = fm::FileId>, - diagnostics: &[FileDiagnostic], + diagnostics: &[CustomDiagnostic], deny_warnings: bool, silence_warnings: bool, ) -> ReportedErrors { // Report warnings before any errors let (warnings_and_bugs, mut errors): (Vec<_>, _) = - diagnostics.iter().partition(|item| !item.diagnostic.is_error()); + diagnostics.iter().partition(|item| !item.is_error()); let (warnings, mut bugs): (Vec<_>, _) = - warnings_and_bugs.iter().partition(|item| item.diagnostic.is_warning()); + warnings_and_bugs.iter().partition(|item| item.is_warning()); let mut diagnostics = if silence_warnings { Vec::new() } else { warnings }; diagnostics.append(&mut bugs); diagnostics.append(&mut errors); @@ -210,14 +205,14 @@ pub fn report_all<'files>( ReportedErrors { error_count } } -impl FileDiagnostic { +impl CustomDiagnostic { /// Print the report; return true if it was an error. pub fn report<'files>( &self, files: &'files impl Files<'files, FileId = fm::FileId>, deny_warnings: bool, ) -> bool { - report(files, &self.diagnostic, Some(self.file_id), deny_warnings) + report(files, self, deny_warnings) } } @@ -225,7 +220,6 @@ impl FileDiagnostic { pub fn report<'files>( files: &'files impl Files<'files, FileId = fm::FileId>, custom_diagnostic: &CustomDiagnostic, - file: Option, deny_warnings: bool, ) -> bool { let color_choice = @@ -234,7 +228,7 @@ pub fn report<'files>( let config = term::Config::default(); let stack_trace = stack_trace(files, &custom_diagnostic.call_stack); - let diagnostic = convert_diagnostic(custom_diagnostic, file, stack_trace, deny_warnings); + let diagnostic = convert_diagnostic(custom_diagnostic, stack_trace, deny_warnings); term::emit(&mut writer.lock(), &config, files, &diagnostic).unwrap(); deny_warnings || custom_diagnostic.is_error() @@ -242,7 +236,6 @@ pub fn report<'files>( fn convert_diagnostic( cd: &CustomDiagnostic, - file: Option, stack_trace: String, deny_warnings: bool, ) -> Diagnostic { @@ -253,19 +246,18 @@ fn convert_diagnostic( _ => Diagnostic::error(), }; - let secondary_labels = if let Some(file_id) = file { - cd.secondaries - .iter() - .map(|sl| { - let start_span = sl.span.start() as usize; - let end_span = sl.span.end() as usize; - let file = sl.file.unwrap_or(file_id); - Label::secondary(file, start_span..end_span).with_message(&sl.message) - }) - .collect() - } else { - vec![] - }; + let secondary_labels = cd + .secondaries + .iter() + .map(|custom_label| { + let location = custom_label.location; + let span = location.span; + let start_span = span.start() as usize; + let end_span = span.end() as usize; + let file = location.file; + Label::secondary(file, start_span..end_span).with_message(&custom_label.message) + }) + .collect(); let mut notes = cd.notes.clone(); notes.push(stack_trace); diff --git a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml index 76aa0f50e8a2..f9cc2e7b3bf5 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml @@ -20,6 +20,7 @@ fxhash.workspace = true iter-extended.workspace = true thiserror.workspace = true num-bigint = "0.4" +num-traits.workspace = true im.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/acir_variable.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/acir_variable.rs index a4e81de3d5cd..b8f5edaa3cd6 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/acir_variable.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/acir_variable.rs @@ -1,13 +1,13 @@ use acvm::{ + BlackBoxFunctionSolver, acir::{ + AcirField, BlackBoxFunc, circuit::{ - opcodes::{AcirFunctionId, BlockId, BlockType, MemOp}, AssertionPayload, ExpressionOrMemory, ExpressionWidth, Opcode, + opcodes::{AcirFunctionId, BlockId, BlockType, MemOp}, }, native_types::{Expression, Witness}, - AcirField, BlackBoxFunc, }, - BlackBoxFunctionSolver, }; use fxhash::FxHashMap as HashMap; use iter_extended::{try_vecmap, vecmap}; @@ -22,7 +22,7 @@ use crate::ssa::ir::{ use super::big_int::BigIntContext; use super::generated_acir::{BrilligStdlibFunc, GeneratedAcir, PLACEHOLDER_BRILLIG_INDEX}; -use super::{brillig_directive, AcirDynamicArray, AcirValue}; +use super::{AcirDynamicArray, AcirValue, brillig_directive}; #[derive(Clone, Debug, PartialEq, Eq, Hash)] /// High level Type descriptor for Variables. @@ -80,7 +80,7 @@ impl From for AcirType { } } -impl<'a> From<&'a SsaType> for AcirType { +impl From<&SsaType> for AcirType { fn from(value: &SsaType) -> Self { match value { SsaType::Numeric(numeric_type) => AcirType::NumericType(*numeric_type), @@ -278,7 +278,7 @@ impl> AcirContext { let var_data = match self.vars.get(&var) { Some(var_data) => var_data, None => { - return Err(InternalError::UndeclaredAcirVar { call_stack: self.get_call_stack() }) + return Err(InternalError::UndeclaredAcirVar { call_stack: self.get_call_stack() }); } }; Ok(var_data.to_expression().into_owned()) @@ -777,7 +777,8 @@ impl> AcirContext { pub(crate) fn not_var(&mut self, x: AcirVar, typ: AcirType) -> Result { let bit_size = typ.bit_size::(); // Subtracting from max flips the bits - let max = self.add_constant((1_u128 << bit_size) - 1); + let max = power_of_two::(bit_size) - F::one(); + let max = self.add_constant(max); self.sub_var(max, x) } @@ -841,18 +842,19 @@ impl> AcirContext { } // maximum bit size for q and for [r and rhs] - let mut max_q_bits = bit_size; - let mut max_rhs_bits = bit_size; - // when rhs is constant, we can better estimate the maximum bit sizes - if let Some(rhs_const) = rhs_expr.to_const() { - max_rhs_bits = rhs_const.num_bits(); - if max_rhs_bits != 0 { - if max_rhs_bits > bit_size { - return Ok((zero, zero)); - } - max_q_bits = bit_size - max_rhs_bits + 1; - } - } + let (max_q_bits, max_rhs_bits) = if let Some(rhs_const) = rhs_expr.to_const() { + // when rhs is constant, we can better estimate the maximum bit sizes + let max_rhs_bits = rhs_const.num_bits(); + assert!( + max_rhs_bits <= bit_size, + "attempted to divide by constant larger than operand type" + ); + + let max_q_bits = bit_size - max_rhs_bits + 1; + (max_q_bits, max_rhs_bits) + } else { + (bit_size, bit_size) + }; let [q_value, r_value]: [AcirValue; 2] = self .brillig_call( @@ -908,19 +910,9 @@ impl> AcirContext { self.assert_eq_var(lhs_constraint, rhs_constraint, None)?; // Avoids overflow: 'q*b+r < 2^max_q_bits*2^max_rhs_bits' - let mut avoid_overflow = false; if max_q_bits + max_rhs_bits >= F::max_num_bits() - 1 { // q*b+r can overflow; we avoid this when b is constant - if rhs_expr.is_const() { - avoid_overflow = true; - } else { - // we do not support unbounded division - unreachable!("overflow in unbounded division"); - } - } - - if let Some(rhs_const) = rhs_expr.to_const() { - if avoid_overflow { + if let Some(rhs_const) = rhs_expr.to_const() { // we compute q0 = p/rhs let rhs_big = BigUint::from_bytes_be(&rhs_const.to_be_bytes()); let q0_big = F::modulus() / &rhs_big; @@ -944,6 +936,20 @@ impl> AcirContext { predicate, rhs_const.num_bits(), )?; + } else if bit_size == 128 { + // q and b are u128 and q*b could overflow so we check that either q or b are less than 2^64 + let two_pow_64: F = power_of_two(64); + let two_pow_64 = self.add_constant(two_pow_64); + + let (q_upper, _) = + self.euclidean_division_var(quotient_var, two_pow_64, bit_size, predicate)?; + let (rhs_upper, _) = + self.euclidean_division_var(rhs, two_pow_64, bit_size, predicate)?; + let mul_uppers = self.mul_var(q_upper, rhs_upper)?; + self.assert_eq_var(mul_uppers, zero, None)?; + } else { + // we do not support unbounded division + unreachable!("overflow in unbounded division"); } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/black_box.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/black_box.rs index 7b386d6c1886..a75d53cf10a3 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/black_box.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/black_box.rs @@ -1,9 +1,9 @@ use acvm::{ + BlackBoxFunctionSolver, acir::{ - circuit::opcodes::{ConstantOrWitnessEnum, FunctionInput}, AcirField, BlackBoxFunc, + circuit::opcodes::{ConstantOrWitnessEnum, FunctionInput}, }, - BlackBoxFunctionSolver, }; use iter_extended::vecmap; use num_bigint::BigUint; @@ -11,8 +11,8 @@ use num_bigint::BigUint; use crate::errors::{InternalError, RuntimeError}; use super::{ - acir_variable::{AcirContext, AcirVar}, AcirValue, + acir_variable::{AcirContext, AcirVar}, }; impl> AcirContext { @@ -35,7 +35,7 @@ impl> AcirContext { name: "poseidon_2_permutation call".to_string(), arg: "length".to_string(), call_stack: self.get_call_stack(), - })) + })); } }; @@ -45,7 +45,7 @@ impl> AcirContext { return Err(RuntimeError::InternalError(InternalError::NotAConstant { name: "length".to_string(), call_stack: self.get_call_stack(), - })) + })); } }; @@ -160,7 +160,7 @@ impl> AcirContext { name: "verify proof".to_string(), arg: "proof type".to_string(), call_stack: self.get_call_stack(), - })) + })); } }; @@ -170,7 +170,7 @@ impl> AcirContext { return Err(RuntimeError::InternalError(InternalError::NotAConstant { name: "proof type".to_string(), call_stack: self.get_call_stack(), - })) + })); } }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_call.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_call.rs index f027a7b62f80..020c60968a13 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_call.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_call.rs @@ -1,12 +1,12 @@ use acvm::{ + BlackBoxFunctionSolver, acir::{ + AcirField, brillig::Opcode as BrilligOpcode, circuit::brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}, native_types::{Expression, Witness}, - AcirField, }, - brillig_vm::{MemoryValue, VMStatus, VM}, - BlackBoxFunctionSolver, + brillig_vm::{MemoryValue, VM, VMStatus}, }; use iter_extended::{try_vecmap, vecmap}; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_directive.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_directive.rs index 89fc7f1eda5d..5fab9e345231 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_directive.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/brillig_directive.rs @@ -1,9 +1,9 @@ use acvm::acir::{ + AcirField, brillig::{ BinaryFieldOp, BinaryIntOp, BitSize, HeapVector, IntegerBitSize, MemoryAddress, Opcode as BrilligOpcode, }, - AcirField, }; use crate::brillig::brillig_ir::artifact::GeneratedBrillig; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/generated_acir.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/generated_acir.rs index 141c9f367c2c..25b4771048c9 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/generated_acir.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/generated_acir.rs @@ -3,21 +3,21 @@ use std::collections::BTreeMap; use acvm::acir::{ + AcirField, BlackBoxFunc, circuit::{ + AssertionPayload, BrilligOpcodeLocation, ErrorSelector, OpcodeLocation, brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}, opcodes::{BlackBoxFuncCall, FunctionInput, Opcode as AcirOpcode}, - AssertionPayload, BrilligOpcodeLocation, ErrorSelector, OpcodeLocation, }, native_types::{Expression, Witness}, - AcirField, BlackBoxFunc, }; use super::brillig_directive; use crate::{ + ErrorType, brillig::brillig_ir::artifact::GeneratedBrillig, errors::{InternalError, RuntimeError, SsaReport}, ssa::ir::call_stack::CallStack, - ErrorType, }; use iter_extended::vecmap; @@ -788,7 +788,10 @@ fn intrinsics_check_inputs(name: BlackBoxFunc, input_count: usize) { None => return, }; - assert_eq!(expected_num_inputs,input_count,"Tried to call black box function {name} with {input_count} inputs, but this function's definition requires {expected_num_inputs} inputs"); + assert_eq!( + expected_num_inputs, input_count, + "Tried to call black box function {name} with {input_count} inputs, but this function's definition requires {expected_num_inputs} inputs" + ); } /// Checks that the number of outputs being used to call the blackbox function @@ -818,5 +821,8 @@ fn intrinsics_check_outputs(name: BlackBoxFunc, output_count: usize) { None => return, }; - assert_eq!(expected_num_outputs,output_count,"Tried to call black box function {name} with {output_count} outputs, but this function's definition requires {expected_num_outputs} outputs"); + assert_eq!( + expected_num_outputs, output_count, + "Tried to call black box function {name} with {output_count} outputs, but this function's definition requires {expected_num_outputs} outputs" + ); } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/mod.rs index dc6b3826b60e..a2c4913b5e7a 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/mod.rs @@ -5,15 +5,15 @@ use std::collections::{BTreeMap, HashSet}; use std::fmt::Debug; use acvm::acir::{ + BlackBoxFunc, circuit::{ + AssertionPayload, ErrorSelector, ExpressionWidth, OpcodeLocation, brillig::{BrilligBytecode, BrilligFunctionId}, opcodes::{AcirFunctionId, BlockType}, - AssertionPayload, ErrorSelector, ExpressionWidth, OpcodeLocation, }, native_types::Witness, - BlackBoxFunc, }; -use acvm::{acir::circuit::opcodes::BlockId, acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField, acir::circuit::opcodes::BlockId}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use iter_extended::{try_vecmap, vecmap}; use noirc_frontend::monomorphization::ast::InlineType; @@ -25,12 +25,12 @@ mod brillig_call; mod brillig_directive; mod generated_acir; -use crate::brillig::brillig_gen::gen_brillig_for; use crate::brillig::BrilligOptions; +use crate::brillig::brillig_gen::gen_brillig_for; use crate::brillig::{ + Brillig, brillig_gen::brillig_fn::FunctionContext as BrilligFunctionContext, brillig_ir::artifact::{BrilligParameter, GeneratedBrillig}, - Brillig, }; use crate::errors::{InternalError, InternalWarning, RuntimeError, SsaReport}; use crate::ssa::ir::instruction::Hint; @@ -51,7 +51,7 @@ use crate::ssa::{ }, ssa_gen::Ssa, }; -use acir_variable::{power_of_two, AcirContext, AcirType, AcirVar}; +use acir_variable::{AcirContext, AcirType, AcirVar, power_of_two}; use generated_acir::BrilligStdlibFunc; pub(crate) use generated_acir::GeneratedAcir; use noirc_frontend::hir_def::types::Type as HirType; @@ -404,11 +404,15 @@ impl<'a> Context<'a> { match inline_type { InlineType::Inline | InlineType::InlineAlways => { if function.id() != ssa.main_id { - panic!("ACIR function should have been inlined earlier if not marked otherwise"); + panic!( + "ACIR function should have been inlined earlier if not marked otherwise" + ); } } InlineType::NoPredicates => { - panic!("All ACIR functions marked with #[no_predicates] should be inlined before ACIR gen. This is an SSA exclusive codegen attribute"); + panic!( + "All ACIR functions marked with #[no_predicates] should be inlined before ACIR gen. This is an SSA exclusive codegen attribute" + ); } InlineType::Fold => {} } @@ -863,7 +867,11 @@ impl<'a> Context<'a> { let func = &ssa.functions[id]; match func.runtime() { RuntimeType::Acir(inline_type) => { - assert!(!matches!(inline_type, InlineType::Inline), "ICE: Got an ACIR function named {} that should have already been inlined", func.name()); + assert!( + !matches!(inline_type, InlineType::Inline), + "ICE: Got an ACIR function named {} that should have already been inlined", + func.name() + ); let inputs = vecmap(arguments, |arg| self.convert_value(*arg, dfg)); let output_count = result_ids @@ -874,7 +882,9 @@ impl<'a> Context<'a> { .sum(); let Some(acir_function_id) = ssa.get_entry_point_index(id) else { - unreachable!("Expected an associated final index for call to acir function {id} with args {arguments:?}"); + unreachable!( + "Expected an associated final index for call to acir function {id} with args {arguments:?}" + ); }; let output_vars = self.acir_context.call_acir_function( @@ -956,7 +966,11 @@ impl<'a> Context<'a> { }; // Compiler sanity check - assert_eq!(result_ids.len(), output_values.len(), "ICE: The number of Brillig output values should match the result ids in SSA"); + assert_eq!( + result_ids.len(), + output_values.len(), + "ICE: The number of Brillig output values should match the result ids in SSA" + ); self.handle_ssa_call_outputs(result_ids, output_values, dfg)?; } @@ -1076,7 +1090,7 @@ impl<'a> Context<'a> { found: format!("Instead got {:?}", dfg[instruction]), call_stack: self.acir_context.get_call_stack(), } - .into()) + .into()); } }; // Ensure that array id is fully resolved. @@ -1477,7 +1491,7 @@ impl<'a> Context<'a> { found: format!("Instead got {:?}", dfg[instruction]), call_stack: self.acir_context.get_call_stack(), } - .into()) + .into()); } }; @@ -1525,7 +1539,11 @@ impl<'a> Context<'a> { let value_types = self.convert_value(array, dfg).flat_numeric_types(); // Compiler sanity check - assert_eq!(value_types.len(), array_len, "ICE: The length of the flattened type array should match the length of the dynamic array"); + assert_eq!( + value_types.len(), + array_len, + "ICE: The length of the flattened type array should match the length of the dynamic array" + ); let result_value = AcirValue::DynamicArray(AcirDynamicArray { block_id: result_block_id, @@ -1675,7 +1693,7 @@ impl<'a> Context<'a> { found: format!("{:?}", array_acir_value), call_stack: self.acir_context.get_call_stack(), } - .into()) + .into()); } } } @@ -1685,7 +1703,7 @@ impl<'a> Context<'a> { found: format!("{:?}", &dfg[array_id]), call_stack: self.acir_context.get_call_stack(), } - .into()) + .into()); } }; } @@ -1973,26 +1991,7 @@ impl<'a> Context<'a> { ) -> Result { let lhs = self.convert_numeric_value(binary.lhs, dfg)?; let rhs = self.convert_numeric_value(binary.rhs, dfg)?; - let binary_type = self.type_of_binary_operation(binary, dfg); - match &binary_type { - Type::Numeric(NumericType::Unsigned { bit_size }) - | Type::Numeric(NumericType::Signed { bit_size }) => { - // Conservative max bit size that is small enough such that two operands can be - // multiplied and still fit within the field modulus. This is necessary for the - // truncation technique: result % 2^bit_size to be valid. - let max_integer_bit_size = FieldElement::max_num_bits() / 2; - if *bit_size > max_integer_bit_size { - return Err(RuntimeError::UnsupportedIntegerSize { - num_bits: *bit_size, - max_num_bits: max_integer_bit_size, - call_stack: self.acir_context.get_call_stack(), - }); - } - } - _ => {} - } - let binary_type = AcirType::from(binary_type); let bit_count = binary_type.bit_size::(); let num_type = binary_type.to_numeric_type(); @@ -2113,6 +2112,12 @@ impl<'a> Context<'a> { max_bit_size: u32, dfg: &DataFlowGraph, ) -> Result { + assert_ne!(bit_size, max_bit_size, "Attempted to generate a noop truncation"); + assert!( + bit_size < max_bit_size, + "Attempted to generate a truncation into size larger than max input" + ); + let mut var = self.convert_numeric_value(value_id, dfg)?; match &dfg[value_id] { Value::Instruction { instruction, .. } => { @@ -2192,7 +2197,9 @@ impl<'a> Context<'a> { Ok(self.convert_vars_to_values(vars, dfg, result_ids)) } Intrinsic::ApplyRangeConstraint => { - unreachable!("ICE: `Intrinsic::ApplyRangeConstraint` calls should be transformed into an `Instruction::RangeCheck`"); + unreachable!( + "ICE: `Intrinsic::ApplyRangeConstraint` calls should be transformed into an `Instruction::RangeCheck`" + ); } Intrinsic::ToRadix(endian) => { let field = self.convert_value(arguments[0], dfg).into_var()?; @@ -2895,15 +2902,15 @@ fn can_omit_element_sizes_array(array_typ: &Type) -> bool { mod test { use acvm::{ + FieldElement, acir::{ circuit::{ + ExpressionWidth, Opcode, OpcodeLocation, brillig::BrilligFunctionId, opcodes::{AcirFunctionId, BlackBoxFuncCall}, - ExpressionWidth, Opcode, OpcodeLocation, }, native_types::Witness, }, - FieldElement, }; use noirc_errors::Location; use noirc_frontend::monomorphization::ast::InlineType; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs index 957ebc2b0695..64160528148d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs @@ -12,11 +12,11 @@ use fxhash::FxHashMap as HashMap; use self::{brillig_block::BrilligBlock, brillig_fn::FunctionContext}; use super::{ + Brillig, BrilligOptions, BrilligVariable, ValueId, brillig_ir::{ - artifact::{BrilligArtifact, BrilligParameter, GeneratedBrillig, Label}, BrilligContext, + artifact::{BrilligArtifact, BrilligParameter, GeneratedBrillig, Label}, }, - Brillig, BrilligOptions, BrilligVariable, ValueId, }; use crate::{ errors::InternalError, @@ -88,7 +88,7 @@ pub(crate) fn gen_brillig_for( return Err(InternalError::General { message: format!("Cannot find linked fn {unresolved_fn_label}"), call_stack: CallStack::new(), - }) + }); } }; entry_point.link_with(artifact); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs index 1fc39b582232..cdc6df262402 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs @@ -1,14 +1,14 @@ use acvm::{ + AcirField, acir::{ - brillig::{BlackBoxOp, HeapVector, ValueOrArray}, BlackBoxFunc, + brillig::{BlackBoxOp, HeapVector, ValueOrArray}, }, - AcirField, }; use crate::brillig::brillig_ir::{ - brillig_variable::BrilligVariable, debug_show::DebugToString, registers::RegisterAllocator, - BrilligContext, + BrilligContext, brillig_variable::BrilligVariable, debug_show::DebugToString, + registers::RegisterAllocator, }; /// Transforms SSA's black box function calls into the corresponding brillig instructions @@ -83,7 +83,12 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::BrilligArray(public_key_x), BrilligVariable::BrilligArray(public_key_y), BrilligVariable::BrilligArray(signature), message], + [ + BrilligVariable::BrilligArray(public_key_x), + BrilligVariable::BrilligArray(public_key_y), + BrilligVariable::BrilligArray(signature), + message, + ], [BrilligVariable::SingleAddr(result_register)], ) = (function_arguments, function_results) { @@ -114,7 +119,12 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::BrilligArray(public_key_x), BrilligVariable::BrilligArray(public_key_y), BrilligVariable::BrilligArray(signature), message], + [ + BrilligVariable::BrilligArray(public_key_x), + BrilligVariable::BrilligArray(public_key_y), + BrilligVariable::BrilligArray(signature), + message, + ], [BrilligVariable::SingleAddr(result_register)], ) = (function_arguments, function_results) { @@ -168,7 +178,14 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::SingleAddr(input1_x), BrilligVariable::SingleAddr(input1_y), BrilligVariable::SingleAddr(input1_infinite), BrilligVariable::SingleAddr(input2_x), BrilligVariable::SingleAddr(input2_y), BrilligVariable::SingleAddr(input2_infinite)], + [ + BrilligVariable::SingleAddr(input1_x), + BrilligVariable::SingleAddr(input1_y), + BrilligVariable::SingleAddr(input1_infinite), + BrilligVariable::SingleAddr(input2_x), + BrilligVariable::SingleAddr(input2_y), + BrilligVariable::SingleAddr(input2_infinite), + ], [BrilligVariable::BrilligArray(result_array)], ) = (function_arguments, function_results) { @@ -202,7 +219,12 @@ pub(crate) fn convert_black_box_call {} BlackBoxFunc::BigIntAdd => { if let ( - [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(_lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(_rhs_modulus)], + [ + BrilligVariable::SingleAddr(lhs), + BrilligVariable::SingleAddr(_lhs_modulus), + BrilligVariable::SingleAddr(rhs), + BrilligVariable::SingleAddr(_rhs_modulus), + ], [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(_modulus_id)], ) = (function_arguments, function_results) { @@ -219,7 +241,12 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(_lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(_rhs_modulus)], + [ + BrilligVariable::SingleAddr(lhs), + BrilligVariable::SingleAddr(_lhs_modulus), + BrilligVariable::SingleAddr(rhs), + BrilligVariable::SingleAddr(_rhs_modulus), + ], [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(_modulus_id)], ) = (function_arguments, function_results) { @@ -236,7 +263,12 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(_lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(_rhs_modulus)], + [ + BrilligVariable::SingleAddr(lhs), + BrilligVariable::SingleAddr(_lhs_modulus), + BrilligVariable::SingleAddr(rhs), + BrilligVariable::SingleAddr(_rhs_modulus), + ], [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(_modulus_id)], ) = (function_arguments, function_results) { @@ -253,7 +285,12 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(_lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(_rhs_modulus)], + [ + BrilligVariable::SingleAddr(lhs), + BrilligVariable::SingleAddr(_lhs_modulus), + BrilligVariable::SingleAddr(rhs), + BrilligVariable::SingleAddr(_rhs_modulus), + ], [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(_modulus_id)], ) = (function_arguments, function_results) { @@ -326,12 +363,17 @@ pub(crate) fn convert_black_box_call { if let ( - [BrilligVariable::BrilligArray(input_array), BrilligVariable::BrilligArray(hash_values)], + [ + BrilligVariable::BrilligArray(input_array), + BrilligVariable::BrilligArray(hash_values), + ], [BrilligVariable::BrilligArray(result_array)], ) = (function_arguments, function_results) { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 40dd825be35d..d8f1f9d0997e 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -1,11 +1,11 @@ use crate::brillig::brillig_ir::artifact::Label; use crate::brillig::brillig_ir::brillig_variable::{ - type_to_heap_value_type, BrilligArray, BrilligVariable, SingleAddrVariable, + BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable, type_to_heap_value_type, }; use crate::brillig::brillig_ir::registers::RegisterAllocator; use crate::brillig::brillig_ir::{ - BrilligBinaryOp, BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, + BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, BrilligBinaryOp, BrilligContext, ReservedRegisters, }; use crate::ssa::ir::call_stack::CallStack; use crate::ssa::ir::instruction::{ConstrainError, Hint}; @@ -21,7 +21,7 @@ use crate::ssa::ir::{ }; use acvm::acir::brillig::{MemoryAddress, ValueOrArray}; use acvm::brillig_vm::MEMORY_ADDRESSING_BIT_SIZE; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use iter_extended::vecmap; use num_bigint::BigUint; @@ -29,7 +29,7 @@ use std::collections::BTreeSet; use std::sync::Arc; use super::brillig_black_box::convert_black_box_call; -use super::brillig_block_variables::{allocate_value_with_type, BlockVariables}; +use super::brillig_block_variables::{BlockVariables, allocate_value_with_type}; use super::brillig_fn::FunctionContext; use super::brillig_globals::HoistedConstantsToBrilligGlobals; use super::constant_allocation::InstructionLocation; @@ -460,7 +460,9 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { element_size, ); } else { - unreachable!("ICE: a vector must be preceded by a register containing its length"); + unreachable!( + "ICE: a vector must be preceded by a register containing its length" + ); } self.brillig_context.deallocate_heap_vector(*heap_vector); } @@ -769,21 +771,28 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { // Slice access checks are generated separately against the slice's dynamic length field. if matches!(dfg.type_of_value(*array), Type::Array(..)) - && !dfg.is_safe_index(*index, *array) + && !dfg.is_safe_brillig_index(*index, *array) { self.validate_array_index(array_variable, index_variable); } - let items_pointer = - self.brillig_context.codegen_make_array_or_vector_items_pointer(array_variable); - - self.brillig_context.codegen_load_with_offset( - items_pointer, - index_variable, - destination_variable.extract_register(), - ); - - self.brillig_context.deallocate_register(items_pointer); + if dfg.is_constant(*index) { + self.brillig_context.codegen_load_with_offset( + array_variable.extract_register(), + index_variable, + destination_variable.extract_register(), + ); + } else { + let items_pointer = self + .brillig_context + .codegen_make_array_or_vector_items_pointer(array_variable); + self.brillig_context.codegen_load_with_offset( + items_pointer, + index_variable, + destination_variable.extract_register(), + ); + self.brillig_context.deallocate_register(items_pointer); + } } Instruction::ArraySet { array, index, value, mutable } => { let source_variable = self.convert_ssa_value(*array, dfg); @@ -926,8 +935,64 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { Instruction::EnableSideEffectsIf { .. } => { unreachable!("enable_side_effects not supported by brillig") } - Instruction::IfElse { .. } => { - unreachable!("IfElse instructions should not be possible in brillig") + Instruction::IfElse { then_condition, then_value, else_condition: _, else_value } => { + let then_condition = self.convert_ssa_single_addr_value(*then_condition, dfg); + let then_value = self.convert_ssa_value(*then_value, dfg); + let else_value = self.convert_ssa_value(*else_value, dfg); + let result = self.variables.define_variable( + self.function_context, + self.brillig_context, + dfg.instruction_results(instruction_id)[0], + dfg, + ); + match (then_value, else_value) { + ( + BrilligVariable::SingleAddr(then_address), + BrilligVariable::SingleAddr(else_address), + ) => { + self.brillig_context.conditional_move_instruction( + then_condition, + then_address, + else_address, + result.extract_single_addr(), + ); + } + ( + BrilligVariable::BrilligArray(then_array), + BrilligVariable::BrilligArray(else_array), + ) => { + // Pointer to the array which result from the if-else + let pointer = self.brillig_context.allocate_register(); + self.brillig_context.conditional_move_instruction( + then_condition, + SingleAddrVariable::new_usize(then_array.pointer), + SingleAddrVariable::new_usize(else_array.pointer), + SingleAddrVariable::new_usize(pointer), + ); + let if_else_array = BrilligArray { pointer, size: then_array.size }; + // Copy the if-else array to the result + self.brillig_context + .call_array_copy_procedure(if_else_array, result.extract_array()); + } + ( + BrilligVariable::BrilligVector(then_vector), + BrilligVariable::BrilligVector(else_vector), + ) => { + // Pointer to the vector which result from the if-else + let pointer = self.brillig_context.allocate_register(); + self.brillig_context.conditional_move_instruction( + then_condition, + SingleAddrVariable::new_usize(then_vector.pointer), + SingleAddrVariable::new_usize(else_vector.pointer), + SingleAddrVariable::new_usize(pointer), + ); + let if_else_vector = BrilligVector { pointer }; + // Copy the if-else vector to the result + self.brillig_context + .call_vector_copy_procedure(if_else_vector, result.extract_vector()); + } + _ => unreachable!("ICE - then and else values must have the same type"), + } } Instruction::MakeArray { elements: array, typ } => { let value_id = dfg.instruction_results(instruction_id)[0]; @@ -1432,7 +1497,9 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { NumericType::Unsigned { .. } => (false, false), NumericType::NativeField => (true, false), }, - _ => unreachable!("only numeric types are allowed in binary operations. References are handled separately"), + _ => unreachable!( + "only numeric types are allowed in binary operations. References are handled separately" + ), }; let brillig_binary_op = match binary.operator { @@ -1991,14 +2058,21 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { self.allocate_foreign_call_result_array(element_type, inner_array); // We add one since array.pointer points to [RC, ...items] - let idx = - self.brillig_context.make_usize_constant_instruction((index + 1).into() ); - self.brillig_context.codegen_store_with_offset(array.pointer, idx, inner_array.pointer); + let idx = self + .brillig_context + .make_usize_constant_instruction((index + 1).into()); + self.brillig_context.codegen_store_with_offset( + array.pointer, + idx, + inner_array.pointer, + ); self.brillig_context.deallocate_single_addr(idx); self.brillig_context.deallocate_register(inner_array.pointer); } - Type::Slice(_) => unreachable!("ICE: unsupported slice type in allocate_nested_array(), expects an array or a numeric type"), + Type::Slice(_) => unreachable!( + "ICE: unsupported slice type in allocate_nested_array(), expects an array or a numeric type" + ), _ => (), } index += 1; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs index 4cf8e9214833..027994a11de4 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs @@ -3,12 +3,12 @@ use fxhash::FxHashSet as HashSet; use crate::{ brillig::brillig_ir::{ + BrilligContext, brillig_variable::{ - get_bit_size_from_ssa_type, BrilligArray, BrilligVariable, BrilligVector, - SingleAddrVariable, + BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable, + get_bit_size_from_ssa_type, }, registers::RegisterAllocator, - BrilligContext, }, ssa::ir::{ dfg::DataFlowGraph, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs index d2656354b566..6ff85b9ebe8e 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs @@ -3,7 +3,7 @@ use iter_extended::vecmap; use crate::{ brillig::brillig_ir::{ artifact::BrilligParameter, - brillig_variable::{get_bit_size_from_ssa_type, BrilligVariable}, + brillig_variable::{BrilligVariable, get_bit_size_from_ssa_type}, }, ssa::ir::{ basic_block::BasicBlockId, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_globals.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_globals.rs index 66f0980ac639..317f763078e1 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_globals.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_globals.rs @@ -8,7 +8,7 @@ use super::{ }; use crate::brillig::{Brillig, BrilligOptions, FunctionId}; use crate::{ - brillig::{brillig_ir::BrilligContext, ConstantAllocation, DataFlowGraph}, + brillig::{ConstantAllocation, DataFlowGraph, brillig_ir::BrilligContext}, ssa::ir::types::NumericType, ssa::opt::brillig_entry_points::{build_inner_call_to_entry_points, get_brillig_entry_points}, }; @@ -147,11 +147,7 @@ impl BrilligGlobals { .iter() .filter_map( |(&value, &num_occurrences)| { - if num_occurrences > 1 { - Some(value) - } else { - None - } + if num_occurrences > 1 { Some(value) } else { None } }, ) .collect(); @@ -280,12 +276,12 @@ pub(crate) fn convert_ssa_globals( #[cfg(test)] mod tests { use acvm::{ - acir::brillig::{BitSize, IntegerBitSize, Opcode}, FieldElement, + acir::brillig::{BitSize, IntegerBitSize, Opcode}, }; use crate::brillig::{ - brillig_ir::registers::RegisterAllocator, BrilligOptions, GlobalSpace, LabelType, Ssa, + BrilligOptions, GlobalSpace, LabelType, Ssa, brillig_ir::registers::RegisterAllocator, }; use super::ConstantAllocation; @@ -434,6 +430,8 @@ mod tests { "; let ssa = Ssa::from_str(src).unwrap(); + // Need to run SSA pass that sets up Brillig array gets + let ssa = ssa.brillig_array_gets(); // Need to run DIE to generate the used globals map, which is necessary for Brillig globals generation. let mut ssa = ssa.dead_instruction_elimination(); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index 99645f84ed3d..6387dc3dd371 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -1,14 +1,14 @@ use acvm::acir::brillig::MemoryAddress; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, brillig_variable::{BrilligVariable, BrilligVector, SingleAddrVariable}, registers::RegisterAllocator, - BrilligBinaryOp, }; use super::brillig_block::BrilligBlock; -impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { +impl BrilligBlock<'_, Registers> { fn write_variables(&mut self, write_pointer: MemoryAddress, variables: &[BrilligVariable]) { for (index, variable) in variables.iter().enumerate() { self.brillig_context.store_instruction(write_pointer, variable.extract_register()); @@ -163,6 +163,7 @@ mod tests { use fxhash::FxHashMap as HashMap; use noirc_frontend::monomorphization::ast::InlineType; + use crate::brillig::ValueId; use crate::brillig::brillig_gen::brillig_block::BrilligBlock; use crate::brillig::brillig_gen::brillig_block_variables::BlockVariables; use crate::brillig::brillig_gen::brillig_fn::FunctionContext; @@ -174,8 +175,7 @@ mod tests { use crate::brillig::brillig_ir::tests::{ create_and_run_vm, create_context, create_entry_point_bytecode, }; - use crate::brillig::brillig_ir::{BrilligContext, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE}; - use crate::brillig::ValueId; + use crate::brillig::brillig_ir::{BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, BrilligContext}; use crate::ssa::function_builder::FunctionBuilder; use crate::ssa::ir::function::RuntimeType; use crate::ssa::ir::map::Id; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index 95e6c5175b2d..a0637e8e3c25 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -32,8 +32,8 @@ use registers::{RegisterAllocator, ScratchSpace}; use self::{artifact::BrilligArtifact, debug_show::DebugToString, registers::Stack}; use crate::ssa::ir::call_stack::CallStack; use acvm::{ - acir::brillig::{MemoryAddress, Opcode as BrilligOpcode}, AcirField, + acir::brillig::{MemoryAddress, Opcode as BrilligOpcode}, }; use debug_show::DebugShow; @@ -281,11 +281,11 @@ pub(crate) mod tests { ValueOrArray, }; use acvm::brillig_vm::brillig::HeapValueType; - use acvm::brillig_vm::{VMStatus, VM}; + use acvm::brillig_vm::{VM, VMStatus}; use acvm::{BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement}; - use crate::brillig::brillig_ir::{BrilligBinaryOp, BrilligContext}; use crate::brillig::BrilligOptions; + use crate::brillig::brillig_ir::{BrilligBinaryOp, BrilligContext}; use crate::ssa::ir::function::FunctionId; use super::artifact::{BrilligParameter, GeneratedBrillig, Label, LabelType}; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs index c9223715042f..42052c092306 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs @@ -2,8 +2,8 @@ use acvm::acir::brillig::Opcode as BrilligOpcode; use acvm::acir::circuit::ErrorSelector; use std::collections::{BTreeMap, HashMap}; -use crate::ssa::ir::{basic_block::BasicBlockId, call_stack::CallStack, function::FunctionId}; use crate::ErrorType; +use crate::ssa::ir::{basic_block::BasicBlockId, call_stack::CallStack, function::FunctionId}; use super::procedures::ProcedureId; @@ -304,25 +304,37 @@ impl BrilligArtifact { let jump_instruction = self.byte_code[*location_of_jump].clone(); match jump_instruction { BrilligOpcode::Jump { location } => { - assert_eq!(location, 0, "location is not zero, which means that the jump label does not need resolving"); + assert_eq!( + location, 0, + "location is not zero, which means that the jump label does not need resolving" + ); self.byte_code[*location_of_jump] = BrilligOpcode::Jump { location: resolved_location }; } BrilligOpcode::JumpIfNot { condition, location } => { - assert_eq!(location, 0, "location is not zero, which means that the jump label does not need resolving"); + assert_eq!( + location, 0, + "location is not zero, which means that the jump label does not need resolving" + ); self.byte_code[*location_of_jump] = BrilligOpcode::JumpIfNot { condition, location: resolved_location }; } BrilligOpcode::JumpIf { condition, location } => { - assert_eq!(location, 0, "location is not zero, which means that the jump label does not need resolving"); + assert_eq!( + location, 0, + "location is not zero, which means that the jump label does not need resolving" + ); self.byte_code[*location_of_jump] = BrilligOpcode::JumpIf { condition, location: resolved_location }; } BrilligOpcode::Call { location } => { - assert_eq!(location, 0, "location is not zero, which means that the call label does not need resolving"); + assert_eq!( + location, 0, + "location is not zero, which means that the call label does not need resolving" + ); self.byte_code[*location_of_jump] = BrilligOpcode::Call { location: resolved_location }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs index 0bb18448670a..2a46cae2d241 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs @@ -1,7 +1,7 @@ use acvm::{ - acir::{brillig::BitSize, AcirField}, - brillig_vm::brillig::{HeapValueType, MemoryAddress}, FieldElement, + acir::{AcirField, brillig::BitSize}, + brillig_vm::brillig::{HeapValueType, MemoryAddress}, }; use serde::{Deserialize, Serialize}; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs index 9f573543e2a8..1fc37882e482 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs @@ -1,8 +1,8 @@ -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::{ - debug_show::DebugToString, instructions::BrilligBinaryOp, registers::RegisterAllocator, - BrilligContext, ReservedRegisters, + BrilligContext, ReservedRegisters, debug_show::DebugToString, instructions::BrilligBinaryOp, + registers::RegisterAllocator, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs index 4da3aa4d6d2e..5036326fbc97 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs @@ -1,12 +1,12 @@ -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use crate::ssa::ir::function::FunctionId; use super::{ + BrilligBinaryOp, BrilligContext, ReservedRegisters, brillig_variable::{BrilligVariable, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, Stack}, - BrilligBinaryOp, BrilligContext, ReservedRegisters, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs index 9512a60e6998..388c5f7a343e 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs @@ -1,19 +1,19 @@ use acvm::{ + AcirField, acir::{ brillig::{HeapVector, MemoryAddress}, circuit::ErrorSelector, }, - AcirField, }; use crate::ssa::ir::instruction::ErrorType; use super::{ + BrilligBinaryOp, BrilligContext, ReservedRegisters, artifact::BrilligParameter, brillig_variable::{BrilligArray, BrilligVariable, SingleAddrVariable}, debug_show::DebugToString, registers::RegisterAllocator, - BrilligBinaryOp, BrilligContext, ReservedRegisters, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs index 42c13a3c2355..3daf04861107 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs @@ -1,15 +1,15 @@ use acvm::acir::{ - brillig::{BlackBoxOp, IntegerBitSize}, AcirField, + brillig::{BlackBoxOp, IntegerBitSize}, }; use crate::brillig::brillig_ir::BrilligBinaryOp; use super::{ + BrilligContext, brillig_variable::{BrilligArray, SingleAddrVariable}, debug_show::DebugToString, registers::RegisterAllocator, - BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs index a34034bb5505..2936aea486ed 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs @@ -1,15 +1,15 @@ use acvm::{ - acir::brillig::{HeapArray, HeapVector, MemoryAddress, ValueOrArray}, AcirField, + acir::brillig::{HeapArray, HeapVector, MemoryAddress, ValueOrArray}, }; use crate::brillig::brillig_ir::BrilligBinaryOp; use super::{ + BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, BrilligContext, ReservedRegisters, brillig_variable::{BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::RegisterAllocator, - BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs index f609c3422886..a5dbc2811c3c 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs @@ -1,7 +1,7 @@ -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; -use super::{debug_show::DebugToString, registers::RegisterAllocator, BrilligContext}; +use super::{BrilligContext, debug_show::DebugToString, registers::RegisterAllocator}; impl BrilligContext { /// This function moves values from a set of registers to another set of registers. @@ -126,15 +126,15 @@ impl LoopDetector { #[cfg(test)] mod tests { use acvm::{ - acir::brillig::{MemoryAddress, Opcode}, FieldElement, + acir::brillig::{MemoryAddress, Opcode}, }; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::{ brillig::{ - brillig_ir::{artifact::Label, registers::Stack, BrilligContext}, BrilligOptions, + brillig_ir::{BrilligContext, artifact::Label, registers::Stack}, }, ssa::ir::function::FunctionId, }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index 283c0d67eb8b..c0b6492618c1 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -3,8 +3,8 @@ use super::BrilligBinaryOp; use crate::brillig::brillig_ir::ReservedRegisters; use acvm::{ - acir::brillig::{BlackBoxOp, HeapArray, HeapVector, MemoryAddress, ValueOrArray}, FieldElement, + acir::brillig::{BlackBoxOp, HeapArray, HeapVector, MemoryAddress, ValueOrArray}, }; /// Trait for converting values into debug-friendly strings. @@ -126,6 +126,24 @@ impl DebugShow { debug_println!(self.enable_debug_trace, " MOV {}, {}", destination, source); } + /// Emits a `conditional mov` instruction. + pub(crate) fn conditional_mov_instruction( + &self, + destination: MemoryAddress, + source_then: MemoryAddress, + source_else: MemoryAddress, + condition: MemoryAddress, + ) { + debug_println!( + self.enable_debug_trace, + " {} = MOV if {} then {}, else {}", + destination, + condition, + source_then, + source_else + ); + } + /// Emits a `cast` instruction. pub(crate) fn cast_instruction( &self, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs index 4a5be8bb19c6..e1450f0f1188 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs @@ -1,15 +1,15 @@ use crate::{brillig::BrilligOptions, ssa::ir::function::FunctionId}; use super::{ + BrilligBinaryOp, BrilligContext, ReservedRegisters, artifact::{BrilligArtifact, BrilligParameter}, brillig_variable::{BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::Stack, - BrilligBinaryOp, BrilligContext, ReservedRegisters, }; use acvm::acir::{ - brillig::{HeapVector, MemoryAddress}, AcirField, + brillig::{HeapVector, MemoryAddress}, }; pub(crate) const MAX_STACK_SIZE: usize = 16 * MAX_STACK_FRAME_SIZE; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs index 9dd541c71807..fad9892cfb16 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs @@ -1,23 +1,23 @@ use acvm::{ + FieldElement, acir::{ + AcirField, brillig::{ BinaryFieldOp, BinaryIntOp, BitSize, BlackBoxOp, HeapValueType, HeapVector, MemoryAddress, Opcode as BrilligOpcode, ValueOrArray, }, - AcirField, }, - FieldElement, }; use crate::ssa::ir::function::FunctionId; use super::{ + BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, BrilligContext, ReservedRegisters, artifact::{Label, UnresolvedJumpLocation}, brillig_variable::SingleAddrVariable, debug_show::DebugToString, procedures::ProcedureId, registers::RegisterAllocator, - BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; /// Low level instructions of the brillig IR, used by the brillig ir codegens and brillig_gen @@ -75,6 +75,28 @@ impl BrilligContext< ); } + /// Insert a conditional move instruction + pub(crate) fn conditional_move_instruction( + &mut self, + condition: SingleAddrVariable, + then_address: SingleAddrVariable, + else_address: SingleAddrVariable, + destination: SingleAddrVariable, + ) { + self.debug_show.conditional_mov_instruction( + destination.address, + then_address.address, + else_address.address, + condition.address, + ); + self.push_opcode(BrilligOpcode::ConditionalMov { + destination: destination.address, + source_a: then_address.address, + source_b: else_address.address, + condition: condition.address, + }); + } + fn binary( &mut self, lhs: SingleAddrVariable, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_copy.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_copy.rs index 0a6e8824223e..cccd08fe2d50 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_copy.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_copy.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligArray, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_reverse.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_reverse.rs index a5a11d61bef7..aa5d1cfeece8 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_reverse.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/array_reverse.rs @@ -1,12 +1,12 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligContext, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/check_max_stack_depth.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/check_max_stack_depth.rs index 4d5abe934209..fd8552903c71 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/check_max_stack_depth.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/check_max_stack_depth.rs @@ -2,11 +2,11 @@ use acvm::AcirField; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, BrilligContext, ReservedRegisters, brillig_variable::SingleAddrVariable, debug_show::DebugToString, entry_point::{MAX_STACK_FRAME_SIZE, MAX_STACK_SIZE}, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, ReservedRegisters, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mem_copy.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mem_copy.rs index cdd995424836..9f3f1daa2ac9 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mem_copy.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mem_copy.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligContext, brillig_variable::SingleAddrVariable, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mod.rs index 8e1761d0eee4..a2d00786ff8c 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mod.rs @@ -25,12 +25,12 @@ use vector_pop_back::compile_vector_pop_back_procedure; use vector_pop_front::compile_vector_pop_front_procedure; use vector_remove::compile_vector_remove_procedure; -use crate::brillig::{brillig_ir::AcirField, BrilligOptions}; +use crate::brillig::{BrilligOptions, brillig_ir::AcirField}; use super::{ + BrilligContext, artifact::{BrilligArtifact, Label}, debug_show::DebugToString, - BrilligContext, }; /// Procedures are a set of complex operations that are common in the noir language. diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_insert.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_insert.rs index 1c1a738509cb..dced68922ef3 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_insert.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_insert.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; -use super::{prepare_vector_push::reallocate_vector_for_insertion, ProcedureId}; +use super::{ProcedureId, prepare_vector_push::reallocate_vector_for_insertion}; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_push.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_push.rs index 798cf2385e5b..4d985ccb12b7 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_push.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/prepare_vector_push.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/revert_with_string.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/revert_with_string.rs index 83d7dfc618d9..1601451ef50e 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/revert_with_string.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/revert_with_string.rs @@ -2,9 +2,9 @@ use acvm::AcirField; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligContext, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_copy.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_copy.rs index 6d2f9c4afb4f..8e53fb8d01cd 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_copy.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_copy.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_back.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_back.rs index bfc9d5128526..bd71b39f9e71 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_back.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_back.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_front.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_front.rs index 49123ca2f500..8df8f1c6b12f 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_front.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_pop_front.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_remove.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_remove.rs index 7abc43286ee7..686fc74210a6 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_remove.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/vector_remove.rs @@ -1,13 +1,13 @@ use std::vec; -use acvm::{acir::brillig::MemoryAddress, AcirField}; +use acvm::{AcirField, acir::brillig::MemoryAddress}; use super::ProcedureId; use crate::brillig::brillig_ir::{ + BrilligBinaryOp, BrilligContext, brillig_variable::{BrilligVector, SingleAddrVariable}, debug_show::DebugToString, registers::{RegisterAllocator, ScratchSpace}, - BrilligBinaryOp, BrilligContext, }; impl BrilligContext { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs index 093c99dec3b5..2fc7f6fc7115 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs @@ -6,9 +6,9 @@ use iter_extended::vecmap; use crate::brillig::brillig_ir::entry_point::MAX_STACK_SIZE; use super::{ + BrilligContext, ReservedRegisters, brillig_variable::SingleAddrVariable, entry_point::{MAX_SCRATCH_SPACE, MAX_STACK_FRAME_SIZE}, - BrilligContext, ReservedRegisters, }; pub(crate) trait RegisterAllocator { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs b/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs index 94c0e0554a4d..deaefd40ae32 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs @@ -7,9 +7,9 @@ //! An Error of the former is a user Error //! //! An Error of the latter is an error in the implementation of the compiler -use acvm::FieldElement; use iter_extended::vecmap; -use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic}; +use noirc_errors::{CustomDiagnostic, Location}; +use noirc_frontend::signed_field::SignedField; use thiserror::Error; use crate::ssa::ir::{call_stack::CallStack, types::NumericType}; @@ -23,7 +23,7 @@ pub enum RuntimeError { InvalidRangeConstraint { num_bits: u32, call_stack: CallStack }, #[error("The value `{value:?}` cannot fit into `{typ}` which has range `{range}`")] IntegerOutOfBounds { - value: FieldElement, + value: SignedField, typ: NumericType, range: String, call_stack: CallStack, @@ -34,7 +34,9 @@ pub enum RuntimeError { UnInitialized { name: String, call_stack: CallStack }, #[error("Integer sized {num_bits:?} is over the max supported size of {max_num_bits:?}")] UnsupportedIntegerSize { num_bits: u32, max_num_bits: u32, call_stack: CallStack }, - #[error("Integer {value}, sized {num_bits:?}, is over the max supported size of {max_num_bits:?} for the blackbox function's inputs")] + #[error( + "Integer {value}, sized {num_bits:?}, is over the max supported size of {max_num_bits:?} for the blackbox function's inputs" + )] InvalidBlackBoxInputBitSize { value: String, num_bits: u32, @@ -59,7 +61,9 @@ pub enum RuntimeError { UnconstrainedSliceReturnToConstrained { call_stack: CallStack }, #[error("All `oracle` methods should be wrapped in an unconstrained fn")] UnconstrainedOracleReturnToConstrained { call_stack: CallStack }, - #[error("Could not resolve some references to the array. All references must be resolved at compile time")] + #[error( + "Could not resolve some references to the array. All references must be resolved at compile time" + )] UnknownReference { call_stack: CallStack }, } @@ -69,8 +73,8 @@ pub enum SsaReport { Bug(InternalBug), } -impl From for FileDiagnostic { - fn from(error: SsaReport) -> FileDiagnostic { +impl From for CustomDiagnostic { + fn from(error: SsaReport) -> CustomDiagnostic { match error { SsaReport::Warning(warning) => { let message = warning.to_string(); @@ -83,11 +87,10 @@ impl From for FileDiagnostic { }, }; let call_stack = vecmap(call_stack, |location| location); - let file_id = call_stack.last().map(|location| location.file).unwrap_or_default(); let location = call_stack.last().expect("Expected RuntimeError to have a location"); let diagnostic = - Diagnostic::simple_warning(message, secondary_message, location.span); - diagnostic.with_call_stack(call_stack).in_file(file_id) + CustomDiagnostic::simple_warning(message, secondary_message, *location); + diagnostic.with_call_stack(call_stack) } SsaReport::Bug(bug) => { let message = bug.to_string(); @@ -101,10 +104,10 @@ impl From for FileDiagnostic { InternalBug::AssertFailed { call_stack } => ("As a result, the compiled circuit is ensured to fail. Other assertions may also fail during execution".to_string(), call_stack) }; let call_stack = vecmap(call_stack, |location| location); - let file_id = call_stack.last().map(|location| location.file).unwrap_or_default(); let location = call_stack.last().expect("Expected RuntimeError to have a location"); - let diagnostic = Diagnostic::simple_bug(message, secondary_message, location.span); - diagnostic.with_call_stack(call_stack).in_file(file_id) + let diagnostic = + CustomDiagnostic::simple_bug(message, secondary_message, *location); + diagnostic.with_call_stack(call_stack) } } } @@ -178,24 +181,23 @@ impl RuntimeError { } } -impl From for FileDiagnostic { - fn from(error: RuntimeError) -> FileDiagnostic { +impl From for CustomDiagnostic { + fn from(error: RuntimeError) -> CustomDiagnostic { let call_stack = vecmap(error.call_stack(), |location| *location); - let file_id = call_stack.last().map(|location| location.file).unwrap_or_default(); let diagnostic = error.into_diagnostic(); - diagnostic.with_call_stack(call_stack).in_file(file_id) + diagnostic.with_call_stack(call_stack) } } impl RuntimeError { - fn into_diagnostic(self) -> Diagnostic { + fn into_diagnostic(self) -> CustomDiagnostic { match self { RuntimeError::InternalError(cause) => { - Diagnostic::simple_error( + CustomDiagnostic::simple_error( "Internal Consistency Evaluators Errors: \n This is likely a bug. Consider opening an issue at https://github.com/noir-lang/noir/issues".to_owned(), cause.to_string(), - noirc_errors::Span::inclusive(0, 0) + Location::dummy(), ) } RuntimeError::UnknownLoopBound { .. } => { @@ -203,10 +205,10 @@ impl RuntimeError { let location = self.call_stack().last().expect("Expected RuntimeError to have a location"); - Diagnostic::simple_error( + CustomDiagnostic::simple_error( primary_message, "If attempting to fetch the length of a slice, try converting to an array. Slices only use dynamic lengths.".to_string(), - location.span, + *location, ) } _ => { @@ -214,7 +216,7 @@ impl RuntimeError { let location = self.call_stack().last().unwrap_or_else(|| panic!("Expected RuntimeError to have a location. Error message: {message}")); - Diagnostic::simple_error(message, String::new(), location.span) + CustomDiagnostic::simple_error(message, String::new(), *location) } } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/lib.rs b/noir/noir-repo/compiler/noirc_evaluator/src/lib.rs index 75ea557d3de0..5c212e8f38a9 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/lib.rs @@ -20,10 +20,20 @@ pub(crate) fn trim_leading_whitespace_from_lines(src: &str) -> String { while first_line.is_empty() { first_line = lines.next().unwrap(); } + let first_line_original_length = first_line.len(); let mut result = first_line.trim_start().to_string(); + let first_line_trimmed_length = result.len(); + + // Try to see how many spaces we chopped off the first line + let difference = first_line_original_length - first_line_trimmed_length; for line in lines { result.push('\n'); - result.push_str(line.trim_start()); + // Try to remove just `difference` spaces to preserve indents + if line.len() - line.trim_start().len() >= difference { + result.push_str(&line.chars().skip(difference).collect::()); + } else { + result.push_str(line.trim_start()); + } } result } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs index 74196d9a7666..935918c6b7e8 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs @@ -18,14 +18,14 @@ use crate::{ errors::{RuntimeError, SsaReport}, }; use acvm::{ + FieldElement, acir::{ circuit::{ - brillig::BrilligBytecode, Circuit, ErrorSelector, ExpressionWidth, - Program as AcirProgram, PublicInputs, + Circuit, ErrorSelector, ExpressionWidth, Program as AcirProgram, PublicInputs, + brillig::BrilligBytecode, }, native_types::Witness, }, - FieldElement, }; use ir::instruction::ErrorType; @@ -34,7 +34,7 @@ use noirc_errors::debug_info::{DebugFunctions, DebugInfo, DebugTypes, DebugVaria use noirc_frontend::ast::Visibility; use noirc_frontend::{hir_def::function::FunctionSignature, monomorphization::ast::Program}; use ssa_gen::Ssa; -use tracing::{span, Level}; +use tracing::{Level, span}; use crate::acir::{Artifacts, GeneratedAcir}; @@ -72,8 +72,8 @@ pub struct SsaEvaluatorOptions { /// Skip the check for under constrained values pub skip_underconstrained_check: bool, - /// Enable the missing Brillig call constraints check - pub enable_brillig_constraints_check: bool, + /// Skip the missing Brillig call constraints check + pub skip_brillig_constraints_check: bool, /// Enable the lookback feature of the Brillig call constraints /// check (prevents some rare false positives, leads to a slowdown @@ -132,7 +132,7 @@ pub(crate) fn optimize_into_acir( // It could happen that we inlined all calls to a given brillig function. // In that case it's unused so we can remove it. This is what we check next. .run_pass(Ssa::remove_unreachable_functions, "Removing Unreachable Functions (4th)") - .run_pass(Ssa::dead_instruction_elimination, "Dead Instruction Elimination (3rd)") + .run_pass(Ssa::dead_instruction_elimination_acir, "Dead Instruction Elimination (3rd)") .finish(); if !options.skip_underconstrained_check { @@ -143,7 +143,7 @@ pub(crate) fn optimize_into_acir( )); } - if options.enable_brillig_constraints_check { + if !options.skip_brillig_constraints_check { ssa_level_warnings.extend(time( "After Check for Missing Brillig Call Constraints", options.print_codegen_timings, @@ -211,9 +211,11 @@ fn optimize_all(builder: SsaBuilder, options: &SsaEvaluatorOptions) -> Result Result, // Map of argument value ids to the Brillig call ids employing them call_arguments: HashMap>, - // Maintains count of calls being tracked - tracking_count: usize, + // The set of calls currently being tracked + tracking: HashSet, // Opt-in to use the lookback feature (tracking the argument values // of a Brillig call before the call happens if their usage precedes // it). Can prevent certain false positives, at the cost of @@ -138,8 +138,6 @@ struct BrilligTaintedIds { array_elements: HashMap>, // Initial result value ids, along with element ids for arrays root_results: HashSet, - // The flag signaling that the call should be now tracked - tracking: bool, } #[derive(Clone, Debug)] @@ -195,7 +193,6 @@ impl BrilligTaintedIds { results: results_status, array_elements, root_results: HashSet::from_iter(results.iter().copied()), - tracking: false, } } @@ -327,7 +324,7 @@ impl DependencyContext { function.dfg[block].instructions().iter().for_each(|instruction| { if let Instruction::Call { func, arguments } = &function.dfg[*instruction] { if let Value::Function(callee) = &function.dfg[*func] { - if all_functions[&callee].runtime().is_brillig() { + if all_functions[callee].runtime().is_brillig() { // Skip already visited locations (happens often in unrolled functions) let call_stack = function.dfg.get_instruction_call_stack(*instruction); let location = call_stack.last(); @@ -394,23 +391,19 @@ impl DependencyContext { for argument in &arguments { if let Some(calls) = self.call_arguments.get(argument) { for call in calls { - if let Some(tainted_ids) = self.tainted.get_mut(call) { - tainted_ids.tracking = true; - self.tracking_count += 1; + if self.tainted.contains_key(call) { + self.tracking.insert(*call); } } } } } - if let Some(tainted_ids) = self.tainted.get_mut(instruction) { - if !tainted_ids.tracking { - tainted_ids.tracking = true; - self.tracking_count += 1; - } + if self.tainted.contains_key(instruction) { + self.tracking.insert(*instruction); } // We can skip over instructions while nothing is being tracked - if self.tracking_count > 0 { + if !self.tracking.is_empty() { let mut results = Vec::new(); // Collect non-constant instruction results @@ -431,8 +424,10 @@ impl DependencyContext { if let Some(value_id) = self.memory_slots.get(address) { self.update_children(&[*value_id], &results); } else { - panic!("load instruction {} has attempted to access previously unused memory location", - instruction); + panic!( + "load instruction {} has attempted to access previously unused memory location", + instruction + ); } } // Record the condition to set as future parent for the following values @@ -502,7 +497,10 @@ impl DependencyContext { RuntimeType::Brillig(..) => {} }, Value::ForeignFunction(..) => { - panic!("should not be able to reach foreign function from non-Brillig functions, {func_id} in function {}", function.name()); + panic!( + "should not be able to reach foreign function from non-Brillig functions, {func_id} in function {}", + function.name() + ); } Value::Instruction { .. } | Value::NumericConstant { .. } @@ -519,7 +517,7 @@ impl DependencyContext { // results involving the array in question, to properly // populate the array element tainted sets Instruction::ArrayGet { array, index } => { - self.process_array_get(function, *array, *index, &results); + self.process_array_get(*array, *index, &results, function); // Record all the used arguments as parents of the results self.update_children(&arguments, &results); } @@ -558,7 +556,10 @@ impl DependencyContext { .tainted .keys() .map(|brillig_call| { - trace!("tainted structure for {}: {:?}", brillig_call, self.tainted[brillig_call]); + trace!( + "tainted structure for {:?}: {:?}", + brillig_call, self.tainted[brillig_call] + ); SsaReport::Bug(InternalBug::UncheckedBrilligCall { call_stack: function.dfg.get_instruction_call_stack(*brillig_call), }) @@ -582,8 +583,8 @@ impl DependencyContext { self.side_effects_condition.map(|v| parents.insert(v)); // Don't update sets for the calls not yet being tracked - for (_, tainted_ids) in self.tainted.iter_mut() { - if tainted_ids.tracking { + for call in &self.tracking { + if let Some(tainted_ids) = self.tainted.get_mut(call) { tainted_ids.update_children(&parents, children); } } @@ -600,15 +601,15 @@ impl DependencyContext { .collect(); // Skip untracked calls - for (_, tainted_ids) in self.tainted.iter_mut() { - if tainted_ids.tracking { + for call in &self.tracking { + if let Some(tainted_ids) = self.tainted.get_mut(call) { tainted_ids.store_partial_constraints(&constrained_values); } } - self.tainted.retain(|_, tainted_ids| { + self.tainted.retain(|call, tainted_ids| { if tainted_ids.check_constrained() { - self.tracking_count -= 1; + self.tracking.remove(call); false } else { true @@ -619,10 +620,10 @@ impl DependencyContext { /// Process ArrayGet instruction for tracked Brillig calls fn process_array_get( &mut self, - function: &Function, array: ValueId, index: ValueId, element_results: &[ValueId], + function: &Function, ) { use acvm::acir::AcirField; @@ -630,8 +631,8 @@ impl DependencyContext { if let Some(value) = function.dfg.get_numeric_constant(index) { if let Some(index) = value.try_to_u32() { // Skip untracked calls - for (_, tainted_ids) in self.tainted.iter_mut() { - if tainted_ids.tracking { + for call in &self.tracking { + if let Some(tainted_ids) = self.tainted.get_mut(call) { tainted_ids.process_array_get(array, index as usize, element_results); } } @@ -826,13 +827,18 @@ impl Context { } }, Value::ForeignFunction(..) => { - panic!("Should not be able to reach foreign function from non-Brillig functions, {func_id} in function {}", function.name()); + panic!( + "Should not be able to reach foreign function from non-Brillig functions, {func_id} in function {}", + function.name() + ); } Value::Instruction { .. } | Value::NumericConstant { .. } | Value::Param { .. } | Value::Global(_) => { - panic!("At the point we are running disconnect there shouldn't be any other values as arguments") + panic!( + "At the point we are running disconnect there shouldn't be any other values as arguments" + ) } } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index e4c00358c8c0..95944129c4eb 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -2,7 +2,7 @@ pub(crate) mod data_bus; use std::{borrow::Cow, collections::BTreeMap, sync::Arc}; -use acvm::{acir::circuit::ErrorSelector, FieldElement}; +use acvm::{FieldElement, acir::circuit::ErrorSelector}; use noirc_errors::Location; use noirc_frontend::hir_def::types::Type as HirType; use noirc_frontend::monomorphization::ast::InlineType; @@ -85,7 +85,11 @@ impl FunctionBuilder { /// This should only be used immediately following construction of a FunctionBuilder /// and will panic if there are any already finished functions. pub(crate) fn set_runtime(&mut self, runtime: RuntimeType) { - assert_eq!(self.finished_functions.len(), 0, "Attempted to set runtime on a FunctionBuilder with finished functions. A FunctionBuilder's runtime should only be set on its initial function"); + assert_eq!( + self.finished_functions.len(), + 0, + "Attempted to set runtime on a FunctionBuilder with finished functions. A FunctionBuilder's runtime should only be set on its initial function" + ); self.current_function.set_runtime(runtime); } @@ -584,7 +588,7 @@ impl std::ops::Index for FunctionBuilder { mod tests { use std::sync::Arc; - use acvm::{acir::AcirField, FieldElement}; + use acvm::{FieldElement, acir::AcirField}; use crate::ssa::ir::{ instruction::{Endian, Intrinsic}, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index aa517d431047..132985830ebf 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -18,12 +18,12 @@ use super::{ value::{Value, ValueId}, }; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use fxhash::FxHashMap as HashMap; use iter_extended::vecmap; use serde::{Deserialize, Serialize}; -use serde_with::serde_as; use serde_with::DisplayFromStr; +use serde_with::serde_as; /// The DataFlowGraph contains most of the actual data in a function including /// its blocks, instructions, and values. This struct is largely responsible for @@ -239,10 +239,9 @@ impl DataFlowGraph { instruction, Instruction::IncrementRc { .. } | Instruction::DecrementRc { .. } ), - RuntimeType::Brillig(_) => !matches!( - instruction, - Instruction::EnableSideEffectsIf { .. } | Instruction::IfElse { .. } - ), + RuntimeType::Brillig(_) => { + !matches!(instruction, Instruction::EnableSideEffectsIf { .. }) + } } } @@ -340,14 +339,19 @@ impl DataFlowGraph { } } let mut instructions = instructions.unwrap_or(vec![instruction]); - assert!(!instructions.is_empty(), "`SimplifyResult::SimplifiedToInstructionMultiple` must not return empty vector"); + assert!( + !instructions.is_empty(), + "`SimplifyResult::SimplifiedToInstructionMultiple` must not return empty vector" + ); if instructions.len() > 1 { // There's currently no way to pass results from one instruction in `instructions` on to the next. // We then restrict this to only support multiple instructions if they're all `Instruction::Constrain` // as this instruction type does not have any results. assert!( - instructions.iter().all(|instruction| matches!(instruction, Instruction::Constrain(..))), + instructions + .iter() + .all(|instruction| matches!(instruction, Instruction::Constrain(..))), "`SimplifyResult::SimplifiedToInstructionMultiple` only supports `Constrain` instructions" ); } @@ -372,6 +376,11 @@ impl DataFlowGraph { } } + /// Replace an existing instruction with a new one. + pub(crate) fn set_instruction(&mut self, id: InstructionId, instruction: Instruction) { + self.instructions[id] = instruction; + } + /// Set the value of value_to_replace to refer to the value referred to by new_value. /// /// This is the preferred method to call for optimizations simplifying @@ -658,10 +667,28 @@ impl DataFlowGraph { pub(crate) fn is_safe_index(&self, index: ValueId, array: ValueId) -> bool { #[allow(clippy::match_like_matches_macro)] match (self.type_of_value(array), self.get_numeric_constant(index)) { - (Type::Array(_, len), Some(index)) if index.to_u128() < (len as u128) => true, + (Type::Array(elements, len), Some(index)) + if index.to_u128() < (len as u128 * elements.len() as u128) => + { + true + } + _ => false, + } + } + + /// Arrays are represented as `[RC, ...items]` where RC stands for reference count. + /// By the time of Brillig generation we expect all constant indices + /// to already account for the extra offset from the RC. + pub(crate) fn is_safe_brillig_index(&self, index: ValueId, array: ValueId) -> bool { + #[allow(clippy::match_like_matches_macro)] + match (self.type_of_value(array), self.get_numeric_constant(index)) { + (Type::Array(elements, len), Some(index)) => { + (index.to_u128() - 1) < (len as u128 * elements.len() as u128) + } _ => false, } } + /// Sets the terminator instruction for the given basic block pub(crate) fn set_block_terminator( &mut self, @@ -880,7 +907,7 @@ impl<'dfg> InsertInstructionResult<'dfg> { } } -impl<'dfg> std::ops::Index for InsertInstructionResult<'dfg> { +impl std::ops::Index for InsertInstructionResult<'_> { type Output = ValueId; fn index(&self, index: usize) -> &Self::Output { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dom.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dom.rs index 3dde6240e183..a6f878dafbf0 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dom.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dom.rs @@ -136,10 +136,7 @@ impl DominatorTree { if let Some(value) = f(block_id) { return Some(value); } - block_id = match self.immediate_dominator(block_id) { - Some(immediate_dominator) => immediate_dominator, - None => return None, - } + block_id = self.immediate_dominator(block_id)?; } } @@ -295,8 +292,8 @@ mod tests { } // Testing setup for a function with an unreachable block2 - fn unreachable_node_setup( - ) -> (DominatorTree, BasicBlockId, BasicBlockId, BasicBlockId, BasicBlockId) { + fn unreachable_node_setup() + -> (DominatorTree, BasicBlockId, BasicBlockId, BasicBlockId, BasicBlockId) { // func() { // block0(cond: u1): // jmpif v0 block2() block3() diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs index 9e4557e06a66..13b5ead5eb61 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs @@ -79,6 +79,13 @@ impl<'f> FunctionInserter<'f> { (instruction, self.function.dfg.get_instruction_call_stack_id(id)) } + /// Get an instruction, map all its values, and replace it with the resolved instruction. + pub(crate) fn map_instruction_in_place(&mut self, id: InstructionId) { + let mut instruction = self.function.dfg[id].clone(); + instruction.map_values_mut(|id| self.resolve(id)); + self.function.dfg.set_instruction(id, instruction); + } + /// Maps a terminator in place, replacing any ValueId in the terminator with the /// resolved version of that value id from this FunctionInserter's internal value mapping. pub(crate) fn map_terminator_in_place(&mut self, block: BasicBlockId) { @@ -251,4 +258,22 @@ impl<'f> FunctionInserter<'f> { self.values.entry(*param).or_insert(*new_param); } } + + /// Merge the internal mapping into the given mapping + /// The merge is guaranteed to be coherent because ambiguous cases are prevented + pub(crate) fn extract_mapping(&self, mapping: &mut HashMap) { + for (k, v) in &self.values { + if mapping.contains_key(k) { + unreachable!("cannot merge key"); + } + if mapping.contains_key(v) { + unreachable!("cannot merge value"); + } + mapping.insert(*k, *v); + } + } + + pub(crate) fn set_mapping(&mut self, mapping: HashMap) { + self.values = mapping; + } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 178e15e4e322..1bef9079eb82 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -1,11 +1,11 @@ -use binary::truncate_field; +use binary::{truncate, truncate_field}; use serde::{Deserialize, Serialize}; use std::hash::{Hash, Hasher}; use acvm::{ - acir::AcirField, - acir::{circuit::ErrorSelector, BlackBoxFunc}, FieldElement, + acir::AcirField, + acir::{BlackBoxFunc, circuit::ErrorSelector}, }; use fxhash::FxHasher64; use iter_extended::vecmap; @@ -911,8 +911,10 @@ impl Instruction { // would be incorrect however since the extra bits on the field would not be flipped. Value::NumericConstant { constant, typ } if typ.is_unsigned() => { // As we're casting to a `u128`, we need to clear out any upper bits that the NOT fills. - let value = !constant.to_u128() % (1 << typ.bit_size()); - SimplifiedTo(dfg.make_constant(value.into(), *typ)) + let bit_size = typ.bit_size(); + assert!(bit_size <= 128); + let not_value: u128 = truncate(!constant.to_u128(), bit_size); + SimplifiedTo(dfg.make_constant(not_value.into(), *typ)) } Value::Instruction { instruction, .. } => { // !!v => v @@ -1001,11 +1003,7 @@ impl Instruction { // // In order for the truncation to be a noop, we then require `max_quotient_bits < bit_size`. let max_quotient_bits = max_numerator_bits - divisor_bits; - if max_quotient_bits < *bit_size { - SimplifiedTo(*value) - } else { - None - } + if max_quotient_bits < *bit_size { SimplifiedTo(*value) } else { None } } _ => None, @@ -1034,11 +1032,7 @@ impl Instruction { Instruction::DecrementRc { .. } => None, Instruction::RangeCheck { value, max_bit_size, .. } => { let max_potential_bits = dfg.get_value_max_num_bits(*value); - if max_potential_bits < *max_bit_size { - Remove - } else { - None - } + if max_potential_bits <= *max_bit_size { Remove } else { None } } Instruction::IfElse { then_condition, then_value, else_condition, else_value } => { let then_condition = dfg.resolve(*then_condition); @@ -1470,3 +1464,32 @@ impl SimplifyResult { } } } + +#[cfg(test)] +mod tests { + use crate::ssa::{opt::assert_normalized_ssa_equals, ssa_gen::Ssa}; + + #[test] + fn removes_range_constraints_on_constants() { + let src = " + acir(inline) fn main f0 { + b0(v0: Field): + range_check Field 0 to 1 bits + range_check Field 1 to 1 bits + range_check Field 255 to 8 bits + range_check Field 256 to 8 bits + return + } + "; + let ssa = Ssa::from_str_simplifying(src).unwrap(); + + let expected = " + acir(inline) fn main f0 { + b0(v0: Field): + range_check Field 256 to 8 bits + return + } + "; + assert_normalized_ssa_equals(ssa, expected); + } +} diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs index 1550c9ea050e..1ec2e33ee8d6 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs @@ -1,4 +1,5 @@ -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; +use num_traits::ToPrimitive as _; use serde::{Deserialize, Serialize}; use super::{ @@ -139,11 +140,11 @@ impl Binary { }; } - let lhs_is_zero = lhs_value.map_or(false, |lhs| lhs.is_zero()); - let rhs_is_zero = rhs_value.map_or(false, |rhs| rhs.is_zero()); + let lhs_is_zero = lhs_value.is_some_and(|lhs| lhs.is_zero()); + let rhs_is_zero = rhs_value.is_some_and(|rhs| rhs.is_zero()); - let lhs_is_one = lhs_value.map_or(false, |lhs| lhs.is_one()); - let rhs_is_one = rhs_value.map_or(false, |rhs| rhs.is_one()); + let lhs_is_one = lhs_value.is_some_and(|lhs| lhs.is_one()); + let rhs_is_one = rhs_value.is_some_and(|rhs| rhs.is_one()); match self.operator { BinaryOp::Add { .. } => { @@ -306,14 +307,18 @@ impl Binary { let bitmask_plus_one = bitmask.to_u128() + 1; if bitmask_plus_one.is_power_of_two() { let value = if lhs_value.is_some() { rhs } else { lhs }; - let num_bits = bitmask_plus_one.ilog2(); - return SimplifyResult::SimplifiedToInstruction( - Instruction::Truncate { - value, - bit_size: num_bits, - max_bit_size: lhs_type.bit_size(), - }, - ); + let bit_size = bitmask_plus_one.ilog2(); + let max_bit_size = lhs_type.bit_size(); + + if bit_size == max_bit_size { + // If we're truncating a value into the full size of its type then + // the truncation is a noop. + return SimplifyResult::SimplifiedTo(value); + } else { + return SimplifyResult::SimplifiedToInstruction( + Instruction::Truncate { value, bit_size, max_bit_size }, + ); + } } } @@ -509,7 +514,7 @@ fn convert_signed_integer_to_field_element(int: i128, bit_size: u32) -> FieldEle } /// Truncates `int` to fit within `bit_size` bits. -fn truncate(int: u128, bit_size: u32) -> u128 { +pub(super) fn truncate(int: u128, bit_size: u32) -> u128 { if bit_size == 128 { int } else { @@ -574,8 +579,8 @@ impl BinaryOp { BinaryOp::Xor => |x, y| Some(x ^ y), BinaryOp::Eq => |x, y| Some((x == y) as u128), BinaryOp::Lt => |x, y| Some((x < y) as u128), - BinaryOp::Shl => |x, y| Some(x << y), - BinaryOp::Shr => |x, y| Some(x >> y), + BinaryOp::Shl => |x, y| y.to_u32().and_then(|y| x.checked_shl(y)), + BinaryOp::Shr => |x, y| y.to_u32().and_then(|y| x.checked_shr(y)), } } @@ -591,8 +596,8 @@ impl BinaryOp { BinaryOp::Xor => |x, y| Some(x ^ y), BinaryOp::Eq => |x, y| Some((x == y) as i128), BinaryOp::Lt => |x, y| Some((x < y) as i128), - BinaryOp::Shl => |x, y| Some(x << y), - BinaryOp::Shr => |x, y| Some(x >> y), + BinaryOp::Shl => |x, y| y.to_u32().and_then(|y| x.checked_shl(y)), + BinaryOp::Shr => |x, y| y.to_u32().and_then(|y| x.checked_shr(y)), } } @@ -620,7 +625,7 @@ mod test { use proptest::prelude::*; use super::{ - convert_signed_integer_to_field_element, truncate_field, + BinaryOp, convert_signed_integer_to_field_element, truncate_field, try_convert_field_element_to_signed_integer, }; use acvm::{AcirField, FieldElement}; @@ -649,6 +654,17 @@ mod test { let truncated_as_bigint = FieldElement::from_be_bytes_reduce(&truncated_as_bigint.to_bytes_be()); prop_assert_eq!(truncated_as_field, truncated_as_bigint); } + } + + #[test] + fn get_u128_function_shift_works_with_values_larger_than_127() { + assert!(BinaryOp::Shr.get_u128_function()(1, 128).is_none()); + assert!(BinaryOp::Shl.get_u128_function()(1, 128).is_none()); + } + #[test] + fn get_i128_function_shift_works_with_values_larger_than_127() { + assert!(BinaryOp::Shr.get_i128_function()(1, 128).is_none()); + assert!(BinaryOp::Shl.get_i128_function()(1, 128).is_none()); } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index 6ee7aa0192ce..d32a562a037e 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -2,8 +2,8 @@ use fxhash::FxHashMap as HashMap; use std::{collections::VecDeque, sync::Arc}; use acvm::{ - acir::{AcirField, BlackBoxFunc}, FieldElement, + acir::{AcirField, BlackBoxFunc}, }; use bn254_blackbox_solver::derive_generators; use iter_extended::vecmap; @@ -170,7 +170,7 @@ pub(super) fn simplify_call( } Intrinsic::SlicePopBack => { let length = dfg.get_numeric_constant(arguments[0]); - if length.map_or(true, |length| length.is_zero()) { + if length.is_none_or(|length| length.is_zero()) { // If the length is zero then we're trying to pop the last element from an empty slice. // Defer the error to acir_gen. return SimplifyResult::None; @@ -185,7 +185,7 @@ pub(super) fn simplify_call( } Intrinsic::SlicePopFront => { let length = dfg.get_numeric_constant(arguments[0]); - if length.map_or(true, |length| length.is_zero()) { + if length.is_none_or(|length| length.is_zero()) { // If the length is zero then we're trying to pop the first element from an empty slice. // Defer the error to acir_gen. return SimplifyResult::None; @@ -243,7 +243,7 @@ pub(super) fn simplify_call( } Intrinsic::SliceRemove => { let length = dfg.get_numeric_constant(arguments[0]); - if length.map_or(true, |length| length.is_zero()) { + if length.is_none_or(|length| length.is_zero()) { // If the length is zero then we're trying to remove an element from an empty slice. // Defer the error to acir_gen. return SimplifyResult::None; @@ -610,7 +610,9 @@ fn simplify_black_box_func( "ICE: `BlackBoxFunc::RANGE` calls should be transformed into a `Instruction::Cast`" ) } - BlackBoxFunc::Sha256Compression => SimplifyResult::None, //TODO(Guillaume) + BlackBoxFunc::Sha256Compression => { + blackbox::simplify_sha256_compression(dfg, arguments, block, call_stack) + } BlackBoxFunc::AES128Encrypt => SimplifyResult::None, } } @@ -718,10 +720,10 @@ fn simplify_derive_generators( ); let is_infinite = dfg.make_constant(FieldElement::zero(), NumericType::bool()); let mut results = Vec::new(); - for gen in generators { - let x_big: BigUint = gen.x.into(); + for generator in generators { + let x_big: BigUint = generator.x.into(); let x = FieldElement::from_be_bytes_reduce(&x_big.to_bytes_be()); - let y_big: BigUint = gen.y.into(); + let y_big: BigUint = generator.y.into(); let y = FieldElement::from_be_bytes_reduce(&y_big.to_bytes_be()); results.push(dfg.make_constant(x, NumericType::NativeField)); results.push(dfg.make_constant(y, NumericType::NativeField)); @@ -742,7 +744,7 @@ fn simplify_derive_generators( #[cfg(test)] mod tests { - use crate::ssa::{opt::assert_normalized_ssa_equals, Ssa}; + use crate::ssa::{Ssa, opt::assert_normalized_ssa_equals}; #[test] fn simplify_derive_generators_has_correct_type() { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs index fac2e8b4d5a1..b3696610b17f 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs @@ -1,6 +1,7 @@ use std::sync::Arc; -use acvm::{acir::AcirField, BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement}; +use acvm::blackbox_solver::sha256_compression; +use acvm::{BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement, acir::AcirField}; use crate::ssa::ir::call_stack::CallStackId; use crate::ssa::ir::instruction::BlackBoxFunc; @@ -233,6 +234,55 @@ pub(super) fn simplify_poseidon2_permutation( } } +pub(super) fn simplify_sha256_compression( + dfg: &mut DataFlowGraph, + arguments: &[ValueId], + block: BasicBlockId, + call_stack: CallStackId, +) -> SimplifyResult { + match (dfg.get_array_constant(arguments[0]), dfg.get_array_constant(arguments[1])) { + (Some((state, _)), Some((msg_blocks, _))) + if array_is_constant(dfg, &state) && array_is_constant(dfg, &msg_blocks) => + { + let state: Option> = state + .iter() + .map(|id| { + dfg.get_numeric_constant(*id) + .expect("value id from array should point at constant") + .try_to_u32() + }) + .collect(); + + let Some(mut state) = state.and_then(|vec| <[u32; 8]>::try_from(vec).ok()) else { + return SimplifyResult::None; + }; + + let msg_blocks: Option> = msg_blocks + .iter() + .map(|id| { + dfg.get_numeric_constant(*id) + .expect("value id from array should point at constant") + .try_to_u32() + }) + .collect(); + + let Some(msg_blocks) = msg_blocks.and_then(|vec| <[u32; 16]>::try_from(vec).ok()) + else { + return SimplifyResult::None; + }; + + sha256_compression(&mut state, &msg_blocks); + + let new_state = state.into_iter().map(FieldElement::from); + let typ = NumericType::Unsigned { bit_size: 32 }; + let result_array = make_constant_array(dfg, new_state, typ, block, call_stack); + + SimplifyResult::SimplifiedTo(result_array) + } + _ => SimplifyResult::None, + } +} + pub(super) fn simplify_hash( dfg: &mut DataFlowGraph, arguments: &[ValueId], @@ -308,16 +358,16 @@ pub(super) fn simplify_signature( #[cfg(feature = "bn254")] #[cfg(test)] -mod test { - use crate::ssa::opt::assert_normalized_ssa_equals; +mod multi_scalar_mul { use crate::ssa::Ssa; + use crate::ssa::opt::assert_normalized_ssa_equals; #[cfg(feature = "bn254")] #[test] fn full_constant_folding() { let src = r#" acir(inline) fn main f0 { - b0(): + b0(): v0 = make_array [Field 2, Field 3, Field 5, Field 5] : [Field; 4] v1 = make_array [Field 1, Field 17631683881184975370165255887551781615748388533673675138860, Field 0, Field 1, Field 17631683881184975370165255887551781615748388533673675138860, Field 0] : [Field; 6] v2 = call multi_scalar_mul (v1, v0) -> [Field; 3] @@ -327,7 +377,7 @@ mod test { let expected_src = r#" acir(inline) fn main f0 { - b0(): + b0(): v3 = make_array [Field 2, Field 3, Field 5, Field 5] : [Field; 4] v7 = make_array [Field 1, Field 17631683881184975370165255887551781615748388533673675138860, Field 0, Field 1, Field 17631683881184975370165255887551781615748388533673675138860, Field 0] : [Field; 6] v10 = make_array [Field 1478523918288173385110236399861791147958001875200066088686689589556927843200, Field 700144278551281040379388961242974992655630750193306467120985766322057145630, Field 0] : [Field; 3] @@ -342,7 +392,7 @@ mod test { fn simplify_zero() { let src = r#" acir(inline) fn main f0 { - b0(v0: Field, v1: Field): + b0(v0: Field, v1: Field): v2 = make_array [v0, Field 0, Field 0, Field 0, v0, Field 0] : [Field; 6] v3 = make_array [ Field 0, Field 0, Field 1, v0, v1, Field 0, Field 1, v0, Field 0] : [Field; 9] @@ -355,7 +405,7 @@ mod test { //First point is zero, second scalar is zero, so we should be left with the scalar mul of the last point. let expected_src = r#" acir(inline) fn main f0 { - b0(v0: Field, v1: Field): + b0(v0: Field, v1: Field): v3 = make_array [v0, Field 0, Field 0, Field 0, v0, Field 0] : [Field; 6] v5 = make_array [Field 0, Field 0, Field 1, v0, v1, Field 0, Field 1, v0, Field 0] : [Field; 9] v6 = make_array [v0, Field 0] : [Field; 2] @@ -372,7 +422,7 @@ mod test { fn partial_constant_folding() { let src = r#" acir(inline) fn main f0 { - b0(v0: Field, v1: Field): + b0(v0: Field, v1: Field): v2 = make_array [Field 1, Field 0, v0, Field 0, Field 2, Field 0] : [Field; 6] v3 = make_array [ Field 1, Field 17631683881184975370165255887551781615748388533673675138860, Field 0, v0, v1, Field 0, Field 1, Field 17631683881184975370165255887551781615748388533673675138860, Field 0] : [Field; 9] @@ -383,7 +433,7 @@ mod test { //First and last scalar/point are constant, so we should be left with the msm of the middle point and the folded constant point let expected_src = r#" acir(inline) fn main f0 { - b0(v0: Field, v1: Field): + b0(v0: Field, v1: Field): v5 = make_array [Field 1, Field 0, v0, Field 0, Field 2, Field 0] : [Field; 6] v7 = make_array [Field 1, Field 17631683881184975370165255887551781615748388533673675138860, Field 0, v0, v1, Field 0, Field 1, Field 17631683881184975370165255887551781615748388533673675138860, Field 0] : [Field; 9] v8 = make_array [v0, Field 0, Field 1, Field 0] : [Field; 4] @@ -395,3 +445,32 @@ mod test { assert_normalized_ssa_equals(ssa, expected_src); } } + +#[cfg(test)] +mod sha256_compression { + use crate::ssa::Ssa; + use crate::ssa::opt::assert_normalized_ssa_equals; + + #[test] + fn is_optimized_out_with_constant_arguments() { + let src = r#" + acir(inline) fn main f0 { + b0(): + v0 = make_array [u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0] : [u32; 8] + v1 = make_array [u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0] : [u32; 16] + v2 = call sha256_compression(v0, v1) -> [u32; 8] + return v2 + }"#; + let ssa = Ssa::from_str_simplifying(src).unwrap(); + let expected_src = r#" + acir(inline) fn main f0 { + b0(): + v1 = make_array [u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0] : [u32; 8] + v2 = make_array [u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0, u32 0] : [u32; 16] + v11 = make_array [u32 2091193876, u32 1113340840, u32 3461668143, u32 3254913767, u32 3068490961, u32 2551409935, u32 2927503052, u32 3205228454] : [u32; 8] + return v11 + } + "#; + assert_normalized_ssa_equals(ssa, expected_src); + } +} diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs index ee2ab43aa5d5..189fd20ca531 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs @@ -1,4 +1,4 @@ -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use num_bigint::BigUint; use super::{DataFlowGraph, Instruction, NumericType, SimplifyResult, Type, Value, ValueId}; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs index 8b3b897088a2..6f26fef071e4 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs @@ -1,4 +1,4 @@ -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use crate::ssa::ir::types::NumericType; @@ -204,4 +204,26 @@ mod tests { "; assert_normalized_ssa_equals(ssa, expected); } + + #[test] + fn simplifies_out_noop_bitwise_ands() { + // Regression test for https://github.com/noir-lang/noir/issues/7451 + let src = " + acir(inline) predicate_pure fn main f0 { + b0(v0: u8): + v1 = and u8 255, v0 + return v1 + } + "; + + let ssa = Ssa::from_str_simplifying(src).unwrap(); + + let expected = " + acir(inline) fn main f0 { + b0(v0: u8): + return v0 + } + "; + assert_normalized_ssa_equals(ssa, expected); + } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/printer.rs index 06f2ecd70eaf..0fac2242abd7 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -6,8 +6,8 @@ use im::Vector; use iter_extended::vecmap; use crate::ssa::{ - ir::types::{NumericType, Type}, Ssa, + ir::types::{NumericType, Type}, }; use super::{ @@ -344,11 +344,7 @@ pub(crate) fn try_to_extract_string_from_error_payload( values: &[ValueId], dfg: &DataFlowGraph, ) -> Option { - if is_string_type && values.len() == 1 { - dfg.get_string(values[0]) - } else { - None - } + if is_string_type && values.len() == 1 { dfg.get_string(values[0]) } else { None } } fn display_constrain_error( diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/types.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/types.rs index 0dd7fd92ee53..ca6242d51c21 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/types.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/types.rs @@ -1,7 +1,8 @@ +use noirc_frontend::signed_field::SignedField; use serde::{Deserialize, Serialize}; use std::sync::Arc; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use iter_extended::vecmap; use crate::ssa::ssa_gen::SSA_WORD_SIZE; @@ -58,28 +59,20 @@ impl NumericType { /// Returns None if the given Field value is within the numeric limits /// for the current NumericType. Otherwise returns a string describing /// the limits, as a range. - pub(crate) fn value_is_outside_limits( - self, - field: FieldElement, - negative: bool, - ) -> Option { + pub(crate) fn value_is_outside_limits(self, value: SignedField) -> Option { match self { NumericType::Unsigned { bit_size } => { - let max = 2u128.pow(bit_size) - 1; - if negative { + let max = if bit_size == 128 { u128::MAX } else { 2u128.pow(bit_size) - 1 }; + if value.is_negative { return Some(format!("0..={}", max)); } - if field <= max.into() { - None - } else { - Some(format!("0..={}", max)) - } + if value.field <= max.into() { None } else { Some(format!("0..={}", max)) } } NumericType::Signed { bit_size } => { let min = 2u128.pow(bit_size - 1); let max = 2u128.pow(bit_size - 1) - 1; - let target_max = if negative { min } else { max }; - if field <= target_max.into() { + let target_max = if value.is_negative { min } else { max }; + if value.field <= target_max.into() { None } else { Some(format!("-{}..={}", min, max)) @@ -307,19 +300,19 @@ mod tests { #[test] fn test_u8_value_is_outside_limits() { let u8 = NumericType::Unsigned { bit_size: 8 }; - assert!(u8.value_is_outside_limits(FieldElement::from(1_i128), true).is_some()); - assert!(u8.value_is_outside_limits(FieldElement::from(0_i128), false).is_none()); - assert!(u8.value_is_outside_limits(FieldElement::from(255_i128), false).is_none()); - assert!(u8.value_is_outside_limits(FieldElement::from(256_i128), false).is_some()); + assert!(u8.value_is_outside_limits(SignedField::negative(1_i128)).is_some()); + assert!(u8.value_is_outside_limits(SignedField::positive(0_i128)).is_none()); + assert!(u8.value_is_outside_limits(SignedField::positive(255_i128)).is_none()); + assert!(u8.value_is_outside_limits(SignedField::positive(256_i128)).is_some()); } #[test] fn test_i8_value_is_outside_limits() { let i8 = NumericType::Signed { bit_size: 8 }; - assert!(i8.value_is_outside_limits(FieldElement::from(129_i128), true).is_some()); - assert!(i8.value_is_outside_limits(FieldElement::from(128_i128), true).is_none()); - assert!(i8.value_is_outside_limits(FieldElement::from(0_i128), false).is_none()); - assert!(i8.value_is_outside_limits(FieldElement::from(127_i128), false).is_none()); - assert!(i8.value_is_outside_limits(FieldElement::from(128_i128), false).is_some()); + assert!(i8.value_is_outside_limits(SignedField::negative(129_i128)).is_some()); + assert!(i8.value_is_outside_limits(SignedField::negative(128_i128)).is_none()); + assert!(i8.value_is_outside_limits(SignedField::positive(0_i128)).is_none()); + assert!(i8.value_is_outside_limits(SignedField::positive(127_i128)).is_none()); + assert!(i8.value_is_outside_limits(SignedField::positive(128_i128)).is_some()); } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/array_set.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/array_set.rs index 05ceafcf4509..7bef8e7b1ae2 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/array_set.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/array_set.rs @@ -39,7 +39,11 @@ impl Function { let reachable_blocks = self.reachable_blocks(); if !self.runtime().is_entry_point() { - assert_eq!(reachable_blocks.len(), 1, "Expected there to be 1 block remaining in Acir function for array_set optimization"); + assert_eq!( + reachable_blocks.len(), + 1, + "Expected there to be 1 block remaining in Acir function for array_set optimization" + ); } let mut context = Context::new(&self.dfg); @@ -187,7 +191,7 @@ fn make_mutable( #[cfg(test)] mod tests { - use crate::ssa::{opt::assert_normalized_ssa_equals, Ssa}; + use crate::ssa::{Ssa, opt::assert_normalized_ssa_equals}; #[test] fn array_set_in_loop_with_conditional_clone() { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/basic_conditional.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/basic_conditional.rs new file mode 100644 index 000000000000..669a3dd77830 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/basic_conditional.rs @@ -0,0 +1,526 @@ +use std::collections::HashSet; + +use acvm::AcirField; +use fxhash::FxHashMap as HashMap; +use iter_extended::vecmap; + +use crate::ssa::{ + Ssa, + ir::{ + basic_block::BasicBlockId, + cfg::ControlFlowGraph, + dfg::DataFlowGraph, + function::{Function, FunctionId}, + function_inserter::FunctionInserter, + instruction::{BinaryOp, Instruction, TerminatorInstruction}, + post_order::PostOrder, + value::ValueId, + }, +}; + +use super::flatten_cfg::Context; +#[derive(Debug, Clone)] +struct BasicConditional { + block_entry: BasicBlockId, + block_then: Option, + block_else: Option, + block_exit: BasicBlockId, +} + +impl Ssa { + #[tracing::instrument(level = "trace", skip(self))] + /// This pass flatten simple IF-THEN-ELSE statements + /// This optimization pass identifies simple conditional control flow patterns in unconstrained code + /// and flattens them to reduce the number of basic blocks and improve performance. + /// + /// e.g: if c {a} else {b} would be flattened to c*(a-b)+b + /// A simple conditional pattern is defined as an IF-THEN (with optional ELSE) statement, with no nested conditional nor loop statements + /// Performance improvement is based on a simple execution cost metric + pub(crate) fn flatten_basic_conditionals(mut self) -> Ssa { + // Retrieve the 'no_predicates' attribute of the functions in a map, to avoid problems with borrowing + let mut no_predicates = HashMap::default(); + for function in self.functions.values() { + no_predicates.insert(function.id(), function.is_no_predicates()); + } + for function in self.functions.values_mut() { + flatten_function(function, &mut no_predicates); + } + self + } +} + +/// Returns the blocks of the simple conditional sub-graph whose input block is the entry. +/// Returns None if the input block is not the entry block of a simple conditional. +fn is_conditional( + block: BasicBlockId, + cfg: &ControlFlowGraph, + function: &Function, +) -> Option { + // jump overhead is the cost for doing the conditional and jump around the blocks + // We use 10 as a rough estimate, the real cost is less. + let jump_overhead = 10; + let mut successors = cfg.successors(block); + let mut result = None; + // a conditional must have 2 branches + if successors.len() != 2 { + return None; + } + let left = successors.next().unwrap(); + let right = successors.next().unwrap(); + let mut left_successors = cfg.successors(left); + let mut right_successors = cfg.successors(right); + let left_successors_len = left_successors.len(); + let right_successors_len = right_successors.len(); + let next_left = left_successors.next(); + let next_right = right_successors.next(); + if next_left == Some(block) || next_right == Some(block) { + // this is a loop, not a conditional + return None; + } + if left_successors_len == 1 && right_successors_len == 1 && next_left == next_right { + // The branches join on one block so it is a non-nested conditional + let cost_left = block_cost(left, &function.dfg); + let cost_right = block_cost(right, &function.dfg); + // For the flattening to be valuable, we compare the cost of the flattened code with the average cost of the 2 branches, + // including an overhead to take into account the jumps between the blocks. + let cost = cost_right.saturating_add(cost_left); + if cost < cost / 2 + jump_overhead { + if let Some(TerminatorInstruction::JmpIf { + condition: _, + then_destination, + else_destination, + call_stack: _, + }) = function.dfg[block].terminator() + { + result = Some(BasicConditional { + block_entry: block, + block_then: Some(*then_destination), + block_else: Some(*else_destination), + block_exit: next_left.unwrap(), + }); + } + } + } else if left_successors_len == 1 && next_left == Some(right) { + // Left branch joins the right branch, e.g if/then statement with no else + // This case may not happen (i.e not generated), but it is safer to handle it (e.g in case it happens due to some optimizations) + let cost = block_cost(left, &function.dfg); + if cost < cost / 2 + jump_overhead { + if let Some(TerminatorInstruction::JmpIf { + condition: _, + then_destination, + else_destination, + call_stack: _, + }) = function.dfg[block].terminator() + { + let (block_then, block_else) = if left == *then_destination { + (Some(left), None) + } else if left == *else_destination { + (None, Some(left)) + } else { + return None; + }; + + result = Some(BasicConditional { + block_entry: block, + block_then, + block_else, + block_exit: right, + }); + } + } + } else if right_successors_len == 1 && next_right == Some(left) { + // Right branch joins the left branch, e.g if/else statement with no then + // This case may not happen (i.e not generated), but it is safer to handle it (e.g in case it happens due to some optimizations) + let cost = block_cost(right, &function.dfg); + if cost < cost / 2 + jump_overhead { + if let Some(TerminatorInstruction::JmpIf { + condition: _, + then_destination, + else_destination, + call_stack: _, + }) = function.dfg[block].terminator() + { + let (block_then, block_else) = if right == *then_destination { + (Some(right), None) + } else if right == *else_destination { + (None, Some(right)) + } else { + return None; + }; + result = Some(BasicConditional { + block_entry: block, + block_then, + block_else, + block_exit: right, + }); + } + } + } + // A conditional exit would have exactly 2 predecessors + result.filter(|result| cfg.predecessors(result.block_exit).len() == 2) +} + +/// Computes a cost estimate of a basic block +/// returns u32::MAX if the block has side-effect instructions +/// WARNING: these are estimates of the runtime cost of each instruction, +/// 1 being the cost of the simplest instruction. These numbers can be improved. +fn block_cost(block: BasicBlockId, dfg: &DataFlowGraph) -> u32 { + let mut cost: u32 = 0; + for instruction in dfg[block].instructions() { + let instruction_cost = match &dfg[*instruction] { + Instruction::Binary(binary) => { + match binary.operator { + BinaryOp::Add { unchecked } + | BinaryOp::Sub { unchecked } + | BinaryOp::Mul { unchecked } => if unchecked { 3 } else { return u32::MAX }, + BinaryOp::Div + | BinaryOp::Mod => return u32::MAX, + BinaryOp::Eq => 1, + BinaryOp::Lt => 5, + BinaryOp::And + | BinaryOp::Or + | BinaryOp::Xor => 1, + BinaryOp::Shl + | BinaryOp::Shr => return u32::MAX, + } + }, + // A Cast can be either simplified, or lead to a truncate + Instruction::Cast(_, _) => 3, + Instruction::Not(_) => 1, + Instruction::Truncate { .. } => 7, + + Instruction::Constrain(_,_,_) + | Instruction::ConstrainNotEqual(_,_,_) + | Instruction::RangeCheck { .. } + // Calls with no-predicate set to true could be supported, but + // they are likely to be too costly anyways. Simple calls would + // have been inlined already. + | Instruction::Call { .. } + | Instruction::Load { .. } + | Instruction::Store { .. } + | Instruction::ArraySet { .. } => return u32::MAX, + + Instruction::ArrayGet { array, index } => { + // A get can fail because of out-of-bound index + let mut in_bound = false; + // check if index is in bound + if let (Some(index), Some(len)) = (dfg.get_numeric_constant(*index), dfg.try_get_array_length(*array)) { + // The index is in-bounds + if index.to_u128() < len as u128 { + in_bound = true; + } + } + if !in_bound { + return u32::MAX; + } + 1 + }, + // if less than 10 elements, it is translated into a store for each element + // if more than 10, it is a loop, so 20 should be a good estimate, worst case being 10 stores and ~10 index increments + Instruction::MakeArray { .. } => 20, + + Instruction::Allocate + | Instruction::EnableSideEffectsIf { .. } + | Instruction::IncrementRc { .. } + | Instruction::DecrementRc { .. } + | Instruction::Noop => 0, + Instruction::IfElse { .. } => 1, + }; + cost += instruction_cost; + } + cost +} + +/// Identifies all simple conditionals in the function and flattens them +fn flatten_function(function: &mut Function, no_predicates: &mut HashMap) { + // This pass is dedicated to brillig functions + if !function.runtime().is_brillig() { + return; + } + let cfg = ControlFlowGraph::with_function(function); + let mut stack = vec![function.entry_block()]; + let mut processed = HashSet::new(); + let mut conditionals = Vec::new(); + + // 1. Process all blocks of the cfg, starting from the root and following the successors + while let Some(block) = stack.pop() { + // Avoid cycles + if processed.contains(&block) { + continue; + } + processed.insert(block); + + // Identify the simple conditionals + if let Some(conditional) = is_conditional(block, &cfg, function) { + // no need to check the branches, process the join block directly + stack.push(conditional.block_exit); + conditionals.push(conditional); + } else { + stack.extend(cfg.successors(block)); + } + } + + // 2. Flatten all simple conditionals + // process basic conditionals in reverse order so that + // a conditional does not impact the previous ones + conditionals.reverse(); + flatten_multiple(&conditionals, function, no_predicates); +} + +fn flatten_multiple( + conditionals: &Vec, + function: &mut Function, + no_predicates: &mut HashMap, +) { + // 1. process each basic conditional, using a new context per conditional + let post_order = PostOrder::with_function(function); + + let mut mapping = HashMap::default(); + for conditional in conditionals { + let cfg = ControlFlowGraph::with_function(function); + let cfg_root = function.entry_block(); + let mut branch_ends = HashMap::default(); + branch_ends.insert(conditional.block_entry, conditional.block_exit); + let mut context = Context::new(function, cfg, branch_ends, cfg_root); + context.flatten_single_conditional(conditional, no_predicates); + // extract the mapping into 'mapping + context.inserter.extract_mapping(&mut mapping); + } + // 2. re-map the full program for values that may been simplified. + if !mapping.is_empty() { + for block in post_order.as_slice() { + Context::map_block_with_mapping(mapping.clone(), function, *block); + } + } +} + +impl Context<'_> { + fn flatten_single_conditional( + &mut self, + conditional: &BasicConditional, + no_predicates: &mut HashMap, + ) { + // Manually inline 'then', 'else' and 'exit' into the entry block + //0. initialize the context for flattening a 'single conditional' + let old_target = self.target_block; + let old_no_predicate = self.no_predicate; + let mut queue = vec![]; + self.target_block = conditional.block_entry; + self.no_predicate = true; + //1. process 'then' branch + self.inline_block(conditional.block_entry, no_predicates); + let to_process = self.handle_terminator(conditional.block_entry, &queue); + queue.extend(to_process); + if let Some(then) = conditional.block_then { + assert_eq!(queue.pop(), conditional.block_then); + self.inline_block(then, no_predicates); + let to_process = self.handle_terminator(then, &queue); + + for incoming_block in to_process { + if !queue.contains(&incoming_block) { + queue.push(incoming_block); + } + } + } + + //2. process 'else' branch, in case there is no 'then' + let next = queue.pop(); + if next == conditional.block_else { + let next = next.unwrap(); + self.inline_block(next, no_predicates); + let _ = self.handle_terminator(next, &queue); + } else { + assert_eq!(next, Some(conditional.block_exit)); + } + + //3. process 'exit' block + self.inline_block(conditional.block_exit, no_predicates); + // Manually set the terminator of the entry block to the one of the exit block + let terminator = + self.inserter.function.dfg[conditional.block_exit].terminator().unwrap().clone(); + let new_terminator = match terminator { + TerminatorInstruction::JmpIf { + condition, + then_destination, + else_destination, + call_stack, + } => { + let condition = self.inserter.resolve(condition); + TerminatorInstruction::JmpIf { + condition, + then_destination, + else_destination, + call_stack, + } + } + TerminatorInstruction::Jmp { destination, arguments, call_stack } => { + let arguments = vecmap(arguments, |value| self.inserter.resolve(value)); + TerminatorInstruction::Jmp { destination, arguments, call_stack } + } + TerminatorInstruction::Return { return_values, call_stack } => { + let return_values = vecmap(return_values, |value| self.inserter.resolve(value)); + TerminatorInstruction::Return { return_values, call_stack } + } + }; + self.inserter.function.dfg.set_block_terminator(conditional.block_entry, new_terminator); + self.inserter.map_data_bus_in_place(); + //4. restore the context, in case it is re-used. + self.target_block = old_target; + self.no_predicate = old_no_predicate; + } + + fn map_block_with_mapping( + mapping: HashMap, + func: &mut Function, + block: BasicBlockId, + ) { + // Map all instructions in the block + let mut inserter = FunctionInserter::new(func); + inserter.set_mapping(mapping); + let instructions = inserter.function.dfg[block].instructions().to_vec(); + for instruction in instructions { + inserter.map_instruction_in_place(instruction); + } + inserter.map_terminator_in_place(block); + } +} + +#[cfg(test)] +mod test { + use crate::ssa::{Ssa, opt::assert_normalized_ssa_equals}; + + #[test] + fn basic_jmpif() { + let src = " + brillig(inline) fn foo f0 { + b0(v0: u32): + v3 = eq v0, u32 0 + jmpif v3 then: b2, else: b1 + b1(): + jmp b3(u32 5) + b2(): + jmp b3(u32 3) + b3(v1: u32): + return v1 + } + "; + let ssa = Ssa::from_str(src).unwrap(); + assert_eq!(ssa.main().reachable_blocks().len(), 4); + + let expected = " + brillig(inline) fn foo f0 { + b0(v0: u32): + v2 = eq v0, u32 0 + v3 = not v2 + v4 = cast v2 as u32 + v5 = cast v3 as u32 + v7 = unchecked_mul v4, u32 3 + v9 = unchecked_mul v5, u32 5 + v10 = unchecked_add v7, v9 + return v10 + } + "; + + let ssa = ssa.flatten_basic_conditionals(); + assert_normalized_ssa_equals(ssa, expected); + } + + #[test] + fn array_jmpif() { + let src = r#" + brillig(inline) fn foo f0 { + b0(v0: u32): + v3 = eq v0, u32 5 + jmpif v3 then: b2, else: b1 + b1(): + v6 = make_array b"foo" + jmp b3(v6) + b2(): + v10 = make_array b"bar" + jmp b3(v10) + b3(v1: [u8; 3]): + return v1 + } + "#; + let ssa = Ssa::from_str(src).unwrap(); + assert_eq!(ssa.main().reachable_blocks().len(), 4); + let ssa = ssa.flatten_basic_conditionals(); + // make_array is not simplified + assert_normalized_ssa_equals(ssa, src); + } + + #[test] + fn nested_jmpifs() { + let src = " + brillig(inline) fn foo f0 { + b0(v0: u32): + v5 = eq v0, u32 5 + v6 = not v5 + jmpif v5 then: b5, else: b1 + b1(): + v8 = lt v0, u32 3 + jmpif v8 then: b3, else: b2 + b2(): + v9 = truncate v0 to 2 bits, max_bit_size: 32 + jmp b4(v9) + b3(): + v10 = truncate v0 to 1 bits, max_bit_size: 32 + jmp b4(v10) + b4(v1: u32): + jmp b9(v1) + b5(): + v12 = lt u32 2, v0 + jmpif v12 then: b7, else: b6 + b6(): + v13 = truncate v0 to 3 bits, max_bit_size: 32 + jmp b8(v13) + b7(): + v14 = and v0, u32 2 + jmp b8(v14) + b8(v2: u32): + jmp b9(v2) + b9(v3: u32): + return v3 + } + "; + let ssa = Ssa::from_str(src).unwrap(); + assert_eq!(ssa.main().reachable_blocks().len(), 10); + + let expected = " + brillig(inline) fn foo f0 { + b0(v0: u32): + v3 = eq v0, u32 5 + v4 = not v3 + jmpif v3 then: b2, else: b1 + b1(): + v6 = lt v0, u32 3 + v7 = truncate v0 to 1 bits, max_bit_size: 32 + v8 = not v6 + v9 = truncate v0 to 2 bits, max_bit_size: 32 + v10 = cast v6 as u32 + v11 = cast v8 as u32 + v12 = unchecked_mul v10, v7 + v13 = unchecked_mul v11, v9 + v14 = unchecked_add v12, v13 + jmp b3(v14) + b2(): + v16 = lt u32 2, v0 + v17 = and v0, u32 2 + v18 = not v16 + v19 = truncate v0 to 3 bits, max_bit_size: 32 + v20 = cast v16 as u32 + v21 = cast v18 as u32 + v22 = unchecked_mul v20, v17 + v23 = unchecked_mul v21, v19 + v24 = unchecked_add v22, v23 + jmp b3(v24) + b3(v1: u32): + return v1 + } + "; + + let ssa = ssa.flatten_basic_conditionals(); + assert_eq!(ssa.main().reachable_blocks().len(), 4); + assert_normalized_ssa_equals(ssa, expected); + } +} diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_array_gets.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_array_gets.rs new file mode 100644 index 000000000000..c5d485dd1613 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_array_gets.rs @@ -0,0 +1,168 @@ +//! In the Brillig runtime arrays are represented as [RC, ...items], +//! Certain operations such as array gets only utilize the items pointer. +//! Without handling the items pointer offset in SSA, it is left to Brillig generation +//! to offset the array pointer. +//! +//! Slices are represented as Brillig vectors, where the items pointer instead starts at three rather than one. +//! A Brillig vector is represented as [RC, Size, Capacity, ...items]. +//! +//! For array operations with constant indices adding an instruction to offset the pointer +//! is unnecessary as we already know the index. This pass looks for such array operations +//! with constant indices and replaces their index with the appropriate offset. + +use fxhash::FxHashMap as HashMap; + +use crate::{ + brillig::brillig_ir::BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, + ssa::{ + Ssa, + ir::{ + function::Function, + instruction::Instruction, + types::{NumericType, Type}, + }, + }, +}; + +impl Ssa { + #[tracing::instrument(level = "trace", skip(self))] + pub(crate) fn brillig_array_gets(mut self) -> Ssa { + let brillig_functions = + self.functions.values_mut().filter(|function| function.runtime().is_brillig()); + for function in brillig_functions { + function.brillig_array_gets(); + } + + self + } +} + +impl Function { + pub(super) fn brillig_array_gets(&mut self) { + let reachable_blocks = self.reachable_blocks(); + + let mut instructions_to_update = HashMap::default(); + for block_id in reachable_blocks.into_iter() { + for instruction_id in self.dfg[block_id].instructions() { + if let Instruction::ArrayGet { array, index } = self.dfg[*instruction_id] { + if self.dfg.is_constant(index) { + instructions_to_update.insert( + *instruction_id, + (Instruction::ArrayGet { array, index }, block_id), + ); + } + } + } + } + + for (instruction_id, _) in instructions_to_update { + let new_instruction = match self.dfg[instruction_id] { + Instruction::ArrayGet { array, index } => { + let index_constant = + self.dfg.get_numeric_constant(index).expect("ICE: Expected constant index"); + let offset = if matches!(self.dfg.type_of_value(array), Type::Array(..)) { + // Brillig arrays are [RC, ...items] + 1u128 + } else { + // Brillig vectors are [RC, Size, Capacity, ...items] + 3u128 + }; + let index = self.dfg.make_constant( + index_constant + offset.into(), + NumericType::unsigned(BRILLIG_MEMORY_ADDRESSING_BIT_SIZE), + ); + Instruction::ArrayGet { array, index } + } + _ => { + continue; + } + }; + self.dfg[instruction_id] = new_instruction; + } + } +} + +#[cfg(test)] +mod tests { + use crate::ssa::opt::assert_normalized_ssa_equals; + + use super::Ssa; + + #[test] + fn offset_array_get_constant_index() { + let src = " + brillig(inline) fn main f0 { + b0(v0: [Field; 3]): + v2 = array_get v0, index u32 0 -> Field + return v2 + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.brillig_array_gets(); + + let expected = " + brillig(inline) fn main f0 { + b0(v0: [Field; 3]): + v2 = array_get v0, index u32 1 -> Field + return v2 + } + "; + + assert_normalized_ssa_equals(ssa, expected); + } + + #[test] + fn do_not_offset_dynamic_array_get() { + let src = " + brillig(inline) fn main f0 { + b0(v0: [Field; 3], v1: u32): + v2 = array_get v0, index v1 -> Field + return v2 + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.brillig_array_gets(); + assert_normalized_ssa_equals(ssa, src); + } + + #[test] + fn do_not_offset_array_get_in_acir() { + let src = " + acir(inline) fn main f0 { + b0(v0: [Field; 3]): + v2 = array_get v0, index u32 0 -> Field + return v2 + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.brillig_array_gets(); + assert_normalized_ssa_equals(ssa, src); + } + + #[test] + fn offset_slice_array_get_constant_index() { + let src = " + brillig(inline) fn main f0 { + b0(v0: [Field]): + v2 = array_get v0, index u32 0 -> Field + return v2 + } + "; + + let ssa = Ssa::from_str(src).unwrap(); + let ssa = ssa.brillig_array_gets(); + + let expected = " + brillig(inline) fn main f0 { + b0(v0: [Field]): + v2 = array_get v0, index u32 3 -> Field + return v2 + } + "; + + assert_normalized_ssa_equals(ssa, expected); + } +} diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_entry_points.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_entry_points.rs index e03f14b2721f..a2844a599975 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_entry_points.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/brillig_entry_points.rs @@ -13,7 +13,7 @@ //! generated for different entry points can conflict. //! //! To provide a more concrete example, let's take this program: -//! ``` +//! ```noir //! global ONE: Field = 1; //! global TWO: Field = 2; //! global THREE: Field = 3; @@ -40,7 +40,7 @@ //! } //! ``` //! The two entry points will have different global allocation maps: -//! ``` +//! ```noir //! GlobalInit(Id(1)): //! CONST M32835 = 1 //! CONST M32836 = 2 @@ -64,12 +64,12 @@ use std::collections::{BTreeMap, BTreeSet}; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::ssa::{ + Ssa, ir::{ function::{Function, FunctionId}, instruction::Instruction, value::Value, }, - Ssa, }; use super::inlining::called_functions_vec; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/check_u128_mul_overflow.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/check_u128_mul_overflow.rs new file mode 100644 index 000000000000..ae0c0f1e1033 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/check_u128_mul_overflow.rs @@ -0,0 +1,285 @@ +use acvm::{AcirField, FieldElement}; + +use crate::ssa::{ + ir::{ + basic_block::BasicBlockId, + call_stack::CallStackId, + dfg::DataFlowGraph, + function::Function, + instruction::{Binary, BinaryOp, ConstrainError, Instruction}, + types::NumericType, + value::ValueId, + }, + ssa_gen::Ssa, +}; + +impl Ssa { + /// An SSA pass that checks that multiplying two u128 doesn't overflow because + /// both operands are greater or equal than 2^64. + /// If both are, then the result is surely greater or equal than 2^128 so it would overflow. + /// The operands can still overflow if just one of them is less than 2^64, but in that case the result + /// will be less than 2^192 so it fits in a Field value, and acir will check that it fits in a u128. + #[tracing::instrument(level = "trace", skip(self))] + pub(crate) fn check_u128_mul_overflow(mut self) -> Ssa { + for function in self.functions.values_mut() { + function.check_u128_mul_overflow(); + } + self + } +} + +impl Function { + pub(crate) fn check_u128_mul_overflow(&mut self) { + if !self.runtime().is_acir() { + return; + } + + for block in self.reachable_blocks() { + let instructions = self.dfg[block].take_instructions(); + + for instruction in instructions { + self.dfg[block].insert_instruction(instruction); + + let Instruction::Binary(Binary { + lhs, + rhs, + operator: BinaryOp::Mul { unchecked: false }, + }) = &self.dfg[instruction] + else { + continue; + }; + + let binary_type = self.dfg.type_of_value(*lhs).unwrap_numeric(); + let NumericType::Unsigned { bit_size: 128 } = binary_type else { + continue; + }; + + let call_stack = self.dfg.get_instruction_call_stack_id(instruction); + check_u128_mul_overflow(*lhs, *rhs, block, &mut self.dfg, call_stack); + } + } + } +} + +fn check_u128_mul_overflow( + lhs: ValueId, + rhs: ValueId, + block: BasicBlockId, + dfg: &mut DataFlowGraph, + call_stack: CallStackId, +) { + let lhs_value = dfg.get_numeric_constant(lhs); + let rhs_value = dfg.get_numeric_constant(rhs); + + let two_pow_64 = 1_u128 << 64; + + // If lhs is less than 2^64 then the condition trivially holds. + if let Some(value) = lhs_value { + if value.to_u128() < two_pow_64 { + return; + } + } + + // Same goes for rhs + if let Some(value) = rhs_value { + if value.to_u128() < two_pow_64 { + return; + } + } + + let u128 = NumericType::unsigned(128); + let two_pow_64 = dfg.make_constant(two_pow_64.into(), u128); + + let res = if lhs_value.is_some() && rhs_value.is_some() { + // If both values are known at compile time, at this point we know it overflows + dfg.make_constant(FieldElement::one(), u128) + } else if lhs_value.is_some() { + // If only the left-hand side is known we just need to check that the right-hand side + // isn't greater than 2^64 + let instruction = + Instruction::Binary(Binary { lhs: rhs, rhs: two_pow_64, operator: BinaryOp::Div }); + dfg.insert_instruction_and_results(instruction, block, None, call_stack).first() + } else if rhs_value.is_some() { + // Same goes for the other side + let instruction = + Instruction::Binary(Binary { lhs, rhs: two_pow_64, operator: BinaryOp::Div }); + dfg.insert_instruction_and_results(instruction, block, None, call_stack).first() + } else { + // Check both sides + let instruction = + Instruction::Binary(Binary { lhs, rhs: two_pow_64, operator: BinaryOp::Div }); + let divided_lhs = + dfg.insert_instruction_and_results(instruction, block, None, call_stack).first(); + + let instruction = + Instruction::Binary(Binary { lhs: rhs, rhs: two_pow_64, operator: BinaryOp::Div }); + let divided_rhs = + dfg.insert_instruction_and_results(instruction, block, None, call_stack).first(); + + // Unchecked as operands are restricted to be less than 2^64 so multiplying them cannot overflow. + let mul = BinaryOp::Mul { unchecked: true }; + let instruction = + Instruction::Binary(Binary { lhs: divided_lhs, rhs: divided_rhs, operator: mul }); + dfg.insert_instruction_and_results(instruction, block, None, call_stack).first() + }; + + let zero = dfg.make_constant(FieldElement::zero(), u128); + let instruction = Instruction::Constrain( + res, + zero, + Some(ConstrainError::StaticString("attempt to multiply with overflow".to_string())), + ); + dfg.insert_instruction_and_results(instruction, block, None, call_stack); +} + +#[cfg(test)] +mod tests { + use crate::ssa::{opt::assert_normalized_ssa_equals, ssa_gen::Ssa}; + + #[test] + fn does_not_insert_check_if_lhs_is_less_than_two_pow_64() { + let src = " + acir(inline) fn main f0 { + b0(v0: u128): + v2 = mul u128 18446744073709551615, v0 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, src); + } + + #[test] + fn does_not_insert_check_if_rhs_is_less_than_two_pow_64() { + let src = " + acir(inline) fn main f0 { + b0(v0: u128): + v2 = mul v0, u128 18446744073709551615 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, src); + } + + #[test] + fn inserts_check_for_lhs() { + let src = " + acir(inline) fn main f0 { + b0(v0: u128): + v2 = mul v0, u128 18446744073709551617 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let expected = r#" + acir(inline) fn main f0 { + b0(v0: u128): + v2 = mul v0, u128 18446744073709551617 + v4 = div v0, u128 18446744073709551616 + constrain v4 == u128 0, "attempt to multiply with overflow" + return + } + "#; + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, expected); + } + + #[test] + fn inserts_check_for_rhs() { + let src = " + acir(inline) fn main f0 { + b0(v0: u128): + v2 = mul u128 18446744073709551617, v0 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let expected = r#" + acir(inline) fn main f0 { + b0(v0: u128): + v2 = mul u128 18446744073709551617, v0 + v4 = div v0, u128 18446744073709551616 + constrain v4 == u128 0, "attempt to multiply with overflow" + return + } + "#; + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, expected); + } + + #[test] + fn inserts_check_for_both_operands() { + let src = " + acir(inline) fn main f0 { + b0(v0: u128, v1: u128): + v2 = mul v0, v1 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let expected = r#" + acir(inline) fn main f0 { + b0(v0: u128, v1: u128): + v2 = mul v0, v1 + v4 = div v0, u128 18446744073709551616 + v5 = div v1, u128 18446744073709551616 + v6 = unchecked_mul v4, v5 + constrain v6 == u128 0, "attempt to multiply with overflow" + return + } + "#; + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, expected); + } + + #[test] + fn inserts_assertion_failure_if_overflow_is_guaranteed() { + let src = " + acir(inline) fn main f0 { + b0(): + v2 = mul u128 18446744073709551617, u128 18446744073709551616 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + // The multiplication remains, but it will be later removed by DIE + let expected = r#" + acir(inline) fn main f0 { + b0(): + v2 = mul u128 18446744073709551617, u128 18446744073709551616 + constrain u128 1 == u128 0, "attempt to multiply with overflow" + return + } + "#; + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, expected); + } + + #[test] + fn does_nothing_for_brillig() { + let src = " + brillig(inline) fn main f0 { + b0(): + v2 = mul u128 18446744073709551617, u128 18446744073709551616 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let ssa = ssa.check_u128_mul_overflow(); + assert_normalized_ssa_equals(ssa, src); + } +} diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index 05bd48b88306..3bde1d7530f0 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -22,9 +22,9 @@ use std::collections::{BTreeMap, HashSet, VecDeque}; use acvm::{ - acir::AcirField, - brillig_vm::{MemoryValue, VMStatus, VM}, FieldElement, + acir::AcirField, + brillig_vm::{MemoryValue, VM, VMStatus}, }; use bn254_blackbox_solver::Bn254BlackBoxSolver; use im::Vector; @@ -32,9 +32,9 @@ use iter_extended::vecmap; use crate::{ brillig::{ + Brillig, BrilligOptions, brillig_gen::gen_brillig_for, brillig_ir::{artifact::BrilligParameter, brillig_variable::get_bit_size_from_ssa_type}, - Brillig, BrilligOptions, }, ssa::{ ir::{ @@ -95,6 +95,11 @@ impl Ssa { let brillig_info = Some(BrilligInfo { brillig, brillig_functions: &brillig_functions }); for function in self.functions.values_mut() { + // We have already performed our final Brillig generation, so constant folding + // Brillig functions is unnecessary work. + if function.dfg.runtime().is_brillig() { + continue; + } function.constant_fold(false, brillig_info); } @@ -808,6 +813,7 @@ mod test { use crate::{ brillig::BrilligOptions, ssa::{ + Ssa, function_builder::FunctionBuilder, ir::{ function::RuntimeType, @@ -815,7 +821,6 @@ mod test { types::{NumericType, Type}, }, opt::assert_normalized_ssa_equals, - Ssa, }, }; @@ -1446,6 +1451,8 @@ mod test { } "; let ssa = Ssa::from_str(src).unwrap(); + // Need to run SSA pass that sets up Brillig array gets + let ssa = ssa.brillig_array_gets(); let brillig = ssa.to_brillig(&BrilligOptions::default()); let expected = " 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 d23cfee8a14f..e742ad4aa5d8 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 @@ -17,7 +17,7 @@ use crate::ssa::{ ssa_gen::Ssa, }; -use super::rc::{pop_rc_for, RcInstruction}; +use super::rc::{RcInstruction, pop_rc_for}; impl Ssa { /// Performs Dead Instruction Elimination (DIE) to remove any instructions with @@ -26,20 +26,22 @@ impl Ssa { /// This step should come after the flattening of the CFG and mem2reg. #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn dead_instruction_elimination(self) -> Ssa { - self.dead_instruction_elimination_inner(true) + self.dead_instruction_elimination_inner(true, false) } - fn dead_instruction_elimination_inner(mut self, flattened: bool) -> Ssa { + /// Post the Brillig generation we do not need to run this pass on Brillig functions. + #[tracing::instrument(level = "trace", skip(self))] + pub(crate) fn dead_instruction_elimination_acir(self) -> Ssa { + self.dead_instruction_elimination_inner(true, true) + } + + fn dead_instruction_elimination_inner(mut self, flattened: bool, skip_brillig: bool) -> Ssa { let mut used_globals_map: HashMap<_, _> = self .functions .par_iter_mut() .filter_map(|(id, func)| { - let set = func.dead_instruction_elimination(true, flattened); - if func.runtime().is_brillig() { - Some((*id, set)) - } else { - None - } + let set = func.dead_instruction_elimination(true, flattened, skip_brillig); + if func.runtime().is_brillig() { Some((*id, set)) } else { None } }) .collect(); @@ -79,7 +81,12 @@ impl Function { &mut self, insert_out_of_bounds_checks: bool, flattened: bool, + skip_brillig: bool, ) -> HashSet { + if skip_brillig && self.dfg.runtime().is_brillig() { + return HashSet::default(); + } + let mut context = Context { flattened, ..Default::default() }; context.mark_function_parameter_arrays_as_used(self); @@ -103,7 +110,7 @@ impl Function { // instructions (we don't want to remove those checks, or instructions that are // dependencies of those checks) if inserted_out_of_bounds_checks { - return self.dead_instruction_elimination(false, flattened); + return self.dead_instruction_elimination(false, flattened, skip_brillig); } context.remove_rc_instructions(&mut self.dfg); @@ -128,9 +135,8 @@ struct Context { /// them just yet. flattened: bool, - // When tracking mutations we consider arrays with the same type as all being possibly mutated. - // This we consider to span all blocks of the functions. - mutated_array_types: HashSet, + /// Track IncrementRc instructions per block to determine whether they are useless. + rc_tracker: RcTracker, } impl Context { @@ -160,10 +166,8 @@ impl Context { let block = &function.dfg[block_id]; self.mark_terminator_values_as_used(function, block); - // Lend the shared array type to the tracker. - let mut mutated_array_types = std::mem::take(&mut self.mutated_array_types); - let mut rc_tracker = RcTracker::new(&mut mutated_array_types); - rc_tracker.mark_terminator_arrays_as_used(function, block); + self.rc_tracker.new_block(); + self.rc_tracker.mark_terminator_arrays_as_used(function, block); let instructions_len = block.instructions().len(); @@ -196,12 +200,11 @@ impl Context { } } - rc_tracker.track_inc_rcs_to_remove(*instruction_id, function); + self.rc_tracker.track_inc_rcs_to_remove(*instruction_id, function); } - self.instructions_to_remove.extend(rc_tracker.get_non_mutated_arrays(&function.dfg)); - self.instructions_to_remove.extend(rc_tracker.rc_pairs_to_remove); - + self.instructions_to_remove.extend(self.rc_tracker.get_non_mutated_arrays(&function.dfg)); + self.instructions_to_remove.extend(self.rc_tracker.rc_pairs_to_remove.drain()); // If there are some instructions that might trigger an out of bounds error, // first add constrain checks. Then run the DIE pass again, which will remove those // but leave the constrains (any any value needed by those constrains) @@ -221,9 +224,6 @@ impl Context { .instructions_mut() .retain(|instruction| !self.instructions_to_remove.contains(instruction)); - // Take the mutated array back. - self.mutated_array_types = mutated_array_types; - false } @@ -272,11 +272,15 @@ impl Context { let typ = typ.get_contained_array(); // Want to store the array type which is being referenced, // because it's the underlying array that the `inc_rc` is associated with. - self.mutated_array_types.insert(typ.clone()); + self.add_mutated_array_type(typ.clone()); } } } + fn add_mutated_array_type(&mut self, typ: Type) { + self.rc_tracker.mutated_array_types.insert(typ.get_contained_array().clone()); + } + /// Go through the RC instructions collected when we figured out which values were unused; /// for each RC that refers to an unused value, remove the RC as well. fn remove_rc_instructions(&self, dfg: &mut DataFlowGraph) { @@ -608,8 +612,9 @@ fn apply_side_effects( (lhs, rhs) } +#[derive(Default)] /// Per block RC tracker. -struct RcTracker<'a> { +struct RcTracker { // We can track IncrementRc instructions per block to determine whether they are useless. // IncrementRc and DecrementRc instructions are normally side effectual instructions, but we remove // them if their value is not used anywhere in the function. However, even when their value is used, their existence @@ -624,7 +629,8 @@ struct RcTracker<'a> { // If an array is the same type as one of those non-mutated array types, we can safely remove all IncrementRc instructions on that array. inc_rcs: HashMap>, // Mutated arrays shared across the blocks of the function. - mutated_array_types: &'a mut HashSet, + // When tracking mutations we consider arrays with the same type as all being possibly mutated. + mutated_array_types: HashSet, // The SSA often creates patterns where after simplifications we end up with repeat // IncrementRc instructions on the same value. We track whether the previous instruction was an IncrementRc, // and if the current instruction is also an IncrementRc on the same value we remove the current instruction. @@ -632,15 +638,12 @@ struct RcTracker<'a> { previous_inc_rc: Option, } -impl<'a> RcTracker<'a> { - fn new(mutated_array_types: &'a mut HashSet) -> Self { - Self { - rcs_with_possible_pairs: Default::default(), - rc_pairs_to_remove: Default::default(), - inc_rcs: Default::default(), - previous_inc_rc: Default::default(), - mutated_array_types, - } +impl RcTracker { + fn new_block(&mut self) { + self.rcs_with_possible_pairs.clear(); + self.rc_pairs_to_remove.clear(); + self.inc_rcs.clear(); + self.previous_inc_rc = Default::default(); } fn mark_terminator_arrays_as_used(&mut self, function: &Function, block: &BasicBlock) { @@ -751,6 +754,7 @@ mod test { use noirc_frontend::monomorphization::ast::InlineType; use crate::ssa::{ + Ssa, function_builder::FunctionBuilder, ir::{ function::RuntimeType, @@ -758,7 +762,6 @@ mod test { types::{NumericType, Type}, }, opt::assert_normalized_ssa_equals, - Ssa, }; #[test] @@ -1099,7 +1102,7 @@ mod test { let ssa = Ssa::from_str(src).unwrap(); // Even though these ACIR functions only have 1 block, we have not inlined and flattened anything yet. - let ssa = ssa.dead_instruction_elimination_inner(false); + let ssa = ssa.dead_instruction_elimination_inner(false, false); let expected = " acir(inline) fn main f0 { @@ -1121,4 +1124,38 @@ mod test { "; assert_normalized_ssa_equals(ssa, expected); } + + #[test] + fn do_not_remove_inc_rc_if_mutated_in_other_block() { + let src = " + brillig(inline) fn main f0 { + b0(v0: &mut [Field; 3]): + v1 = load v0 -> [Field; 3] + inc_rc v1 + jmp b1() + b1(): + v2 = load v0 -> [Field; 3] + v3 = array_set v2, index u32 0, value u32 0 + store v3 at v0 + return + } + "; + let ssa = Ssa::from_str(src).unwrap(); + + let expected = " + brillig(inline) fn main f0 { + b0(v0: &mut [Field; 3]): + v1 = load v0 -> [Field; 3] + inc_rc v1 + jmp b1() + b1(): + v2 = load v0 -> [Field; 3] + v4 = array_set v2, index u32 0, value u32 0 + store v4 at v0 + return + } + "; + let ssa = ssa.dead_instruction_elimination(); + assert_normalized_ssa_equals(ssa, expected); + } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 76f8495c0094..a25e3db2b080 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -133,7 +133,7 @@ //! store v12 at v5 (new store) use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; -use acvm::{acir::AcirField, acir::BlackBoxFunc, FieldElement}; +use acvm::{FieldElement, acir::AcirField, acir::BlackBoxFunc}; use iter_extended::vecmap; use crate::ssa::{ @@ -176,12 +176,15 @@ impl Ssa { } } -struct Context<'f> { - inserter: FunctionInserter<'f>, +pub(crate) struct Context<'f> { + pub(crate) inserter: FunctionInserter<'f>, /// This ControlFlowGraph is the graph from before the function was modified by this flattening pass. cfg: ControlFlowGraph, + /// Target block of the flattening + pub(crate) target_block: BasicBlockId, + /// Maps start of branch -> end of branch branch_ends: HashMap, @@ -213,6 +216,10 @@ struct Context<'f> { /// us from unnecessarily inserting extra instructions, and keeps ids unique which /// helps simplifications. not_instructions: HashMap, + + /// Flag to tell the context to not issue 'enable_side_effect' instructions during flattening. + /// This should be set to true only by flatten_single(), when no instruction is known to fail. + pub(crate) no_predicate: bool, } #[derive(Clone)] @@ -249,6 +256,7 @@ fn flatten_function_cfg(function: &mut Function, no_predicates: &HashMap Context<'f> { - fn flatten(&mut self, no_predicates: &HashMap) { + //impl Context<'_> { + pub(crate) fn new( + function: &'f mut Function, + cfg: ControlFlowGraph, + branch_ends: HashMap, + target_block: BasicBlockId, + ) -> Self { + Context { + inserter: FunctionInserter::new(function), + cfg, + branch_ends, + condition_stack: Vec::new(), + arguments_stack: Vec::new(), + local_allocations: HashSet::default(), + not_instructions: HashMap::default(), + target_block, + no_predicate: false, + } + } + + pub(crate) fn flatten(&mut self, no_predicates: &HashMap) { // Flatten the CFG by inlining all instructions from the queued blocks // until all blocks have been flattened. // We follow the terminator of each block to determine which blocks to // process next - let mut queue = vec![self.inserter.function.entry_block()]; + let mut queue = vec![self.target_block]; while let Some(block) = queue.pop() { self.inline_block(block, no_predicates); let to_process = self.handle_terminator(block, &queue); @@ -318,10 +348,14 @@ impl<'f> Context<'f> { result } - // Inline all instructions from the given block into the entry block, and track slice capacities - fn inline_block(&mut self, block: BasicBlockId, no_predicates: &HashMap) { - if self.inserter.function.entry_block() == block { - // we do not inline the entry block into itself + // Inline all instructions from the given block into the target block, and track slice capacities + pub(crate) fn inline_block( + &mut self, + block: BasicBlockId, + no_predicates: &HashMap, + ) { + if self.target_block == block { + // we do not inline the target block into itself // for the outer block before we start inlining return; } @@ -354,7 +388,7 @@ impl<'f> Context<'f> { /// For a normal block, it would be its successor /// For blocks related to a conditional statement, we ensure to process /// the 'then-branch', then the 'else-branch' (if it exists), and finally the end block - fn handle_terminator( + pub(crate) fn handle_terminator( &mut self, block: BasicBlockId, work_list: &[BasicBlockId], @@ -388,9 +422,9 @@ impl<'f> Context<'f> { let return_values = vecmap(return_values.clone(), |value| self.inserter.resolve(value)); let new_return = TerminatorInstruction::Return { return_values, call_stack }; - let entry = self.inserter.function.entry_block(); + let target = self.target_block; - self.inserter.function.dfg.set_block_terminator(entry, new_return); + self.inserter.function.dfg.set_block_terminator(target, new_return); vec![] } } @@ -544,7 +578,7 @@ impl<'f> Context<'f> { } else { self.inserter.function.dfg.make_constant(FieldElement::zero(), NumericType::bool()) }; - let block = self.inserter.function.entry_block(); + let block = self.target_block; // Cannot include this in the previous vecmap since it requires exclusive access to self let args = vecmap(args, |(then_arg, else_arg)| { @@ -568,11 +602,11 @@ impl<'f> Context<'f> { destination } - /// Insert a new instruction into the function's entry block. + /// Insert a new instruction into the target block. /// Unlike push_instruction, this function will not map any ValueIds. /// within the given instruction, nor will it modify self.values in any way. fn insert_instruction(&mut self, instruction: Instruction, call_stack: CallStackId) -> ValueId { - let block = self.inserter.function.entry_block(); + let block = self.target_block; self.inserter .function .dfg @@ -580,7 +614,7 @@ impl<'f> Context<'f> { .first() } - /// Inserts a new instruction into the function's entry block, using the given + /// Inserts a new instruction into the target block, using the given /// control type variables to specify result types if needed. /// Unlike push_instruction, this function will not map any ValueIds. /// within the given instruction, nor will it modify self.values in any way. @@ -590,7 +624,7 @@ impl<'f> Context<'f> { ctrl_typevars: Option>, call_stack: CallStackId, ) -> InsertInstructionResult { - let block = self.inserter.function.entry_block(); + let block = self.target_block; self.inserter.function.dfg.insert_instruction_and_results( instruction, block, @@ -600,11 +634,14 @@ impl<'f> Context<'f> { } /// Checks the branch condition on the top of the stack and uses it to build and insert an - /// `EnableSideEffectsIf` instruction into the entry block. + /// `EnableSideEffectsIf` instruction into the target block. /// /// If the stack is empty, a "true" u1 constant is taken to be the active condition. This is /// necessary for re-enabling side-effects when re-emerging to a branch depth of 0. fn insert_current_side_effects_enabled(&mut self) { + if self.no_predicate { + return; + } let condition = match self.get_last_condition() { Some(cond) => cond, None => { @@ -616,7 +653,7 @@ impl<'f> Context<'f> { self.insert_instruction_with_typevars(enable_side_effects, None, call_stack); } - /// Push the given instruction to the end of the entry block of the current function. + /// Push the given instruction to the end of the target block of the current function. /// /// Note that each ValueId of the instruction will be mapped via self.inserter.resolve. /// As a result, the instruction that will be pushed will actually be a new instruction @@ -631,8 +668,8 @@ impl<'f> Context<'f> { let instruction = self.handle_instruction_side_effects(instruction, call_stack); let instruction_is_allocate = matches!(&instruction, Instruction::Allocate); - let entry = self.inserter.function.entry_block(); - let results = self.inserter.push_instruction_value(instruction, id, entry, call_stack); + let results = + self.inserter.push_instruction_value(instruction, id, self.target_block, call_stack); // Remember an allocate was created local to this branch so that we do not try to merge store // values across branches for it later. @@ -816,13 +853,13 @@ mod test { use acvm::acir::AcirField; use crate::ssa::{ + Ssa, ir::{ dfg::DataFlowGraph, instruction::{Instruction, TerminatorInstruction}, value::{Value, ValueId}, }, opt::assert_normalized_ssa_equals, - Ssa, }; #[test] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs index 780912852085..7aad174d327d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs @@ -102,7 +102,9 @@ impl<'cfg> Context<'cfg> { } else if successors.len() == 1 { self.find_join_point(successors.next().unwrap()) } else if successors.len() == 0 { - unreachable!("return encountered before a join point was found. This can only happen if early-return was added to the language without implementing it by jmping to a join block first") + unreachable!( + "return encountered before a join point was found. This can only happen if early-return was added to the language without implementing it by jmping to a join block first" + ) } else { unreachable!("A block can only have 0, 1, or 2 successors"); } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs index f4638cf85e45..6e8c7df2bba5 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs @@ -1,4 +1,4 @@ -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use fxhash::{FxHashMap as HashMap, FxHashSet}; use crate::ssa::ir::{ diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/hint.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/hint.rs index be088c3da948..911554e5d6dd 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/hint.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/hint.rs @@ -6,8 +6,8 @@ mod tests { brillig::BrilligOptions, errors::RuntimeError, ssa::{ - opt::assert_normalized_ssa_equals, optimize_all, Ssa, SsaBuilder, SsaEvaluatorOptions, - SsaLogging, + Ssa, SsaBuilder, SsaEvaluatorOptions, SsaLogging, opt::assert_normalized_ssa_equals, + optimize_all, }, }; @@ -20,7 +20,7 @@ mod tests { emit_ssa: None, skip_underconstrained_check: true, enable_brillig_constraints_check_lookback: false, - enable_brillig_constraints_check: false, + skip_brillig_constraints_check: true, inliner_aggressiveness: 0, max_bytecode_increase_percent: None, }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining.rs index e5753aeba4e8..161eea182d64 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining.rs @@ -23,7 +23,7 @@ use crate::ssa::{ pub(super) mod inline_info; -pub(super) use inline_info::{compute_inline_infos, InlineInfo, InlineInfos}; +pub(super) use inline_info::{InlineInfo, InlineInfos, compute_inline_infos}; /// An arbitrary limit to the maximum number of recursive call /// frames at any point in time. @@ -278,7 +278,9 @@ impl InlineContext { if self.recursion_level > RECURSION_LIMIT { panic!( - "Attempted to recur more than {RECURSION_LIMIT} times during inlining function '{}':\n{}", source_function.name(), source_function + "Attempted to recur more than {RECURSION_LIMIT} times during inlining function '{}':\n{}", + source_function.name(), + source_function ); } @@ -349,10 +351,14 @@ impl<'function> PerFunctionContext<'function> { return id; } } - unreachable!("All Value::Instructions should already be known during inlining after creating the original inlined instruction. Unknown value {id} = {value:?}") + unreachable!( + "All Value::Instructions should already be known during inlining after creating the original inlined instruction. Unknown value {id} = {value:?}" + ) } value @ Value::Param { .. } => { - unreachable!("All Value::Params should already be known from previous calls to translate_block. Unknown value {id} = {value:?}") + unreachable!( + "All Value::Params should already be known from previous calls to translate_block. Unknown value {id} = {value:?}" + ) } Value::NumericConstant { constant, typ } => { // The dfg indexes a global's inner value directly, so we need to check here @@ -778,10 +784,11 @@ impl<'function> PerFunctionContext<'function> { mod test { use std::cmp::max; - use acvm::{acir::AcirField, FieldElement}; + use acvm::{FieldElement, acir::AcirField}; use noirc_frontend::monomorphization::ast::InlineType; use crate::ssa::{ + Ssa, function_builder::FunctionBuilder, ir::{ basic_block::BasicBlockId, @@ -791,7 +798,6 @@ mod test { types::{NumericType, Type}, }, opt::{assert_normalized_ssa_equals, inlining::inline_info::compute_bottom_up_order}, - Ssa, }; #[test] @@ -1268,29 +1274,29 @@ mod test { fn inline_simple_functions_with_zero_instructions() { let src = " acir(inline) fn main f0 { - b0(v0: Field): - v2 = call f1(v0) -> Field - v3 = call f1(v0) -> Field - v4 = add v2, v3 - return v4 + b0(v0: Field): + v2 = call f1(v0) -> Field + v3 = call f1(v0) -> Field + v4 = add v2, v3 + return v4 } acir(inline) fn foo f1 { - b0(v0: Field): - return v0 + b0(v0: Field): + return v0 } "; let ssa = Ssa::from_str(src).unwrap(); let expected = " acir(inline) fn main f0 { - b0(v0: Field): - v1 = add v0, v0 - return v1 + b0(v0: Field): + v1 = add v0, v0 + return v1 } acir(inline) fn foo f1 { - b0(v0: Field): - return v0 + b0(v0: Field): + return v0 } "; @@ -1302,33 +1308,33 @@ mod test { fn inline_simple_functions_with_one_instruction() { let src = " acir(inline) fn main f0 { - b0(v0: Field): - v2 = call f1(v0) -> Field - v3 = call f1(v0) -> Field - v4 = add v2, v3 - return v4 + b0(v0: Field): + v2 = call f1(v0) -> Field + v3 = call f1(v0) -> Field + v4 = add v2, v3 + return v4 } acir(inline) fn foo f1 { - b0(v0: Field): - v2 = add v0, Field 1 - return v2 + b0(v0: Field): + v2 = add v0, Field 1 + return v2 } "; let ssa = Ssa::from_str(src).unwrap(); let expected = " acir(inline) fn main f0 { - b0(v0: Field): - v2 = add v0, Field 1 - v3 = add v0, Field 1 - v4 = add v2, v3 - return v4 + b0(v0: Field): + v2 = add v0, Field 1 + v3 = add v0, Field 1 + v4 = add v2, v3 + return v4 } acir(inline) fn foo f1 { - b0(v0: Field): - v2 = add v0, Field 1 - return v2 + b0(v0: Field): + v2 = add v0, Field 1 + return v2 } "; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining/inline_info.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining/inline_info.rs index 26bb2cad6752..d40baaae6a35 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining/inline_info.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining/inline_info.rs @@ -311,11 +311,7 @@ fn mark_functions_to_retain_recursive( let inlined_function_weights: i64 = called_functions.iter().fold(0, |acc, callee| { let info = &inline_infos[callee]; // If the callee is not going to be inlined then we can ignore its cost. - if info.should_inline { - acc.saturating_add(info.weight) - } else { - acc - } + if info.should_inline { acc.saturating_add(info.weight) } else { acc } }); let this_function_weight = inlined_function_weights diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs index 19fc6a7f5a20..9bbac1a4c0c8 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs @@ -7,22 +7,22 @@ //! - Already marked as loop invariants //! //! We also check that we are not hoisting instructions with side effects. -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::ssa::{ + Ssa, ir::{ basic_block::BasicBlockId, function::Function, function_inserter::FunctionInserter, instruction::{ - binary::eval_constant_binary_op, Binary, BinaryOp, Instruction, InstructionId, + Binary, BinaryOp, Instruction, InstructionId, binary::eval_constant_binary_op, }, post_order::PostOrder, types::Type, value::ValueId, }, - Ssa, }; use super::unrolling::{Loop, Loops}; @@ -373,8 +373,8 @@ impl<'f> LoopInvariantContext<'f> { #[cfg(test)] mod test { - use crate::ssa::opt::assert_normalized_ssa_equals; use crate::ssa::Ssa; + use crate::ssa::opt::assert_normalized_ssa_equals; #[test] fn simple_loop_invariant_code_motion() { @@ -840,15 +840,15 @@ mod test { let src = " brillig(inline) fn main f0 { b0(v0: u32, v1: u32): - jmp b1(u32 0) + jmp b1(u32 0) b1(v2: u32): - v5 = lt v2, u32 4 - jmpif v5 then: b3, else: b2 + v5 = lt v2, u32 4 + jmpif v5 then: b3, else: b2 b2(): - return + return b3(): - v7 = sub v2, u32 1 - jmp b1(v7) + v7 = sub v2, u32 1 + jmp b1(v7) } "; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/make_constrain_not_equal.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/make_constrain_not_equal.rs index 21f536eba2d4..28e59f924292 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/make_constrain_not_equal.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/make_constrain_not_equal.rs @@ -39,7 +39,7 @@ impl Function { if self .dfg .get_numeric_constant(*rhs) - .map_or(false, |constant| constant.is_zero()) + .is_some_and(|constant| constant.is_zero()) { if let Value::Instruction { instruction, .. } = &self.dfg[self.dfg.resolve(*lhs)] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index ce76825877a6..9b58a0de3294 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -201,7 +201,7 @@ impl<'f> PerFunctionContext<'f> { let is_dereference = block .expressions .get(store_address) - .map_or(false, |expression| matches!(expression, Expression::Dereference(_))); + .is_some_and(|expression| matches!(expression, Expression::Dereference(_))); if !self.last_loads.contains_key(store_address) && !store_alias_used @@ -668,10 +668,11 @@ impl<'f> PerFunctionContext<'f> { mod tests { use std::sync::Arc; - use acvm::{acir::AcirField, FieldElement}; + use acvm::{FieldElement, acir::AcirField}; use im::vector; use crate::ssa::{ + Ssa, function_builder::FunctionBuilder, ir::{ basic_block::BasicBlockId, @@ -681,7 +682,6 @@ mod tests { types::Type, }, opt::assert_normalized_ssa_equals, - Ssa, }; #[test] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs index 91e27f07b8e9..8c74852b53bf 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg/block.rs @@ -58,11 +58,7 @@ pub(super) enum ReferenceValue { impl ReferenceValue { fn unify(self, other: Self) -> Self { - if self == other { - self - } else { - ReferenceValue::Unknown - } + if self == other { self } else { ReferenceValue::Unknown } } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mod.rs index 4d8a652b94d9..a9784d4c7cfb 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mod.rs @@ -7,7 +7,10 @@ mod array_set; mod as_slice_length; mod assert_constant; +mod basic_conditional; +mod brillig_array_gets; pub(crate) mod brillig_entry_points; +mod check_u128_mul_overflow; mod constant_folding; mod defunctionalize; mod die; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/preprocess_fns.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/preprocess_fns.rs index 764fb6dd65b5..6fa1e88ee2d4 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/preprocess_fns.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/preprocess_fns.rs @@ -1,8 +1,8 @@ //! Pre-process functions before inlining them into others. use crate::ssa::{ - ir::function::{Function, RuntimeType}, Ssa, + ir::function::{Function, RuntimeType}, }; use super::inlining::{self, InlineInfo}; @@ -59,7 +59,7 @@ impl Ssa { // Try to reduce the number of blocks. function.simplify_function(); // Remove leftover instructions. - function.dead_instruction_elimination(true, false); + function.dead_instruction_elimination(true, false, false); // Put it back into the SSA, so the next functions can pick it up. self.functions.insert(id, function); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/pure.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/pure.rs index d790d035eb05..b975413fc2e5 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/pure.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/pure.rs @@ -171,7 +171,7 @@ impl Function { | Value::Instruction { .. } | Value::Param { .. } | Value::NumericConstant { .. } => { - return (Purity::Impure, BTreeSet::new()) + return (Purity::Impure, BTreeSet::new()); } } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs index e36be71aeea2..b4427a1c91bf 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, sync::Arc}; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use crate::ssa::{ ir::{ @@ -167,6 +167,7 @@ impl Context<'_> { let lhs_typ = self.function.dfg.type_of_value(lhs).unwrap_numeric(); let base = self.field_constant(FieldElement::from(2_u128)); let pow = self.pow(base, rhs); + let pow = self.pow_or_max_for_bit_size(pow, rhs, bit_size, lhs_typ); let pow = self.insert_cast(pow, lhs_typ); if lhs_typ.is_unsigned() { // unsigned right bit shift is just a normal division @@ -205,6 +206,53 @@ impl Context<'_> { } } + /// Returns `pow` or the maximum value allowed for `typ` if 2^rhs is guaranteed to exceed that maximum. + fn pow_or_max_for_bit_size( + &mut self, + pow: ValueId, + rhs: ValueId, + bit_size: u32, + typ: NumericType, + ) -> ValueId { + let max = if typ.is_unsigned() { + if bit_size == 128 { u128::MAX } else { (1_u128 << bit_size) - 1 } + } else { + 1_u128 << (bit_size - 1) + }; + let max = self.field_constant(FieldElement::from(max)); + + // Here we check whether rhs is less than the bit_size: if it's not then it will overflow. + // Then we do: + // + // rhs_is_less_than_bit_size = lt rhs, bit_size + // rhs_is_not_less_than_bit_size = not rhs_is_less_than_bit_size + // pow_when_is_less_than_bit_size = rhs_is_less_than_bit_size * pow + // pow_when_is_not_less_than_bit_size = rhs_is_not_less_than_bit_size * max + // pow = add pow_when_is_less_than_bit_size, pow_when_is_not_less_than_bit_size + // + // All operations here are unchecked because they work on field types. + let rhs_typ = self.function.dfg.type_of_value(rhs).unwrap_numeric(); + let bit_size = self.numeric_constant(bit_size as u128, rhs_typ); + let rhs_is_less_than_bit_size = self.insert_binary(rhs, BinaryOp::Lt, bit_size); + let rhs_is_not_less_than_bit_size = self.insert_not(rhs_is_less_than_bit_size); + let rhs_is_less_than_bit_size = + self.insert_cast(rhs_is_less_than_bit_size, NumericType::NativeField); + let rhs_is_not_less_than_bit_size = + self.insert_cast(rhs_is_not_less_than_bit_size, NumericType::NativeField); + let pow_when_is_less_than_bit_size = + self.insert_binary(rhs_is_less_than_bit_size, BinaryOp::Mul { unchecked: true }, pow); + let pow_when_is_not_less_than_bit_size = self.insert_binary( + rhs_is_not_less_than_bit_size, + BinaryOp::Mul { unchecked: true }, + max, + ); + self.insert_binary( + pow_when_is_less_than_bit_size, + BinaryOp::Add { unchecked: true }, + pow_when_is_not_less_than_bit_size, + ) + } + /// Computes lhs^rhs via square&multiply, using the bits decomposition of rhs /// Pseudo-code of the computation: /// let mut r = 1; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs index 942fe67b5d5c..44a1a0b7acf2 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs @@ -10,7 +10,7 @@ //! before the [Instruction]. Continue inserting instructions until the next [Instruction::EnableSideEffectsIf] is encountered. use std::collections::HashSet; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use crate::ssa::{ ir::{ @@ -92,7 +92,7 @@ impl Context { let condition_is_one = function .dfg .get_numeric_constant(*condition) - .map_or(false, |condition| condition.is_one()); + .is_some_and(|condition| condition.is_one()); if condition_is_one { new_instructions.push(instruction_id); last_side_effects_enabled_instruction = None; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs index 8dde79a3c60d..94b0c1d0a851 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs @@ -1,6 +1,6 @@ use std::collections::hash_map::Entry; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use fxhash::FxHashMap as HashMap; use crate::ssa::ir::function::RuntimeType; @@ -8,6 +8,7 @@ use crate::ssa::ir::instruction::Hint; use crate::ssa::ir::types::NumericType; use crate::ssa::ir::value::ValueId; use crate::ssa::{ + Ssa, ir::{ dfg::DataFlowGraph, function::Function, @@ -16,7 +17,6 @@ use crate::ssa::{ value::Value, }, opt::flatten_cfg::value_merger::ValueMerger, - Ssa, }; impl Ssa { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs index 22fdf0a79872..3d812870c064 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs @@ -254,7 +254,9 @@ fn remove_block_parameters( let jump_args = match function.dfg[predecessor].unwrap_terminator_mut() { TerminatorInstruction::Jmp { arguments, .. } => std::mem::take(arguments), - TerminatorInstruction::JmpIf { .. } => unreachable!("If jmpif instructions are modified to support block arguments in the future, this match will need to be updated"), + TerminatorInstruction::JmpIf { .. } => unreachable!( + "If jmpif instructions are modified to support block arguments in the future, this match will need to be updated" + ), _ => unreachable!( "Predecessor was already validated to have only a single jmp destination" ), @@ -293,6 +295,7 @@ fn try_inline_into_predecessor( #[cfg(test)] mod test { use crate::ssa::{ + Ssa, function_builder::FunctionBuilder, ir::{ instruction::{BinaryOp, TerminatorInstruction}, @@ -300,7 +303,6 @@ mod test { types::Type, }, opt::assert_normalized_ssa_equals, - Ssa, }; use acvm::acir::AcirField; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs index 547a8a042c65..43afb9fa41a7 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -20,7 +20,7 @@ //! only used by Brillig bytecode. use std::collections::BTreeSet; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use im::HashSet; use crate::{ @@ -490,9 +490,19 @@ impl Loop { context.inline_instructions_from_block(); // Mutate the terminator if possible so that it points at the iteration block. match context.dfg()[fresh_block].unwrap_terminator() { - TerminatorInstruction::JmpIf { condition, then_destination, else_destination, call_stack } => { + TerminatorInstruction::JmpIf { + condition, + then_destination, + else_destination, + call_stack, + } => { let condition = *condition; - let next_blocks = context.handle_jmpif(condition, *then_destination, *else_destination, *call_stack); + let next_blocks = context.handle_jmpif( + condition, + *then_destination, + *else_destination, + *call_stack, + ); // If there is only 1 next block the jmpif evaluated to a single known block. // This is the expected case and lets us know if we should loop again or not. @@ -515,7 +525,9 @@ impl Loop { Err(context.inserter.function.dfg.get_value_call_stack(condition)) } } - other => unreachable!("Expected loop header to terminate in a JmpIf to the loop body, but found {other:?} instead"), + other => unreachable!( + "Expected loop header to terminate in a JmpIf to the loop body, but found {other:?} instead" + ), } } @@ -1021,9 +1033,9 @@ mod tests { use test_case::test_case; use crate::errors::RuntimeError; - use crate::ssa::{ir::value::ValueId, opt::assert_normalized_ssa_equals, Ssa}; + use crate::ssa::{Ssa, ir::value::ValueId, opt::assert_normalized_ssa_equals}; - use super::{is_new_size_ok, BoilerplateStats, Loops}; + use super::{BoilerplateStats, Loops, is_new_size_ok}; /// Tries to unroll all loops in each SSA function once, calling the `Function` directly, /// bypassing the iterative loop done by the SSA which does further optimisations. diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index fc9b1ae98bcd..d7ca5832f064 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -15,8 +15,8 @@ use crate::ssa::{ }; use super::{ - ast::AssertMessage, Identifier, ParsedBlock, ParsedFunction, ParsedGlobal, ParsedGlobalValue, - ParsedInstruction, ParsedSsa, ParsedTerminator, ParsedValue, RuntimeType, Ssa, SsaError, Type, + Identifier, ParsedBlock, ParsedFunction, ParsedGlobal, ParsedGlobalValue, ParsedInstruction, + ParsedSsa, ParsedTerminator, ParsedValue, RuntimeType, Ssa, SsaError, Type, ast::AssertMessage, }; impl ParsedSsa { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/lexer.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/lexer.rs index e22b6a661de0..e6e15d1559d3 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/lexer.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/lexer.rs @@ -171,7 +171,7 @@ impl<'a> Lexer<'a> { return Err(LexerError::InvalidIntegerLiteral { span: Span::inclusive(start, end), found: integer_str, - }) + }); } }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/mod.rs index 4c5f6334430d..9f4d38648e20 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/mod.rs @@ -5,11 +5,11 @@ use std::{ }; use super::{ + Ssa, ir::{ instruction::BinaryOp, types::{NumericType, Type}, }, - Ssa, }; use acvm::{AcirField, FieldElement}; @@ -675,11 +675,7 @@ impl<'a> Parser<'a> { } fn parse_value_or_error(&mut self) -> ParseResult { - if let Some(value) = self.parse_value()? { - Ok(value) - } else { - self.expected_value() - } + if let Some(value) = self.parse_value()? { Ok(value) } else { self.expected_value() } } fn parse_value(&mut self) -> ParseResult> { @@ -818,7 +814,7 @@ impl<'a> Parser<'a> { } fn eat_identifier(&mut self) -> ParseResult> { - let span = self.token.to_span(); + let span = self.token.span(); if let Some(name) = self.eat_ident()? { Ok(Some(Identifier::new(name, span))) } else { @@ -852,11 +848,7 @@ impl<'a> Parser<'a> { } fn eat_ident_or_error(&mut self) -> ParseResult { - if let Some(ident) = self.eat_ident()? { - Ok(ident) - } else { - self.expected_identifier() - } + if let Some(ident) = self.eat_ident()? { Ok(ident) } else { self.expected_identifier() } } fn eat_int(&mut self) -> ParseResult> { @@ -879,11 +871,7 @@ impl<'a> Parser<'a> { } fn eat_int_or_error(&mut self) -> ParseResult { - if let Some(int) = self.eat_int()? { - Ok(int) - } else { - self.expected_int() - } + if let Some(int) = self.eat_int()? { Ok(int) } else { self.expected_int() } } fn eat_int_type(&mut self) -> ParseResult> { @@ -933,11 +921,7 @@ impl<'a> Parser<'a> { } fn eat_or_error(&mut self, token: Token) -> ParseResult<()> { - if self.eat(token.clone())? { - Ok(()) - } else { - self.expected_token(token) - } + if self.eat(token.clone())? { Ok(()) } else { self.expected_token(token) } } fn at(&self, token: Token) -> bool { @@ -960,56 +944,53 @@ impl<'a> Parser<'a> { fn expected_instruction_or_terminator(&mut self) -> ParseResult { Err(ParserError::ExpectedInstructionOrTerminator { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } fn expected_string_or_data(&mut self) -> ParseResult { Err(ParserError::ExpectedStringOrData { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } fn expected_byte_string(&mut self) -> ParseResult { Err(ParserError::ExpectedByteString { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } fn expected_identifier(&mut self) -> ParseResult { Err(ParserError::ExpectedIdentifier { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } fn expected_int(&mut self) -> ParseResult { - Err(ParserError::ExpectedInt { - found: self.token.token().clone(), - span: self.token.to_span(), - }) + Err(ParserError::ExpectedInt { found: self.token.token().clone(), span: self.token.span() }) } fn expected_type(&mut self) -> ParseResult { Err(ParserError::ExpectedType { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } fn expected_value(&mut self) -> ParseResult { Err(ParserError::ExpectedValue { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } fn expected_global_value(&mut self) -> ParseResult { Err(ParserError::ExpectedGlobalValue { found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } @@ -1017,7 +998,7 @@ impl<'a> Parser<'a> { Err(ParserError::ExpectedToken { token, found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } @@ -1025,7 +1006,7 @@ impl<'a> Parser<'a> { Err(ParserError::ExpectedOneOfTokens { tokens: tokens.to_vec(), found: self.token.token().clone(), - span: self.token.to_span(), + span: self.token.span(), }) } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 358c2e89a411..a865a31a060d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -1,7 +1,7 @@ #![cfg(test)] use crate::{ - ssa::{opt::assert_normalized_ssa_equals, Ssa}, + ssa::{Ssa, opt::assert_normalized_ssa_equals}, trim_leading_whitespace_from_lines, }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/token.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/token.rs index eb09209466d4..280a2ca95a7c 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -12,7 +12,7 @@ impl SpannedToken { SpannedToken(Spanned::from(span, token)) } - pub(crate) fn to_span(&self) -> Span { + pub(crate) fn span(&self) -> Span { self.0.span() } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index cbac4bd6d840..5db3ecb91b7f 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -1,12 +1,13 @@ use std::collections::BTreeMap; use std::sync::{Arc, Mutex, RwLock}; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use iter_extended::vecmap; use noirc_errors::Location; use noirc_frontend::ast::{BinaryOpKind, Signedness}; use noirc_frontend::monomorphization::ast::{self, GlobalId, InlineType, LocalId, Parameters}; use noirc_frontend::monomorphization::ast::{FuncId, Program}; +use noirc_frontend::signed_field::SignedField; use crate::errors::RuntimeError; use crate::ssa::function_builder::FunctionBuilder; @@ -19,8 +20,8 @@ use crate::ssa::ir::map::AtomicCounter; use crate::ssa::ir::types::{NumericType, Type}; use crate::ssa::ir::value::ValueId; -use super::value::{Tree, Value, Values}; use super::GlobalsGraph; +use super::value::{Tree, Value, Values}; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; /// The FunctionContext is the main context object for translating a @@ -289,32 +290,30 @@ impl<'a> FunctionContext<'a> { /// otherwise values like 2^128 can be assigned to a u8 without error or wrapping. pub(super) fn checked_numeric_constant( &mut self, - value: impl Into, - negative: bool, + value: SignedField, numeric_type: NumericType, ) -> Result { - let value = value.into(); - - if let Some(range) = numeric_type.value_is_outside_limits(value, negative) { + if let Some(range) = numeric_type.value_is_outside_limits(value) { let call_stack = self.builder.get_call_stack(); return Err(RuntimeError::IntegerOutOfBounds { - value: if negative { -value } else { value }, + value, typ: numeric_type, range, call_stack, }); } - let value = if negative { + let value = if value.is_negative { match numeric_type { - NumericType::NativeField => -value, + NumericType::NativeField => -value.field, NumericType::Signed { bit_size } | NumericType::Unsigned { bit_size } => { + assert!(bit_size < 128); let base = 1_u128 << bit_size; - FieldElement::from(base) - value + FieldElement::from(base) - value.field } } } else { - value + value.field }; Ok(self.builder.numeric_constant(value, numeric_type)) @@ -355,7 +354,8 @@ impl<'a> FunctionContext<'a> { /// Insert constraints ensuring that the operation does not overflow the bit size of the result /// - /// If the result is unsigned, overflow will be checked during acir-gen (cf. issue #4456), except for bit-shifts, because we will convert them to field multiplication + /// If the result is unsigned, overflow will be checked during acir-gen (cf. issue #4456), except for + /// bit-shifts, because we will convert them to field multiplication /// /// If the result is signed, we just prepare it for check_signed_overflow() by casting it to /// an unsigned value representing the signed integer. @@ -755,11 +755,7 @@ impl<'a> FunctionContext<'a> { Ok(match lvalue { ast::LValue::Ident(ident) => { let (reference, should_auto_deref) = self.ident_lvalue(ident); - if should_auto_deref { - LValue::Dereference { reference } - } else { - LValue::Ident - } + if should_auto_deref { LValue::Dereference { reference } } else { LValue::Ident } } ast::LValue::Index { array, index, location, .. } => { self.index_lvalue(array, index, location)?.2 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 91aa8b2914d2..a954ac3ab93c 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 @@ -130,7 +130,7 @@ pub(crate) fn generate_ssa(program: Program) -> Result { Ok(ssa) } -impl<'a> FunctionContext<'a> { +impl FunctionContext<'_> { /// Codegen a function's body and set its return value to that of its last parameter. /// For functions returning nothing, this will be an empty list. fn codegen_function_body(&mut self, body: &Expression) -> Result<(), RuntimeError> { @@ -233,10 +233,10 @@ impl<'a> FunctionContext<'a> { _ => unreachable!("ICE: unexpected slice literal type, got {}", array.typ), }) } - ast::Literal::Integer(value, negative, typ, location) => { + ast::Literal::Integer(value, typ, location) => { self.builder.set_location(*location); let typ = Self::convert_non_tuple_type(typ).unwrap_numeric(); - self.checked_numeric_constant(*value, *negative, typ).map(Into::into) + self.checked_numeric_constant(*value, typ).map(Into::into) } ast::Literal::Bool(value) => { // Don't need to call checked_numeric_constant here since `value` can only be true or false @@ -252,7 +252,7 @@ impl<'a> FunctionContext<'a> { let value = value.replace('{', "{{").replace('}', "}}"); string.push_str(&value); } - FmtStrFragment::Interpolation(value, _span) => { + FmtStrFragment::Interpolation(value, _) => { string.push('{'); string.push_str(value); string.push('}'); @@ -820,9 +820,7 @@ impl<'a> FunctionContext<'a> { typ: NumericType, ) -> Result { match constructor { - Constructor::Int(value) => { - self.checked_numeric_constant(value.field, value.is_negative, typ) - } + Constructor::Int(value) => self.checked_numeric_constant(*value, typ), other => Ok(self.builder.numeric_constant(other.variant_index(), typ)), } } diff --git a/noir/noir-repo/compiler/noirc_frontend/Cargo.toml b/noir/noir-repo/compiler/noirc_frontend/Cargo.toml index f9e36f7f3e0e..e5231565041c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_frontend/Cargo.toml @@ -33,7 +33,6 @@ strum.workspace = true strum_macros.workspace = true fxhash.workspace = true - [dev-dependencies] base64.workspace = true proptest.workspace = true diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/enumeration.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/enumeration.rs index 6789a200e6aa..5ac60be5fba5 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/enumeration.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/enumeration.rs @@ -4,7 +4,7 @@ use crate::ast::{Ident, UnresolvedGenerics, UnresolvedType}; use crate::token::SecondaryAttribute; use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::Location; use super::{Documented, ItemVisibility}; @@ -16,7 +16,7 @@ pub struct NoirEnumeration { pub visibility: ItemVisibility, pub generics: UnresolvedGenerics, pub variants: Vec>, - pub span: Span, + pub location: Location, } impl NoirEnumeration { 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 01705e0af881..398e52676950 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs @@ -7,16 +7,15 @@ use crate::ast::{ Ident, ItemVisibility, Path, Pattern, Statement, StatementKind, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, Visibility, }; -use crate::node_interner::{ - ExprId, InternedExpressionKind, InternedStatementKind, QuotedTypeId, TypeId, -}; +use crate::node_interner::{ExprId, InternedExpressionKind, InternedStatementKind, QuotedTypeId}; +use crate::signed_field::SignedField; use crate::token::{Attributes, FmtStrFragment, FunctionAttribute, Token, Tokens}; use crate::{Kind, Type}; -use acvm::{acir::AcirField, FieldElement}; +use acvm::FieldElement; use iter_extended::vecmap; -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Located, Location, Span}; -use super::{AsTraitPath, TypePath}; +use super::{AsTraitPath, TypePath, UnsafeExpression}; #[derive(Debug, PartialEq, Eq, Clone)] pub enum ExpressionKind { @@ -39,8 +38,8 @@ pub enum ExpressionKind { Parenthesized(Box), Quote(Tokens), Unquote(Box), - Comptime(BlockExpression, Span), - Unsafe(BlockExpression, Span), + Comptime(BlockExpression, Location), + Unsafe(UnsafeExpression), AsTraitPath(AsTraitPath), TypePath(TypePath), @@ -76,7 +75,7 @@ pub enum UnresolvedGeneric { /// splices existing types into a generic list. In this case we have /// to validate the type refers to a named generic and treat that /// as a ResolvedGeneric when this is resolved. - Resolved(QuotedTypeId, Span), + Resolved(QuotedTypeId, Location), } #[derive(Error, PartialEq, Eq, Debug, Clone)] @@ -87,14 +86,18 @@ pub struct UnsupportedNumericGenericType { } impl UnresolvedGeneric { - pub fn span(&self) -> Span { + pub fn location(&self) -> Location { match self { - UnresolvedGeneric::Variable(ident) => ident.0.span(), - UnresolvedGeneric::Numeric { ident, typ } => ident.0.span().merge(typ.span), - UnresolvedGeneric::Resolved(_, span) => *span, + UnresolvedGeneric::Variable(ident) => ident.0.location(), + UnresolvedGeneric::Numeric { ident, typ } => ident.location().merge(typ.location), + UnresolvedGeneric::Resolved(_, location) => *location, } } + pub fn span(&self) -> Span { + self.location().span + } + pub fn kind(&self) -> Result { match self { UnresolvedGeneric::Variable(_) => Ok(Kind::Normal), @@ -168,8 +171,8 @@ impl ExpressionKind { match (operator, &rhs) { ( UnaryOp::Minus, - Expression { kind: ExpressionKind::Literal(Literal::Integer(field, sign)), .. }, - ) => ExpressionKind::Literal(Literal::Integer(*field, !sign)), + Expression { kind: ExpressionKind::Literal(Literal::Integer(field)), .. }, + ) => ExpressionKind::Literal(Literal::Integer(-*field)), _ => ExpressionKind::Prefix(Box::new(PrefixExpression { operator, rhs })), } } @@ -197,7 +200,7 @@ impl ExpressionKind { } pub fn integer(contents: FieldElement) -> ExpressionKind { - ExpressionKind::Literal(Literal::Integer(contents, false)) + ExpressionKind::Literal(Literal::Integer(SignedField::positive(contents))) } pub fn boolean(contents: bool) -> ExpressionKind { @@ -219,18 +222,14 @@ impl ExpressionKind { pub fn constructor( (typ, fields): (UnresolvedType, Vec<(Ident, Expression)>), ) -> ExpressionKind { - ExpressionKind::Constructor(Box::new(ConstructorExpression { - typ, - fields, - struct_type: None, - })) + ExpressionKind::Constructor(Box::new(ConstructorExpression { typ, fields })) } } #[derive(Debug, Eq, Clone)] pub struct Expression { pub kind: ExpressionKind, - pub span: Span, + pub location: Location, } // This is important for tests. Two expressions are the same, if their Kind is the same @@ -242,23 +241,23 @@ impl PartialEq for Expression { } impl Expression { - pub fn new(kind: ExpressionKind, span: Span) -> Expression { - Expression { kind, span } + pub fn new(kind: ExpressionKind, location: Location) -> Expression { + Expression { kind, location } } - /// Returns the innermost span that gives this expression its type. - pub fn type_span(&self) -> Span { + /// Returns the innermost location that gives this expression its type. + pub fn type_location(&self) -> Location { match &self.kind { ExpressionKind::Block(block_expression) | ExpressionKind::Comptime(block_expression, _) - | ExpressionKind::Unsafe(block_expression, _) => { + | ExpressionKind::Unsafe(UnsafeExpression { block: block_expression, .. }) => { if let Some(statement) = block_expression.statements.last() { - statement.type_span() + statement.type_location() } else { - self.span + self.location } } - ExpressionKind::Parenthesized(expression) => expression.type_span(), + ExpressionKind::Parenthesized(expression) => expression.type_location(), ExpressionKind::Literal(..) | ExpressionKind::Prefix(..) | ExpressionKind::Index(..) @@ -281,12 +280,12 @@ impl Expression { | ExpressionKind::Resolved(..) | ExpressionKind::Interned(..) | ExpressionKind::InternedStatement(..) - | ExpressionKind::Error => self.span, + | ExpressionKind::Error => self.location, } } } -pub type BinaryOp = Spanned; +pub type BinaryOp = Located; #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Copy, Clone)] #[cfg_attr(test, derive(strum_macros::EnumIter))] @@ -411,7 +410,7 @@ pub enum Literal { Array(ArrayLiteral), Slice(ArrayLiteral), Bool(bool), - Integer(FieldElement, /*sign*/ bool), // false for positive integer and true for negative + Integer(SignedField), Str(String), RawStr(String, u8), FmtStr(Vec, u32 /* length */), @@ -478,7 +477,7 @@ pub struct FunctionDefinition { pub generics: UnresolvedGenerics, pub parameters: Vec, pub body: BlockExpression, - pub span: Span, + pub location: Location, pub where_clause: Vec, pub return_type: FunctionReturnType, pub return_visibility: Visibility, @@ -503,13 +502,13 @@ pub struct Param { pub visibility: Visibility, pub pattern: Pattern, pub typ: UnresolvedType, - pub span: Span, + pub location: Location, } #[derive(Debug, PartialEq, Eq, Clone)] pub enum FunctionReturnType { /// Returns type is not specified. - Default(Span), + Default(Location), /// Everything else. Ty(UnresolvedType), } @@ -541,11 +540,6 @@ pub struct MethodCallExpression { pub struct ConstructorExpression { pub typ: UnresolvedType, pub fields: Vec<(Ident, Expression)>, - - /// This may be filled out during macro expansion - /// so that we can skip re-resolving the type name since it - /// would be lost at that point. - pub struct_type: Option, } #[derive(Debug, PartialEq, Eq, Clone)] @@ -583,7 +577,7 @@ impl BlockExpression { pub struct ConstrainExpression { pub kind: ConstrainKind, pub arguments: Vec, - pub span: Span, + pub location: Location, } impl Display for ConstrainExpression { @@ -659,7 +653,7 @@ impl Display for ExpressionKind { Lambda(lambda) => lambda.fmt(f), Parenthesized(sub_expr) => write!(f, "({sub_expr})"), Comptime(block, _) => write!(f, "comptime {block}"), - Unsafe(block, _) => write!(f, "unsafe {block}"), + Unsafe(UnsafeExpression { block, .. }) => write!(f, "unsafe {block}"), Error => write!(f, "Error"), Resolved(_) => write!(f, "?Resolved"), Interned(_) => write!(f, "?Interned"), @@ -693,12 +687,8 @@ impl Display for Literal { write!(f, "&[{repeated_element}; {length}]") } Literal::Bool(boolean) => write!(f, "{}", if *boolean { "true" } else { "false" }), - Literal::Integer(integer, sign) => { - if *sign { - write!(f, "-{}", integer.to_u128()) - } else { - write!(f, "{}", integer.to_u128()) - } + Literal::Integer(signed_field) => { + write!(f, "{signed_field}") } Literal::Str(string) => write!(f, "\"{string}\""), Literal::RawStr(string, num_hashes) => { @@ -877,7 +867,7 @@ impl FunctionDefinition { visibility: Visibility::Private, pattern: Pattern::Identifier(ident.clone()), typ: unresolved_type.clone(), - span: ident.span().merge(unresolved_type.span), + location: ident.location().merge(unresolved_type.location), }) .collect(); @@ -890,7 +880,7 @@ impl FunctionDefinition { generics: generics.clone(), parameters: p, body, - span: name.span(), + location: name.location(), where_clause, return_type: return_type.clone(), return_visibility: Visibility::Private, @@ -898,13 +888,14 @@ impl FunctionDefinition { } pub fn signature(&self) -> String { - let parameters = vecmap(&self.parameters, |Param { visibility, pattern, typ, span: _ }| { - if *visibility == Visibility::Public { - format!("{pattern}: {visibility} {typ}") - } else { - format!("{pattern}: {typ}") - } - }); + let parameters = + vecmap(&self.parameters, |Param { visibility, pattern, typ, location: _ }| { + if *visibility == Visibility::Public { + format!("{pattern}: {visibility} {typ}") + } else { + format!("{pattern}: {typ}") + } + }); let where_clause = vecmap(&self.where_clause, ToString::to_string); let where_clause_str = if !where_clause.is_empty() { @@ -933,12 +924,19 @@ impl Display for FunctionDefinition { impl FunctionReturnType { pub fn get_type(&self) -> Cow { match self { - FunctionReturnType::Default(span) => { - Cow::Owned(UnresolvedType { typ: UnresolvedTypeData::Unit, span: *span }) + FunctionReturnType::Default(location) => { + Cow::Owned(UnresolvedType { typ: UnresolvedTypeData::Unit, location: *location }) } FunctionReturnType::Ty(typ) => Cow::Borrowed(typ), } } + + pub fn location(&self) -> Location { + match self { + FunctionReturnType::Default(location) => *location, + FunctionReturnType::Ty(typ) => typ.location, + } + } } impl Display for FunctionReturnType { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/function.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/function.rs index 8957564e0d63..f0e4ac812cae 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/function.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/function.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use noirc_errors::Span; +use noirc_errors::{Location, Span}; use crate::{ ast::{FunctionReturnType, Ident, Param, Visibility}, @@ -65,7 +65,9 @@ impl NoirFunction { pub fn return_type(&self) -> UnresolvedType { match &self.def.return_type { - FunctionReturnType::Default(span) => UnresolvedTypeData::Unit.with_span(*span), + FunctionReturnType::Default(location) => { + UnresolvedTypeData::Unit.with_location(*location) + } FunctionReturnType::Ty(ty) => ty.clone(), } } @@ -96,8 +98,11 @@ impl NoirFunction { pub fn number_of_statements(&self) -> usize { self.def.body.statements.len() } + pub fn location(&self) -> Location { + self.def.location + } pub fn span(&self) -> Span { - self.def.span + self.location().span } pub fn foreign(&self) -> Option<&FunctionDefinition> { 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 33c73eb2482e..8e74ce8877e9 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs @@ -14,6 +14,7 @@ mod traits; mod type_alias; mod visitor; +use noirc_errors::Location; pub use visitor::AttributeTarget; pub use visitor::Visitor; @@ -34,10 +35,10 @@ pub use traits::*; pub use type_alias::*; use crate::{ + BinaryTypeOperator, node_interner::{InternedUnresolvedTypeData, QuotedTypeId}, parser::{ParserError, ParserErrorReason}, token::IntType, - BinaryTypeOperator, }; use acvm::acir::AcirField; use iter_extended::vecmap; @@ -50,6 +51,7 @@ pub enum IntegerBitSize { Sixteen, ThirtyTwo, SixtyFour, + HundredTwentyEight, } impl IntegerBitSize { @@ -60,6 +62,7 @@ impl IntegerBitSize { IntegerBitSize::Sixteen => 16, IntegerBitSize::ThirtyTwo => 32, IntegerBitSize::SixtyFour => 64, + IntegerBitSize::HundredTwentyEight => 128, } } } @@ -79,6 +82,7 @@ impl From for u32 { Sixteen => 16, ThirtyTwo => 32, SixtyFour => 64, + HundredTwentyEight => 128, } } } @@ -96,6 +100,7 @@ impl TryFrom for IntegerBitSize { 16 => Ok(Sixteen), 32 => Ok(ThirtyTwo), 64 => Ok(SixtyFour), + 128 => Ok(HundredTwentyEight), _ => Err(InvalidIntegerBitSizeError(value)), } } @@ -165,7 +170,7 @@ pub enum UnresolvedTypeData { #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub struct UnresolvedType { pub typ: UnresolvedTypeData, - pub span: Span, + pub location: Location, } /// An argument to a generic type or trait. @@ -231,12 +236,12 @@ impl From> for GenericTypeArgs { #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum UnresolvedTypeExpression { Variable(Path), - Constant(FieldElement, Span), + Constant(FieldElement, Location), BinaryOperation( Box, BinaryTypeOperator, Box, - Span, + Location, ), AsTraitPath(Box), } @@ -352,7 +357,7 @@ impl UnresolvedType { } pub fn from_path(mut path: Path) -> Self { - let span = path.span; + let location = path.location; let last_segment = path.segments.last_mut().unwrap(); let generics = last_segment.generics.take(); let generic_type_args = if let Some(generics) = generics { @@ -365,7 +370,7 @@ impl UnresolvedType { GenericTypeArgs::default() }; let typ = UnresolvedTypeData::Named(path, generic_type_args, true); - UnresolvedType { typ, span } + UnresolvedType { typ, location } } pub(crate) fn contains_unspecified(&self) -> bool { @@ -380,7 +385,11 @@ impl UnresolvedTypeData { use {IntType::*, UnresolvedTypeData::Integer}; match token { Signed(num_bits) => { - Ok(Integer(Signedness::Signed, IntegerBitSize::try_from(num_bits)?)) + if num_bits == 128 { + Err(InvalidIntegerBitSizeError(128)) + } else { + Ok(Integer(Signedness::Signed, IntegerBitSize::try_from(num_bits)?)) + } } Unsigned(num_bits) => { Ok(Integer(Signedness::Unsigned, IntegerBitSize::try_from(num_bits)?)) @@ -388,8 +397,12 @@ impl UnresolvedTypeData { } } - pub fn with_span(&self, span: Span) -> UnresolvedType { - UnresolvedType { typ: self.clone(), span } + pub fn with_location(&self, location: Location) -> UnresolvedType { + UnresolvedType { typ: self.clone(), location } + } + + pub fn with_dummy_location(&self) -> UnresolvedType { + self.with_location(Location::dummy()) } fn contains_unspecified(&self) -> bool { @@ -455,37 +468,43 @@ impl UnresolvedTypeExpression { #[allow(clippy::result_large_err)] pub(crate) fn from_expr( expr: Expression, - span: Span, + location: Location, ) -> Result { Self::from_expr_helper(expr).map_err(|err_expr| { - ParserError::with_reason(ParserErrorReason::InvalidTypeExpression(err_expr), span) + ParserError::with_reason(ParserErrorReason::InvalidTypeExpression(err_expr), location) }) } - pub fn span(&self) -> Span { + pub fn location(&self) -> Location { match self { - UnresolvedTypeExpression::Variable(path) => path.span(), - UnresolvedTypeExpression::Constant(_, span) => *span, - UnresolvedTypeExpression::BinaryOperation(_, _, _, span) => *span, + UnresolvedTypeExpression::Variable(path) => path.location, + UnresolvedTypeExpression::Constant(_, location) => *location, + UnresolvedTypeExpression::BinaryOperation(_, _, _, location) => *location, UnresolvedTypeExpression::AsTraitPath(path) => { - path.trait_path.span.merge(path.impl_item.span()) + path.trait_path.location.merge(path.impl_item.location()) } } } + pub fn span(&self) -> Span { + self.location().span + } + fn from_expr_helper(expr: Expression) -> Result { match expr.kind { - ExpressionKind::Literal(Literal::Integer(int, _)) => match int.try_to_u32() { - Some(int) => Ok(UnresolvedTypeExpression::Constant(int.into(), expr.span)), + ExpressionKind::Literal(Literal::Integer(int)) => match int.try_to_unsigned::() { + Some(int) => Ok(UnresolvedTypeExpression::Constant(int.into(), expr.location)), None => Err(expr), }, ExpressionKind::Variable(path) => Ok(UnresolvedTypeExpression::Variable(path)), ExpressionKind::Prefix(prefix) if prefix.operator == UnaryOp::Minus => { - let lhs = - Box::new(UnresolvedTypeExpression::Constant(FieldElement::zero(), expr.span)); + let lhs = Box::new(UnresolvedTypeExpression::Constant( + FieldElement::zero(), + expr.location, + )); let rhs = Box::new(UnresolvedTypeExpression::from_expr_helper(prefix.rhs)?); let op = BinaryTypeOperator::Subtraction; - Ok(UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, expr.span)) + Ok(UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, expr.location)) } ExpressionKind::Infix(infix) if Self::operator_allowed(infix.operator.contents) => { let lhs = Box::new(UnresolvedTypeExpression::from_expr_helper(infix.lhs)?); @@ -511,7 +530,7 @@ impl UnresolvedTypeExpression { unreachable!("impossible via `operator_allowed` check") } }; - Ok(UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, expr.span)) + Ok(UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, expr.location)) } ExpressionKind::AsTraitPath(path) => { Ok(UnresolvedTypeExpression::AsTraitPath(Box::new(path))) 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 372f20f87800..78b29b8c1fdd 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs @@ -1,9 +1,9 @@ use std::fmt::Display; -use acvm::acir::AcirField; use acvm::FieldElement; +use acvm::acir::AcirField; use iter_extended::vecmap; -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Located, Location, Span}; use super::{ BinaryOpKind, BlockExpression, ConstructorExpression, Expression, ExpressionKind, @@ -11,14 +11,13 @@ use super::{ MethodCallExpression, UnresolvedType, }; use crate::ast::UnresolvedTypeData; -use crate::elaborator::types::SELF_TYPE_NAME; use crate::elaborator::Turbofish; -use crate::lexer::token::SpannedToken; +use crate::elaborator::types::SELF_TYPE_NAME; use crate::node_interner::{ InternedExpressionKind, InternedPattern, InternedStatementKind, NodeInterner, }; use crate::parser::{ParserError, ParserErrorReason}; -use crate::token::{SecondaryAttribute, Token}; +use crate::token::{LocatedToken, SecondaryAttribute, Token}; /// This is used when an identifier fails to parse in the parser. /// Instead of failing the parse, we can often recover using this @@ -33,7 +32,7 @@ pub const WILDCARD_TYPE: &str = "_"; #[derive(Debug, PartialEq, Eq, Clone)] pub struct Statement { pub kind: StatementKind, - pub span: Span, + pub location: Location, } /// Ast node for statements in noir. Statements are always within a block { } @@ -45,7 +44,7 @@ pub enum StatementKind { Expression(Expression), Assign(AssignStatement), For(ForLoopStatement), - Loop(Expression, Span /* loop keyword span */), + Loop(Expression, Location /* loop keyword location */), While(WhileStatement), Break, Continue, @@ -66,19 +65,19 @@ impl Statement { pub fn add_semicolon( mut self, semi: Option, - span: Span, + location: Location, last_statement_in_block: bool, emit_error: &mut dyn FnMut(ParserError), ) -> Self { - self.kind = self.kind.add_semicolon(semi, span, last_statement_in_block, emit_error); + self.kind = self.kind.add_semicolon(semi, location, last_statement_in_block, emit_error); self } - /// Returns the innermost span that gives this statement its type. - pub fn type_span(&self) -> Span { + /// Returns the innermost location that gives this statement its type. + pub fn type_location(&self) -> Location { match &self.kind { - StatementKind::Expression(expression) => expression.type_span(), - StatementKind::Comptime(statement) => statement.type_span(), + StatementKind::Expression(expression) => expression.type_location(), + StatementKind::Comptime(statement) => statement.type_location(), StatementKind::Let(..) | StatementKind::Assign(..) | StatementKind::For(..) @@ -88,7 +87,7 @@ impl Statement { | StatementKind::Continue | StatementKind::Semi(..) | StatementKind::Interned(..) - | StatementKind::Error => self.span, + | StatementKind::Error => self.location, } } } @@ -97,12 +96,12 @@ impl StatementKind { pub fn add_semicolon( self, semi: Option, - span: Span, + location: Location, last_statement_in_block: bool, emit_error: &mut dyn FnMut(ParserError), ) -> Self { let missing_semicolon = - ParserError::with_reason(ParserErrorReason::MissingSeparatingSemi, span); + ParserError::with_reason(ParserErrorReason::MissingSeparatingSemi, location); match self { StatementKind::Let(_) @@ -119,7 +118,7 @@ impl StatementKind { } StatementKind::Comptime(mut statement) => { *statement = - statement.add_semicolon(semi, span, last_statement_in_block, emit_error); + statement.add_semicolon(semi, location, last_statement_in_block, emit_error); StatementKind::Comptime(statement) } // A semicolon on a for loop, loop or while is optional and does nothing @@ -174,7 +173,7 @@ impl StatementKind { } #[derive(Eq, Debug, Clone, Default)] -pub struct Ident(pub Spanned); +pub struct Ident(pub Located); impl Ident { pub fn is_self_type_name(&self) -> bool { @@ -218,15 +217,15 @@ impl Display for Ident { } } -impl From> for Ident { - fn from(a: Spanned) -> Ident { +impl From> for Ident { + fn from(a: Located) -> Ident { Ident(a) } } impl From for Ident { fn from(a: String) -> Ident { - Spanned::from_position(Default::default(), Default::default(), a).into() + Located::from(Location::dummy(), a).into() } } impl From<&str> for Ident { @@ -235,37 +234,32 @@ impl From<&str> for Ident { } } -impl From for Ident { - fn from(st: SpannedToken) -> Ident { - let spanned_str = Spanned::from(st.to_span(), st.token().to_string()); - Ident(spanned_str) +impl From for Ident { + fn from(lt: LocatedToken) -> Ident { + let located_str = Located::from(lt.location(), lt.token().to_string()); + Ident(located_str) } } impl From for Expression { fn from(i: Ident) -> Expression { - Expression { - span: i.0.span(), - kind: ExpressionKind::Variable(Path { - span: i.span(), - segments: vec![PathSegment::from(i)], - kind: PathKind::Plain, - }), - } + let location = i.location(); + let kind = ExpressionKind::Variable(Path::plain(vec![PathSegment::from(i)], location)); + Expression { location, kind } } } impl Ident { - pub fn span(&self) -> Span { - self.0.span() + pub fn location(&self) -> Location { + self.0.location() } - pub fn from_token(token: Token, span: Span) -> Ident { - Ident::from(SpannedToken::new(token, span)) + pub fn span(&self) -> Span { + self.0.span() } - pub fn new(text: String, span: Span) -> Ident { - Ident(Spanned::from(span, text)) + pub fn new(text: String, location: Location) -> Ident { + Ident(Located::from(location, text)) } } @@ -302,7 +296,7 @@ pub enum PathKind { pub struct UseTree { pub prefix: Path, pub kind: UseTreeKind, - pub span: Span, + pub location: Location, } impl Display for UseTree { @@ -361,6 +355,12 @@ impl UseTree { } } +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct UnsafeExpression { + pub block: BlockExpression, + pub unsafe_keyword_location: Location, +} + /// A special kind of path in the form `::ident`. /// Note that this path must consist of exactly two segments. /// @@ -390,10 +390,16 @@ pub struct TypePath { pub struct Path { pub segments: Vec, pub kind: PathKind, - pub span: Span, + pub location: Location, + // The location of `kind` (this is the same as `location` for plain kinds) + pub kind_location: Location, } impl Path { + pub fn plain(segments: Vec, location: Location) -> Self { + Self { segments, location, kind: PathKind::Plain, kind_location: location } + } + pub fn pop(&mut self) -> PathSegment { self.segments.pop().unwrap() } @@ -404,17 +410,18 @@ impl Path { } /// Construct a PathKind::Plain from this single - pub fn from_single(name: String, span: Span) -> Path { - let segment = Ident::from(Spanned::from(span, name)); + pub fn from_single(name: String, location: Location) -> Path { + let segment = Ident::from(Located::from(location, name)); Path::from_ident(segment) } pub fn from_ident(name: Ident) -> Path { - Path { span: name.span(), segments: vec![PathSegment::from(name)], kind: PathKind::Plain } + let location = name.location(); + Path::plain(vec![PathSegment::from(name)], location) } pub fn span(&self) -> Span { - self.span + self.location.span } pub fn last_segment(&self) -> PathSegment { @@ -487,7 +494,7 @@ impl Path { pub struct PathSegment { pub ident: Ident, pub generics: Option>, - pub span: Span, + pub location: Location, } impl PathSegment { @@ -498,20 +505,29 @@ impl PathSegment { /// /// 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()) + if self.ident.location().file == self.location.file { + Span::from(self.ident.span().end()..self.location.span.end()) + } else { + self.location.span + } + } + + pub fn turbofish_location(&self) -> Location { + Location::new(self.turbofish_span(), self.location.file) } pub fn turbofish(&self) -> Option { - self.generics - .as_ref() - .map(|generics| Turbofish { span: self.turbofish_span(), generics: generics.clone() }) + self.generics.as_ref().map(|generics| Turbofish { + location: self.turbofish_location(), + generics: generics.clone(), + }) } } impl From for PathSegment { fn from(ident: Ident) -> PathSegment { - let span = ident.span(); - PathSegment { ident, generics: None, span } + let location = ident.location(); + PathSegment { ident, generics: None, location } } } @@ -550,35 +566,39 @@ pub struct AssignStatement { #[derive(Debug, PartialEq, Eq, Clone)] pub enum LValue { Ident(Ident), - MemberAccess { object: Box, field_name: Ident, span: Span }, - Index { array: Box, index: Expression, span: Span }, - Dereference(Box, Span), - Interned(InternedExpressionKind, Span), + MemberAccess { object: Box, field_name: Ident, location: Location }, + Index { array: Box, index: Expression, location: Location }, + Dereference(Box, Location), + Interned(InternedExpressionKind, Location), } #[derive(Debug, PartialEq, Eq, Clone)] pub enum Pattern { Identifier(Ident), - Mutable(Box, Span, /*is_synthesized*/ bool), - Tuple(Vec, Span), - Struct(Path, Vec<(Ident, Pattern)>, Span), - Interned(InternedPattern, Span), + Mutable(Box, Location, /*is_synthesized*/ bool), + Tuple(Vec, Location), + Struct(Path, Vec<(Ident, Pattern)>, Location), + Interned(InternedPattern, Location), } impl Pattern { pub fn is_synthesized(&self) -> bool { matches!(self, Pattern::Mutable(_, _, true)) } - - pub fn span(&self) -> Span { + pub fn location(&self) -> Location { match self { - Pattern::Identifier(ident) => ident.span(), - Pattern::Mutable(_, span, _) - | Pattern::Tuple(_, span) - | Pattern::Struct(_, _, span) - | Pattern::Interned(_, span) => *span, + Pattern::Identifier(ident) => ident.location(), + Pattern::Mutable(_, location, _) + | Pattern::Tuple(_, location) + | Pattern::Struct(_, _, location) + | Pattern::Interned(_, location) => *location, } } + + pub fn span(&self) -> Span { + self.location().span + } + pub fn name_ident(&self) -> &Ident { match self { Pattern::Identifier(name_ident) => name_ident, @@ -591,17 +611,17 @@ impl Pattern { match self { Pattern::Identifier(ident) => Some(Expression { kind: ExpressionKind::Variable(Path::from_ident(ident.clone())), - span: ident.span(), + location: ident.location(), }), Pattern::Mutable(_, _, _) => None, - Pattern::Tuple(patterns, span) => { + Pattern::Tuple(patterns, location) => { let mut expressions = Vec::new(); for pattern in patterns { expressions.push(pattern.try_as_expression(interner)?); } - Some(Expression { kind: ExpressionKind::Tuple(expressions), span: *span }) + Some(Expression { kind: ExpressionKind::Tuple(expressions), location: *location }) } - Pattern::Struct(path, patterns, span) => { + Pattern::Struct(path, patterns, location) => { let mut fields = Vec::new(); for (field, pattern) in patterns { let expression = pattern.try_as_expression(interner)?; @@ -611,9 +631,8 @@ impl Pattern { kind: ExpressionKind::Constructor(Box::new(ConstructorExpression { typ: UnresolvedType::from_path(path.clone()), fields, - struct_type: None, })), - span: *span, + location: *location, }) } Pattern::Interned(id, _) => interner.get_pattern(*id).try_as_expression(interner), @@ -625,13 +644,13 @@ impl LValue { pub fn as_expression(&self) -> Expression { let kind = match self { LValue::Ident(ident) => ExpressionKind::Variable(Path::from_ident(ident.clone())), - LValue::MemberAccess { object, field_name, span: _ } => { + LValue::MemberAccess { object, field_name, location: _ } => { ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { lhs: object.as_expression(), rhs: field_name.clone(), })) } - LValue::Index { array, index, span: _ } => { + LValue::Index { array, index, location: _ } => { ExpressionKind::Index(Box::new(IndexExpression { collection: array.as_expression(), index: index.clone(), @@ -645,52 +664,58 @@ impl LValue { } LValue::Interned(id, _) => ExpressionKind::Interned(*id), }; - let span = self.span(); - Expression::new(kind, span) + Expression::new(kind, self.location()) } pub fn from_expression(expr: Expression) -> Option { - LValue::from_expression_kind(expr.kind, expr.span) + LValue::from_expression_kind(expr.kind, expr.location) } - pub fn from_expression_kind(expr: ExpressionKind, span: Span) -> Option { + pub fn from_expression_kind(expr: ExpressionKind, location: Location) -> Option { match expr { ExpressionKind::Variable(path) => Some(LValue::Ident(path.as_ident().unwrap().clone())), ExpressionKind::MemberAccess(member_access) => Some(LValue::MemberAccess { object: Box::new(LValue::from_expression(member_access.lhs)?), field_name: member_access.rhs, - span, + location, }), ExpressionKind::Index(index) => Some(LValue::Index { array: Box::new(LValue::from_expression(index.collection)?), index: index.index, - span, + location, }), ExpressionKind::Prefix(prefix) => { if matches!( prefix.operator, crate::ast::UnaryOp::Dereference { implicitly_added: false } ) { - Some(LValue::Dereference(Box::new(LValue::from_expression(prefix.rhs)?), span)) + Some(LValue::Dereference( + Box::new(LValue::from_expression(prefix.rhs)?), + location, + )) } else { None } } ExpressionKind::Parenthesized(expr) => LValue::from_expression(*expr), - ExpressionKind::Interned(id) => Some(LValue::Interned(id, span)), + ExpressionKind::Interned(id) => Some(LValue::Interned(id, location)), _ => None, } } - pub fn span(&self) -> Span { + pub fn location(&self) -> Location { match self { - LValue::Ident(ident) => ident.span(), - LValue::MemberAccess { span, .. } - | LValue::Index { span, .. } - | LValue::Dereference(_, span) => *span, - LValue::Interned(_, span) => *span, + LValue::Ident(ident) => ident.location(), + LValue::MemberAccess { location, .. } + | LValue::Index { location, .. } + | LValue::Dereference(_, location) + | LValue::Interned(_, location) => *location, } } + + pub fn span(&self) -> Span { + self.location().span + } } #[derive(Debug, PartialEq, Eq, Clone)] @@ -707,13 +732,16 @@ impl ForBounds { /// Returns the `start` and `end` expressions. pub(crate) fn into_half_open(self) -> (Expression, Expression) { let end = if self.inclusive { - let end_span = self.end.span; + let end_location = self.end.location; let end = ExpressionKind::Infix(Box::new(InfixExpression { lhs: self.end, - operator: Spanned::from(end_span, BinaryOpKind::Add), - rhs: Expression::new(ExpressionKind::integer(FieldElement::from(1u32)), end_span), + operator: Located::from(end_location, BinaryOpKind::Add), + rhs: Expression::new( + ExpressionKind::integer(FieldElement::from(1u32)), + end_location, + ), })); - Expression::new(end, end_span) + Expression::new(end, end_location) } else { self.end }; @@ -758,7 +786,7 @@ impl ForRange { self, identifier: Ident, block: Expression, - for_loop_span: Span, + for_loop_location: Location, ) -> Statement { // Counter used to generate unique names when desugaring // code in the parser requires the creation of fresh variables. @@ -769,96 +797,91 @@ impl ForRange { unreachable!() } ForRange::Array(array) => { - let array_span = array.span; + let array_location = array.location; let start_range = ExpressionKind::integer(FieldElement::zero()); - let start_range = Expression::new(start_range, array_span); + let start_range = Expression::new(start_range, array_location); let next_unique_id = unique_name_counter; unique_name_counter += 1; let array_name = format!("$i{next_unique_id}"); - let array_span = array.span; - let array_ident = Ident::new(array_name, array_span); + let array_location = array.location; + let array_ident = Ident::new(array_name, array_location); // let fresh1 = array; let let_array = Statement { kind: StatementKind::new_let( Pattern::Identifier(array_ident.clone()), - UnresolvedTypeData::Unspecified.with_span(Default::default()), + UnresolvedTypeData::Unspecified.with_dummy_location(), array, vec![], ), - span: array_span, + location: array_location, }; // array.len() let segments = vec![PathSegment::from(array_ident)]; - let array_ident = ExpressionKind::Variable(Path { - segments, - kind: PathKind::Plain, - span: array_span, - }); + let array_ident = ExpressionKind::Variable(Path::plain(segments, array_location)); let end_range = ExpressionKind::MethodCall(Box::new(MethodCallExpression { - object: Expression::new(array_ident.clone(), array_span), - method_name: Ident::new("len".to_string(), array_span), + object: Expression::new(array_ident.clone(), array_location), + method_name: Ident::new("len".to_string(), array_location), generics: None, is_macro_call: false, arguments: vec![], })); - let end_range = Expression::new(end_range, array_span); + let end_range = Expression::new(end_range, array_location); let next_unique_id = unique_name_counter; let index_name = format!("$i{next_unique_id}"); - let fresh_identifier = Ident::new(index_name.clone(), array_span); + let fresh_identifier = Ident::new(index_name.clone(), array_location); // array[i] - 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 segments = vec![PathSegment::from(Ident::new(index_name, array_location))]; + let index_ident = ExpressionKind::Variable(Path::plain(segments, array_location)); let loop_element = ExpressionKind::Index(Box::new(IndexExpression { - collection: Expression::new(array_ident, array_span), - index: Expression::new(index_ident, array_span), + collection: Expression::new(array_ident, array_location), + index: Expression::new(index_ident, array_location), })); // let elem = array[i]; let let_elem = Statement { kind: StatementKind::new_let( Pattern::Identifier(identifier), - UnresolvedTypeData::Unspecified.with_span(Default::default()), - Expression::new(loop_element, array_span), + UnresolvedTypeData::Unspecified.with_dummy_location(), + Expression::new(loop_element, array_location), vec![], ), - span: array_span, + location: array_location, }; - let block_span = block.span; + let block_location = block.location; let new_block = BlockExpression { statements: vec![ let_elem, - Statement { kind: StatementKind::Expression(block), span: block_span }, + Statement { + kind: StatementKind::Expression(block), + location: block_location, + }, ], }; - let new_block = Expression::new(ExpressionKind::Block(new_block), block_span); + let new_block = Expression::new(ExpressionKind::Block(new_block), block_location); let for_loop = Statement { kind: StatementKind::For(ForLoopStatement { identifier: fresh_identifier, range: ForRange::range(start_range, end_range), block: new_block, - span: for_loop_span, + location: for_loop_location, }), - span: for_loop_span, + location: for_loop_location, }; let block = ExpressionKind::Block(BlockExpression { statements: vec![let_array, for_loop], }); Statement { - kind: StatementKind::Expression(Expression::new(block, for_loop_span)), - span: for_loop_span, + kind: StatementKind::Expression(Expression::new(block, for_loop_location)), + location: for_loop_location, } } } @@ -870,14 +893,14 @@ pub struct ForLoopStatement { pub identifier: Ident, pub range: ForRange, pub block: Expression, - pub span: Span, + pub location: Location, } #[derive(Debug, PartialEq, Eq, Clone)] pub struct WhileStatement { pub condition: Expression, pub body: Expression, - pub while_keyword_span: Span, + pub while_keyword_location: Location, } impl Display for StatementKind { @@ -921,10 +944,10 @@ impl Display for LValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { LValue::Ident(ident) => ident.fmt(f), - LValue::MemberAccess { object, field_name, span: _ } => { + LValue::MemberAccess { object, field_name, location: _ } => { write!(f, "{object}.{field_name}") } - LValue::Index { array, index, span: _ } => write!(f, "{array}[{index}]"), + LValue::Index { array, index, location: _ } => write!(f, "{array}[{index}]"), LValue::Dereference(lvalue, _span) => write!(f, "*{lvalue}"), LValue::Interned(_, _) => write!(f, "?Interned"), } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs index 8c8fe6e63872..7162a2d3ba68 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs @@ -4,7 +4,7 @@ use crate::ast::{Ident, UnresolvedGenerics, UnresolvedType}; use crate::token::SecondaryAttribute; use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::Location; use super::{Documented, ItemVisibility}; @@ -16,7 +16,7 @@ pub struct NoirStruct { pub visibility: ItemVisibility, pub generics: UnresolvedGenerics, pub fields: Vec>, - pub span: Span, + pub location: Location, } impl 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 e5040463d174..d7cc48ec7421 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/traits.rs @@ -1,7 +1,7 @@ use std::fmt::Display; use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::Location; use crate::ast::{ BlockExpression, Expression, FunctionReturnType, Ident, NoirFunction, Path, UnresolvedGenerics, @@ -20,7 +20,7 @@ pub struct NoirTrait { pub generics: UnresolvedGenerics, pub bounds: Vec, pub where_clause: Vec, - pub span: Span, + pub location: Location, pub items: Vec>, pub attributes: Vec, pub visibility: ItemVisibility, @@ -57,10 +57,10 @@ pub enum TraitItem { #[derive(Clone, Debug)] pub struct TypeImpl { pub object_type: UnresolvedType, - pub type_span: Span, + pub type_location: Location, pub generics: UnresolvedGenerics, pub where_clause: Vec, - pub methods: Vec<(Documented, Span)>, + pub methods: Vec<(Documented, Location)>, } /// Ast node for an implementation of a trait for a particular type @@ -104,7 +104,7 @@ pub struct TraitBound { #[derive(Clone, Debug)] pub struct TraitImplItem { pub kind: TraitImplItemKind, - pub span: Span, + pub location: Location, } #[derive(Clone, Debug)] @@ -197,11 +197,7 @@ impl Display for TraitItem { "{unconstrained}{visibility}{is_comptime}fn {name}<{generics}>({parameters}) -> {return_type} where {where_clause}" )?; - if let Some(body) = body { - write!(f, "{body}") - } else { - write!(f, ";") - } + if let Some(body) = body { write!(f, "{body}") } else { write!(f, ";") } } TraitItem::Constant { name, typ, default_value } => { write!(f, "let {name}: {typ}")?; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/type_alias.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/type_alias.rs index 5b26044c3289..532593fad0b0 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/type_alias.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/type_alias.rs @@ -1,6 +1,6 @@ use super::{Ident, ItemVisibility, UnresolvedGenerics, UnresolvedType}; use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::Location; use std::fmt::Display; /// Ast node for type aliases @@ -10,7 +10,7 @@ pub struct NoirTypeAlias { pub generics: UnresolvedGenerics, pub typ: UnresolvedType, pub visibility: ItemVisibility, - pub span: Span, + pub location: Location, } impl Display for NoirTypeAlias { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs index 7fe89489c3cf..b2142f266551 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs @@ -1,7 +1,7 @@ -use acvm::FieldElement; use noirc_errors::Span; use crate::{ + ParsedModule, QuotedType, ast::{ ArrayLiteral, AsTraitPath, AssignStatement, BlockExpression, CallExpression, CastExpression, ConstrainExpression, ConstructorExpression, Expression, ExpressionKind, @@ -16,15 +16,15 @@ use crate::{ InternedUnresolvedTypeData, QuotedTypeId, }, parser::{Item, ItemKind, ParsedSubModule}, + signed_field::SignedField, token::{FmtStrFragment, MetaAttribute, SecondaryAttribute, Tokens}, - ParsedModule, QuotedType, }; use super::{ ForBounds, FunctionReturnType, GenericTypeArgs, IntegerBitSize, ItemVisibility, MatchExpression, NoirEnumeration, Pattern, Signedness, TraitBound, TraitImplItemKind, TypePath, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, - UnresolvedTypeExpression, + UnresolvedTypeExpression, UnsafeExpression, }; #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -172,7 +172,7 @@ pub trait Visitor { fn visit_literal_bool(&mut self, _: bool, _: Span) {} - fn visit_literal_integer(&mut self, _value: FieldElement, _negative: bool, _: Span) {} + fn visit_literal_integer(&mut self, _value: SignedField, _: Span) {} fn visit_literal_str(&mut self, _: &str, _: Span) {} @@ -242,7 +242,7 @@ pub trait Visitor { true } - fn visit_unsafe(&mut self, _: &BlockExpression, _: Span) -> bool { + fn visit_unsafe_expression(&mut self, _: &UnsafeExpression, _: Span) -> bool { true } @@ -520,31 +520,33 @@ impl Item { } pub fn accept_children(&self, visitor: &mut impl Visitor) { + let span = self.location.span; + match &self.kind { ItemKind::Submodules(parsed_sub_module) => { - parsed_sub_module.accept(self.span, visitor); + parsed_sub_module.accept(span, visitor); } - ItemKind::Function(noir_function) => noir_function.accept(self.span, visitor), + ItemKind::Function(noir_function) => noir_function.accept(span, visitor), ItemKind::TraitImpl(noir_trait_impl) => { - noir_trait_impl.accept(self.span, visitor); + noir_trait_impl.accept(span, visitor); } - ItemKind::Impl(type_impl) => type_impl.accept(self.span, visitor), + ItemKind::Impl(type_impl) => type_impl.accept(span, visitor), ItemKind::Global(let_statement, _visibility) => { - if visitor.visit_global(let_statement, self.span) { + if visitor.visit_global(let_statement, span) { let_statement.accept(visitor); } } - ItemKind::Trait(noir_trait) => noir_trait.accept(self.span, visitor), + ItemKind::Trait(noir_trait) => noir_trait.accept(span, visitor), ItemKind::Import(use_tree, visibility) => { - if visitor.visit_import(use_tree, self.span, *visibility) { + if visitor.visit_import(use_tree, span, *visibility) { use_tree.accept(visitor); } } - ItemKind::TypeAlias(noir_type_alias) => noir_type_alias.accept(self.span, visitor), - ItemKind::Struct(noir_struct) => noir_struct.accept(self.span, visitor), - ItemKind::Enum(noir_enum) => noir_enum.accept(self.span, visitor), + ItemKind::TypeAlias(noir_type_alias) => noir_type_alias.accept(span, visitor), + ItemKind::Struct(noir_struct) => noir_struct.accept(span, visitor), + ItemKind::Enum(noir_enum) => noir_enum.accept(span, visitor), ItemKind::ModuleDecl(module_declaration) => { - module_declaration.accept(self.span, visitor); + module_declaration.accept(span, visitor); } ItemKind::InnerAttribute(attribute) => { attribute.accept(AttributeTarget::Module, visitor); @@ -620,7 +622,7 @@ impl TraitImplItem { } pub fn accept_children(&self, visitor: &mut impl Visitor) { - self.kind.accept(self.span, visitor); + self.kind.accept(self.location.span, visitor); } } @@ -663,8 +665,8 @@ impl TypeImpl { pub fn accept_children(&self, visitor: &mut impl Visitor) { self.object_type.accept(visitor); - for (method, span) in &self.methods { - method.item.accept(*span, visitor); + for (method, location) in &self.methods { + method.item.accept(location.span, visitor); } } } @@ -843,79 +845,78 @@ impl Expression { } pub fn accept_children(&self, visitor: &mut impl Visitor) { + let span = self.location.span; match &self.kind { - ExpressionKind::Literal(literal) => literal.accept(self.span, visitor), + ExpressionKind::Literal(literal) => literal.accept(span, visitor), ExpressionKind::Block(block_expression) => { - block_expression.accept(Some(self.span), visitor); + block_expression.accept(Some(span), visitor); } ExpressionKind::Prefix(prefix_expression) => { - prefix_expression.accept(self.span, visitor); + prefix_expression.accept(span, visitor); } ExpressionKind::Index(index_expression) => { - index_expression.accept(self.span, visitor); + index_expression.accept(span, visitor); } ExpressionKind::Call(call_expression) => { - call_expression.accept(self.span, visitor); + call_expression.accept(span, visitor); } ExpressionKind::MethodCall(method_call_expression) => { - method_call_expression.accept(self.span, visitor); + method_call_expression.accept(span, visitor); } ExpressionKind::Constrain(constrain) => { constrain.accept(visitor); } ExpressionKind::Constructor(constructor_expression) => { - constructor_expression.accept(self.span, visitor); + constructor_expression.accept(span, visitor); } ExpressionKind::MemberAccess(member_access_expression) => { - member_access_expression.accept(self.span, visitor); + member_access_expression.accept(span, visitor); } ExpressionKind::Cast(cast_expression) => { - cast_expression.accept(self.span, visitor); + cast_expression.accept(span, visitor); } ExpressionKind::Infix(infix_expression) => { - infix_expression.accept(self.span, visitor); + infix_expression.accept(span, visitor); } ExpressionKind::If(if_expression) => { - if_expression.accept(self.span, visitor); + if_expression.accept(span, visitor); } ExpressionKind::Match(match_expression) => { - match_expression.accept(self.span, visitor); + match_expression.accept(span, visitor); } ExpressionKind::Tuple(expressions) => { - if visitor.visit_tuple(expressions, self.span) { + if visitor.visit_tuple(expressions, span) { visit_expressions(expressions, visitor); } } - ExpressionKind::Lambda(lambda) => lambda.accept(self.span, visitor), + ExpressionKind::Lambda(lambda) => lambda.accept(span, visitor), ExpressionKind::Parenthesized(expression) => { - if visitor.visit_parenthesized(expression, self.span) { + if visitor.visit_parenthesized(expression, span) { expression.accept(visitor); } } ExpressionKind::Unquote(expression) => { - if visitor.visit_unquote(expression, self.span) { + if visitor.visit_unquote(expression, span) { expression.accept(visitor); } } ExpressionKind::Comptime(block_expression, _) => { - if visitor.visit_comptime_expression(block_expression, self.span) { + if visitor.visit_comptime_expression(block_expression, span) { block_expression.accept(None, visitor); } } - ExpressionKind::Unsafe(block_expression, _) => { - if visitor.visit_unsafe(block_expression, self.span) { - block_expression.accept(None, visitor); - } + ExpressionKind::Unsafe(unsafe_expression) => { + unsafe_expression.accept(span, visitor); } ExpressionKind::Variable(path) => { - if visitor.visit_variable(path, self.span) { + if visitor.visit_variable(path, span) { path.accept(visitor); } } ExpressionKind::AsTraitPath(as_trait_path) => { - as_trait_path.accept(self.span, visitor); + as_trait_path.accept(span, visitor); } - ExpressionKind::TypePath(path) => path.accept(self.span, visitor), + ExpressionKind::TypePath(path) => path.accept(span, visitor), ExpressionKind::Quote(tokens) => visitor.visit_quote(tokens), ExpressionKind::Resolved(expr_id) => visitor.visit_resolved_expression(*expr_id), ExpressionKind::Interned(id) => visitor.visit_interned_expression(*id), @@ -945,8 +946,8 @@ impl Literal { } } Literal::Bool(value) => visitor.visit_literal_bool(*value, span), - Literal::Integer(value, negative) => { - visitor.visit_literal_integer(*value, *negative, span); + Literal::Integer(value) => { + visitor.visit_literal_integer(*value, span); } Literal::Str(str) => visitor.visit_literal_str(str, span), Literal::RawStr(str, length) => visitor.visit_literal_raw_str(str, *length, span), @@ -1262,23 +1263,23 @@ impl LValue { pub fn accept_children(&self, visitor: &mut impl Visitor) { match self { LValue::Ident(ident) => visitor.visit_lvalue_ident(ident), - LValue::MemberAccess { object, field_name, span } => { - if visitor.visit_lvalue_member_access(object, field_name, *span) { + LValue::MemberAccess { object, field_name, location } => { + if visitor.visit_lvalue_member_access(object, field_name, location.span) { object.accept(visitor); } } - LValue::Index { array, index, span } => { - if visitor.visit_lvalue_index(array, index, *span) { + LValue::Index { array, index, location } => { + if visitor.visit_lvalue_index(array, index, location.span) { array.accept(visitor); index.accept(visitor); } } - LValue::Dereference(lvalue, span) => { - if visitor.visit_lvalue_dereference(lvalue, *span) { + LValue::Dereference(lvalue, location) => { + if visitor.visit_lvalue_dereference(lvalue, location.span) { lvalue.accept(visitor); } } - LValue::Interned(id, span) => visitor.visit_lvalue_interned(*id, *span), + LValue::Interned(id, location) => visitor.visit_lvalue_interned(*id, location.span), } } } @@ -1301,6 +1302,18 @@ impl ForRange { } } +impl UnsafeExpression { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_unsafe_expression(self, span) { + self.accept_children(span, visitor); + } + } + + pub fn accept_children(&self, span: Span, visitor: &mut impl Visitor) { + self.block.accept(Some(span), visitor); + } +} + impl AsTraitPath { pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { if visitor.visit_as_trait_path(self, span) { @@ -1339,73 +1352,84 @@ impl UnresolvedType { pub fn accept_children(&self, visitor: &mut impl Visitor) { match &self.typ { UnresolvedTypeData::Array(unresolved_type_expression, unresolved_type) => { - if visitor.visit_array_type(unresolved_type_expression, unresolved_type, self.span) - { + if visitor.visit_array_type( + unresolved_type_expression, + unresolved_type, + self.location.span, + ) { unresolved_type.accept(visitor); } } UnresolvedTypeData::Slice(unresolved_type) => { - if visitor.visit_slice_type(unresolved_type, self.span) { + if visitor.visit_slice_type(unresolved_type, self.location.span) { unresolved_type.accept(visitor); } } UnresolvedTypeData::Parenthesized(unresolved_type) => { - if visitor.visit_parenthesized_type(unresolved_type, self.span) { + if visitor.visit_parenthesized_type(unresolved_type, self.location.span) { unresolved_type.accept(visitor); } } UnresolvedTypeData::Named(path, generic_type_args, _) => { - if visitor.visit_named_type(path, generic_type_args, self.span) { + if visitor.visit_named_type(path, generic_type_args, self.location.span) { path.accept(visitor); generic_type_args.accept(visitor); } } UnresolvedTypeData::TraitAsType(path, generic_type_args) => { - if visitor.visit_trait_as_type(path, generic_type_args, self.span) { + if visitor.visit_trait_as_type(path, generic_type_args, self.location.span) { path.accept(visitor); generic_type_args.accept(visitor); } } UnresolvedTypeData::MutableReference(unresolved_type) => { - if visitor.visit_mutable_reference_type(unresolved_type, self.span) { + if visitor.visit_mutable_reference_type(unresolved_type, self.location.span) { unresolved_type.accept(visitor); } } UnresolvedTypeData::Tuple(unresolved_types) => { - if visitor.visit_tuple_type(unresolved_types, self.span) { + if visitor.visit_tuple_type(unresolved_types, self.location.span) { visit_unresolved_types(unresolved_types, visitor); } } UnresolvedTypeData::Function(args, ret, env, unconstrained) => { - if visitor.visit_function_type(args, ret, env, *unconstrained, self.span) { + if visitor.visit_function_type(args, ret, env, *unconstrained, self.location.span) { visit_unresolved_types(args, visitor); ret.accept(visitor); env.accept(visitor); } } UnresolvedTypeData::AsTraitPath(as_trait_path) => { - if visitor.visit_as_trait_path_type(as_trait_path, self.span) { - as_trait_path.accept(self.span, visitor); + if visitor.visit_as_trait_path_type(as_trait_path, self.location.span) { + as_trait_path.accept(self.location.span, visitor); } } - UnresolvedTypeData::Expression(expr) => visitor.visit_expression_type(expr, self.span), + UnresolvedTypeData::Expression(expr) => { + visitor.visit_expression_type(expr, self.location.span); + } UnresolvedTypeData::FormatString(expr, typ) => { - if visitor.visit_format_string_type(expr, typ, self.span) { + if visitor.visit_format_string_type(expr, typ, self.location.span) { typ.accept(visitor); } } - UnresolvedTypeData::String(expr) => visitor.visit_string_type(expr, self.span), - UnresolvedTypeData::Unspecified => visitor.visit_unspecified_type(self.span), - UnresolvedTypeData::Quoted(typ) => visitor.visit_quoted_type(typ, self.span), - UnresolvedTypeData::FieldElement => visitor.visit_field_element_type(self.span), - UnresolvedTypeData::Integer(signdness, size) => { - visitor.visit_integer_type(*signdness, *size, self.span); + UnresolvedTypeData::String(expr) => visitor.visit_string_type(expr, self.location.span), + UnresolvedTypeData::Unspecified => visitor.visit_unspecified_type(self.location.span), + UnresolvedTypeData::Quoted(typ) => visitor.visit_quoted_type(typ, self.location.span), + UnresolvedTypeData::FieldElement => { + visitor.visit_field_element_type(self.location.span); + } + UnresolvedTypeData::Integer(signedness, size) => { + visitor.visit_integer_type(*signedness, *size, self.location.span); + } + UnresolvedTypeData::Bool => visitor.visit_bool_type(self.location.span), + UnresolvedTypeData::Unit => visitor.visit_unit_type(self.location.span), + UnresolvedTypeData::Resolved(id) => { + visitor.visit_resolved_type(*id, self.location.span); + } + UnresolvedTypeData::Interned(id) => { + visitor.visit_interned_type(*id, self.location.span); } - UnresolvedTypeData::Bool => visitor.visit_bool_type(self.span), - UnresolvedTypeData::Unit => visitor.visit_unit_type(self.span), - UnresolvedTypeData::Resolved(id) => visitor.visit_resolved_type(*id, self.span), - UnresolvedTypeData::Interned(id) => visitor.visit_interned_type(*id, self.span), - UnresolvedTypeData::Error => visitor.visit_error_type(self.span), + UnresolvedTypeData::Error => visitor.visit_error_type(self.location.span), } } } @@ -1484,28 +1508,28 @@ impl Pattern { pub fn accept_children(&self, visitor: &mut impl Visitor) { match self { Pattern::Identifier(ident) => visitor.visit_identifier_pattern(ident), - Pattern::Mutable(pattern, span, is_synthesized) => { - if visitor.visit_mutable_pattern(pattern, *span, *is_synthesized) { + Pattern::Mutable(pattern, location, is_synthesized) => { + if visitor.visit_mutable_pattern(pattern, location.span, *is_synthesized) { pattern.accept(visitor); } } - Pattern::Tuple(patterns, span) => { - if visitor.visit_tuple_pattern(patterns, *span) { + Pattern::Tuple(patterns, location) => { + if visitor.visit_tuple_pattern(patterns, location.span) { for pattern in patterns { pattern.accept(visitor); } } } - Pattern::Struct(path, fields, span) => { - if visitor.visit_struct_pattern(path, fields, *span) { + Pattern::Struct(path, fields, location) => { + if visitor.visit_struct_pattern(path, fields, location.span) { path.accept(visitor); for (_, pattern) in fields { pattern.accept(visitor); } } } - Pattern::Interned(id, span) => { - visitor.visit_interned_pattern(id, *span); + Pattern::Interned(id, location) => { + visitor.visit_interned_pattern(id, location.span); } } } 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 d80302401e74..48bed1c41997 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs @@ -1,13 +1,15 @@ use crate::ast::PathSegment; use crate::parse_program; use crate::parser::ParsedModule; +use crate::signed_field::SignedField; use crate::{ ast, - ast::{Path, PathKind}, + ast::Path, parser::{Item, ItemKind}, }; +use fm::FileId; use noirc_errors::debug_info::{DebugFnId, DebugFunction}; -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Location, Span}; use std::collections::HashMap; use std::collections::VecDeque; use std::mem::take; @@ -56,7 +58,7 @@ impl Default for DebugInstrumenter { } impl DebugInstrumenter { - pub fn instrument_module(&mut self, module: &mut ParsedModule) { + pub fn instrument_module(&mut self, module: &mut ParsedModule, file: FileId) { module.items.iter_mut().for_each(|item| { if let Item { kind: ItemKind::Function(f), .. } = item { self.walk_fn(&mut f.def); @@ -64,7 +66,7 @@ impl DebugInstrumenter { }); // this part absolutely must happen after ast traversal above // so that oracle functions don't get wrapped, resulting in infinite recursion: - self.insert_state_set_oracle(module, 8); + self.insert_state_set_oracle(module, 8, file); } fn insert_var(&mut self, var_name: &str) -> Option { @@ -102,7 +104,7 @@ impl DebugInstrumenter { let func_args = func.parameters.iter().map(|param| pattern_to_string(¶m.pattern)).collect(); let fn_id = self.insert_function(func_name, func_args); - let enter_stmt = build_debug_call_stmt("enter", fn_id, func.span); + let enter_stmt = build_debug_call_stmt("enter", fn_id, func.location); self.scope.push(HashMap::default()); let set_fn_params: Vec<_> = func @@ -122,11 +124,11 @@ impl DebugInstrumenter { let func_body = &mut func.body.statements; let mut statements = take(func_body); - self.walk_scope(&mut statements, func.span); + self.walk_scope(&mut statements, func.location); // walk_scope ensures that the last statement is the return value of the function let last_stmt = statements.pop().expect("at least one statement after walk_scope"); - let exit_stmt = build_debug_call_stmt("exit", fn_id, last_stmt.span); + let exit_stmt = build_debug_call_stmt("exit", fn_id, last_stmt.location); // rebuild function body func_body.push(enter_stmt); @@ -138,7 +140,7 @@ impl DebugInstrumenter { // Modify a vector of statements in-place, adding instrumentation for sets and drops. // This function will consume a scope level. - fn walk_scope(&mut self, statements: &mut Vec, span: Span) { + fn walk_scope(&mut self, statements: &mut Vec, location: Location) { statements.iter_mut().for_each(|stmt| self.walk_statement(stmt)); // extract and save the return value from the scope if there is one @@ -148,12 +150,12 @@ impl DebugInstrumenter { Some(ast::Statement { kind: ast::StatementKind::Expression(ret_expr), .. }) => { let save_ret_expr = ast::Statement { kind: ast::StatementKind::new_let( - ast::Pattern::Identifier(ident("__debug_expr", ret_expr.span)), - ast::UnresolvedTypeData::Unspecified.with_span(Default::default()), + ast::Pattern::Identifier(ident("__debug_expr", ret_expr.location)), + ast::UnresolvedTypeData::Unspecified.with_dummy_location(), ret_expr.clone(), vec![], ), - span: ret_expr.span, + location: ret_expr.location, }; statements.push(save_ret_expr); true @@ -165,39 +167,44 @@ impl DebugInstrumenter { } }; - let span = Span::empty(span.end()); + let span = Span::empty(location.span.end()); + let location = Location::new(span, location.file); // drop scope variables let scope_vars = self.scope.pop().unwrap_or_default(); - let drop_vars_stmts = scope_vars.values().map(|var_id| build_drop_var_stmt(*var_id, span)); + let drop_vars_stmts = + scope_vars.values().map(|var_id| build_drop_var_stmt(*var_id, location)); statements.extend(drop_vars_stmts); // return the saved value in __debug_expr, or unit otherwise let last_stmt = if has_ret_expr { ast::Statement { kind: ast::StatementKind::Expression(ast::Expression { - kind: ast::ExpressionKind::Variable(ast::Path { - segments: vec![PathSegment::from(ident("__debug_expr", span))], - kind: PathKind::Plain, - span, - }), - span, + kind: ast::ExpressionKind::Variable(ast::Path::plain( + vec![PathSegment::from(ident("__debug_expr", location))], + location, + )), + location, }), - span, + location, } } else { ast::Statement { kind: ast::StatementKind::Expression(ast::Expression { kind: ast::ExpressionKind::Literal(ast::Literal::Unit), - span, + location, }), - span, + location, } }; statements.push(last_stmt); } - fn walk_let_statement(&mut self, let_stmt: &ast::LetStatement, span: &Span) -> ast::Statement { + fn walk_let_statement( + &mut self, + let_stmt: &ast::LetStatement, + location: Location, + ) -> ast::Statement { // rewrites let statements written like this: // let (((a,b,c),D { d }),e,f) = x; // @@ -221,7 +228,7 @@ impl DebugInstrumenter { if *is_mut { ast::Pattern::Mutable( Box::new(ast::Pattern::Identifier(id.clone())), - id.span(), + id.location(), true, ) } else { @@ -238,7 +245,7 @@ impl DebugInstrumenter { if id.0.contents == "_" { ast::Expression { kind: ast::ExpressionKind::Literal(ast::Literal::Unit), - span: id.span(), + location: id.location(), } } else { id_expr(id) @@ -247,7 +254,7 @@ impl DebugInstrumenter { .collect(); let mut block_stmts = - vec![ast::Statement { kind: ast::StatementKind::Let(let_stmt.clone()), span: *span }]; + vec![ast::Statement { kind: ast::StatementKind::Let(let_stmt.clone()), location }]; block_stmts.extend(vars.iter().filter_map(|(id, _)| { let var_id = self.insert_var(&id.0.contents)?; Some(build_assign_var_stmt(var_id, id_expr(id))) @@ -255,31 +262,31 @@ impl DebugInstrumenter { block_stmts.push(ast::Statement { kind: ast::StatementKind::Expression(ast::Expression { kind: ast::ExpressionKind::Tuple(vars_exprs), - span: let_stmt.pattern.span(), + location: let_stmt.pattern.location(), }), - span: let_stmt.pattern.span(), + location: let_stmt.pattern.location(), }); ast::Statement { kind: ast::StatementKind::new_let( - ast::Pattern::Tuple(vars_pattern, let_stmt.pattern.span()), - ast::UnresolvedTypeData::Unspecified.with_span(Default::default()), + ast::Pattern::Tuple(vars_pattern, let_stmt.pattern.location()), + ast::UnresolvedTypeData::Unspecified.with_dummy_location(), ast::Expression { kind: ast::ExpressionKind::Block(ast::BlockExpression { statements: block_stmts, }), - span: let_stmt.expression.span, + location: let_stmt.expression.location, }, vec![], ), - span: *span, + location, } } fn walk_assign_statement( &mut self, assign_stmt: &ast::AssignStatement, - span: &Span, + location: Location, ) -> ast::Statement { // X = Y becomes: // X = { @@ -293,26 +300,26 @@ impl DebugInstrumenter { // }; let let_kind = ast::StatementKind::new_let( - ast::Pattern::Identifier(ident("__debug_expr", assign_stmt.expression.span)), - ast::UnresolvedTypeData::Unspecified.with_span(Default::default()), + ast::Pattern::Identifier(ident("__debug_expr", assign_stmt.expression.location)), + ast::UnresolvedTypeData::Unspecified.with_dummy_location(), assign_stmt.expression.clone(), vec![], ); - let expression_span = assign_stmt.expression.span; + let expression_location = assign_stmt.expression.location; let new_assign_stmt = match &assign_stmt.lvalue { ast::LValue::Ident(id) => { let var_id = self .lookup_var(&id.0.contents) .unwrap_or_else(|| panic!("var lookup failed for var_name={}", &id.0.contents)); - build_assign_var_stmt(var_id, id_expr(&ident("__debug_expr", id.span()))) + build_assign_var_stmt(var_id, id_expr(&ident("__debug_expr", id.location()))) } - ast::LValue::Dereference(_lv, span) => { + ast::LValue::Dereference(_lv, location) => { // TODO: this is a dummy statement for now, but we should // somehow track the derefence and update the pointed to // variable ast::Statement { - kind: ast::StatementKind::Expression(uint_expr(0, *span)), - span: *span, + kind: ast::StatementKind::Expression(uint_expr(0, *location)), + location: *location, } } _ => { @@ -327,12 +334,12 @@ impl DebugInstrumenter { }); break; } - ast::LValue::MemberAccess { object, field_name, span } => { + ast::LValue::MemberAccess { object, field_name, location } => { cursor = object; let field_name_id = self.insert_field_name(&field_name.0.contents); - indexes.push(sint_expr(-(field_name_id.0 as i128), *span)); + indexes.push(sint_expr(-(field_name_id.0 as i128), *location)); } - ast::LValue::Index { index, array, span: _ } => { + ast::LValue::Index { index, array, location: _ } => { cursor = array; indexes.push(index.clone()); } @@ -347,13 +354,13 @@ impl DebugInstrumenter { build_assign_member_stmt( var_id, &indexes, - &id_expr(&ident("__debug_expr", expression_span)), + &id_expr(&ident("__debug_expr", expression_location)), ) } }; let ret_kind = - ast::StatementKind::Expression(id_expr(&ident("__debug_expr", expression_span))); + ast::StatementKind::Expression(id_expr(&ident("__debug_expr", expression_location))); ast::Statement { kind: ast::StatementKind::Assign(ast::AssignStatement { @@ -361,23 +368,23 @@ impl DebugInstrumenter { expression: ast::Expression { kind: ast::ExpressionKind::Block(ast::BlockExpression { statements: vec![ - ast::Statement { kind: let_kind, span: expression_span }, + ast::Statement { kind: let_kind, location: expression_location }, new_assign_stmt, - ast::Statement { kind: ret_kind, span: expression_span }, + ast::Statement { kind: ret_kind, location: expression_location }, ], }), - span: expression_span, + location: expression_location, }, }), - span: *span, + location, } } fn walk_expr(&mut self, expr: &mut ast::Expression) { match &mut expr.kind { - ast::ExpressionKind::Block(ast::BlockExpression { ref mut statements, .. }) => { + ast::ExpressionKind::Block(ast::BlockExpression { statements, .. }) => { self.scope.push(HashMap::default()); - self.walk_scope(statements, expr.span); + self.walk_scope(statements, expr.location); } ast::ExpressionKind::Prefix(prefix_expr) => { self.walk_expr(&mut prefix_expr.rhs); @@ -389,19 +396,19 @@ impl DebugInstrumenter { ast::ExpressionKind::Call(call_expr) => { // TODO: push a stack frame or something here? self.walk_expr(&mut call_expr.func); - call_expr.arguments.iter_mut().for_each(|ref mut expr| { + call_expr.arguments.iter_mut().for_each(|expr| { self.walk_expr(expr); }); } ast::ExpressionKind::MethodCall(mc_expr) => { // TODO: also push a stack frame here self.walk_expr(&mut mc_expr.object); - mc_expr.arguments.iter_mut().for_each(|ref mut expr| { + mc_expr.arguments.iter_mut().for_each(|expr| { self.walk_expr(expr); }); } ast::ExpressionKind::Constructor(c_expr) => { - c_expr.fields.iter_mut().for_each(|(_id, ref mut expr)| { + c_expr.fields.iter_mut().for_each(|(_id, expr)| { self.walk_expr(expr); }); } @@ -442,9 +449,10 @@ impl DebugInstrumenter { let var_id = self.insert_var(var_name); let set_and_drop_stmt = var_id.map(|var_id| { + let span = Span::empty(for_stmt.location.span.end()); ( build_assign_var_stmt(var_id, id_expr(&for_stmt.identifier)), - build_drop_var_stmt(var_id, Span::empty(for_stmt.span.end())), + build_drop_var_stmt(var_id, Location::new(span, for_stmt.location.file)), ) }); @@ -453,7 +461,7 @@ impl DebugInstrumenter { let mut statements = Vec::new(); let block_statement = ast::Statement { kind: ast::StatementKind::Semi(for_stmt.block.clone()), - span: for_stmt.block.span, + location: for_stmt.block.location, }; if let Some((set_stmt, drop_stmt)) = set_and_drop_stmt { @@ -466,17 +474,17 @@ impl DebugInstrumenter { for_stmt.block = ast::Expression { kind: ast::ExpressionKind::Block(ast::BlockExpression { statements }), - span: for_stmt.span, + location: for_stmt.location, }; } fn walk_statement(&mut self, stmt: &mut ast::Statement) { match &mut stmt.kind { ast::StatementKind::Let(let_stmt) => { - *stmt = self.walk_let_statement(let_stmt, &stmt.span); + *stmt = self.walk_let_statement(let_stmt, stmt.location); } ast::StatementKind::Assign(assign_stmt) => { - *stmt = self.walk_assign_statement(assign_stmt, &stmt.span); + *stmt = self.walk_assign_statement(assign_stmt, stmt.location); } ast::StatementKind::Expression(expr) => { self.walk_expr(expr); @@ -484,20 +492,21 @@ impl DebugInstrumenter { ast::StatementKind::Semi(expr) => { self.walk_expr(expr); } - ast::StatementKind::For(ref mut for_stmt) => { + ast::StatementKind::For(for_stmt) => { self.walk_for(for_stmt); } _ => {} // Constrain, Error } } - fn insert_state_set_oracle(&self, module: &mut ParsedModule, n: u32) { + fn insert_state_set_oracle(&self, module: &mut ParsedModule, n: u32, file: FileId) { let member_assigns = (1..=n) .map(|i| format!["__debug_member_assign_{i}"]) .collect::>() .join(",\n"); - let (program, errors) = parse_program(&format!( - r#" + let (program, errors) = parse_program( + &format!( + r#" use __debug::{{ __debug_var_assign, __debug_var_drop, @@ -506,7 +515,9 @@ impl DebugInstrumenter { __debug_dereference_assign, {member_assigns}, }};"# - )); + ), + file, + ); if !errors.is_empty() { panic!("errors parsing internal oracle definitions: {errors:?}") } @@ -619,36 +630,34 @@ pub fn build_debug_crate_file() -> String { } fn build_assign_var_stmt(var_id: SourceVarId, expr: ast::Expression) -> ast::Statement { - let span = expr.span; + let location = expr.location; let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression { func: Box::new(ast::Expression { - kind: ast::ExpressionKind::Variable(ast::Path { - segments: vec![PathSegment::from(ident("__debug_var_assign", span))], - kind: PathKind::Plain, - span, - }), - span, + kind: ast::ExpressionKind::Variable(ast::Path::plain( + vec![PathSegment::from(ident("__debug_var_assign", location))], + location, + )), + location, }), is_macro_call: false, - arguments: vec![uint_expr(var_id.0 as u128, span), expr], + arguments: vec![uint_expr(var_id.0 as u128, location), expr], })); - ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } + ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, location }), location } } -fn build_drop_var_stmt(var_id: SourceVarId, span: Span) -> ast::Statement { +fn build_drop_var_stmt(var_id: SourceVarId, location: Location) -> ast::Statement { let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression { func: Box::new(ast::Expression { - kind: ast::ExpressionKind::Variable(ast::Path { - segments: vec![PathSegment::from(ident("__debug_var_drop", span))], - kind: PathKind::Plain, - span, - }), - span, + kind: ast::ExpressionKind::Variable(ast::Path::plain( + vec![PathSegment::from(ident("__debug_var_drop", location))], + location, + )), + location, }), is_macro_call: false, - arguments: vec![uint_expr(var_id.0 as u128, span)], + arguments: vec![uint_expr(var_id.0 as u128, location)], })); - ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } + ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, location }), location } } fn build_assign_member_stmt( @@ -660,44 +669,39 @@ fn build_assign_member_stmt( if arity > MAX_MEMBER_ASSIGN_DEPTH { unreachable!("Assignment to member exceeds maximum depth for debugging"); } - let span = expr.span; + let location = expr.location; let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression { func: Box::new(ast::Expression { - kind: ast::ExpressionKind::Variable(ast::Path { - segments: vec![PathSegment::from(ident( - &format!["__debug_member_assign_{arity}"], - span, - ))], - kind: PathKind::Plain, - span, - }), - span, + kind: ast::ExpressionKind::Variable(ast::Path::plain( + vec![PathSegment::from(ident(&format!["__debug_member_assign_{arity}"], location))], + location, + )), + location, }), is_macro_call: false, arguments: [ - vec![uint_expr(var_id.0 as u128, span)], + vec![uint_expr(var_id.0 as u128, location)], vec![expr.clone()], indexes.iter().rev().cloned().collect(), ] .concat(), })); - ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } + ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, location }), location } } -fn build_debug_call_stmt(fname: &str, fn_id: DebugFnId, span: Span) -> ast::Statement { +fn build_debug_call_stmt(fname: &str, fn_id: DebugFnId, location: Location) -> ast::Statement { let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression { func: Box::new(ast::Expression { - kind: ast::ExpressionKind::Variable(ast::Path { - segments: vec![PathSegment::from(ident(&format!["__debug_fn_{fname}"], span))], - kind: PathKind::Plain, - span, - }), - span, + kind: ast::ExpressionKind::Variable(ast::Path::plain( + vec![PathSegment::from(ident(&format!["__debug_fn_{fname}"], location))], + location, + )), + location, }), is_macro_call: false, - arguments: vec![uint_expr(fn_id.0 as u128, span)], + arguments: vec![uint_expr(fn_id.0 as u128, location)], })); - ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } + ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, location }), location } } fn pattern_vars(pattern: &ast::Pattern) -> Vec<(ast::Ident, bool)> { @@ -750,31 +754,28 @@ fn pattern_to_string(pattern: &ast::Pattern) -> String { } } -fn ident(s: &str, span: Span) -> ast::Ident { - ast::Ident(Spanned::from(span, s.to_string())) +fn ident(s: &str, location: Location) -> ast::Ident { + ast::Ident::new(s.to_string(), location) } fn id_expr(id: &ast::Ident) -> ast::Expression { ast::Expression { - kind: ast::ExpressionKind::Variable(Path { - segments: vec![PathSegment::from(id.clone())], - kind: PathKind::Plain, - span: id.span(), - }), - span: id.span(), + kind: ast::ExpressionKind::Variable(Path::plain( + vec![PathSegment::from(id.clone())], + id.location(), + )), + location: id.location(), } } -fn uint_expr(x: u128, span: Span) -> ast::Expression { - ast::Expression { - kind: ast::ExpressionKind::Literal(ast::Literal::Integer(x.into(), false)), - span, - } +fn uint_expr(x: u128, location: Location) -> ast::Expression { + let value = SignedField::positive(x); + let kind = ast::ExpressionKind::Literal(ast::Literal::Integer(value)); + ast::Expression { kind, location } } -fn sint_expr(x: i128, span: Span) -> ast::Expression { - ast::Expression { - kind: ast::ExpressionKind::Literal(ast::Literal::Integer(x.abs().into(), x < 0)), - span, - } +fn sint_expr(x: i128, location: Location) -> ast::Expression { + let value = SignedField::from_signed(x); + let kind = ast::ExpressionKind::Literal(ast::Literal::Integer(value)); + ast::Expression { kind, location } } 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 b78787249cb7..7091f7b261cd 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -1,10 +1,10 @@ use std::{collections::BTreeMap, fmt::Display}; -use fm::FileId; use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use crate::{ + Type, TypeBindings, UnificationError, ast::{Documented, Expression, ExpressionKind}, hir::{ comptime::{Interpreter, InterpreterError, Value}, @@ -22,28 +22,23 @@ use crate::{ node_interner::{DefinitionKind, DependencyId, FuncId, NodeInterner, TraitId, TypeId}, parser::{Item, ItemKind}, token::{MetaAttribute, SecondaryAttribute}, - Type, TypeBindings, UnificationError, }; -use super::{Elaborator, FunctionContext, ResolverMeta}; +use super::{ElaborateReason, Elaborator, FunctionContext, ResolverMeta}; #[derive(Debug, Copy, Clone)] struct AttributeContext { - // The file where generated items should be added - file: FileId, // The module where generated items should be added module: LocalModuleId, - // The file where the attribute is located - attribute_file: FileId, // The module where the attribute is located attribute_module: LocalModuleId, } -type CollectedAttributes = Vec<(FuncId, Value, Vec, AttributeContext, Span)>; +type CollectedAttributes = Vec<(FuncId, Value, Vec, AttributeContext, Location)>; impl AttributeContext { - fn new(file: FileId, module: LocalModuleId) -> Self { - Self { file, module, attribute_file: file, attribute_module: module } + fn new(module: LocalModuleId) -> Self { + Self { module, attribute_module: module } } } @@ -62,7 +57,6 @@ impl<'context> Elaborator<'context> { 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()); } }) @@ -71,14 +65,12 @@ impl<'context> Elaborator<'context> { pub fn elaborate_item_from_comptime_in_module<'a, T>( &'a mut self, module: ModuleId, - file: FileId, f: impl FnOnce(&mut Elaborator<'a>) -> T, ) -> T { self.elaborate_item_from_comptime(f, |elaborator| { elaborator.current_item = None; elaborator.crate_id = module.krate; elaborator.local_module = module.local_id; - elaborator.file = file; }) } @@ -95,16 +87,15 @@ impl<'context> Elaborator<'context> { self.usage_tracker, self.crate_graph, self.crate_id, - self.debug_comptime_in_file, self.interpreter_call_stack.clone(), - self.pedantic_solving, + self.options, + self.elaborate_reasons.clone(), ); elaborator.function_context.push(FunctionContext::default()); elaborator.scopes.start_function(); elaborator.local_module = self.local_module; - elaborator.file = self.file; setup(&mut elaborator); @@ -167,7 +158,7 @@ impl<'context> Elaborator<'context> { attribute_context, attributes_to_run, ) { - this.errors.push(error); + this.push_err(error); } }); } @@ -180,12 +171,12 @@ impl<'context> Elaborator<'context> { item: Value, attribute_context: AttributeContext, attributes_to_run: &mut CollectedAttributes, - ) -> Result<(), (CompilationError, FileId)> { - self.file = attribute_context.attribute_file; + ) -> Result<(), CompilationError> { self.local_module = attribute_context.attribute_module; - let span = attribute.span; + let location = attribute.location; - let function = Expression { kind: ExpressionKind::Variable(attribute.name.clone()), span }; + let function = + Expression { kind: ExpressionKind::Variable(attribute.name.clone()), location }; let arguments = attribute.arguments.clone(); // Elaborate the function, rolling back any errors generated in case it is unknown @@ -197,22 +188,25 @@ impl<'context> Elaborator<'context> { let definition_id = match self.interner.expression(&function) { HirExpression::Ident(ident, _) => ident.id, _ => { - let error = - ResolverError::AttributeFunctionIsNotAPath { function: function_string, span }; - return Err((error.into(), self.file)); + let error = ResolverError::AttributeFunctionIsNotAPath { + function: function_string, + location, + }; + return Err(error.into()); } }; let Some(definition) = self.interner.try_definition(definition_id) else { - let error = ResolverError::AttributeFunctionNotInScope { name: function_string, span }; - return Err((error.into(), self.file)); + let error = + ResolverError::AttributeFunctionNotInScope { name: function_string, location }; + return Err(error.into()); }; let DefinitionKind::Function(function) = definition.kind else { - return Err((ResolverError::NonFunctionInAnnotation { span }.into(), self.file)); + return Err(ResolverError::NonFunctionInAnnotation { location }.into()); }; - attributes_to_run.push((function, item, arguments, attribute_context, span)); + attributes_to_run.push((function, item, arguments, attribute_context, location)); Ok(()) } @@ -224,8 +218,7 @@ impl<'context> Elaborator<'context> { item: Value, location: Location, generated_items: &mut CollectedItems, - ) -> Result<(), (CompilationError, FileId)> { - self.file = attribute_context.file; + ) -> Result<(), CompilationError> { self.local_module = attribute_context.module; let mut interpreter = self.setup_interpreter(); @@ -236,20 +229,19 @@ impl<'context> Elaborator<'context> { arguments, location, ) - .map_err(|error| error.into_compilation_error_pair())?; + .map_err(CompilationError::from)?; arguments.insert(0, (item, location)); let value = interpreter .call_function(function, arguments, TypeBindings::new(), location) - .map_err(|error| error.into_compilation_error_pair())?; + .map_err(CompilationError::from)?; self.debug_comptime(location, |interner| value.display(interner).to_string()); if value != Value::Unit { - let items = value - .into_top_level_items(location, self) - .map_err(|error| error.into_compilation_error_pair())?; + let items = + value.into_top_level_items(location, self).map_err(CompilationError::from)?; self.add_items(items, generated_items, location); } @@ -303,7 +295,7 @@ impl<'context> Elaborator<'context> { let mut varargs = im::Vector::new(); for (i, arg) in arguments.into_iter().enumerate() { - let arg_location = Location::new(arg.span, location.file); + let arg_location = arg.location; let param_type = parameters.get(i).or(varargs_elem_type).unwrap_or(&Type::Error); let mut push_arg = |arg| { @@ -350,9 +342,14 @@ impl<'context> Elaborator<'context> { generated_items: &mut CollectedItems, location: Location, ) { + let previous_errors = + self.push_elaborate_reason_and_take_errors(ElaborateReason::RunningAttribute, location); + for item in items { self.add_item(item, generated_items, location); } + + self.pop_elaborate_reason(previous_errors); } pub(crate) fn add_item( @@ -371,13 +368,12 @@ impl<'context> Elaborator<'context> { self.usage_tracker, &function, module_id, - self.file, item.doc_comments, &mut self.errors, ) { let functions = vec![(self.local_module, id, function)]; generated_items.functions.push(UnresolvedFunctions { - file_id: self.file, + file_id: location.file, functions, trait_id: None, self_type: None, @@ -390,12 +386,12 @@ impl<'context> Elaborator<'context> { self.interner, &mut trait_impl, self.crate_id, - self.file, + location.file, self.local_module, ); generated_items.trait_impls.push(UnresolvedTraitImpl { - file_id: self.file, + file_id: location.file, module_id: self.local_module, r#trait: trait_impl.r#trait, object_type: trait_impl.object_type, @@ -420,14 +416,14 @@ impl<'context> Elaborator<'context> { self.usage_tracker, Documented::new(global, item.doc_comments), visibility, - self.file, + location.file, self.local_module, self.crate_id, ); generated_items.globals.push(global); if let Some(error) = error { - self.errors.push(error); + self.push_err(error); } } ItemKind::Struct(struct_def) => { @@ -436,7 +432,6 @@ impl<'context> Elaborator<'context> { self.def_maps.get_mut(&self.crate_id).unwrap(), self.usage_tracker, Documented::new(struct_def, item.doc_comments), - self.file, self.local_module, self.crate_id, &mut self.errors, @@ -450,7 +445,7 @@ impl<'context> Elaborator<'context> { self.def_maps.get_mut(&self.crate_id).unwrap(), self.usage_tracker, Documented::new(enum_def, item.doc_comments), - self.file, + location.file, self.local_module, self.crate_id, &mut self.errors, @@ -464,7 +459,7 @@ impl<'context> Elaborator<'context> { self.interner, generated_items, r#impl, - self.file, + location.file, module, &mut self.errors, ); @@ -476,9 +471,10 @@ impl<'context> Elaborator<'context> { | ItemKind::TypeAlias(_) | ItemKind::Submodules(_) | ItemKind::InnerAttribute(_) => { + let location = item.location; let item = item.kind.to_string(); let error = InterpreterError::UnsupportedTopLevelItemUnquote { item, location }; - self.errors.push(error.into_compilation_error_pair()); + self.push_err(error); } } } @@ -488,7 +484,7 @@ impl<'context> Elaborator<'context> { Some(DependencyId::Function(function)) => Some(function), _ => None, }; - Interpreter::new(self, self.crate_id, current_function, self.pedantic_solving) + Interpreter::new(self, self.crate_id, current_function) } pub(super) fn debug_comptime T>( @@ -496,12 +492,11 @@ impl<'context> Elaborator<'context> { location: Location, mut expr_f: F, ) { - if Some(location.file) == self.debug_comptime_in_file { + if Some(location.file) == self.options.debug_comptime_in_file { let displayed_expr = expr_f(self.interner); - self.errors.push(( - InterpreterError::debug_evaluate_comptime(displayed_expr, location).into(), - location.file, - )); + let error: CompilationError = + InterpreterError::debug_evaluate_comptime(displayed_expr, location).into(); + self.push_err(error); } } @@ -520,7 +515,7 @@ impl<'context> Elaborator<'context> { for (trait_id, trait_) in traits { let attributes = &trait_.trait_def.attributes; let item = Value::TraitDefinition(*trait_id); - let context = AttributeContext::new(trait_.file_id, trait_.module_id); + let context = AttributeContext::new(trait_.module_id); self.collect_comptime_attributes_on_item( attributes, item, @@ -532,7 +527,7 @@ impl<'context> Elaborator<'context> { for (struct_id, struct_def) in types { let attributes = &struct_def.struct_def.attributes; let item = Value::StructDefinition(*struct_id); - let context = AttributeContext::new(struct_def.file_id, struct_def.module_id); + let context = AttributeContext::new(struct_def.module_id); self.collect_comptime_attributes_on_item( attributes, item, @@ -547,9 +542,7 @@ impl<'context> Elaborator<'context> { self.sort_attributes_by_run_order(&mut attributes_to_run); // run - for (attribute, item, args, context, span) in attributes_to_run { - let location = Location::new(span, context.attribute_file); - + for (attribute, item, args, context, location) in attributes_to_run { let mut generated_items = CollectedItems::default(); self.elaborate_in_comptime_context(|this| { if let Err(error) = this.run_attribute( @@ -560,12 +553,19 @@ impl<'context> Elaborator<'context> { location, &mut generated_items, ) { - this.errors.push(error); + this.push_err(error); } }); if !generated_items.is_empty() { + let previous_errors = self.push_elaborate_reason_and_take_errors( + ElaborateReason::RunningAttribute, + location, + ); + self.elaborate_items(generated_items); + + self.pop_elaborate_reason(previous_errors); } } } @@ -575,8 +575,8 @@ impl<'context> Elaborator<'context> { // Sort each attribute by (module, location in file) so that we can execute in // the order they were defined in, running attributes in child modules first. - attributes.sort_by_key(|(_, _, _, ctx, span)| { - (module_order[&ctx.attribute_module], span.start()) + attributes.sort_by_key(|(_, _, _, ctx, location)| { + (module_order[&ctx.attribute_module], location.span.start()) }); } @@ -592,9 +592,7 @@ impl<'context> Elaborator<'context> { let attribute = &module_attribute.attribute; let context = AttributeContext { - file: module_attribute.file_id, module: module_attribute.module_id, - attribute_file: module_attribute.attribute_file_id, attribute_module: module_attribute.attribute_module_id, }; @@ -611,7 +609,7 @@ impl<'context> Elaborator<'context> { self.self_type = function_set.self_type.clone(); for (local_module, function_id, function) in &function_set.functions { - let context = AttributeContext::new(function_set.file_id, *local_module); + let context = AttributeContext::new(*local_module); let attributes = function.secondary_attributes(); let item = Value::FunctionDefinition(*function_id); self.collect_comptime_attributes_on_item( @@ -653,4 +651,35 @@ impl<'context> Elaborator<'context> { _ => false, } } + + /// Pushes an ElaborateReason but also `std::mem::take`s the current errors and returns them. + pub(crate) fn push_elaborate_reason_and_take_errors( + &mut self, + reason: ElaborateReason, + location: Location, + ) -> Vec { + self.elaborate_reasons.push_back((reason, location)); + std::mem::take(&mut self.errors) + } + + /// Pops en ElaborateREason. Receives the errors that were returned by `push_elaborate_reason` + /// so they are restored, while also wrapping errors in the current Elaborator in a ComptimeError. + pub(crate) fn pop_elaborate_reason(&mut self, previous_errors: Vec) { + let new_errors = std::mem::take(&mut self.errors); + let new_errors = self.wrap_errors_in_macro_error(new_errors); + self.errors = previous_errors; + self.push_errors(new_errors); + self.elaborate_reasons.pop_back(); + } + + fn wrap_errors_in_macro_error(&self, errors: Vec) -> Vec { + vecmap(errors, |error| self.wrap_error_in_macro_error(error)) + } + + fn wrap_error_in_macro_error(&self, mut error: CompilationError) -> CompilationError { + for (reason, location) in self.elaborate_reasons.iter().rev() { + error = CompilationError::ComptimeError(reason.to_macro_error(error, *location)); + } + error + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/enums.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/enums.rs index 02d9bfae4946..c0bc86b51b0d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/enums.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/enums.rs @@ -1,29 +1,107 @@ +use std::collections::{BTreeMap, BTreeSet}; + use fxhash::FxHashMap as HashMap; -use iter_extended::{try_vecmap, vecmap}; -use noirc_errors::{Location, Span}; +use iter_extended::{btree_map, try_vecmap, vecmap}; +use noirc_errors::Location; +use rangemap::StepLite; use crate::{ + DataType, Kind, Shared, Type, ast::{ - EnumVariant, Expression, ExpressionKind, FunctionKind, Literal, NoirEnumeration, - StatementKind, UnresolvedType, Visibility, + ConstructorExpression, EnumVariant, Expression, ExpressionKind, FunctionKind, Ident, + Literal, NoirEnumeration, StatementKind, UnresolvedType, Visibility, }, elaborator::path_resolution::PathResolutionItem, hir::{comptime::Value, resolution::errors::ResolverError, type_check::TypeCheckError}, hir_def::{ expr::{ Case, Constructor, HirBlockExpression, HirEnumConstructorExpression, HirExpression, - HirIdent, HirMatch, SignedField, + HirIdent, HirMatch, }, function::{FuncMeta, FunctionBody, HirFunction, Parameters}, stmt::{HirLetStatement, HirPattern, HirStatement}, }, node_interner::{DefinitionId, DefinitionKind, ExprId, FunctionModifiers, GlobalValue, TypeId}, + signed_field::SignedField, token::Attributes, - DataType, Kind, Shared, Type, }; use super::Elaborator; +const WILDCARD_PATTERN: &str = "_"; + +struct MatchCompiler<'elab, 'ctx> { + elaborator: &'elab mut Elaborator<'ctx>, + has_missing_cases: bool, + // We iterate on this to issue errors later so it needs to be a BTreeMap (versus HashMap) to be + // deterministic. + unreachable_cases: BTreeMap, +} + +/// A Pattern is anything that can appear before the `=>` in a match rule. +#[derive(Debug, Clone)] +enum Pattern { + /// A pattern checking for a tag and possibly binding variables such as `Some(42)` + Constructor(Constructor, Vec), + /// An integer literal pattern such as `4`, `12345`, or `-56` + Int(SignedField), + /// A pattern binding a variable such as `a` or `_` + Binding(DefinitionId), + + /// Multiple patterns combined with `|` where we should match this pattern if any + /// constituent pattern matches. e.g. `Some(3) | None` or `Some(1) | Some(2) | None` + #[allow(unused)] + Or(Vec), + + /// An integer range pattern such as `1..20` which will match any integer n such that + /// 1 <= n < 20. + #[allow(unused)] + Range(SignedField, SignedField), + + /// An error occurred while translating this pattern. This Pattern kind always translates + /// to a Fail branch in the decision tree, although the compiler is expected to halt + /// with errors before execution. + Error, +} + +#[derive(Clone)] +struct Column { + variable_to_match: DefinitionId, + pattern: Pattern, +} + +impl Column { + fn new(variable_to_match: DefinitionId, pattern: Pattern) -> Self { + Column { variable_to_match, pattern } + } +} + +#[derive(Clone)] +pub(super) struct Row { + columns: Vec, + guard: Option, + body: RowBody, + original_body: RowBody, + location: Location, +} + +type RowBody = ExprId; + +impl Row { + fn new(columns: Vec, guard: Option, body: RowBody, location: Location) -> Row { + Row { columns, guard, body, original_body: body, location } + } +} + +impl Row { + fn remove_column(&mut self, variable: DefinitionId) -> Option { + self.columns + .iter() + .position(|c| c.variable_to_match == variable) + .map(|idx| self.columns.remove(idx)) + } +} + impl Elaborator<'_> { /// Defines the value of an enum variant that we resolve an enum /// variant expression to. E.g. `Foo::Bar` in `Foo::Bar(baz)`. @@ -75,13 +153,13 @@ impl Elaborator<'_> { self_type: &Type, ) { let name = &variant.name; - let location = Location::new(variant.name.span(), self.file); + let location = variant.name.location(); let global_id = self.interner.push_empty_global( name.clone(), type_id.local_module_id(), type_id.krate(), - self.file, + name.location().file, Vec::new(), false, false, @@ -127,7 +205,7 @@ impl Elaborator<'_> { ) { let name_string = variant.name.to_string(); let datatype_ref = datatype.borrow(); - let location = Location::new(variant.name.span(), self.file); + let location = variant.name.location(); let id = self.interner.push_empty_fn(); @@ -176,7 +254,7 @@ impl Elaborator<'_> { function_body: FunctionBody::Resolved, source_crate: self.crate_id, source_module: type_id.local_module_id(), - source_file: self.file, + source_file: variant.name.location().file, self_type: None, }; @@ -219,7 +297,7 @@ impl Elaborator<'_> { HirPattern::Identifier(ident) => { let id = self.interner.push_expr(HirExpression::Ident(ident.clone(), None)); self.interner.push_expr_type(id, typ.clone()); - self.interner.push_expr_location(id, location.span, location.file); + self.interner.push_expr_location(id, location); id } _ => unreachable!(), @@ -235,7 +313,7 @@ impl Elaborator<'_> { let enum_generics = self_type.borrow().generic_types(); let typ = Type::DataType(self_type.clone(), enum_generics); self.interner.push_expr_type(body, typ); - self.interner.push_expr_location(body, location.span, location.file); + self.interner.push_expr_location(body, location); body } @@ -270,41 +348,55 @@ impl Elaborator<'_> { let rows = vecmap(rules, |(pattern, branch)| { self.push_scope(); - let pattern = self.expression_to_pattern(pattern, &expected_pattern_type); + let pattern_location = pattern.location; + let pattern = + self.expression_to_pattern(pattern, &expected_pattern_type, &mut Vec::new()); let columns = vec![Column::new(variable_to_match, pattern)]; let guard = None; - let body_span = branch.type_span(); + let body_location = branch.type_location(); let (body, body_type) = self.elaborate_expression(branch); self.unify(&body_type, &result_type, || TypeCheckError::TypeMismatch { expected_typ: result_type.to_string(), expr_typ: body_type.to_string(), - expr_span: body_span, + expr_location: body_location, }); self.pop_scope(); - Row::new(columns, guard, body) + Row::new(columns, guard, body, pattern_location) }); (rows, result_type) } /// Convert an expression into a Pattern, defining any variables within. - fn expression_to_pattern(&mut self, expression: Expression, expected_type: &Type) -> Pattern { - let expr_span = expression.type_span(); + fn expression_to_pattern( + &mut self, + expression: Expression, + expected_type: &Type, + variables_defined: &mut Vec, + ) -> Pattern { + let expr_location = expression.type_location(); let unify_with_expected_type = |this: &mut Self, actual| { this.unify(actual, expected_type, || TypeCheckError::TypeMismatch { expected_typ: expected_type.to_string(), expr_typ: actual.to_string(), - expr_span, + expr_location, }); }; + // We want the actual expression's location here, not the innermost one from `type_location()` + let syntax_error = |this: &mut Self| { + let errors = ResolverError::InvalidSyntaxInPattern { location: expression.location }; + this.push_err(errors); + Pattern::Error + }; + match expression.kind { - ExpressionKind::Literal(Literal::Integer(value, negative)) => { + ExpressionKind::Literal(Literal::Integer(value)) => { let actual = self.interner.next_type_variable_with_kind(Kind::IntegerOrField); unify_with_expected_type(self, &actual); - Pattern::Int(SignedField::new(value, negative)) + Pattern::Int(value) } ExpressionKind::Literal(Literal::Bool(value)) => { unify_with_expected_type(self, &Type::Bool); @@ -318,39 +410,42 @@ impl Elaborator<'_> { // - Possible diagnostics improvement: warn if `a` is defined as a variable // when there is a matching enum variant with name `Foo::a` which can // be imported. The user likely intended to reference the enum variant. - let path_len = path.segments.len(); - let location = Location::new(path.span(), self.file); + let location = path.location; let last_ident = path.last_ident(); + // Setting this to `Some` allows us to shadow globals with the same name. + // We should avoid this if there is a `::` in the path since that means the + // user is trying to resolve to a non-local item. + let shadow_existing = path.is_ident().then_some(last_ident); + match self.resolve_path_or_error(path) { Ok(resolution) => self.path_resolution_to_constructor( resolution, + shadow_existing, Vec::new(), expected_type, - location.span, + location, + variables_defined, ), - Err(_) if path_len == 1 => { - // Define the variable - let kind = DefinitionKind::Local(None); - // TODO: `allow_shadowing` is false while I'm too lazy to add a check that we - // don't define the same name multiple times in one pattern. - let id = self.add_variable_decl(last_ident, false, false, true, kind).id; - self.interner.push_definition_type(id, expected_type.clone()); - Pattern::Binding(id) - } Err(error) => { - self.push_err(error); - // Default to defining a variable of the same name although this could - // cause further match warnings/errors (e.g. redundant cases). - let id = self.fresh_match_variable(expected_type.clone(), location); - Pattern::Binding(id) + if let Some(name) = shadow_existing { + self.define_pattern_variable(name, expected_type, variables_defined) + } else { + self.push_err(error); + Pattern::Error + } } } } - ExpressionKind::Call(call) => { - self.expression_to_constructor(*call.func, call.arguments, expected_type) + ExpressionKind::Call(call) => self.expression_to_constructor( + *call.func, + call.arguments, + expected_type, + variables_defined, + ), + ExpressionKind::Constructor(constructor) => { + self.constructor_to_pattern(*constructor, variables_defined) } - ExpressionKind::Constructor(_) => todo!("handle constructors"), ExpressionKind::Tuple(fields) => { let field_types = vecmap(0..fields.len(), |_| self.interner.next_type_variable()); let actual = Type::Tuple(field_types.clone()); @@ -358,23 +453,25 @@ impl Elaborator<'_> { let fields = vecmap(fields.into_iter().enumerate(), |(i, field)| { let expected = field_types.get(i).unwrap_or(&Type::Error); - self.expression_to_pattern(field, expected) + self.expression_to_pattern(field, expected, variables_defined) }); Pattern::Constructor(Constructor::Tuple(field_types.clone()), fields) } - ExpressionKind::Parenthesized(expr) => self.expression_to_pattern(*expr, expected_type), + ExpressionKind::Parenthesized(expr) => { + self.expression_to_pattern(*expr, expected_type, variables_defined) + } ExpressionKind::Interned(id) => { let kind = self.interner.get_expression_kind(id); - let expr = Expression::new(kind.clone(), expression.span); - self.expression_to_pattern(expr, expected_type) + let expr = Expression::new(kind.clone(), expression.location); + self.expression_to_pattern(expr, expected_type, variables_defined) } ExpressionKind::InternedStatement(id) => { if let StatementKind::Expression(expr) = self.interner.get_statement_kind(id) { - self.expression_to_pattern(expr.clone(), expected_type) + self.expression_to_pattern(expr.clone(), expected_type, variables_defined) } else { - panic!("Invalid expr kind {expression}") + syntax_error(self) } } @@ -393,14 +490,85 @@ impl Elaborator<'_> { | ExpressionKind::Quote(_) | ExpressionKind::Unquote(_) | ExpressionKind::Comptime(_, _) - | ExpressionKind::Unsafe(_, _) + | ExpressionKind::Unsafe(_) | ExpressionKind::AsTraitPath(_) | ExpressionKind::TypePath(_) | ExpressionKind::Resolved(_) - | ExpressionKind::Error => { - panic!("Invalid expr kind {expression}") + | ExpressionKind::Error => syntax_error(self), + } + } + + fn define_pattern_variable( + &mut self, + name: Ident, + expected_type: &Type, + variables_defined: &mut Vec, + ) -> Pattern { + // Define the variable + let kind = DefinitionKind::Local(None); + + if let Some(existing) = variables_defined.iter().find(|elem| *elem == &name) { + // Allow redefinition of `_` only, to ignore variables + if name.0.contents != WILDCARD_PATTERN { + self.push_err(ResolverError::VariableAlreadyDefinedInPattern { + existing: existing.clone(), + new_location: name.location(), + }); } + } else { + variables_defined.push(name.clone()); } + + let id = self.add_variable_decl(name, false, true, true, kind).id; + self.interner.push_definition_type(id, expected_type.clone()); + Pattern::Binding(id) + } + + fn constructor_to_pattern( + &mut self, + constructor: ConstructorExpression, + variables_defined: &mut Vec, + ) -> Pattern { + let location = constructor.typ.location; + let typ = self.resolve_type(constructor.typ); + + let Some((struct_name, mut expected_field_types)) = + self.struct_name_and_field_types(&typ, location) + else { + return Pattern::Error; + }; + + let mut fields = BTreeMap::default(); + for (field_name, field) in constructor.fields { + let Some(field_index) = + expected_field_types.iter().position(|(name, _)| *name == field_name.0.contents) + else { + let error = if fields.contains_key(&field_name.0.contents) { + ResolverError::DuplicateField { field: field_name } + } else { + let struct_definition = struct_name.clone(); + ResolverError::NoSuchField { field: field_name, struct_definition } + }; + self.push_err(error); + continue; + }; + + let (field_name, expected_field_type) = expected_field_types.swap_remove(field_index); + let pattern = + self.expression_to_pattern(field, &expected_field_type, variables_defined); + fields.insert(field_name, pattern); + } + + if !expected_field_types.is_empty() { + let struct_definition = struct_name; + let missing_fields = vecmap(expected_field_types, |(name, _)| name); + let error = + ResolverError::MissingFields { location, missing_fields, struct_definition }; + self.push_err(error); + } + + let args = vecmap(fields, |(_name, field)| field); + Pattern::Constructor(Constructor::Variant(typ, 0), args) } fn expression_to_constructor( @@ -408,56 +576,92 @@ impl Elaborator<'_> { name: Expression, args: Vec, expected_type: &Type, + variables_defined: &mut Vec, ) -> Pattern { + let syntax_error = |this: &mut Self| { + this.push_err(ResolverError::InvalidSyntaxInPattern { location: name.location }); + Pattern::Error + }; + match name.kind { ExpressionKind::Variable(path) => { - let span = path.span(); - let location = Location::new(span, self.file); + let location = path.location; match self.resolve_path_or_error(path) { - Ok(resolution) => { - self.path_resolution_to_constructor(resolution, args, expected_type, span) - } + // Use None for `name` here - we don't want to define a variable if this + // resolves to an existing item. + Ok(resolution) => self.path_resolution_to_constructor( + resolution, + None, + args, + expected_type, + location, + variables_defined, + ), Err(error) => { self.push_err(error); - let id = self.fresh_match_variable(expected_type.clone(), location); - Pattern::Binding(id) + Pattern::Error } } } ExpressionKind::Parenthesized(expr) => { - self.expression_to_constructor(*expr, args, expected_type) + self.expression_to_constructor(*expr, args, expected_type, variables_defined) } ExpressionKind::Interned(id) => { let kind = self.interner.get_expression_kind(id); - let expr = Expression::new(kind.clone(), name.span); - self.expression_to_constructor(expr, args, expected_type) + let expr = Expression::new(kind.clone(), name.location); + self.expression_to_constructor(expr, args, expected_type, variables_defined) } ExpressionKind::InternedStatement(id) => { if let StatementKind::Expression(expr) = self.interner.get_statement_kind(id) { - self.expression_to_constructor(expr.clone(), args, expected_type) + self.expression_to_constructor( + expr.clone(), + args, + expected_type, + variables_defined, + ) } else { - panic!("Invalid expr kind {name}") + syntax_error(self) } } - other => todo!("invalid constructor `{other}`"), + _ => syntax_error(self), } } + /// Convert a PathResolutionItem - usually an enum variant or global - to a Constructor. + /// If `name` is `Some`, we'll define a Pattern::Binding instead of erroring if the + /// item doesn't resolve to a variant or global. This would shadow an existing + /// value such as a free function. Generally this is desired unless the variable was + /// a path with multiple components such as `foo::bar` which should always be treated as + /// a path to an existing item. fn path_resolution_to_constructor( &mut self, - name: PathResolutionItem, + resolution: PathResolutionItem, + name: Option, args: Vec, expected_type: &Type, - span: Span, + location: Location, + variables_defined: &mut Vec, ) -> Pattern { - let (actual_type, expected_arg_types, variant_index) = match name { + let (actual_type, expected_arg_types, variant_index) = match &resolution { PathResolutionItem::Global(id) => { // variant constant - let global = self.interner.get_global(id); - let variant_index = match global.value { - GlobalValue::Resolved(Value::Enum(tag, ..)) => tag, - _ => todo!("Value is not an enum constant"), + self.elaborate_global_if_unresolved(id); + let global = self.interner.get_global(*id); + let variant_index = match &global.value { + GlobalValue::Resolved(Value::Enum(tag, ..)) => *tag, + // This may be a global constant. Treat it like a normal constant + GlobalValue::Resolved(value) => { + let value = value.clone(); + return self.global_constant_to_integer_constructor( + value, + expected_type, + location, + ); + } + // We tried to resolve this value above so there must have been an error + // in doing so. Avoid reporting an additional error. + _ => return Pattern::Error, }; let global_type = self.interner.definition_type(global.definition_id); @@ -466,8 +670,12 @@ impl Elaborator<'_> { } PathResolutionItem::Method(_type_id, _type_turbofish, func_id) => { // TODO(#7430): Take type_turbofish into account when instantiating the function's type - let meta = self.interner.function_meta(&func_id); - let Some(variant_index) = meta.enum_variant_index else { todo!("not a variant") }; + let meta = self.interner.function_meta(func_id); + let Some(variant_index) = meta.enum_variant_index else { + let item = resolution.description(); + self.push_err(ResolverError::UnexpectedItemInPattern { location, item }); + return Pattern::Error; + }; let (actual_type, expected_arg_types) = match meta.typ.instantiate(self.interner).0 { @@ -477,18 +685,22 @@ impl Elaborator<'_> { (actual_type, expected_arg_types, variant_index) } - PathResolutionItem::Module(_) => todo!("path_resolution_to_constructor {name:?}"), - PathResolutionItem::Type(_) => todo!("path_resolution_to_constructor {name:?}"), - PathResolutionItem::TypeAlias(_) => todo!("path_resolution_to_constructor {name:?}"), - PathResolutionItem::Trait(_) => todo!("path_resolution_to_constructor {name:?}"), - PathResolutionItem::ModuleFunction(_) => { - todo!("path_resolution_to_constructor {name:?}") - } - PathResolutionItem::TypeAliasFunction(_, _, _) => { - todo!("path_resolution_to_constructor {name:?}") - } - PathResolutionItem::TraitFunction(_, _, _) => { - todo!("path_resolution_to_constructor {name:?}") + PathResolutionItem::Module(_) + | PathResolutionItem::Type(_) + | PathResolutionItem::TypeAlias(_) + | PathResolutionItem::Trait(_) + | PathResolutionItem::ModuleFunction(_) + | PathResolutionItem::TypeAliasFunction(_, _, _) + | PathResolutionItem::TraitFunction(_, _, _) => { + // This variable refers to an existing item + if let Some(name) = name { + // If name is set, shadow the existing item + return self.define_pattern_variable(name, expected_type, variables_defined); + } else { + let item = resolution.description(); + self.push_err(ResolverError::UnexpectedItemInPattern { location, item }); + return Pattern::Error; + } } }; @@ -497,46 +709,149 @@ impl Elaborator<'_> { self.unify(&actual_type, expected_type, || TypeCheckError::TypeMismatch { expected_typ: expected_type.to_string(), expr_typ: actual_type.to_string(), - expr_span: span, + expr_location: location, }); if args.len() != expected_arg_types.len() { - // error expected N args, found M? + let expected = expected_arg_types.len(); + let found = args.len(); + self.push_err(TypeCheckError::ArityMisMatch { expected, found, location }); + return Pattern::Error; } let args = args.into_iter().zip(expected_arg_types); let args = vecmap(args, |(arg, expected_arg_type)| { - self.expression_to_pattern(arg, &expected_arg_type) + self.expression_to_pattern(arg, &expected_arg_type, variables_defined) }); let constructor = Constructor::Variant(actual_type, variant_index); Pattern::Constructor(constructor, args) } + fn global_constant_to_integer_constructor( + &mut self, + constant: Value, + expected_type: &Type, + location: Location, + ) -> Pattern { + let actual_type = constant.get_type(); + self.unify(&actual_type, expected_type, || TypeCheckError::TypeMismatch { + expected_typ: expected_type.to_string(), + expr_typ: actual_type.to_string(), + expr_location: location, + }); + + // Convert a signed integer type like i32 to SignedField + macro_rules! signed_to_signed_field { + ($value:expr) => {{ + let negative = $value < 0; + // Widen the value so that SignedType::MIN does not wrap to 0 when negated below + let mut widened = $value as i128; + if negative { + widened = -widened; + } + SignedField::new(widened.into(), negative) + }}; + } + + let value = match constant { + Value::Bool(value) => SignedField::positive(value), + Value::Field(value) => SignedField::positive(value), + Value::I8(value) => signed_to_signed_field!(value), + Value::I16(value) => signed_to_signed_field!(value), + Value::I32(value) => signed_to_signed_field!(value), + Value::I64(value) => signed_to_signed_field!(value), + Value::U1(value) => SignedField::positive(value), + Value::U8(value) => SignedField::positive(value as u128), + Value::U16(value) => SignedField::positive(value as u128), + Value::U32(value) => SignedField::positive(value), + Value::U64(value) => SignedField::positive(value), + Value::U128(value) => SignedField::positive(value), + Value::Zeroed(_) => SignedField::positive(0u32), + _ => { + self.push_err(ResolverError::NonIntegerGlobalUsedInPattern { location }); + return Pattern::Error; + } + }; + + Pattern::Int(value) + } + + fn struct_name_and_field_types( + &mut self, + typ: &Type, + location: Location, + ) -> Option<(Ident, Vec<(String, Type)>)> { + if let Type::DataType(typ, generics) = typ.follow_bindings_shallow().as_ref() { + if let Some(fields) = typ.borrow().get_fields(generics) { + return Some((typ.borrow().name.clone(), fields)); + } + } + + let error = ResolverError::NonStructUsedInConstructor { typ: typ.to_string(), location }; + self.push_err(error); + None + } + /// Compiles the rows of a match expression, outputting a decision tree for the match. /// /// This is an adaptation of https://github.com/yorickpeterse/pattern-matching-in-rust/tree/main/jacobs2021 /// which is an implementation of https://julesjacobs.com/notes/patternmatching/patternmatching.pdf - pub(super) fn elaborate_match_rows(&mut self, rows: Vec) -> HirMatch { - self.compile_rows(rows).unwrap_or_else(|error| { - self.push_err(error); - HirMatch::Failure - }) + pub(super) fn elaborate_match_rows( + &mut self, + rows: Vec, + type_matched_on: &Type, + location: Location, + ) -> HirMatch { + MatchCompiler::run(self, rows, type_matched_on, location) + } +} + +impl<'elab, 'ctx> MatchCompiler<'elab, 'ctx> { + fn run( + elaborator: &'elab mut Elaborator<'ctx>, + rows: Vec, + type_matched_on: &Type, + location: Location, + ) -> HirMatch { + let mut compiler = Self { + elaborator, + has_missing_cases: false, + unreachable_cases: rows.iter().map(|row| (row.body, row.location)).collect(), + }; + + let hir_match = compiler.compile_rows(rows).unwrap_or_else(|error| { + compiler.elaborator.push_err(error); + HirMatch::Failure { missing_case: false } + }); + + if compiler.has_missing_cases { + compiler.issue_missing_cases_error(&hir_match, type_matched_on, location); + } + + if !compiler.unreachable_cases.is_empty() { + compiler.issue_unreachable_cases_warning(); + } + + hir_match } fn compile_rows(&mut self, mut rows: Vec) -> Result { if rows.is_empty() { - eprintln!("Warning: missing case"); - return Ok(HirMatch::Failure); + self.has_missing_cases = true; + return Ok(HirMatch::Failure { missing_case: true }); } self.push_tests_against_bare_variables(&mut rows); // If the first row is a match-all we match it and the remaining rows are ignored. - if rows.first().map_or(false, |row| row.columns.is_empty()) { + if rows.first().is_some_and(|row| row.columns.is_empty()) { let row = rows.remove(0); return Ok(match row.guard { - None => HirMatch::Success(row.body), + None => { + self.unreachable_cases.remove(&row.original_body); + HirMatch::Success(row.body) + } Some(cond) => { let remaining = self.compile_rows(rows)?; HirMatch::Guard { cond, body: row.body, otherwise: Box::new(remaining) } @@ -545,9 +860,10 @@ impl Elaborator<'_> { } let branch_var = self.branch_variable(&rows); - let location = self.interner.definition(branch_var).location; + let location = self.elaborator.interner.definition(branch_var).location; - match self.interner.definition_type(branch_var).follow_bindings_shallow().into_owned() { + let definition_type = self.elaborator.interner.definition_type(branch_var); + match definition_type.follow_bindings_shallow().into_owned() { Type::FieldElement | Type::Integer(_, _) => { let (cases, fallback) = self.compile_int_cases(rows, branch_var)?; Ok(HirMatch::Switch(branch_var, cases, Some(fallback))) @@ -557,8 +873,6 @@ impl Elaborator<'_> { Ok(HirMatch::Switch(branch_var, cases, Some(fallback))) } - Type::Array(_, _) => todo!(), - Type::Slice(_) => todo!(), Type::Bool => { let cases = vec![ (Constructor::False, Vec::new(), Vec::new()), @@ -609,12 +923,16 @@ impl Elaborator<'_> { } else { drop(def); let typ = Type::DataType(type_def, generics); - todo!("Cannot match on type {typ}") + Err(ResolverError::TypeUnsupportedInMatch { typ, location }) } } - typ @ (Type::Alias(_, _) - | Type::TypeVariable(_) + // We could match on these types in the future + typ @ (Type::Array(_, _) + | Type::Slice(_) | Type::String(_) + // But we'll never be able to match on these + | Type::Alias(_, _) + | Type::TypeVariable(_) | Type::FmtString(_, _) | Type::TraitAsType(_, _, _) | Type::NamedGeneric(_, _) @@ -625,7 +943,9 @@ impl Elaborator<'_> { | Type::Constant(_, _) | Type::Quoted(_) | Type::InfixExpr(_, _, _, _) - | Type::Error) => todo!("Cannot match on type {typ:?}"), + | Type::Error) => { + Err(ResolverError::TypeUnsupportedInMatch { typ, location }) + }, } } @@ -640,8 +960,8 @@ impl Elaborator<'_> { fn fresh_match_variable(&mut self, variable_type: Type, location: Location) -> DefinitionId { let name = "internal_match_variable".to_string(); let kind = DefinitionKind::Local(None); - let id = self.interner.push_definition(name, false, false, kind, location); - self.interner.push_definition_type(id, variable_type); + let id = self.elaborator.interner.push_definition(name, false, false, kind, location); + self.elaborator.interner.push_definition_type(id, variable_type); id } @@ -663,10 +983,9 @@ impl Elaborator<'_> { let (key, cons) = match col.pattern { Pattern::Int(val) => ((val, val), Constructor::Int(val)), Pattern::Range(start, stop) => ((start, stop), Constructor::Range(start, stop)), - pattern => { - eprintln!("Unexpected pattern for integer type: {pattern:?}"); - continue; - } + // Any other pattern shouldn't have an integer type and we expect a type + // check error to already have been issued. + _ => continue, }; if let Some(index) = tested.get(&key) { @@ -721,6 +1040,7 @@ impl Elaborator<'_> { /// /// Types with infinite constructors (e.g. int and string) are handled /// separately; they don't need most of this work anyway. + #[allow(clippy::type_complexity)] fn compile_constructor_cases( &mut self, rows: Vec, @@ -737,7 +1057,7 @@ impl Elaborator<'_> { cols.push(Column::new(*var, pat)); } - cases[idx].2.push(Row::new(cols, row.guard, row.body)); + cases[idx].2.push(Row::new(cols, row.guard, row.body, row.location)); } } else { for (_, _, rows) in &mut cases { @@ -836,16 +1156,16 @@ impl Elaborator<'_> { /// Creates: /// `{ let = ; }` fn let_binding(&mut self, variable: DefinitionId, rhs: DefinitionId, body: ExprId) -> ExprId { - let location = self.interner.definition(rhs).location; + let location = self.elaborator.interner.definition(rhs).location; - let r#type = self.interner.definition_type(variable); - let rhs_type = self.interner.definition_type(rhs); + let r#type = self.elaborator.interner.definition_type(variable); + let rhs_type = self.elaborator.interner.definition_type(rhs); let variable = HirIdent::non_trait_method(variable, location); let rhs = HirExpression::Ident(HirIdent::non_trait_method(rhs, location), None); - let rhs = self.interner.push_expr(rhs); - self.interner.push_expr_type(rhs, rhs_type); - self.interner.push_expr_location(rhs, location.span, location.file); + let rhs = self.elaborator.interner.push_expr(rhs); + self.elaborator.interner.push_expr_type(rhs, rhs_type); + self.elaborator.interner.push_expr_location(rhs, location); let let_ = HirStatement::Let(HirLetStatement { pattern: HirPattern::Identifier(variable), @@ -856,72 +1176,185 @@ impl Elaborator<'_> { is_global_let: false, }); - let body_type = self.interner.id_type(body); - let let_ = self.interner.push_stmt(let_); - let body = self.interner.push_stmt(HirStatement::Expression(body)); + let body_type = self.elaborator.interner.id_type(body); + let let_ = self.elaborator.interner.push_stmt(let_); + let body = self.elaborator.interner.push_stmt(HirStatement::Expression(body)); - self.interner.push_stmt_location(let_, location.span, location.file); - self.interner.push_stmt_location(body, location.span, location.file); + self.elaborator.interner.push_stmt_location(let_, location); + self.elaborator.interner.push_stmt_location(body, location); let block = HirExpression::Block(HirBlockExpression { statements: vec![let_, body] }); - let block = self.interner.push_expr(block); - self.interner.push_expr_type(block, body_type); - self.interner.push_expr_location(block, location.span, location.file); + let block = self.elaborator.interner.push_expr(block); + self.elaborator.interner.push_expr_type(block, body_type); + self.elaborator.interner.push_expr_location(block, location); block } -} -/// A Pattern is anything that can appear before the `=>` in a match rule. -#[derive(Debug, Clone)] -enum Pattern { - /// A pattern checking for a tag and possibly binding variables such as `Some(42)` - Constructor(Constructor, Vec), - /// An integer literal pattern such as `4`, `12345`, or `-56` - Int(SignedField), - /// A pattern binding a variable such as `a` or `_` - Binding(DefinitionId), + /// Any case that isn't branched to when the match is finished must be covered by another + /// case and is thus redundant. + fn issue_unreachable_cases_warning(&mut self) { + for location in self.unreachable_cases.values().copied() { + self.elaborator.push_err(TypeCheckError::UnreachableCase { location }); + } + } - /// Multiple patterns combined with `|` where we should match this pattern if any - /// constituent pattern matches. e.g. `Some(3) | None` or `Some(1) | Some(2) | None` - #[allow(unused)] - Or(Vec), + /// Traverse the resulting HirMatch to build counter-examples of values which would + /// not be covered by the match. + fn issue_missing_cases_error( + &mut self, + tree: &HirMatch, + type_matched_on: &Type, + location: Location, + ) { + let starting_id = match tree { + HirMatch::Switch(id, ..) => *id, + _ => return self.issue_missing_cases_error_for_type(type_matched_on, location), + }; - /// An integer range pattern such as `1..20` which will match any integer n such that - /// 1 <= n < 20. - #[allow(unused)] - Range(SignedField, SignedField), -} + let mut cases = BTreeSet::new(); + self.find_missing_values(tree, &mut Default::default(), &mut cases, starting_id); -#[derive(Clone)] -struct Column { - variable_to_match: DefinitionId, - pattern: Pattern, -} + // It's possible to trigger this matching on an empty enum like `enum Void {}` + if !cases.is_empty() { + self.elaborator.push_err(TypeCheckError::MissingCases { cases, location }); + } + } -impl Column { - fn new(variable_to_match: DefinitionId, pattern: Pattern) -> Self { - Column { variable_to_match, pattern } + /// Issue a missing cases error if necessary for the given type, assuming that no + /// case of the type is covered. This is the case for empty matches `match foo {}`. + /// Note that this is expected not to error if the given type is an enum with zero variants. + fn issue_missing_cases_error_for_type(&mut self, type_matched_on: &Type, location: Location) { + let typ = type_matched_on.follow_bindings_shallow(); + if let Type::DataType(shared, generics) = typ.as_ref() { + if let Some(variants) = shared.borrow().get_variants(generics) { + let cases: BTreeSet<_> = variants.into_iter().map(|(name, _)| name).collect(); + if !cases.is_empty() { + self.elaborator.push_err(TypeCheckError::MissingCases { cases, location }); + } + return; + } + } + let typ = typ.to_string(); + self.elaborator.push_err(TypeCheckError::MissingManyCases { typ, location }); } -} -#[derive(Clone)] -pub(super) struct Row { - columns: Vec, - guard: Option, - body: ExprId, -} + fn find_missing_values( + &self, + tree: &HirMatch, + env: &mut HashMap)>, + missing_cases: &mut BTreeSet, + starting_id: DefinitionId, + ) { + match tree { + HirMatch::Success(_) | HirMatch::Failure { missing_case: false } => (), + HirMatch::Guard { otherwise, .. } => { + self.find_missing_values(otherwise, env, missing_cases, starting_id); + } + HirMatch::Failure { missing_case: true } => { + let case = Self::construct_missing_case(starting_id, env); + missing_cases.insert(case); + } + HirMatch::Switch(definition_id, cases, else_case) => { + for case in cases { + let name = case.constructor.to_string(); + env.insert(*definition_id, (name, case.arguments.clone())); + self.find_missing_values(&case.body, env, missing_cases, starting_id); + } -impl Row { - fn new(columns: Vec, guard: Option, body: ExprId) -> Row { - Row { columns, guard, body } + if let Some(else_case) = else_case { + let typ = self.elaborator.interner.definition_type(*definition_id); + + for case in self.missing_cases(cases, &typ) { + env.insert(*definition_id, case); + self.find_missing_values(else_case, env, missing_cases, starting_id); + } + } + + env.remove(definition_id); + } + } } -} -impl Row { - fn remove_column(&mut self, variable: DefinitionId) -> Option { - self.columns - .iter() - .position(|c| c.variable_to_match == variable) - .map(|idx| self.columns.remove(idx)) + fn missing_cases(&self, cases: &[Case], typ: &Type) -> Vec<(String, Vec)> { + // We expect `cases` to come from a `Switch` which should always have + // at least 2 cases, otherwise it should be a Success or Failure node. + let first = &cases[0]; + + if matches!(&first.constructor, Constructor::Int(_) | Constructor::Range(..)) { + return self.missing_integer_cases(cases, typ); + } + + let all_constructors = first.constructor.all_constructors(); + let mut all_constructors = + btree_map(all_constructors, |(constructor, arg_count)| (constructor, arg_count)); + + for case in cases { + all_constructors.remove(&case.constructor); + } + + vecmap(all_constructors, |(constructor, arg_count)| { + // Safety: this id should only be used in `env` of `find_missing_values` which + // only uses it for display and defaults to "_" on unknown ids. + let args = vecmap(0..arg_count, |_| DefinitionId::dummy_id()); + (constructor.to_string(), args) + }) + } + + fn missing_integer_cases( + &self, + cases: &[Case], + typ: &Type, + ) -> Vec<(String, Vec)> { + // We could give missed cases for field ranges of `0 .. field_modulus` but since the field + // used in Noir may change we recommend a match-all pattern instead. + // If the type is a type variable, we don't know exactly which integer type this may + // resolve to so also just suggest a catch-all in that case. + if typ.is_field() || typ.is_bindable() { + return vec![(WILDCARD_PATTERN.to_string(), Vec::new())]; + } + + let mut missing_cases = rangemap::RangeInclusiveSet::new(); + + let int_max = SignedField::positive(typ.integral_maximum_size().unwrap()); + let int_min = typ.integral_minimum_size().unwrap(); + missing_cases.insert(int_min..=int_max); + + for case in cases { + match &case.constructor { + Constructor::Int(signed_field) => { + missing_cases.remove(*signed_field..=*signed_field); + } + Constructor::Range(start, end) => { + // Our ranges are exclusive, so adjust for that + missing_cases.remove(*start..=end.sub_one()); + } + _ => unreachable!( + "missing_integer_cases should only be called with Int or Range constructors" + ), + } + } + + vecmap(missing_cases, |range| { + if range.start() == range.end() { + (format!("{}", range.start()), Vec::new()) + } else { + (format!("{}..={}", range.start(), range.end()), Vec::new()) + } + }) + } + + fn construct_missing_case( + starting_id: DefinitionId, + env: &HashMap)>, + ) -> String { + let Some((constructor, arguments)) = env.get(&starting_id) else { + return WILDCARD_PATTERN.to_string(); + }; + + let no_arguments = arguments.is_empty(); + + let args = vecmap(arguments, |arg| Self::construct_missing_case(*arg, env)).join(", "); + + if no_arguments { constructor.clone() } else { format!("{constructor}({args})") } } } 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 3b25f85a25c2..2ac779d3e772 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -1,29 +1,33 @@ use acvm::{AcirField, FieldElement}; use iter_extended::vecmap; -use noirc_errors::{Location, Span, Spanned}; +use noirc_errors::{Located, Location}; use rustc_hash::FxHashSet as HashSet; use crate::{ + DataType, Kind, QuotedType, Shared, Type, ast::{ - ArrayLiteral, BinaryOpKind, BlockExpression, CallExpression, CastExpression, + ArrayLiteral, AsTraitPath, BinaryOpKind, BlockExpression, CallExpression, CastExpression, ConstrainExpression, ConstrainKind, ConstructorExpression, Expression, ExpressionKind, Ident, IfExpression, IndexExpression, InfixExpression, ItemVisibility, Lambda, Literal, MatchExpression, MemberAccessExpression, MethodCallExpression, Path, PathSegment, - PrefixExpression, StatementKind, UnaryOp, UnresolvedTypeData, UnresolvedTypeExpression, + PrefixExpression, StatementKind, TraitBound, UnaryOp, UnresolvedTraitConstraint, + UnresolvedTypeData, UnresolvedTypeExpression, UnsafeExpression, }, hir::{ comptime::{self, InterpreterError}, + def_collector::dc_crate::CompilationError, resolution::{ errors::ResolverError, import::PathResolutionError, visibility::method_call_is_visible, }, - type_check::{generics::TraitGenerics, TypeCheckError}, + type_check::{TypeCheckError, generics::TraitGenerics}, }, hir_def::{ expr::{ HirArrayLiteral, HirBinaryOp, HirBlockExpression, HirCallExpression, HirCastExpression, HirConstrainExpression, HirConstructorExpression, HirExpression, HirIdent, HirIfExpression, HirIndexExpression, HirInfixExpression, HirLambda, HirLiteral, - HirMemberAccess, HirMethodCallExpression, HirPrefixExpression, + HirMatch, HirMemberAccess, HirMethodCallExpression, HirPrefixExpression, ImplKind, + TraitMethod, }, stmt::{HirLetStatement, HirPattern, HirStatement}, traits::{ResolvedTraitBound, TraitConstraint}, @@ -32,12 +36,11 @@ use crate::{ DefinitionId, DefinitionKind, ExprId, FuncId, InternedStatementKind, StmtId, TraitMethodId, }, token::{FmtStrFragment, Tokens}, - DataType, Kind, QuotedType, Shared, Type, }; use super::{Elaborator, LambdaContext, UnsafeBlockStatus}; -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { pub(crate) fn elaborate_expression(&mut self, expr: Expression) -> (ExprId, Type) { self.elaborate_expression_with_target_type(expr, None) } @@ -48,58 +51,57 @@ impl<'context> Elaborator<'context> { target_type: Option<&Type>, ) -> (ExprId, Type) { let (hir_expr, typ) = match expr.kind { - ExpressionKind::Literal(literal) => self.elaborate_literal(literal, expr.span), + ExpressionKind::Literal(literal) => self.elaborate_literal(literal, expr.location), ExpressionKind::Block(block) => self.elaborate_block(block, target_type), - ExpressionKind::Prefix(prefix) => return self.elaborate_prefix(*prefix, expr.span), + ExpressionKind::Prefix(prefix) => return self.elaborate_prefix(*prefix, expr.location), 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), + ExpressionKind::Call(call) => self.elaborate_call(*call, expr.location), + ExpressionKind::MethodCall(call) => self.elaborate_method_call(*call, expr.location), ExpressionKind::Constrain(constrain) => self.elaborate_constrain(constrain), ExpressionKind::Constructor(constructor) => self.elaborate_constructor(*constructor), ExpressionKind::MemberAccess(access) => { - return self.elaborate_member_access(*access, expr.span) + return self.elaborate_member_access(*access, expr.location); } - ExpressionKind::Cast(cast) => self.elaborate_cast(*cast, expr.span), - ExpressionKind::Infix(infix) => return self.elaborate_infix(*infix, expr.span), + ExpressionKind::Cast(cast) => self.elaborate_cast(*cast, expr.location), + ExpressionKind::Infix(infix) => return self.elaborate_infix(*infix, expr.location), ExpressionKind::If(if_) => self.elaborate_if(*if_, target_type), - ExpressionKind::Match(match_) => self.elaborate_match(*match_, expr.span), + ExpressionKind::Match(match_) => self.elaborate_match(*match_, expr.location), ExpressionKind::Variable(variable) => return self.elaborate_variable(variable), ExpressionKind::Tuple(tuple) => self.elaborate_tuple(tuple, target_type), ExpressionKind::Lambda(lambda) => { self.elaborate_lambda_with_target_type(*lambda, target_type) } ExpressionKind::Parenthesized(expr) => { - return self.elaborate_expression_with_target_type(*expr, target_type) + return self.elaborate_expression_with_target_type(*expr, target_type); } - ExpressionKind::Quote(quote) => self.elaborate_quote(quote, expr.span), + ExpressionKind::Quote(quote) => self.elaborate_quote(quote, expr.location), ExpressionKind::Comptime(comptime, _) => { - return self.elaborate_comptime_block(comptime, expr.span, target_type) + return self.elaborate_comptime_block(comptime, expr.location, target_type); } - ExpressionKind::Unsafe(block_expression, span) => { - self.elaborate_unsafe_block(block_expression, span, target_type) + ExpressionKind::Unsafe(unsafe_expression) => { + self.elaborate_unsafe_block(unsafe_expression, target_type) } ExpressionKind::Resolved(id) => return (id, self.interner.id_type(id)), ExpressionKind::Interned(id) => { let expr_kind = self.interner.get_expression_kind(id); - let expr = Expression::new(expr_kind.clone(), expr.span); + let expr = Expression::new(expr_kind.clone(), expr.location); return self.elaborate_expression(expr); } ExpressionKind::InternedStatement(id) => { - return self.elaborate_interned_statement_as_expr(id, expr.span); + return self.elaborate_interned_statement_as_expr(id, expr.location); } ExpressionKind::Error => (HirExpression::Error, Type::Error), ExpressionKind::Unquote(_) => { - self.push_err(ResolverError::UnquoteUsedOutsideQuote { span: expr.span }); + self.push_err(ResolverError::UnquoteUsedOutsideQuote { location: expr.location }); (HirExpression::Error, Type::Error) } - ExpressionKind::AsTraitPath(_) => { - self.push_err(ResolverError::AsTraitPathNotYetImplemented { span: expr.span }); - (HirExpression::Error, Type::Error) + ExpressionKind::AsTraitPath(path) => { + return self.elaborate_as_trait_path(path); } ExpressionKind::TypePath(path) => return self.elaborate_type_path(path), }; let id = self.interner.push_expr(hir_expr); - self.interner.push_expr_location(id, expr.span, self.file); + self.interner.push_expr_location(id, expr.location); self.interner.push_expr_type(id, typ.clone()); (id, typ) } @@ -107,21 +109,24 @@ impl<'context> Elaborator<'context> { fn elaborate_interned_statement_as_expr( &mut self, id: InternedStatementKind, - span: Span, + location: Location, ) -> (ExprId, Type) { match self.interner.get_statement_kind(id) { StatementKind::Expression(expr) | StatementKind::Semi(expr) => { self.elaborate_expression(expr.clone()) } - StatementKind::Interned(id) => self.elaborate_interned_statement_as_expr(*id, span), + StatementKind::Interned(id) => self.elaborate_interned_statement_as_expr(*id, location), StatementKind::Error => { - let expr = Expression::new(ExpressionKind::Error, span); + let expr = Expression::new(ExpressionKind::Error, location); self.elaborate_expression(expr) } other => { let statement = other.to_string(); - self.push_err(ResolverError::InvalidInternedStatementInExpr { statement, span }); - let expr = Expression::new(ExpressionKind::Error, span); + self.push_err(ResolverError::InvalidInternedStatementInExpr { + statement, + location, + }); + let expr = Expression::new(ExpressionKind::Error, location); self.elaborate_expression(expr) } } @@ -154,11 +159,11 @@ impl<'context> Elaborator<'context> { if let HirStatement::Semi(expr) = self.interner.statement(&id) { let inner_expr_type = self.interner.id_type(expr); - let span = self.interner.expr_span(&expr); + let location = self.interner.expr_location(&expr); self.unify(&inner_expr_type, &Type::Unit, || TypeCheckError::UnusedResultError { expr_type: inner_expr_type.clone(), - expr_span: span, + expr_location: location, }); } @@ -173,8 +178,7 @@ impl<'context> Elaborator<'context> { fn elaborate_unsafe_block( &mut self, - block: BlockExpression, - span: Span, + unsafe_expression: UnsafeExpression, target_type: Option<&Type>, ) -> (HirExpression, Type) { // Before entering the block we cache the old value of `in_unsafe_block` so it can be restored. @@ -182,18 +186,21 @@ impl<'context> Elaborator<'context> { let is_nested_unsafe_block = !matches!(old_in_unsafe_block, UnsafeBlockStatus::NotInUnsafeBlock); if is_nested_unsafe_block { - let span = Span::from(span.start()..span.start() + 6); // Only highlight the `unsafe` keyword - self.push_err(TypeCheckError::NestedUnsafeBlock { span }); + self.push_err(TypeCheckError::NestedUnsafeBlock { + location: unsafe_expression.unsafe_keyword_location, + }); } self.unsafe_block_status = UnsafeBlockStatus::InUnsafeBlockWithoutUnconstrainedCalls; - let (hir_block_expression, typ) = self.elaborate_block_expression(block, target_type); + let (hir_block_expression, typ) = + self.elaborate_block_expression(unsafe_expression.block, target_type); if let UnsafeBlockStatus::InUnsafeBlockWithoutUnconstrainedCalls = self.unsafe_block_status { - let span = Span::from(span.start()..span.start() + 6); // Only highlight the `unsafe` keyword - self.push_err(TypeCheckError::UnnecessaryUnsafeBlock { span }); + self.push_err(TypeCheckError::UnnecessaryUnsafeBlock { + location: unsafe_expression.unsafe_keyword_location, + }); } // Finally, we restore the original value of `self.in_unsafe_block`, @@ -206,14 +213,13 @@ impl<'context> Elaborator<'context> { (HirExpression::Unsafe(hir_block_expression), typ) } - fn elaborate_literal(&mut self, literal: Literal, span: Span) -> (HirExpression, Type) { + fn elaborate_literal(&mut self, literal: Literal, location: Location) -> (HirExpression, Type) { use HirExpression::Literal as Lit; match literal { Literal::Unit => (Lit(HirLiteral::Unit), Type::Unit), Literal::Bool(b) => (Lit(HirLiteral::Bool(b)), Type::Bool), - Literal::Integer(integer, sign) => { - let int = HirLiteral::Integer(integer, sign); - (Lit(int), self.polymorphic_integer_or_field()) + Literal::Integer(integer) => { + (Lit(HirLiteral::Integer(integer)), self.polymorphic_integer_or_field()) } Literal::Str(str) | Literal::RawStr(str, _) => { let len = Type::Constant(str.len().into(), Kind::u32()); @@ -221,10 +227,10 @@ impl<'context> Elaborator<'context> { } Literal::FmtStr(fragments, length) => self.elaborate_fmt_string(fragments, length), Literal::Array(array_literal) => { - self.elaborate_array_literal(array_literal, span, true) + self.elaborate_array_literal(array_literal, location, true) } Literal::Slice(array_literal) => { - self.elaborate_array_literal(array_literal, span, false) + self.elaborate_array_literal(array_literal, location, false) } } } @@ -232,24 +238,24 @@ impl<'context> Elaborator<'context> { fn elaborate_array_literal( &mut self, array_literal: ArrayLiteral, - span: Span, + location: Location, is_array: bool, ) -> (HirExpression, Type) { let (expr, elem_type, length) = match array_literal { ArrayLiteral::Standard(elements) => { let first_elem_type = self.interner.next_type_variable(); - let first_span = elements.first().map(|elem| elem.span).unwrap_or(span); + let first_location = elements.first().map(|elem| elem.location).unwrap_or(location); let elements = vecmap(elements.into_iter().enumerate(), |(i, elem)| { - let span = elem.span; + let location = elem.location; let (elem_id, elem_type) = self.elaborate_expression(elem); self.unify(&elem_type, &first_elem_type, || { TypeCheckError::NonHomogeneousArray { - first_span, + first_location, first_type: first_elem_type.to_string(), first_index: 0, - second_span: span, + second_location: location, second_type: elem_type.to_string(), second_index: i, } @@ -262,14 +268,15 @@ impl<'context> Elaborator<'context> { (HirArrayLiteral::Standard(elements), first_elem_type, length) } ArrayLiteral::Repeated { repeated_element, length } => { - let span = length.span; - let length = - UnresolvedTypeExpression::from_expr(*length, span).unwrap_or_else(|error| { + let location = length.location; + let length = UnresolvedTypeExpression::from_expr(*length, location).unwrap_or_else( + |error| { self.push_err(ResolverError::ParserError(Box::new(error))); - UnresolvedTypeExpression::Constant(FieldElement::zero(), span) - }); + UnresolvedTypeExpression::Constant(FieldElement::zero(), location) + }, + ); - let length = self.convert_expression_type(length, &Kind::u32(), span); + let length = self.convert_expression_type(length, &Kind::u32(), location); let (repeated_element, elem_type) = self.elaborate_expression(*repeated_element); let length_clone = length.clone(); @@ -295,7 +302,7 @@ impl<'context> Elaborator<'context> { let mut capture_types = Vec::new(); for fragment in &fragments { - if let FmtStrFragment::Interpolation(ident_name, string_span) = fragment { + if let FmtStrFragment::Interpolation(ident_name, location) = fragment { let scope_tree = self.scopes.current_scope_tree(); let variable = scope_tree.find(ident_name); @@ -303,23 +310,20 @@ impl<'context> Elaborator<'context> { old_value.num_times_used += 1; old_value.ident.clone() } else if let Ok((definition_id, _)) = - self.lookup_global(Path::from_single(ident_name.to_string(), *string_span)) + self.lookup_global(Path::from_single(ident_name.to_string(), *location)) { - HirIdent::non_trait_method( - definition_id, - Location::new(*string_span, self.file), - ) + HirIdent::non_trait_method(definition_id, *location) } else { self.push_err(ResolverError::VariableNotDeclared { name: ident_name.to_owned(), - span: *string_span, + location: *location, }); continue; }; let hir_expr = HirExpression::Ident(hir_ident.clone(), None); let expr_id = self.interner.push_expr(hir_expr); - self.interner.push_expr_location(expr_id, *string_span, self.file); + self.interner.push_expr_location(expr_id, *location); let typ = self.type_check_variable(hir_ident, expr_id, None); self.interner.push_expr_type(expr_id, typ.clone()); capture_types.push(typ); @@ -332,8 +336,8 @@ impl<'context> Elaborator<'context> { (HirExpression::Literal(HirLiteral::FmtStr(fragments, fmt_str_idents, length)), typ) } - fn elaborate_prefix(&mut self, prefix: PrefixExpression, span: Span) -> (ExprId, Type) { - let rhs_span = prefix.rhs.span; + fn elaborate_prefix(&mut self, prefix: PrefixExpression, location: Location) -> (ExprId, Type) { + let rhs_location = prefix.rhs.location; let (rhs, rhs_type) = self.elaborate_expression(prefix.rhs); let trait_id = self.interner.get_prefix_operator_trait_method(&prefix.operator); @@ -341,55 +345,81 @@ impl<'context> Elaborator<'context> { let operator = prefix.operator; if let UnaryOp::MutableReference = operator { - self.check_can_mutate(rhs, rhs_span); + self.check_can_mutate(rhs, rhs_location); } let expr = HirExpression::Prefix(HirPrefixExpression { operator, rhs, trait_method_id: trait_id }); let expr_id = self.interner.push_expr(expr); - self.interner.push_expr_location(expr_id, span, self.file); + self.interner.push_expr_location(expr_id, location); - let result = self.prefix_operand_type_rules(&operator, &rhs_type, span); - let typ = self.handle_operand_type_rules_result(result, &rhs_type, trait_id, expr_id, span); + let result = self.prefix_operand_type_rules(&operator, &rhs_type, location); + let typ = + self.handle_operand_type_rules_result(result, &rhs_type, trait_id, expr_id, location); self.interner.push_expr_type(expr_id, typ.clone()); (expr_id, typ) } - fn check_can_mutate(&mut self, expr_id: ExprId, span: Span) { + pub(super) fn check_can_mutate(&mut self, expr_id: ExprId, location: Location) { let expr = self.interner.expression(&expr_id); match expr { HirExpression::Ident(hir_ident, _) => { if let Some(definition) = self.interner.try_definition(hir_ident.id) { + let name = definition.name.clone(); if !definition.mutable { self.push_err(TypeCheckError::CannotMutateImmutableVariable { - name: definition.name.clone(), - span, + name, + location, }); + } else { + self.check_can_mutate_lambda_capture(hir_ident.id, name, location); } } } + HirExpression::Index(_) => { + self.push_err(TypeCheckError::MutableReferenceToArrayElement { location }); + } HirExpression::MemberAccess(member_access) => { - self.check_can_mutate(member_access.lhs, span); + self.check_can_mutate(member_access.lhs, location); } _ => (), } } + // We must check whether the mutable variable we are attempting to mutate + // comes from a lambda capture. All captures are immutable so we want to error + // if the user attempts to mutate a captured variable inside of a lambda without mutable references. + pub(super) fn check_can_mutate_lambda_capture( + &mut self, + id: DefinitionId, + name: String, + location: Location, + ) { + if let Some(lambda_context) = self.lambda_stack.last() { + let typ = self.interner.definition_type(id); + if !typ.is_mutable_ref() && lambda_context.captures.iter().any(|var| var.ident.id == id) + { + self.push_err(TypeCheckError::MutableCaptureWithoutRef { name, location }); + } + } + } + fn elaborate_index(&mut self, index_expr: IndexExpression) -> (HirExpression, Type) { - let span = index_expr.index.span; + let location = index_expr.index.location; + let (index, index_type) = self.elaborate_expression(index_expr.index); let expected = self.polymorphic_integer_or_field(); self.unify(&index_type, &expected, || TypeCheckError::TypeMismatch { expected_typ: "an integer".to_owned(), expr_typ: index_type.to_string(), - expr_span: span, + expr_location: location, }); // When writing `a[i]`, if `a : &mut ...` then automatically dereference `a` as many // times as needed to get the underlying array. - let lhs_span = index_expr.collection.span; + let lhs_location = index_expr.collection.location; let (lhs, lhs_type) = self.elaborate_expression(index_expr.collection); let (collection, lhs_type) = self.insert_auto_dereferences(lhs, lhs_type); @@ -400,14 +430,16 @@ impl<'context> Elaborator<'context> { Type::Slice(base_type) => *base_type, Type::Error => Type::Error, Type::TypeVariable(_) => { - self.push_err(TypeCheckError::TypeAnnotationsNeededForIndex { span: lhs_span }); + self.push_err(TypeCheckError::TypeAnnotationsNeededForIndex { + location: lhs_location, + }); Type::Error } typ => { self.push_err(TypeCheckError::TypeMismatch { expected_typ: "Array".to_owned(), expr_typ: typ.to_string(), - expr_span: lhs_span, + expr_location: lhs_location, }); Type::Error } @@ -417,7 +449,11 @@ impl<'context> Elaborator<'context> { (expr, typ) } - fn elaborate_call(&mut self, call: CallExpression, span: Span) -> (HirExpression, Type) { + fn elaborate_call( + &mut self, + call: CallExpression, + location: Location, + ) -> (HirExpression, Type) { let (func, func_type) = self.elaborate_expression(*call.func); let func_type = func_type.follow_bindings(); let func_arg_types = @@ -425,7 +461,7 @@ impl<'context> Elaborator<'context> { let mut arguments = Vec::with_capacity(call.arguments.len()); let args = vecmap(call.arguments.into_iter().enumerate(), |(arg_index, arg)| { - let span = arg.span; + let location = arg.location; let expected_type = func_arg_types.and_then(|args| args.get(arg_index)); let (arg, typ) = if call.is_macro_call { @@ -443,7 +479,7 @@ impl<'context> Elaborator<'context> { } arguments.push(arg); - (typ, arg, span) + (typ, arg, location) }); // Avoid cloning arguments unless this is a macro call @@ -452,10 +488,9 @@ impl<'context> Elaborator<'context> { comptime_args = arguments.clone(); } - let location = Location::new(span, self.file); let is_macro_call = call.is_macro_call; let hir_call = HirCallExpression { func, arguments, location, is_macro_call }; - let mut typ = self.type_check_call(&hir_call, func_type, args, span); + let mut typ = self.type_check_call(&hir_call, func_type, args, location); if is_macro_call { if self.in_comptime_context() { @@ -463,7 +498,7 @@ impl<'context> Elaborator<'context> { } else { return self .call_macro(func, comptime_args, location, typ) - .unwrap_or_else(|| (HirExpression::Error, Type::Error)); + .unwrap_or((HirExpression::Error, Type::Error)); } } @@ -473,15 +508,16 @@ impl<'context> Elaborator<'context> { fn elaborate_method_call( &mut self, method_call: MethodCallExpression, - span: Span, + location: Location, ) -> (HirExpression, Type) { - let object_span = method_call.object.span; + let object_location = method_call.object.location; let (mut object, mut object_type) = self.elaborate_expression(method_call.object); object_type = object_type.follow_bindings(); - let method_name_span = method_call.method_name.span(); + let method_name_location = method_call.method_name.location(); let method_name = method_call.method_name.0.contents.as_str(); - match self.lookup_method(&object_type, method_name, span, true) { + let check_self_param = true; + match self.lookup_method(&object_type, method_name, location, check_self_param) { Some(method_ref) => { // Automatically add `&mut` if the method expects a mutable reference and // the object is not already one. @@ -497,13 +533,16 @@ impl<'context> Elaborator<'context> { &mut object, ); - self.resolve_function_turbofish_generics(&func_id, method_call.generics, span) + self.resolve_function_turbofish_generics( + &func_id, + method_call.generics, + location, + ) } else { None }; - let call_span = Span::from(object_span.start()..method_name_span.end()); - let location = Location::new(call_span, self.file); + let location = object_location.merge(method_name_location); let (function_id, function_name) = method_ref.clone().into_function_id_and_name( object_type.clone(), @@ -533,10 +572,10 @@ impl<'context> Elaborator<'context> { let mut function_args = Vec::with_capacity(method_call.arguments.len() + 1); let mut arguments = Vec::with_capacity(method_call.arguments.len()); - function_args.push((object_type.clone(), object, object_span)); + function_args.push((object_type.clone(), object, object_location)); for (arg_index, arg) in method_call.arguments.into_iter().enumerate() { - let span = arg.span; + let location = arg.location; let expected_type = func_arg_types.and_then(|args| args.get(arg_index + 1)); let (arg, typ) = self.elaborate_expression_with_type(arg, expected_type); @@ -547,7 +586,7 @@ impl<'context> Elaborator<'context> { } arguments.push(arg); - function_args.push((typ, arg, span)); + function_args.push((typ, arg, location)); } let method = method_call.method_name; @@ -564,12 +603,12 @@ impl<'context> Elaborator<'context> { let function_call = method_call.into_function_call(function_id, is_macro_call, location); - self.interner - .add_function_reference(func_id, Location::new(method_name_span, self.file)); + self.interner.add_function_reference(func_id, method_name_location); // Type check the new call now that it has been changed from a method call // to a function call. This way we avoid duplicating code. - let mut typ = self.type_check_call(&function_call, func_type, function_args, span); + let mut typ = + self.type_check_call(&function_call, func_type, function_args, location); if is_macro_call { if self.in_comptime_context() { typ = self.interner.next_type_variable(); @@ -577,7 +616,7 @@ impl<'context> Elaborator<'context> { let args = function_call.arguments.clone(); return self .call_macro(function_call.func, args, location, typ) - .unwrap_or_else(|| (HirExpression::Error, Type::Error)); + .unwrap_or((HirExpression::Error, Type::Error)); } } (HirExpression::Call(function_call), typ) @@ -590,7 +629,7 @@ impl<'context> Elaborator<'context> { &mut self, mut expr: ConstrainExpression, ) -> (HirExpression, Type) { - let span = expr.span; + let location = expr.location; let min_args_count = expr.kind.required_arguments_count(); let max_args_count = min_args_count + 1; let actual_args_count = expr.arguments.len(); @@ -599,14 +638,14 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::AssertionParameterCountMismatch { kind: expr.kind, found: actual_args_count, - span, + location, }); // Given that we already produced an error, let's make this an `assert(true)` so // we don't get further errors. let message = None; let kind = ExpressionKind::Literal(crate::ast::Literal::Bool(true)); - let expr = Expression { kind, span }; + let expr = Expression { kind, location }; (message, expr) } else { let message = @@ -616,17 +655,17 @@ impl<'context> Elaborator<'context> { ConstrainKind::AssertEq => { let rhs = expr.arguments.pop().unwrap(); let lhs = expr.arguments.pop().unwrap(); - let span = Span::from(lhs.span.start()..rhs.span.end()); - let operator = Spanned::from(span, BinaryOpKind::Equal); + let location = lhs.location.merge(rhs.location); + let operator = Located::from(location, BinaryOpKind::Equal); let kind = ExpressionKind::Infix(Box::new(InfixExpression { lhs, operator, rhs })); - Expression { kind, span } + Expression { kind, location } } }; (message, expr) }; - let expr_span = expr.span; + let expr_location = expr.location; let (expr_id, expr_type) = self.elaborate_expression(expr); // Must type check the assertion message expression so that we instantiate bindings @@ -635,10 +674,10 @@ impl<'context> Elaborator<'context> { self.unify(&expr_type, &Type::Bool, || TypeCheckError::TypeMismatch { expr_typ: expr_type.to_string(), expected_typ: Type::Bool.to_string(), - expr_span, + expr_location, }); - (HirExpression::Constrain(HirConstrainExpression(expr_id, self.file, msg)), Type::Unit) + (HirExpression::Constrain(HirConstrainExpression(expr_id, location.file, msg)), Type::Unit) } /// Elaborates an expression knowing that it has to match a given type. @@ -651,12 +690,12 @@ impl<'context> Elaborator<'context> { return self.elaborate_expression(arg); }; - let span = arg.span; + let location = arg.location; let type_hint = if let Some(Type::Function(func_args, _, _, _)) = typ { Some(func_args) } else { None }; let (hir_expr, typ) = self.elaborate_lambda_with_parameter_type_hints(*lambda, type_hint); let id = self.interner.push_expr(hir_expr); - self.interner.push_expr_location(id, span, self.file); + self.interner.push_expr_location(id, location); self.interner.push_expr_type(id, typ.clone()); (id, typ) } @@ -679,7 +718,7 @@ impl<'context> Elaborator<'context> { &mut self, constructor: ConstructorExpression, ) -> (HirExpression, Type) { - let span = constructor.typ.span; + let location = constructor.typ.location; // A constructor type can either be a Path or an interned UnresolvedType. // We represent both as UnresolvedType (with Path being a Named UnresolvedType) @@ -692,10 +731,18 @@ impl<'context> Elaborator<'context> { // If this type is already resolved we can skip the rest of this function // which just resolves the type, and go straight to resolving the fields. let resolved = self.interner.get_quoted_type(id).clone(); - return self.elaborate_constructor_with_type(resolved, constructor.fields, span, None); + return self.elaborate_constructor_with_type( + resolved, + constructor.fields, + location, + None, + ); } let UnresolvedTypeData::Named(mut path, generics, _) = typ else { - self.push_err(ResolverError::NonStructUsedInConstructor { typ: typ.to_string(), span }); + self.push_err(ResolverError::NonStructUsedInConstructor { + typ: typ.to_string(), + location, + }); return (HirExpression::Error, Type::Error); }; @@ -706,25 +753,18 @@ impl<'context> Elaborator<'context> { let last_segment = path.last_segment(); - let typ = if let Some(struct_id) = constructor.struct_type { - let typ = self.interner.get_type(struct_id); - let generics = typ.borrow().instantiate(self.interner); - Type::DataType(typ, generics) - } else { - match self.lookup_type_or_error(path) { - Some(typ) => typ, - None => return (HirExpression::Error, Type::Error), - } + let Some(typ) = self.lookup_type_or_error(path) else { + return (HirExpression::Error, Type::Error); }; - self.elaborate_constructor_with_type(typ, constructor.fields, span, Some(last_segment)) + self.elaborate_constructor_with_type(typ, constructor.fields, location, Some(last_segment)) } fn elaborate_constructor_with_type( &mut self, typ: Type, fields: Vec<(Ident, Expression)>, - span: Span, + location: Location, last_segment: Option, ) -> (HirExpression, Type) { let typ = typ.follow_bindings_shallow(); @@ -735,7 +775,7 @@ impl<'context> Elaborator<'context> { typ => { self.push_err(ResolverError::NonStructUsedInConstructor { typ: typ.to_string(), - span, + location, }); return (HirExpression::Error, Type::Error); } @@ -745,18 +785,18 @@ impl<'context> Elaborator<'context> { // `last_segment` is optional if this constructor was resolved from a quoted type let mut generics = generics.clone(); let mut is_self_type = false; - let mut constructor_type_span = span; + let mut constructor_type_location = location; if let Some(last_segment) = last_segment { - let turbofish_span = last_segment.turbofish_span(); + let turbofish_location = last_segment.turbofish_location(); is_self_type = last_segment.ident.is_self_type_name(); - constructor_type_span = last_segment.ident.span(); + constructor_type_location = last_segment.ident.location(); generics = self.resolve_struct_turbofish_generics( &r#type.borrow(), generics, last_segment.generics, - turbofish_span, + turbofish_location, ); } @@ -767,8 +807,12 @@ impl<'context> Elaborator<'context> { .get_fields_with_visibility(&generics) .expect("This type should already be validated to be a struct"); - let fields = - self.resolve_constructor_expr_fields(struct_type.clone(), field_types, fields, span); + let fields = self.resolve_constructor_expr_fields( + struct_type.clone(), + field_types, + fields, + location, + ); let expr = HirExpression::Constructor(HirConstructorExpression { fields, r#type: struct_type.clone(), @@ -776,8 +820,7 @@ impl<'context> Elaborator<'context> { }); let struct_id = struct_type.borrow().id; - let reference_location = Location::new(constructor_type_span, self.file); - self.interner.add_type_reference(struct_id, reference_location, is_self_type); + self.interner.add_type_reference(struct_id, constructor_type_location, is_self_type); (expr, Type::DataType(struct_type, generics)) } @@ -796,7 +839,7 @@ impl<'context> Elaborator<'context> { struct_type: Shared, field_types: Vec<(String, ItemVisibility, Type)>, fields: Vec<(Ident, Expression)>, - span: Span, + location: Location, ) -> Vec<(Ident, ExprId)> { let mut ret = Vec::with_capacity(fields.len()); let mut seen_fields = HashSet::default(); @@ -815,20 +858,24 @@ impl<'context> Elaborator<'context> { let expected_type = expected_field_with_index.map(|(_, (_, _, typ))| typ).unwrap_or(&Type::Error); - let field_span = field.span; + let field_location = field.location; let (resolved, field_type) = self.elaborate_expression(field); if unseen_fields.contains(&field_name) { unseen_fields.remove(&field_name); seen_fields.insert(field_name.clone()); - self.unify_with_coercions(&field_type, expected_type, resolved, field_span, || { - TypeCheckError::TypeMismatch { + self.unify_with_coercions( + &field_type, + expected_type, + resolved, + field_location, + || TypeCheckError::TypeMismatch { expected_typ: expected_type.to_string(), expr_typ: field_type.to_string(), - expr_span: field_span, - } - }); + expr_location: field_location, + }, + ); } else if seen_fields.contains(&field_name) { // duplicate field self.push_err(ResolverError::DuplicateField { field: field_name.clone() }); @@ -842,20 +889,16 @@ impl<'context> Elaborator<'context> { if let Some((index, visibility)) = expected_index_and_visibility { let struct_type = struct_type.borrow(); - let field_span = field_name.span(); + let field_location = field_name.location(); let field_name = &field_name.0.contents; self.check_struct_field_visibility( &struct_type, field_name, *visibility, - field_span, + field_location, ); - self.interner.add_struct_member_reference( - struct_type.id, - index, - Location::new(field_span, self.file), - ); + self.interner.add_struct_member_reference(struct_type.id, index, field_location); } ret.push((field_name, resolved)); @@ -863,7 +906,7 @@ impl<'context> Elaborator<'context> { if !unseen_fields.is_empty() { self.push_err(ResolverError::MissingFields { - span, + location, missing_fields: unseen_fields.into_iter().map(|field| field.to_string()).collect(), struct_definition: struct_type.borrow().name.clone(), }); @@ -875,39 +918,44 @@ impl<'context> Elaborator<'context> { fn elaborate_member_access( &mut self, access: MemberAccessExpression, - span: Span, + location: Location, ) -> (ExprId, Type) { let (lhs, lhs_type) = self.elaborate_expression(access.lhs); let rhs = access.rhs; - let rhs_span = rhs.span(); + let rhs_location = rhs.location(); // `is_offset` is only used when lhs is a reference and we want to return a reference to rhs let access = HirMemberAccess { lhs, rhs, is_offset: false }; - let expr_id = self.intern_expr(HirExpression::MemberAccess(access.clone()), span); - let typ = self.type_check_member_access(access, expr_id, lhs_type, rhs_span); + let expr_id = self.intern_expr(HirExpression::MemberAccess(access.clone()), location); + let typ = self.type_check_member_access(access, expr_id, lhs_type, rhs_location); self.interner.push_expr_type(expr_id, typ.clone()); (expr_id, typ) } - pub fn intern_expr(&mut self, expr: HirExpression, span: Span) -> ExprId { + pub fn intern_expr(&mut self, expr: HirExpression, location: Location) -> ExprId { let id = self.interner.push_expr(expr); - self.interner.push_expr_location(id, span, self.file); + self.interner.push_expr_location(id, location); id } - fn elaborate_cast(&mut self, cast: CastExpression, span: Span) -> (HirExpression, Type) { + fn elaborate_cast( + &mut self, + cast: CastExpression, + location: Location, + ) -> (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, &lhs_type, &r#type, span); + let result = self.check_cast(&lhs, &lhs_type, &r#type, location); let expr = HirExpression::Cast(HirCastExpression { lhs, r#type }); (expr, result) } - fn elaborate_infix(&mut self, infix: InfixExpression, span: Span) -> (ExprId, Type) { + fn elaborate_infix(&mut self, infix: InfixExpression, location: Location) -> (ExprId, Type) { let (lhs, lhs_type) = self.elaborate_expression(infix.lhs); let (rhs, rhs_type) = self.elaborate_expression(infix.rhs); let trait_id = self.interner.get_operator_trait_method(infix.operator.contents); - let operator = HirBinaryOp::new(infix.operator, self.file); + let file = infix.operator.location().file; + let operator = HirBinaryOp::new(infix.operator, file); let expr = HirExpression::Infix(HirInfixExpression { lhs, operator, @@ -916,11 +964,16 @@ impl<'context> Elaborator<'context> { }); let expr_id = self.interner.push_expr(expr); - self.interner.push_expr_location(expr_id, span, self.file); + self.interner.push_expr_location(expr_id, location); - let result = self.infix_operand_type_rules(&lhs_type, &operator, &rhs_type, span); - let typ = - self.handle_operand_type_rules_result(result, &lhs_type, Some(trait_id), expr_id, span); + let result = self.infix_operand_type_rules(&lhs_type, &operator, &rhs_type, location); + let typ = self.handle_operand_type_rules_result( + result, + &lhs_type, + Some(trait_id), + expr_id, + location, + ); self.interner.push_expr_type(expr_id, typ.clone()); (expr_id, typ) @@ -932,7 +985,7 @@ impl<'context> Elaborator<'context> { operand_type: &Type, trait_id: Option, expr_id: ExprId, - span: Span, + location: Location, ) -> Type { match result { Ok((typ, use_impl)) => { @@ -948,14 +1001,14 @@ impl<'context> Elaborator<'context> { trait_bound: ResolvedTraitBound { trait_id: trait_id.trait_id, trait_generics: TraitGenerics::default(), - span, + location, }, }; self.push_trait_constraint( constraint, expr_id, true, // this constraint should lead to choosing a trait impl ); - self.type_check_operator_method(expr_id, trait_id, operand_type, span); + self.type_check_operator_method(expr_id, trait_id, operand_type, location); } typ } @@ -971,8 +1024,8 @@ impl<'context> Elaborator<'context> { if_expr: IfExpression, target_type: Option<&Type>, ) -> (HirExpression, Type) { - let expr_span = if_expr.condition.type_span(); - let consequence_span = if_expr.consequence.type_span(); + let expr_location = if_expr.condition.type_location(); + let consequence_location = if_expr.consequence.type_location(); let (condition, cond_type) = self.elaborate_expression(if_expr.condition); let (consequence, mut ret_type) = self.elaborate_expression_with_target_type(if_expr.consequence, target_type); @@ -980,23 +1033,24 @@ impl<'context> Elaborator<'context> { self.unify(&cond_type, &Type::Bool, || TypeCheckError::TypeMismatch { expected_typ: Type::Bool.to_string(), expr_typ: cond_type.to_string(), - expr_span, + expr_location, }); - let (alternative, else_type, error_span) = if let Some(alternative) = if_expr.alternative { - let alternative_span = alternative.type_span(); - let (else_, else_type) = - self.elaborate_expression_with_target_type(alternative, target_type); - (Some(else_), else_type, alternative_span) - } else { - (None, Type::Unit, consequence_span) - }; + let (alternative, else_type, error_location) = + if let Some(alternative) = if_expr.alternative { + let alternative_location = alternative.type_location(); + let (else_, else_type) = + self.elaborate_expression_with_target_type(alternative, target_type); + (Some(else_), else_type, alternative_location) + } else { + (None, Type::Unit, consequence_location) + }; self.unify(&ret_type, &else_type, || { let err = TypeCheckError::TypeMismatch { expected_typ: ret_type.to_string(), expr_typ: else_type.to_string(), - expr_span: error_span, + expr_location: error_location, }; let context = if ret_type == Type::Unit { @@ -1021,19 +1075,32 @@ impl<'context> Elaborator<'context> { fn elaborate_match( &mut self, match_expr: MatchExpression, - span: Span, + location: Location, ) -> (HirExpression, Type) { + self.use_unstable_feature(super::UnstableFeature::Enums, location); + + let expr_location = match_expr.expression.location; let (expression, typ) = self.elaborate_expression(match_expr.expression); - let (let_, variable) = self.wrap_in_let(expression, typ); + let (let_, variable) = self.wrap_in_let(expression, typ.clone()); + + let (errored, (rows, result_type)) = + self.errors_occurred_in(|this| this.elaborate_match_rules(variable, match_expr.rules)); + + // Avoid calling `elaborate_match_rows` if there were errors while constructing + // the match rows - it'll just lead to extra errors like `unreachable pattern` + // warnings on branches which previously had type errors. + let tree = HirExpression::Match(if !errored { + self.elaborate_match_rows(rows, &typ, expr_location) + } else { + HirMatch::Failure { missing_case: false } + }); - let (rows, result_type) = self.elaborate_match_rules(variable, match_expr.rules); - let tree = HirExpression::Match(self.elaborate_match_rows(rows)); let tree = self.interner.push_expr(tree); self.interner.push_expr_type(tree, result_type.clone()); - self.interner.push_expr_location(tree, span, self.file); + self.interner.push_expr_location(tree, location); let tree = self.interner.push_stmt(HirStatement::Expression(tree)); - self.interner.push_stmt_location(tree, span, self.file); + self.interner.push_stmt_location(tree, location); let block = HirExpression::Block(HirBlockExpression { statements: vec![let_, tree] }); (block, result_type) @@ -1049,7 +1116,7 @@ impl<'context> Elaborator<'context> { let pattern = HirPattern::Identifier(HirIdent::non_trait_method(variable, location)); let let_ = HirStatement::Let(HirLetStatement::basic(pattern, typ, expr_id)); let let_ = self.interner.push_stmt(let_); - self.interner.push_stmt_location(let_, location.span, location.file); + self.interner.push_stmt_location(let_, location); (let_, variable) } @@ -1121,7 +1188,7 @@ impl<'context> Elaborator<'context> { }); let return_type = self.resolve_inferred_type(lambda.return_type); - let body_span = lambda.body.span; + let body_location = lambda.body.location; let (body, body_type) = self.elaborate_expression(lambda.body); let lambda_context = self.lambda_stack.pop().unwrap(); @@ -1130,7 +1197,7 @@ impl<'context> Elaborator<'context> { self.unify(&body_type, &return_type, || TypeCheckError::TypeMismatch { expected_typ: return_type.to_string(), expr_typ: body_type.to_string(), - expr_span: body_span, + expr_location: body_location, }); let captured_vars = vecmap(&lambda_context.captures, |capture| { @@ -1145,13 +1212,13 @@ impl<'context> Elaborator<'context> { (expr, Type::Function(arg_types, Box::new(body_type), Box::new(env_type), false)) } - fn elaborate_quote(&mut self, mut tokens: Tokens, span: Span) -> (HirExpression, Type) { + fn elaborate_quote(&mut self, mut tokens: Tokens, location: Location) -> (HirExpression, Type) { tokens = self.find_unquoted_exprs_tokens(tokens); if self.in_comptime_context() { (HirExpression::Quote(tokens), Type::Quoted(QuotedType::Quoted)) } else { - self.push_err(ResolverError::QuoteInRuntimeCode { span }); + self.push_err(ResolverError::QuoteInRuntimeCode { location }); (HirExpression::Error, Type::Quoted(QuotedType::Quoted)) } } @@ -1159,7 +1226,7 @@ impl<'context> Elaborator<'context> { fn elaborate_comptime_block( &mut self, block: BlockExpression, - span: Span, + location: Location, target_type: Option<&Type>, ) -> (ExprId, Type) { let (block, _typ) = self.elaborate_in_comptime_context(|this| { @@ -1168,11 +1235,11 @@ impl<'context> Elaborator<'context> { let mut interpreter = self.setup_interpreter(); let value = interpreter.evaluate_block(block); - let (id, typ) = self.inline_comptime_value(value, span); + let (id, typ) = self.inline_comptime_value(value, location); let location = self.interner.id_location(id); self.debug_comptime(location, |interner| { - interner.expression(&id).to_display_ast(interner, location.span).kind + interner.expression(&id).to_display_ast(interner, location).kind }); (id, typ) @@ -1181,12 +1248,13 @@ impl<'context> Elaborator<'context> { pub fn inline_comptime_value( &mut self, value: Result, - span: Span, + location: Location, ) -> (ExprId, Type) { let make_error = |this: &mut Self, error: InterpreterError| { - this.errors.push(error.into_compilation_error_pair()); + let error: CompilationError = error.into(); + this.push_err(error); let error = this.interner.push_expr(HirExpression::Error); - this.interner.push_expr_location(error, span, this.file); + this.interner.push_expr_location(error, location); (error, Type::Error) }; @@ -1195,7 +1263,6 @@ impl<'context> Elaborator<'context> { Err(error) => return make_error(self, error), }; - let location = Location::new(span, self.file); match value.into_expression(self, location) { Ok(new_expr) => { // At this point the Expression was already elaborated and we got a Value. @@ -1225,17 +1292,17 @@ impl<'context> Elaborator<'context> { if meta.is_comptime { Ok(Some(function)) } else { - Err(ResolverError::MacroIsNotComptime { span: location.span }) + Err(ResolverError::MacroIsNotComptime { location }) } } else { - Err(ResolverError::InvalidSyntaxInMacroCall { span: location.span }) + Err(ResolverError::InvalidSyntaxInMacroCall { location }) } } else { // Assume a name resolution error has already been issued Ok(None) } } - _ => Err(ResolverError::InvalidSyntaxInMacroCall { span: location.span }), + _ => Err(ResolverError::InvalidSyntaxInMacroCall { location }), } } @@ -1249,7 +1316,7 @@ impl<'context> Elaborator<'context> { return_type: Type, ) -> Option<(HirExpression, Type)> { self.unify(&return_type, &Type::Quoted(QuotedType::Quoted), || { - TypeCheckError::MacroReturningNonExpr { typ: return_type.clone(), span: location.span } + TypeCheckError::MacroReturningNonExpr { typ: return_type.clone(), location } }); let function = match self.try_get_comptime_function(func, location) { @@ -1260,7 +1327,6 @@ impl<'context> Elaborator<'context> { } }; - let file = self.file; let mut interpreter = self.setup_interpreter(); let mut comptime_args = Vec::new(); let mut errors = Vec::new(); @@ -1271,7 +1337,7 @@ impl<'context> Elaborator<'context> { let location = interpreter.elaborator.interner.expr_location(&argument); comptime_args.push((arg, location)); } - Err(error) => errors.push((error.into(), file)), + Err(error) => errors.push(error.into()), } } @@ -1283,7 +1349,58 @@ impl<'context> Elaborator<'context> { return None; } - let (expr_id, typ) = self.inline_comptime_value(result, location.span); + let (expr_id, typ) = self.inline_comptime_value(result, location); Some((self.interner.expression(&expr_id), typ)) } + + fn elaborate_as_trait_path(&mut self, path: AsTraitPath) -> (ExprId, Type) { + let location = path.typ.location.merge(path.trait_path.location); + + let constraint = UnresolvedTraitConstraint { + typ: path.typ, + trait_bound: TraitBound { + trait_path: path.trait_path, + trait_id: None, + trait_generics: path.trait_generics, + }, + }; + + let typ = self.resolve_type(constraint.typ.clone()); + let Some(trait_bound) = self.resolve_trait_bound(&constraint.trait_bound) else { + // resolve_trait_bound only returns None if it has already issued an error, so don't + // issue another here. + let error = self.interner.push_expr_full(HirExpression::Error, location, Type::Error); + return (error, Type::Error); + }; + + let constraint = TraitConstraint { typ, trait_bound }; + + let the_trait = self.interner.get_trait(constraint.trait_bound.trait_id); + let Some(method) = the_trait.find_method(&path.impl_item.0.contents) else { + let trait_name = the_trait.name.to_string(); + let method_name = path.impl_item.to_string(); + let location = path.impl_item.location(); + self.push_err(ResolverError::NoSuchMethodInTrait { trait_name, method_name, location }); + let error = self.interner.push_expr_full(HirExpression::Error, location, Type::Error); + return (error, Type::Error); + }; + + let trait_method = + TraitMethod { method_id: method, constraint: constraint.clone(), assumed: true }; + + let definition_id = self.interner.trait_method_id(trait_method.method_id); + + let ident = HirIdent { + location: path.impl_item.location(), + id: definition_id, + impl_kind: ImplKind::TraitMethod(trait_method), + }; + + let id = self.interner.push_expr(HirExpression::Ident(ident.clone(), None)); + self.interner.push_expr_location(id, location); + + let typ = self.type_check_variable(ident, id, None); + self.interner.push_expr_type(id, typ.clone()); + (id, typ) + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/lints.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/lints.rs index 3a5844ab21d2..f4c6c7919960 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/lints.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/lints.rs @@ -1,4 +1,5 @@ use crate::{ + Type, ast::{Ident, NoirFunction, Signedness, UnaryOp, Visibility}, graph::CrateId, hir::{ @@ -13,10 +14,9 @@ use crate::{ node_interner::{ DefinitionId, DefinitionKind, ExprId, FuncId, FunctionModifiers, NodeInterner, }, - Type, }; -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Located, Location}; pub(super) fn deprecated_function(interner: &NodeInterner, expr: ExprId) -> Option { let HirExpression::Ident(HirIdent { location, id, impl_kind: _ }, _) = @@ -34,7 +34,7 @@ pub(super) fn deprecated_function(interner: &NodeInterner, expr: ExprId) -> Opti attributes.get_deprecated_note().map(|note| TypeCheckError::CallDeprecated { name: interner.definition_name(id).to_string(), note, - span: location.span, + location, }) } @@ -67,7 +67,7 @@ pub(super) fn low_level_function_outside_stdlib( crate_id: CrateId, ) -> Option { let is_low_level_function = - modifiers.attributes.function().map_or(false, |func| func.is_low_level()); + modifiers.attributes.function().is_some_and(|func| func.is_low_level()); if !crate_id.is_stdlib() && is_low_level_function { let ident = func_meta_name_ident(func, modifiers); Some(ResolverError::LowLevelFunctionOutsideOfStdlib { ident }) @@ -81,7 +81,7 @@ pub(super) fn oracle_not_marked_unconstrained( func: &FuncMeta, modifiers: &FunctionModifiers, ) -> Option { - let is_oracle_function = modifiers.attributes.function().map_or(false, |func| func.is_oracle()); + let is_oracle_function = modifiers.attributes.function().is_some_and(|func| func.is_oracle()); if is_oracle_function && !modifiers.is_unconstrained { let ident = func_meta_name_ident(func, modifiers); Some(ResolverError::OracleMarkedAsConstrained { ident }) @@ -97,16 +97,16 @@ pub(super) fn oracle_called_from_constrained_function( interner: &NodeInterner, called_func: &FuncId, calling_from_constrained_runtime: bool, - span: Span, + location: Location, ) -> Option { if !calling_from_constrained_runtime { return None; } let function_attributes = interner.function_attributes(called_func); - let is_oracle_call = function_attributes.function().map_or(false, |func| func.is_oracle()); + let is_oracle_call = function_attributes.function().is_some_and(|func| func.is_oracle()); if is_oracle_call { - Some(ResolverError::UnconstrainedOracleReturnToConstrained { span }) + Some(ResolverError::UnconstrainedOracleReturnToConstrained { location }) } else { None } @@ -127,13 +127,13 @@ pub(super) fn missing_pub(func: &FuncMeta, modifiers: &FunctionModifiers) -> Opt /// Check that we are not passing a mutable reference from a constrained runtime to an unconstrained runtime. pub(super) fn unconstrained_function_args( - function_args: &[(Type, ExprId, Span)], + function_args: &[(Type, ExprId, Location)], ) -> Vec { function_args .iter() - .filter_map(|(typ, _, span)| { + .filter_map(|(typ, _, location)| { if !typ.is_valid_for_unconstrained_boundary() { - Some(TypeCheckError::ConstrainedReferenceToUnconstrained { span: *span }) + Some(TypeCheckError::ConstrainedReferenceToUnconstrained { location: *location }) } else { None } @@ -144,12 +144,12 @@ pub(super) fn unconstrained_function_args( /// Check that we are not passing a slice from an unconstrained runtime to a constrained runtime. pub(super) fn unconstrained_function_return( return_type: &Type, - span: Span, + location: Location, ) -> Option { if return_type.contains_slice() { - Some(TypeCheckError::UnconstrainedSliceReturnToConstrained { span }) + Some(TypeCheckError::UnconstrainedSliceReturnToConstrained { location }) } else if !return_type.is_valid_for_unconstrained_boundary() { - Some(TypeCheckError::UnconstrainedReferenceToConstrained { span }) + Some(TypeCheckError::UnconstrainedReferenceToConstrained { location }) } else { None } @@ -197,20 +197,20 @@ pub(crate) fn overflowing_int( annotated_type: &Type, ) -> Vec { let expr = interner.expression(rhs_expr); - let span = interner.expr_span(rhs_expr); + let location = interner.expr_location(rhs_expr); let mut errors = Vec::with_capacity(2); match expr { - HirExpression::Literal(HirLiteral::Integer(value, negative)) => match annotated_type { - Type::Integer(Signedness::Unsigned, bit_count) => { - let bit_count: u32 = (*bit_count).into(); - let max = 2u128.pow(bit_count) - 1; - if value > max.into() || negative { + HirExpression::Literal(HirLiteral::Integer(value)) => match annotated_type { + Type::Integer(Signedness::Unsigned, bit_size) => { + let bit_size: u32 = (*bit_size).into(); + let max = if bit_size == 128 { u128::MAX } else { 2u128.pow(bit_size) - 1 }; + if value.field > max.into() || value.is_negative { errors.push(TypeCheckError::OverflowingAssignment { - expr: if negative { -value } else { value }, + expr: value, ty: annotated_type.clone(), range: format!("0..={}", max), - span, + location, }); } } @@ -218,12 +218,14 @@ pub(crate) fn overflowing_int( let bit_count: u32 = (*bit_count).into(); let min = 2u128.pow(bit_count - 1); let max = 2u128.pow(bit_count - 1) - 1; - if (negative && value > min.into()) || (!negative && value > max.into()) { + if (value.is_negative && value.field > min.into()) + || (!value.is_negative && value.field > max.into()) + { errors.push(TypeCheckError::OverflowingAssignment { - expr: if negative { -value } else { value }, + expr: value, ty: annotated_type.clone(), range: format!("-{}..={}", min, max), - span, + location, }); } } @@ -234,7 +236,7 @@ pub(crate) fn overflowing_int( if expr.operator == UnaryOp::Minus && annotated_type.is_unsigned() { errors.push(TypeCheckError::InvalidUnaryOp { kind: annotated_type.to_string(), - span, + location, }); } } @@ -249,7 +251,7 @@ pub(crate) fn overflowing_int( } fn func_meta_name_ident(func: &FuncMeta, modifiers: &FunctionModifiers) -> Ident { - Ident(Spanned::from(func.name.location.span, modifiers.name.clone())) + Ident(Located::from(func.name.location, modifiers.name.clone())) } /// Check that a recursive function *can* return without endlessly calling itself. @@ -257,13 +259,13 @@ pub(crate) fn unbounded_recursion<'a>( interner: &'a NodeInterner, func_id: FuncId, func_name: impl FnOnce() -> &'a str, - func_span: Span, + func_location: Location, body_id: ExprId, ) -> Option { if !can_return_without_recursing(interner, func_id, body_id) { Some(ResolverError::UnconditionalRecursion { name: func_name().to_string(), - span: func_span, + location: func_location, }) } else { None @@ -297,11 +299,7 @@ fn can_return_without_recursing(interner: &NodeInterner, func_id: FuncId, expr_i return true; } let definition = interner.definition(ident.id); - if let DefinitionKind::Function(id) = definition.kind { - func_id != id - } else { - true - } + if let DefinitionKind::Function(id) = definition.kind { func_id != id } else { true } } HirExpression::Block(b) => check_block(b), HirExpression::Prefix(e) => check(e.rhs), @@ -340,11 +338,11 @@ fn can_return_without_recursing_match( match match_expr { HirMatch::Success(expr) => check(*expr), - HirMatch::Failure => true, + HirMatch::Failure { .. } => true, HirMatch::Guard { cond: _, body, otherwise } => check(*body) && check_match(otherwise), HirMatch::Switch(_, cases, otherwise) => { cases.iter().all(|case| check_match(&case.body)) - && otherwise.as_ref().map_or(true, |case| check_match(case)) + && otherwise.as_ref().is_none_or(|case| check_match(case)) } } } 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 d007bee0d8d3..23a9ec5246e8 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs @@ -4,6 +4,15 @@ use std::{ }; use crate::{ + DataType, StructField, TypeBindings, + ast::{ItemVisibility, UnresolvedType}, + graph::CrateGraph, + hir_def::traits::ResolvedTraitBound, + node_interner::GlobalValue, + usage_tracker::UsageTracker, +}; +use crate::{ + EnumVariant, Shared, Type, TypeVariable, ast::{ BlockExpression, FunctionKind, GenericTypeArgs, Ident, NoirFunction, NoirStruct, Param, Path, Pattern, TraitBound, UnresolvedGeneric, UnresolvedGenerics, @@ -11,19 +20,20 @@ use crate::{ }, graph::CrateId, hir::{ + Context, + comptime::ComptimeError, def_collector::{ dc_crate::{ - filter_literal_globals, CollectedItems, CompilationError, ImplMap, UnresolvedEnum, - UnresolvedFunctions, UnresolvedGlobal, UnresolvedStruct, UnresolvedTraitImpl, - UnresolvedTypeAlias, + CollectedItems, CompilationError, ImplMap, UnresolvedEnum, UnresolvedFunctions, + UnresolvedGlobal, UnresolvedStruct, UnresolvedTraitImpl, UnresolvedTypeAlias, + filter_literal_globals, }, errors::DefCollectorErrorKind, }, - def_map::{DefMaps, LocalModuleId, ModuleData, ModuleId, MAIN_FUNCTION}, + def_map::{DefMaps, LocalModuleId, MAIN_FUNCTION, ModuleData, ModuleId}, resolution::errors::ResolverError, scope::ScopeForest as GenericScopeForest, - type_check::{generics::TraitGenerics, TypeCheckError}, - Context, + type_check::{TypeCheckError, generics::TraitGenerics}, }, hir_def::{ expr::{HirCapturedVar, HirIdent}, @@ -35,22 +45,15 @@ use crate::{ DefinitionKind, DependencyId, ExprId, FuncId, FunctionModifiers, GlobalId, NodeInterner, ReferenceId, TraitId, TraitImplId, TypeAliasId, TypeId, }, + parser::{ParserError, ParserErrorReason}, token::SecondaryAttribute, - EnumVariant, Shared, Type, TypeVariable, -}; -use crate::{ - ast::{ItemVisibility, UnresolvedType}, - graph::CrateGraph, - hir_def::traits::ResolvedTraitBound, - node_interner::GlobalValue, - usage_tracker::UsageTracker, - DataType, StructField, TypeBindings, }; mod comptime; mod enums; mod expressions; mod lints; +mod options; mod path_resolution; mod patterns; mod scope; @@ -60,9 +63,10 @@ mod traits; pub mod types; mod unquote; -use fm::FileId; use iter_extended::vecmap; -use noirc_errors::{Location, Span, Spanned}; +use noirc_errors::{Located, Location}; +pub(crate) use options::ElaboratorOptions; +pub use options::{FrontendOptions, UnstableFeature}; pub use path_resolution::Turbofish; use path_resolution::{PathResolution, PathResolutionItem}; use types::bind_ordered_generics; @@ -104,15 +108,13 @@ pub struct Loop { pub struct Elaborator<'context> { scopes: ScopeForest, - pub(crate) errors: Vec<(CompilationError, FileId)>, + pub(crate) errors: Vec, pub(crate) interner: &'context mut NodeInterner, pub(crate) def_maps: &'context mut DefMaps, pub(crate) usage_tracker: &'context mut UsageTracker, pub(crate) crate_graph: &'context CrateGraph, - pub(crate) file: FileId, - unsafe_block_status: UnsafeBlockStatus, current_loop: Option, @@ -178,9 +180,6 @@ pub struct Elaborator<'context> { crate_id: CrateId, - /// The scope of --debug-comptime, or None if unset - debug_comptime_in_file: Option, - /// These are the globals that have yet to be elaborated. /// This map is used to lazily evaluate these globals if they're encountered before /// they are elaborated (e.g. in a function's type or another global's RHS). @@ -194,8 +193,34 @@ pub struct Elaborator<'context> { /// that comptime value and any visibility errors were already reported. silence_field_visibility_errors: usize, - /// Use pedantic ACVM solving - pedantic_solving: bool, + /// Options from the nargo cli + options: ElaboratorOptions<'context>, + + /// Sometimes items are elaborated because a function attribute ran and generated items. + /// The Elaborator keeps track of these reasons so that when an error is produced it will + /// be wrapped in another error that will include this reason. + pub(crate) elaborate_reasons: im::Vector<(ElaborateReason, Location)>, +} + +#[derive(Copy, Clone)] +pub enum ElaborateReason { + /// A function attribute generated an item that's being elaborated. + RunningAttribute, + /// Evaluating `Module::add_item` + AddingItemToModule, +} + +impl ElaborateReason { + fn to_macro_error(self, error: CompilationError, location: Location) -> ComptimeError { + match self { + ElaborateReason::RunningAttribute => { + ComptimeError::ErrorRunningAttribute { error: Box::new(error), location } + } + ElaborateReason::AddingItemToModule => { + ComptimeError::ErrorAddingItemToModule { error: Box::new(error), location } + } + } + } } #[derive(Default)] @@ -224,9 +249,9 @@ impl<'context> Elaborator<'context> { usage_tracker: &'context mut UsageTracker, crate_graph: &'context CrateGraph, crate_id: CrateId, - debug_comptime_in_file: Option, interpreter_call_stack: im::Vector, - pedantic_solving: bool, + options: ElaboratorOptions<'context>, + elaborate_reasons: im::Vector<(ElaborateReason, Location)>, ) -> Self { Self { scopes: ScopeForest::default(), @@ -235,7 +260,6 @@ impl<'context> Elaborator<'context> { def_maps, usage_tracker, crate_graph, - file: FileId::dummy(), unsafe_block_status: UnsafeBlockStatus::NotInUnsafeBlock, current_loop: None, generics: Vec::new(), @@ -248,21 +272,20 @@ impl<'context> Elaborator<'context> { trait_bounds: Vec::new(), function_context: vec![FunctionContext::default()], current_trait_impl: None, - debug_comptime_in_file, unresolved_globals: BTreeMap::new(), current_trait: None, interpreter_call_stack, in_comptime_context: false, silence_field_visibility_errors: 0, - pedantic_solving, + options, + elaborate_reasons, } } pub fn from_context( context: &'context mut Context, crate_id: CrateId, - debug_comptime_in_file: Option, - pedantic_solving: bool, + options: ElaboratorOptions<'context>, ) -> Self { Self::new( &mut context.def_interner, @@ -270,9 +293,9 @@ impl<'context> Elaborator<'context> { &mut context.usage_tracker, &context.crate_graph, crate_id, - debug_comptime_in_file, im::Vector::new(), - pedantic_solving, + options, + im::Vector::new(), ) } @@ -280,28 +303,18 @@ impl<'context> Elaborator<'context> { context: &'context mut Context, crate_id: CrateId, items: CollectedItems, - debug_comptime_in_file: Option, - pedantic_solving: bool, - ) -> Vec<(CompilationError, FileId)> { - Self::elaborate_and_return_self( - context, - crate_id, - items, - debug_comptime_in_file, - pedantic_solving, - ) - .errors + options: ElaboratorOptions<'context>, + ) -> Vec { + Self::elaborate_and_return_self(context, crate_id, items, options).errors } pub fn elaborate_and_return_self( context: &'context mut Context, crate_id: CrateId, items: CollectedItems, - debug_comptime_in_file: Option, - pedantic_solving: bool, + options: ElaboratorOptions<'context>, ) -> Self { - let mut this = - Self::from_context(context, crate_id, debug_comptime_in_file, pedantic_solving); + let mut this = Self::from_context(context, crate_id, options); this.elaborate_items(items); this.check_and_pop_function_context(); this @@ -382,7 +395,12 @@ impl<'context> Elaborator<'context> { self.elaborate_trait_impl(trait_impl); } - self.errors.extend(self.interner.check_for_dependency_cycles()); + self.push_errors(self.interner.check_for_dependency_cycles()); + } + + /// True if we should use pedantic ACVM solving + pub fn pedantic_solving(&self) -> bool { + self.options.pedantic_solving } /// Runs `f` and if it modifies `self.generics`, `self.generics` is truncated @@ -409,7 +427,7 @@ impl<'context> Elaborator<'context> { if let Kind::Numeric(typ) = &generic.kind() { let definition = DefinitionKind::NumericGeneric(generic.type_var.clone(), typ.clone()); - let ident = Ident::new(generic.name.to_string(), generic.span); + let ident = Ident::new(generic.name.to_string(), generic.location); let hir_ident = self.add_variable_decl( ident, false, // mutable false, // allow_shadowing @@ -428,8 +446,8 @@ impl<'context> Elaborator<'context> { let func_meta = func_meta.expect("FuncMetas should be declared before a function is elaborated"); - let (kind, body, body_span) = match func_meta.take_body() { - FunctionBody::Unresolved(kind, body, span) => (kind, body, span), + let (kind, body, body_location) = match func_meta.take_body() { + FunctionBody::Unresolved(kind, body, location) => (kind, body, location), FunctionBody::Resolved => return, // Do not error for the still-resolving case. If there is a dependency cycle, // the dependency cycle check will find it later on. @@ -444,7 +462,6 @@ impl<'context> Elaborator<'context> { ); 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; @@ -460,8 +477,8 @@ impl<'context> Elaborator<'context> { // Check arg and return-value visibility of standalone functions. if self.should_check_function_visibility(&func_meta, &modifiers) { - let name = Ident(Spanned::from( - func_meta.name.location.span, + let name = Ident(Located::from( + func_meta.name.location, self.interner.definition_name(func_meta.name.id).to_string(), )); for (_, typ, _) in func_meta.parameters.iter() { @@ -469,14 +486,14 @@ impl<'context> Elaborator<'context> { &name, modifiers.visibility, typ, - name.span(), + name.location(), ); } self.check_type_is_not_more_private_then_item( &name, modifiers.visibility, func_meta.return_type(), - name.span(), + name.location(), ); } @@ -493,7 +510,7 @@ impl<'context> Elaborator<'context> { self.add_existing_variable_to_scope(name, parameter.clone(), warn_if_unused); } - self.add_trait_constraints_to_scope(&func_meta.trait_constraints, func_meta.location.span); + self.add_trait_constraints_to_scope(&func_meta.trait_constraints, func_meta.location); let (hir_func, body_type) = match kind { FunctionKind::Builtin @@ -503,7 +520,7 @@ impl<'context> Elaborator<'context> { FunctionKind::Normal => { let return_type = func_meta.return_type(); let (block, body_type) = self.elaborate_block(body, Some(return_type)); - let expr_id = self.intern_expr(block, body_span); + let expr_id = self.intern_expr(block, body_location); self.interner.push_expr_type(expr_id, body_type.clone()); (HirFunction::unchecked_from_expr(expr_id), body_type) } @@ -536,7 +553,7 @@ impl<'context> Elaborator<'context> { elaborator.interner, id, || elaborator.interner.definition_name(func_meta.name.id), - func_meta.name.location.span, + func_meta.name.location, hir_func.as_expr(), ) .map(Into::into) @@ -569,7 +586,7 @@ impl<'context> Elaborator<'context> { } for (mut constraint, expr_id, select_impl) in context.trait_constraints { - let span = self.interner.expr_span(&expr_id); + let location = self.interner.expr_location(&expr_id); if matches!(&constraint.typ, Type::MutableReference(_)) { let (_, dereferenced_typ) = @@ -584,7 +601,7 @@ impl<'context> Elaborator<'context> { &constraint.trait_bound.trait_generics.named, expr_id, select_impl, - span, + location, ); } } @@ -635,9 +652,9 @@ impl<'context> Elaborator<'context> { } }; - let span = generic.span(); + let location = generic.location(); let name_owned = name.as_ref().clone(); - let resolved_generic = ResolvedGeneric { name, type_var, span }; + let resolved_generic = ResolvedGeneric { name, type_var, location }; // Check for name collisions of this generic // Checking `is_error` here prevents DuplicateDefinition errors when @@ -647,8 +664,8 @@ impl<'context> Elaborator<'context> { if let Some(generic) = self.find_generic(&name_owned) { self.push_err(ResolverError::DuplicateDefinition { name: name_owned, - first_span: generic.span, - second_span: span, + first_location: generic.location, + second_location: location, }); } else { self.generics.push(resolved_generic.clone()); @@ -675,11 +692,11 @@ impl<'context> Elaborator<'context> { } // An already-resolved generic is only possible if it is the result of a // previous macro call being inserted into a generics list. - UnresolvedGeneric::Resolved(id, span) => { + UnresolvedGeneric::Resolved(id, location) => { match self.interner.get_quoted_type(*id).follow_bindings() { Type::NamedGeneric(type_variable, name) => Ok((type_variable.clone(), name)), other => Err(ResolverError::MacroResultInGenericsListNotAGeneric { - span: *span, + location: *location, typ: other.clone(), }), } @@ -715,8 +732,13 @@ impl<'context> Elaborator<'context> { } } - fn push_err(&mut self, error: impl Into) { - self.errors.push((error.into(), self.file)); + pub(crate) fn push_err(&mut self, error: impl Into) { + let error: CompilationError = error.into(); + self.errors.push(error); + } + + pub(crate) fn push_errors(&mut self, errors: impl IntoIterator) { + self.errors.extend(errors); } fn run_lint(&mut self, lint: impl Fn(&Elaborator) -> Option) { @@ -728,11 +750,7 @@ impl<'context> Elaborator<'context> { pub fn resolve_module_by_path(&mut self, path: Path) -> Option { match self.resolve_path(path.clone()) { Ok(PathResolution { item: PathResolutionItem::Module(module_id), errors }) => { - if errors.is_empty() { - Some(module_id) - } else { - None - } + if errors.is_empty() { Some(module_id) } else { None } } _ => None, } @@ -831,16 +849,16 @@ impl<'context> Elaborator<'context> { let kind = associated_type.type_var.kind(); let type_var = TypeVariable::unbound(new_generic_id, kind); - let span = bound.trait_path.span; + let location = bound.trait_path.location; let name = format!("<{object} as {trait_name}>::{}", associated_type.name); let name = Rc::new(name); let typ = Type::NamedGeneric(type_var.clone(), name.clone()); let typ = self.interner.push_quoted_type(typ); - let typ = UnresolvedTypeData::Resolved(typ).with_span(span); - let ident = Ident::new(associated_type.name.as_ref().clone(), span); + let typ = UnresolvedTypeData::Resolved(typ).with_location(location); + let ident = Ident::new(associated_type.name.as_ref().clone(), location); bound.trait_generics.named_args.push((ident, typ)); - added_generics.push(ResolvedGeneric { name, span, type_var }); + added_generics.push(ResolvedGeneric { name, location, type_var }); } } } @@ -860,7 +878,7 @@ impl<'context> Elaborator<'context> { let trait_bound = self.resolve_trait_bound(&constraint.trait_bound)?; self.add_trait_bound_to_scope( - constraint.trait_bound.trait_path.span, + constraint.trait_bound.trait_path.location, &typ, &trait_bound, trait_bound.trait_id, @@ -872,12 +890,13 @@ impl<'context> Elaborator<'context> { pub fn resolve_trait_bound(&mut self, bound: &TraitBound) -> Option { let the_trait = self.lookup_trait_or_error(bound.trait_path.clone())?; let trait_id = the_trait.id; - let span = bound.trait_path.span; + let location = bound.trait_path.location; - let (ordered, named) = self.resolve_type_args(bound.trait_generics.clone(), trait_id, span); + let (ordered, named) = + self.resolve_type_args(bound.trait_generics.clone(), trait_id, location); let trait_generics = TraitGenerics { ordered, named }; - Some(ResolvedTraitBound { trait_id, trait_generics, span }) + Some(ResolvedTraitBound { trait_id, trait_generics, location }) } /// Extract metadata from a NoirFunction @@ -901,7 +920,7 @@ impl<'context> Elaborator<'context> { self.scopes.start_function(); self.current_item = Some(DependencyId::Function(func_id)); - let location = Location::new(func.name_ident().span(), self.file); + let location = func.name_ident().location(); let id = self.interner.function_definition_id(func_id); let name_ident = HirIdent::non_trait_method(id, location); @@ -926,12 +945,12 @@ impl<'context> Elaborator<'context> { let mut parameter_types = Vec::new(); let mut parameter_idents = Vec::new(); - for Param { visibility, pattern, typ, span: _ } in func.parameters().iter().cloned() { + for Param { visibility, pattern, typ, location: _ } in func.parameters().iter().cloned() { self.run_lint(|_| { lints::unnecessary_pub_argument(func, visibility, is_pub_allowed).map(Into::into) }); - let type_span = typ.span; + let type_location = typ.location; let typ = match typ.typ { UnresolvedTypeData::TraitAsType(path, args) => { self.desugar_impl_trait_arg(path, args, &mut generics, &mut trait_constraints) @@ -944,7 +963,7 @@ impl<'context> Elaborator<'context> { &typ, is_entry_point, has_inline_attribute, - type_span, + type_location, ); if is_entry_point { @@ -1016,9 +1035,9 @@ impl<'context> Elaborator<'context> { has_inline_attribute, source_crate: self.crate_id, source_module: self.local_module, - function_body: FunctionBody::Unresolved(func.kind, body, func.def.span), + function_body: FunctionBody::Unresolved(func.kind, body, func.def.location), self_type: self.self_type.clone(), - source_file: self.file, + source_file: location.file, }; self.interner.push_fn_meta(meta, func_id); @@ -1105,12 +1124,12 @@ impl<'context> Elaborator<'context> { typ: &Type, is_entry_point: bool, has_inline_attribute: bool, - span: Span, + location: Location, ) { if (is_entry_point && !typ.is_valid_for_program_input()) || (has_inline_attribute && !typ.is_valid_non_inlined_function_input()) { - self.push_err(TypeCheckError::InvalidTypeForEntryPoint { span }); + self.push_err(TypeCheckError::InvalidTypeForEntryPoint { location }); } } @@ -1143,10 +1162,14 @@ impl<'context> Elaborator<'context> { } } - fn add_trait_constraints_to_scope(&mut self, constraints: &[TraitConstraint], span: Span) { + fn add_trait_constraints_to_scope( + &mut self, + constraints: &[TraitConstraint], + location: Location, + ) { for constraint in constraints { self.add_trait_bound_to_scope( - span, + location, &constraint.typ, &constraint.trait_bound, constraint.trait_bound.trait_id, @@ -1156,11 +1179,11 @@ impl<'context> Elaborator<'context> { // Also assume `self` implements the current trait if we are inside a trait definition if let Some(trait_id) = self.current_trait { let the_trait = self.interner.get_trait(trait_id); - let constraint = the_trait.as_constraint(the_trait.name.span()); + let constraint = the_trait.as_constraint(the_trait.name.location()); let self_type = self.self_type.clone().expect("Expected a self type if there's a current trait"); self.add_trait_bound_to_scope( - span, + location, &self_type, &constraint.trait_bound, constraint.trait_bound.trait_id, @@ -1182,7 +1205,7 @@ impl<'context> Elaborator<'context> { fn add_trait_bound_to_scope( &mut self, - span: Span, + location: Location, object: &Type, trait_bound: &ResolvedTraitBound, starting_trait_id: TraitId, @@ -1194,7 +1217,11 @@ impl<'context> Elaborator<'context> { if let Some(the_trait) = self.interner.try_get_trait(trait_id) { let trait_name = the_trait.name.to_string(); let typ = object.clone(); - self.push_err(TypeCheckError::UnneededTraitConstraint { trait_name, typ, span }); + self.push_err(TypeCheckError::UnneededTraitConstraint { + trait_name, + typ, + location, + }); } } @@ -1210,20 +1237,23 @@ impl<'context> Elaborator<'context> { let parent_trait_bound = self.instantiate_parent_trait_bound(trait_bound, &parent_trait_bound); - self.add_trait_bound_to_scope(span, object, &parent_trait_bound, starting_trait_id); + self.add_trait_bound_to_scope( + location, + object, + &parent_trait_bound, + starting_trait_id, + ); } } } - fn elaborate_impls(&mut self, impls: Vec<(UnresolvedGenerics, Span, UnresolvedFunctions)>) { + fn elaborate_impls(&mut self, impls: Vec<(UnresolvedGenerics, Location, UnresolvedFunctions)>) { for (_, _, functions) in impls { - self.file = functions.file_id; self.recover_generics(|this| this.elaborate_functions(functions)); } } fn elaborate_trait_impl(&mut self, trait_impl: UnresolvedTraitImpl) { - self.file = trait_impl.file_id; self.local_module = trait_impl.module_id; self.generics = trait_impl.resolved_generics.clone(); @@ -1234,10 +1264,14 @@ impl<'context> Elaborator<'context> { self.check_parent_traits_are_implemented(&trait_impl); self.remove_trait_impl_assumed_trait_implementations(trait_impl.impl_id); - for (module, function, _) in &trait_impl.methods.functions { + for (module, function, noir_function) in &trait_impl.methods.functions { self.local_module = *module; - let errors = check_trait_impl_method_matches_declaration(self.interner, *function); - self.errors.extend(errors.into_iter().map(|error| (error.into(), self.file))); + let errors = check_trait_impl_method_matches_declaration( + self.interner, + *function, + noir_function, + ); + self.push_errors(errors.into_iter().map(|error| error.into())); } self.elaborate_functions(trait_impl.methods); @@ -1293,7 +1327,6 @@ impl<'context> Elaborator<'context> { } let impl_trait = the_trait.name.to_string(); - let the_trait_file = the_trait.location.file; let mut bindings = TypeBindings::new(); bind_ordered_generics( @@ -1329,8 +1362,8 @@ impl<'context> Elaborator<'context> { impl_trait: impl_trait.clone(), missing_trait, type_missing_trait: trait_constraint_type.to_string(), - span: trait_impl.object_type.span, - missing_trait_location: Location::new(trait_bound.span, the_trait_file), + location: trait_impl.object_type.location, + missing_trait_location: trait_bound.location, }); } } @@ -1354,7 +1387,6 @@ impl<'context> Elaborator<'context> { } let impl_trait = the_trait.name.to_string(); - let the_trait_file = the_trait.location.file; let mut bindings = TypeBindings::new(); bind_ordered_generics( @@ -1396,8 +1428,8 @@ impl<'context> Elaborator<'context> { impl_trait: impl_trait.clone(), missing_trait, type_missing_trait: trait_impl.object_type.to_string(), - span: trait_impl.object_type.span, - missing_trait_location: Location::new(parent_trait_bound.span, the_trait_file), + location: trait_impl.object_type.location, + missing_trait_location: parent_trait_bound.location, }); } } @@ -1406,22 +1438,20 @@ impl<'context> Elaborator<'context> { fn collect_impls( &mut self, module: LocalModuleId, - impls: &mut [(UnresolvedGenerics, Span, UnresolvedFunctions)], + impls: &mut [(UnresolvedGenerics, Location, UnresolvedFunctions)], ) { self.local_module = module; - for (generics, span, unresolved) in impls { - self.file = unresolved.file_id; + for (generics, location, unresolved) in impls { let old_generic_count = self.generics.len(); self.add_generics(generics); - self.declare_methods_on_struct(None, unresolved, *span); + self.declare_methods_on_struct(None, unresolved, *location); self.generics.truncate(old_generic_count); } } fn collect_trait_impl(&mut self, trait_impl: &mut UnresolvedTraitImpl) { self.local_module = trait_impl.module_id; - self.file = trait_impl.file_id; self.current_trait_impl = trait_impl.impl_id; let self_type = trait_impl.methods.self_type.clone(); @@ -1429,11 +1459,12 @@ impl<'context> Elaborator<'context> { self_type.expect("Expected struct type to be set before collect_trait_impl"); self.self_type = Some(self_type.clone()); - let self_type_span = trait_impl.object_type.span; + let self_type_location = trait_impl.object_type.location; if matches!(self_type, Type::MutableReference(_)) { - let span = self_type_span; - self.push_err(DefCollectorErrorKind::MutableReferenceInTraitImpl { span }); + self.push_err(DefCollectorErrorKind::MutableReferenceInTraitImpl { + location: self_type_location, + }); } if let Some(trait_id) = trait_impl.trait_id { @@ -1444,8 +1475,8 @@ impl<'context> Elaborator<'context> { self.collect_trait_impl_methods(trait_id, trait_impl, &where_clause); - let span = trait_impl.object_type.span; - self.declare_methods_on_struct(Some(trait_id), &mut trait_impl.methods, span); + let location = trait_impl.object_type.location; + self.declare_methods_on_struct(Some(trait_id), &mut trait_impl.methods, location); let trait_visibility = self.interner.get_trait(trait_id).visibility; @@ -1468,12 +1499,12 @@ impl<'context> Elaborator<'context> { } else { typ.to_string() }; - Ident::new(name, trait_impl.r#trait.span) + Ident::new(name, trait_impl.r#trait.location) } _ => { // We don't error in this case because an error will be produced later on when // solving the trait impl trait type - Ident::new(trait_impl.r#trait.to_string(), trait_impl.r#trait.span) + Ident::new(trait_impl.r#trait.to_string(), trait_impl.r#trait.location) } }; @@ -1489,7 +1520,7 @@ impl<'context> Elaborator<'context> { let generics = vecmap(&self.generics, |generic| generic.type_var.clone()); - if let Err((prev_span, prev_file)) = self.interner.add_trait_implementation( + if let Err(prev_location) = self.interner.add_trait_implementation( self_type.clone(), trait_id, trait_impl.impl_id.expect("impl_id should be set in define_function_metas"), @@ -1498,14 +1529,9 @@ impl<'context> Elaborator<'context> { ) { self.push_err(DefCollectorErrorKind::OverlappingImpl { typ: self_type.clone(), - span: self_type_span, + location: self_type_location, + prev_location, }); - - // The 'previous impl defined here' note must be a separate error currently - // since it may be in a different file and all errors have the same file id. - self.file = prev_file; - self.push_err(DefCollectorErrorKind::OverlappingImplNote { span: prev_span }); - self.file = trait_impl.file_id; } } @@ -1529,7 +1555,7 @@ impl<'context> Elaborator<'context> { &mut self, trait_id: Option, functions: &mut UnresolvedFunctions, - span: Span, + location: Location, ) { let self_type = functions.self_type.as_ref(); let self_type = @@ -1543,7 +1569,7 @@ impl<'context> Elaborator<'context> { // `impl`s are only allowed on types defined within the current crate if trait_id.is_none() && struct_ref.id.krate() != self.crate_id { let type_name = struct_ref.name.to_string(); - self.push_err(DefCollectorErrorKind::ForeignImpl { span, type_name }); + self.push_err(DefCollectorErrorKind::ForeignImpl { location, type_name }); return; } @@ -1589,7 +1615,7 @@ impl<'context> Elaborator<'context> { self.declare_methods(self_type, &function_ids); } } else { - self.push_err(DefCollectorErrorKind::NonStructTypeInImpl { span }); + self.push_err(DefCollectorErrorKind::NonStructTypeInImpl { location }); } } } @@ -1601,10 +1627,12 @@ impl<'context> Elaborator<'context> { if let Some(first_fn) = self.interner.add_method(self_type, method_name.clone(), *method_id, None) { + let first_location = self.interner.function_ident(&first_fn).location(); + let second_location = self.interner.function_ident(method_id).location(); let error = ResolverError::DuplicateDefinition { name: method_name, - first_span: self.interner.function_ident(&first_fn).span(), - second_span: self.interner.function_ident(method_id).span(), + first_location, + second_location, }; self.push_err(error); } @@ -1612,19 +1640,18 @@ impl<'context> Elaborator<'context> { } fn define_type_alias(&mut self, alias_id: TypeAliasId, alias: UnresolvedTypeAlias) { - self.file = alias.file_id; self.local_module = alias.module_id; let name = &alias.type_alias_def.name; let visibility = alias.type_alias_def.visibility; - let span = alias.type_alias_def.typ.span; + let location = alias.type_alias_def.typ.location; let generics = self.add_generics(&alias.type_alias_def.generics); self.current_item = Some(DependencyId::Alias(alias_id)); let typ = self.resolve_type(alias.type_alias_def.typ); if visibility != ItemVisibility::Private { - self.check_type_is_not_more_private_then_item(name, visibility, &typ, span); + self.check_type_is_not_more_private_then_item(name, visibility, &typ, location); } self.interner.set_type_alias(alias_id, typ, generics); @@ -1671,7 +1698,7 @@ impl<'context> Elaborator<'context> { name: &Ident, visibility: ItemVisibility, typ: &Type, - span: Span, + location: Location, ) { match typ { Type::DataType(struct_type, generics) => { @@ -1687,19 +1714,21 @@ impl<'context> Elaborator<'context> { self.push_err(ResolverError::TypeIsMorePrivateThenItem { typ: struct_type.name.to_string(), item: name.to_string(), - span, + location, }); } } } for generic in generics { - self.check_type_is_not_more_private_then_item(name, visibility, generic, span); + self.check_type_is_not_more_private_then_item( + name, visibility, generic, location, + ); } } Type::Tuple(types) => { for typ in types { - self.check_type_is_not_more_private_then_item(name, visibility, typ, span); + self.check_type_is_not_more_private_then_item(name, visibility, typ, location); } } Type::Alias(alias_type, generics) => { @@ -1707,26 +1736,31 @@ impl<'context> Elaborator<'context> { name, visibility, &alias_type.borrow().get_type(generics), - span, + location, ); } Type::CheckedCast { from, to } => { - self.check_type_is_not_more_private_then_item(name, visibility, from, span); - self.check_type_is_not_more_private_then_item(name, visibility, to, span); + self.check_type_is_not_more_private_then_item(name, visibility, from, location); + self.check_type_is_not_more_private_then_item(name, visibility, to, location); } Type::Function(args, return_type, env, _) => { for arg in args { - self.check_type_is_not_more_private_then_item(name, visibility, arg, span); + self.check_type_is_not_more_private_then_item(name, visibility, arg, location); } - self.check_type_is_not_more_private_then_item(name, visibility, return_type, span); - self.check_type_is_not_more_private_then_item(name, visibility, env, span); + self.check_type_is_not_more_private_then_item( + name, + visibility, + return_type, + location, + ); + self.check_type_is_not_more_private_then_item(name, visibility, env, location); } Type::MutableReference(typ) | Type::Array(_, typ) | Type::Slice(typ) => { - self.check_type_is_not_more_private_then_item(name, visibility, typ, span); + self.check_type_is_not_more_private_then_item(name, visibility, typ, location); } Type::InfixExpr(left, _op, right, _) => { - self.check_type_is_not_more_private_then_item(name, visibility, left, span); - self.check_type_is_not_more_private_then_item(name, visibility, right, span); + self.check_type_is_not_more_private_then_item(name, visibility, left, location); + self.check_type_is_not_more_private_then_item(name, visibility, right, location); } Type::FieldElement | Type::Integer(..) @@ -1752,7 +1786,6 @@ impl<'context> Elaborator<'context> { // Resolve each field in each struct. // Each struct should already be present in the NodeInterner after def collection. for (type_id, typ) in structs { - self.file = typ.file_id; self.local_module = typ.module_id; let fields = self.resolve_struct_fields(&typ.struct_def, *type_id); @@ -1766,22 +1799,22 @@ impl<'context> Elaborator<'context> { // Check that the a public struct doesn't have a private type as a public field. if typ.struct_def.visibility != ItemVisibility::Private { for field in &fields { - let ident = Ident(Spanned::from( - field.name.span(), + let ident = Ident::from(Located::from( + field.name.location(), format!("{}::{}", typ.struct_def.name, field.name), )); self.check_type_is_not_more_private_then_item( &ident, field.visibility, &field.typ, - field.name.span(), + field.name.location(), ); } } if self.interner.is_in_lsp_mode() { for (field_index, field) in fields.iter().enumerate() { - let location = Location::new(field.name.span(), self.file); + let location = field.name.location(); let reference_id = ReferenceId::StructMember(*type_id, field_index); self.interner.add_definition_location(reference_id, location, None); } @@ -1805,8 +1838,7 @@ impl<'context> Elaborator<'context> { for (_, field_type) in fields.iter() { if field_type.is_nested_slice() { let location = struct_type.borrow().location; - self.file = location.file; - self.push_err(ResolverError::NestedSlices { span: location.span }); + self.push_err(ResolverError::NestedSlices { location }); } } } @@ -1841,20 +1873,22 @@ impl<'context> Elaborator<'context> { fn collect_enum_definitions(&mut self, enums: &BTreeMap) { for (type_id, typ) in enums { - self.file = typ.file_id; self.local_module = typ.module_id; self.generics.clear(); let datatype = self.interner.get_type(*type_id); - let generics = datatype.borrow().generic_types(); - self.add_existing_generics(&typ.enum_def.generics, &datatype.borrow().generics); + let datatype_ref = datatype.borrow(); + let generics = datatype_ref.generic_types(); + self.add_existing_generics(&typ.enum_def.generics, &datatype_ref.generics); + + self.use_unstable_feature(UnstableFeature::Enums, datatype_ref.name.location()); + drop(datatype_ref); let self_type = Type::DataType(datatype.clone(), generics); let self_type_id = self.interner.push_quoted_type(self_type.clone()); - let unresolved = UnresolvedType { - typ: UnresolvedTypeData::Resolved(self_type_id), - span: typ.enum_def.span, - }; + let location = typ.enum_def.location; + let unresolved = + UnresolvedType { typ: UnresolvedTypeData::Resolved(self_type_id), location }; datatype.borrow_mut().init_variants(); let module_id = ModuleId { krate: self.crate_id, local_id: typ.module_id }; @@ -1881,7 +1915,7 @@ impl<'context> Elaborator<'context> { ); let reference_id = ReferenceId::EnumVariant(*type_id, i); - let location = Location::new(variant.item.name.span(), self.file); + let location = variant.item.name.location(); self.interner.add_definition_location(reference_id, location, Some(module_id)); } } @@ -1889,7 +1923,6 @@ impl<'context> Elaborator<'context> { fn elaborate_global(&mut self, global: UnresolvedGlobal) { let old_module = std::mem::replace(&mut self.local_module, global.module_id); - let old_file = std::mem::replace(&mut self.file, global.file_id); let old_item = self.current_item.take(); let global_id = global.global_id; @@ -1902,16 +1935,16 @@ impl<'context> Elaborator<'context> { None }; - let span = let_stmt.pattern.span(); + let location = let_stmt.pattern.location(); if !self.in_contract() && let_stmt.attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) { - self.push_err(ResolverError::AbiAttributeOutsideContract { span }); + self.push_err(ResolverError::AbiAttributeOutsideContract { location }); } if !let_stmt.comptime && matches!(let_stmt.pattern, Pattern::Mutable(..)) { - self.push_err(ResolverError::MutableGlobal { span }); + self.push_err(ResolverError::MutableGlobal { location }); } let (let_statement, _typ) = self @@ -1923,7 +1956,6 @@ impl<'context> Elaborator<'context> { self.elaborate_comptime_global(global_id); if let Some(name) = name { - let location = Location::new(span, self.file); self.interner.register_global( global_id, name, @@ -1934,7 +1966,6 @@ impl<'context> Elaborator<'context> { } self.local_module = old_module; - self.file = old_file; self.current_item = old_item; } @@ -1950,7 +1981,8 @@ impl<'context> Elaborator<'context> { let mut interpreter = self.setup_interpreter(); if let Err(error) = interpreter.evaluate_let(let_statement) { - self.errors.push(error.into_compilation_error_pair()); + let error: CompilationError = error.into(); + self.push_err(error); } else { let value = interpreter .lookup_id(definition_id, location) @@ -1986,7 +2018,6 @@ impl<'context> Elaborator<'context> { self.local_module = *local_module; for (generics, _, function_set) in function_sets { - self.file = function_set.file_id; self.add_generics(generics); let self_type = self.resolve_type(self_type.clone()); function_set.self_type = Some(self_type.clone()); @@ -1998,20 +2029,19 @@ impl<'context> Elaborator<'context> { } for trait_impl in trait_impls { - self.file = trait_impl.file_id; self.local_module = trait_impl.module_id; - let (trait_id, mut trait_generics, path_span) = match &trait_impl.r#trait.typ { + let (trait_id, mut trait_generics, path_location) = match &trait_impl.r#trait.typ { UnresolvedTypeData::Named(trait_path, trait_generics, _) => { let trait_id = self.resolve_trait_by_path(trait_path.clone()); - (trait_id, trait_generics.clone(), trait_path.span) + (trait_id, trait_generics.clone(), trait_path.location) } UnresolvedTypeData::Resolved(quoted_type_id) => { let typ = self.interner.get_quoted_type(*quoted_type_id); - let span = trait_impl.r#trait.span; + let location = trait_impl.r#trait.location; let Type::TraitAsType(trait_id, _, trait_generics) = typ else { let found = typ.to_string(); - self.push_err(ResolverError::ExpectedTrait { span, found }); + self.push_err(ResolverError::ExpectedTrait { location, found }); continue; }; @@ -2023,24 +2053,24 @@ impl<'context> Elaborator<'context> { ordered_args: vecmap(&trait_generics.ordered, |typ| { let quoted_type_id = self.interner.push_quoted_type(typ.clone()); let typ = UnresolvedTypeData::Resolved(quoted_type_id); - UnresolvedType { typ, span } + UnresolvedType { typ, location } }), named_args: vecmap(&trait_generics.named, |named_type| { let quoted_type_id = self.interner.push_quoted_type(named_type.typ.clone()); let typ = UnresolvedTypeData::Resolved(quoted_type_id); - (named_type.name.clone(), UnresolvedType { typ, span }) + (named_type.name.clone(), UnresolvedType { typ, location }) }), kinds: Vec::new(), }; - (Some(trait_id), trait_generics, span) + (Some(trait_id), trait_generics, location) } _ => { - let span = trait_impl.r#trait.span; + let location = trait_impl.r#trait.location; let found = trait_impl.r#trait.typ.to_string(); - self.push_err(ResolverError::ExpectedTrait { span, found }); - continue; + self.push_err(ResolverError::ExpectedTrait { location, found }); + (None, GenericTypeArgs::default(), location) } }; @@ -2076,7 +2106,7 @@ impl<'context> Elaborator<'context> { .trait_id .map(|trait_id| { // Check for missing generics & associated types for the trait being implemented - self.resolve_trait_args_from_trait_impl(trait_generics, trait_id, path_span) + self.resolve_trait_args_from_trait_impl(trait_generics, trait_id, path_location) }) .unwrap_or_default(); @@ -2096,22 +2126,19 @@ impl<'context> Elaborator<'context> { self.generics.clear(); if let Some(trait_id) = trait_id { - let (span, is_self_type_name) = match &trait_impl.r#trait.typ { + let (location, is_self_type_name) = match &trait_impl.r#trait.typ { UnresolvedTypeData::Named(trait_path, _, _) => { let trait_name = trait_path.last_ident(); - (trait_name.span(), trait_name.is_self_type_name()) + (trait_name.location(), trait_name.is_self_type_name()) } - _ => (trait_impl.r#trait.span, false), + _ => (trait_impl.r#trait.location, false), }; - let location = Location::new(span, trait_impl.file_id); self.interner.add_trait_reference(trait_id, location, is_self_type_name); } } } fn define_function_metas_for_functions(&mut self, function_set: &mut UnresolvedFunctions) { - self.file = function_set.file_id; - for (local_module, id, func) in &mut function_set.functions { self.local_module = *local_module; self.recover_generics(|this| { @@ -2124,11 +2151,29 @@ impl<'context> Elaborator<'context> { /// Defaults to `true` if the current function is unknown. fn in_constrained_function(&self) -> bool { !self.in_comptime_context() - && self.current_item.map_or(true, |id| match id { + && self.current_item.is_none_or(|id| match id { DependencyId::Function(id) => { !self.interner.function_modifiers(&id).is_unconstrained } _ => true, }) } + + /// Register a use of the given unstable feature. Errors if the feature has not + /// been explicitly enabled in this package. + pub fn use_unstable_feature(&mut self, feature: UnstableFeature, location: Location) { + if !self.options.enabled_unstable_features.contains(&feature) { + let reason = ParserErrorReason::ExperimentalFeature(feature); + self.push_err(ParserError::with_reason(reason, location)); + } + } + + /// Run the given function using the resolver and return true if any errors (not warnings) + /// occurred while running it. + pub fn errors_occurred_in(&mut self, f: impl FnOnce(&mut Self) -> T) -> (bool, T) { + let previous_errors = self.errors.len(); + let ret = f(self); + let errored = self.errors.iter().skip(previous_errors).any(|error| error.is_error()); + (errored, ret) + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/options.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/options.rs new file mode 100644 index 000000000000..58bb5e73a618 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/options.rs @@ -0,0 +1,62 @@ +use std::str::FromStr; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum UnstableFeature { + Enums, + ArrayOwnership, +} + +impl std::fmt::Display for UnstableFeature { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Enums => write!(f, "enums"), + Self::ArrayOwnership => write!(f, "array-ownership"), + } + } +} + +impl FromStr for UnstableFeature { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "enums" => Ok(Self::Enums), + "array-ownership" => Ok(Self::ArrayOwnership), + other => Err(format!("Unknown unstable feature '{other}'")), + } + } +} + +/// Generic options struct meant to resolve to ElaboratorOptions below when +/// we can resolve a file path to a file id later. This generic struct is used +/// so that FrontendOptions doesn't need to duplicate fields and methods with ElaboratorOptions. +#[derive(Copy, Clone)] +pub struct GenericOptions<'a, T> { + /// The scope of --debug-comptime, or None if unset + pub debug_comptime_in_file: Option, + + /// Use pedantic ACVM solving + pub pedantic_solving: bool, + + /// Unstable compiler features that were explicitly enabled. Any unstable features + /// that are not in this list result in an error when used. + pub enabled_unstable_features: &'a [UnstableFeature], +} + +/// Options from nargo_cli that need to be passed down to the elaborator +pub(crate) type ElaboratorOptions<'a> = GenericOptions<'a, fm::FileId>; + +/// This is the unresolved version of `ElaboratorOptions` +/// CLI options that need to be passed to the compiler frontend (the elaborator). +pub type FrontendOptions<'a> = GenericOptions<'a, &'a str>; + +impl GenericOptions<'_, T> { + /// A sane default of frontend options for running tests + pub fn test_default() -> GenericOptions<'static, T> { + GenericOptions { + debug_comptime_in_file: None, + pedantic_solving: true, + enabled_unstable_features: &[UnstableFeature::Enums], + } + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/path_resolution.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/path_resolution.rs index 67a99da70eb8..c0dfa90d36c9 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/path_resolution.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/path_resolution.rs @@ -1,9 +1,9 @@ use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use crate::ast::{Ident, Path, PathKind, UnresolvedType}; use crate::hir::def_map::{ModuleData, ModuleDefId, ModuleId, PerNs}; -use crate::hir::resolution::import::{resolve_path_kind, PathResolutionError}; +use crate::hir::resolution::import::{PathResolutionError, resolve_path_kind}; use crate::hir::resolution::errors::ResolverError; use crate::hir::resolution::visibility::item_in_module_is_visible; @@ -12,8 +12,8 @@ use crate::locations::ReferencesTracker; use crate::node_interner::{FuncId, GlobalId, TraitId, TypeAliasId, TypeId}; use crate::{Shared, Type, TypeAlias}; -use super::types::SELF_TYPE_NAME; use super::Elaborator; +use super::types::SELF_TYPE_NAME; #[derive(Debug)] pub(crate) struct PathResolution { @@ -73,7 +73,7 @@ impl PathResolutionItem { #[derive(Debug, Clone)] pub struct Turbofish { pub generics: Vec, - pub span: Span, + pub location: Location, } /// Any item that can appear before the last segment in a path. @@ -102,7 +102,7 @@ enum MethodLookupResult { FoundMultipleTraitMethods(Vec), } -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { pub(super) fn resolve_path_or_error( &mut self, path: Path, @@ -149,7 +149,7 @@ impl<'context> Elaborator<'context> { importing_module: ModuleId, ) -> PathResolutionResult { let references_tracker = if self.interner.is_in_lsp_mode() { - Some(ReferencesTracker::new(self.interner, self.file)) + Some(ReferencesTracker::new(self.interner)) } else { None }; @@ -204,7 +204,7 @@ impl<'context> Elaborator<'context> { Some((typ, visibility, _)) => (typ, visibility), }; - let location = Location::new(last_segment.span, self.file); + let location = last_segment.location; self.interner.add_module_def_id_reference( typ, location, @@ -218,7 +218,7 @@ impl<'context> Elaborator<'context> { if last_segment_generics.is_some() { errors.push(PathResolutionError::TurbofishNotAllowedOnItem { item: format!("module `{last_ident}`"), - span: last_segment.turbofish_span(), + location: last_segment.turbofish_location(), }); } @@ -324,7 +324,7 @@ impl<'context> Elaborator<'context> { let name = path.last_ident(); let is_self_type = name.is_self_type_name(); - let location = Location::new(name.span(), self.file); + let location = name.location(); self.interner.add_module_def_id_reference(module_def_id, location, is_self_type); let item = merge_intermediate_path_resolution_item_with_module_def_id( 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 b01c4e0d768b..6ad8e1ec04c9 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -1,11 +1,12 @@ use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use rustc_hash::FxHashSet as HashSet; use crate::{ + DataType, Kind, Shared, Type, TypeAlias, TypeBindings, ast::{ - Expression, ExpressionKind, Ident, ItemVisibility, Path, Pattern, TypePath, UnresolvedType, - ERROR_IDENT, + ERROR_IDENT, Expression, ExpressionKind, Ident, ItemVisibility, Path, Pattern, TypePath, + UnresolvedType, }, hir::{ def_collector::dc_crate::CompilationError, @@ -17,12 +18,11 @@ use crate::{ stmt::HirPattern, }, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, TraitImplKind}, - DataType, Kind, Shared, Type, TypeAlias, TypeBindings, }; -use super::{path_resolution::PathResolutionItem, Elaborator, ResolverMeta}; +use super::{Elaborator, ResolverMeta, path_resolution::PathResolutionItem}; -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { pub(super) fn elaborate_pattern( &mut self, pattern: Pattern, @@ -66,7 +66,7 @@ impl<'context> Elaborator<'context> { pattern: Pattern, expected_type: Type, definition: DefinitionKind, - mutable: Option, + mutable: Option, new_definitions: &mut Vec, warn_if_unused: bool, ) -> HirPattern { @@ -81,7 +81,7 @@ impl<'context> Elaborator<'context> { let ident = if let DefinitionKind::Global(global_id) = definition { // Globals don't need to be added to scope, they're already in the def_maps let id = self.interner.get_global(global_id).definition_id; - let location = Location::new(name.span(), self.file); + let location = name.location(); HirIdent::non_trait_method(id, location) } else { self.add_variable_decl( @@ -96,23 +96,25 @@ impl<'context> Elaborator<'context> { new_definitions.push(ident.clone()); HirPattern::Identifier(ident) } - Pattern::Mutable(pattern, span, _) => { + Pattern::Mutable(pattern, location, _) => { if let Some(first_mut) = mutable { - self.push_err(ResolverError::UnnecessaryMut { first_mut, second_mut: span }); + self.push_err(ResolverError::UnnecessaryMut { + first_mut, + second_mut: location, + }); } let pattern = self.elaborate_pattern_mut( *pattern, expected_type, definition, - Some(span), + Some(location), new_definitions, warn_if_unused, ); - let location = Location::new(span, self.file); HirPattern::Mutable(Box::new(pattern), location) } - Pattern::Tuple(fields, span) => { + Pattern::Tuple(fields, location) => { let field_types = match expected_type.follow_bindings() { Type::Tuple(fields) => fields, Type::Error => Vec::new(), @@ -123,7 +125,7 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::TypeMismatchWithSource { expected: expected_type, actual: tuple, - span, + location, source: Source::Assignment, }); Vec::new() @@ -141,13 +143,12 @@ impl<'context> Elaborator<'context> { warn_if_unused, ) }); - let location = Location::new(span, self.file); HirPattern::Tuple(fields, location) } - Pattern::Struct(name, fields, span) => self.elaborate_struct_pattern( + Pattern::Struct(name, fields, location) => self.elaborate_struct_pattern( name, fields, - span, + location, expected_type, definition, mutable, @@ -172,14 +173,14 @@ impl<'context> Elaborator<'context> { &mut self, name: Path, fields: Vec<(Ident, Pattern)>, - span: Span, + location: Location, expected_type: Type, definition: DefinitionKind, - mutable: Option, + mutable: Option, new_definitions: &mut Vec, ) -> HirPattern { let last_segment = name.last_segment(); - let name_span = last_segment.ident.span(); + let name_location = last_segment.ident.location(); let is_self_type = last_segment.ident.is_self_type_name(); let error_identifier = |this: &mut Self| { @@ -200,27 +201,26 @@ impl<'context> Elaborator<'context> { None => return error_identifier(self), Some(typ) => { let typ = typ.to_string(); - self.push_err(ResolverError::NonStructUsedInConstructor { typ, span }); + self.push_err(ResolverError::NonStructUsedInConstructor { typ, location }); return error_identifier(self); } }; - let turbofish_span = last_segment.turbofish_span(); + let turbofish_location = last_segment.turbofish_location(); let generics = self.resolve_struct_turbofish_generics( &struct_type.borrow(), generics, last_segment.generics, - turbofish_span, + turbofish_location, ); let actual_type = Type::DataType(struct_type.clone(), generics); - let location = Location::new(span, self.file); self.unify(&actual_type, &expected_type, || TypeCheckError::TypeMismatchWithSource { expected: expected_type.clone(), actual: actual_type.clone(), - span: location.span, + location, source: Source::Assignment, }); @@ -228,7 +228,7 @@ impl<'context> Elaborator<'context> { let fields = self.resolve_constructor_pattern_fields( typ, fields, - span, + location, expected_type.clone(), definition, mutable, @@ -237,11 +237,10 @@ impl<'context> Elaborator<'context> { let struct_id = struct_type.borrow().id; - let reference_location = Location::new(name_span, self.file); - self.interner.add_type_reference(struct_id, reference_location, is_self_type); + self.interner.add_type_reference(struct_id, name_location, is_self_type); for (field_index, field) in fields.iter().enumerate() { - let reference_location = Location::new(field.0.span(), self.file); + let reference_location = field.0.location(); self.interner.add_struct_member_reference(struct_id, field_index, reference_location); } @@ -256,10 +255,10 @@ impl<'context> Elaborator<'context> { &mut self, struct_type: Shared, fields: Vec<(Ident, Pattern)>, - span: Span, + location: Location, expected_type: Type, definition: DefinitionKind, - mutable: Option, + mutable: Option, new_definitions: &mut Vec, ) -> Vec<(Ident, HirPattern)> { let mut ret = Vec::with_capacity(fields.len()); @@ -290,7 +289,7 @@ impl<'context> Elaborator<'context> { &struct_type.borrow(), &field.0.contents, visibility, - field.span(), + field.location(), ); } else if seen_fields.contains(&field) { // duplicate field @@ -308,7 +307,7 @@ impl<'context> Elaborator<'context> { if !unseen_fields.is_empty() { self.push_err(ResolverError::MissingFields { - span, + location, missing_fields: unseen_fields.into_iter().map(|field| field.to_string()).collect(), struct_definition: struct_type.borrow().name.clone(), }); @@ -329,7 +328,7 @@ impl<'context> Elaborator<'context> { return self.add_global_variable_decl(name, global_id); } - let location = Location::new(name.span(), self.file); + let location = name.location(); let name = name.0.contents; let comptime = self.in_comptime_context(); let id = @@ -346,8 +345,8 @@ impl<'context> Elaborator<'context> { if let Some(old_value) = old_value { self.push_err(ResolverError::DuplicateDefinition { name, - first_span: old_value.ident.location.span, - second_span: location.span, + first_location: old_value.ident.location, + second_location: location, }); } } @@ -362,14 +361,18 @@ impl<'context> Elaborator<'context> { ident: HirIdent, warn_if_unused: bool, ) { - let second_span = ident.location.span; + let second_location = ident.location; let resolver_meta = ResolverMeta { num_times_used: 0, ident, warn_if_unused }; let old_value = self.scopes.get_mut_scope().add_key_value(name.clone(), resolver_meta); if let Some(old_value) = old_value { - let first_span = old_value.ident.location.span; - self.push_err(ResolverError::DuplicateDefinition { name, first_span, second_span }); + let first_location = old_value.ident.location; + self.push_err(ResolverError::DuplicateDefinition { + name, + first_location, + second_location, + }); } } @@ -383,9 +386,9 @@ impl<'context> Elaborator<'context> { let old_global_value = scope.add_key_value(name.0.contents.clone(), resolver_meta); if let Some(old_global_value) = old_global_value { self.push_err(ResolverError::DuplicateDefinition { - second_span: name.span(), + first_location: old_global_value.ident.location, + second_location: name.location(), name: name.0.contents, - first_span: old_global_value.ident.location.span, }); } ident @@ -402,7 +405,7 @@ impl<'context> Elaborator<'context> { let scope_tree = self.scopes.current_scope_tree(); let variable = scope_tree.find(&name.0.contents); - let location = Location::new(name.span(), self.file); + let location = name.location(); if let Some((variable_found, scope)) = variable { variable_found.num_times_used += 1; let id = variable_found.ident.id; @@ -410,7 +413,7 @@ impl<'context> Elaborator<'context> { } else { Err(ResolverError::VariableNotDeclared { name: name.0.contents.clone(), - span: name.0.span(), + location: name.0.location(), }) } } @@ -420,7 +423,7 @@ impl<'context> Elaborator<'context> { &mut self, func_id: &FuncId, unresolved_turbofish: Option>, - span: Span, + location: Location, ) -> Option> { let direct_generic_kinds = vecmap(&self.interner.function_meta(func_id).direct_generics, |generic| generic.kind()); @@ -430,7 +433,7 @@ impl<'context> Elaborator<'context> { let type_check_err = TypeCheckError::IncorrectTurbofishGenericCount { expected_count: direct_generic_kinds.len(), actual_count: unresolved_turbofish.len(), - span, + location, }; self.push_err(type_check_err); } @@ -444,7 +447,7 @@ impl<'context> Elaborator<'context> { struct_type: &DataType, generics: Vec, unresolved_turbofish: Option>, - span: Span, + location: Location, ) -> Vec { let kinds = vecmap(&struct_type.generics, |generic| generic.kind()); self.resolve_item_turbofish_generics( @@ -453,7 +456,7 @@ impl<'context> Elaborator<'context> { kinds, generics, unresolved_turbofish, - span, + location, ) } @@ -463,7 +466,7 @@ impl<'context> Elaborator<'context> { trait_generic_kinds: Vec, generics: Vec, unresolved_turbofish: Option>, - span: Span, + location: Location, ) -> Vec { self.resolve_item_turbofish_generics( "trait", @@ -471,7 +474,7 @@ impl<'context> Elaborator<'context> { trait_generic_kinds, generics, unresolved_turbofish, - span, + location, ) } @@ -480,7 +483,7 @@ impl<'context> Elaborator<'context> { type_alias: &TypeAlias, generics: Vec, unresolved_turbofish: Option>, - span: Span, + location: Location, ) -> Vec { let kinds = vecmap(&type_alias.generics, |generic| generic.kind()); self.resolve_item_turbofish_generics( @@ -489,7 +492,7 @@ impl<'context> Elaborator<'context> { kinds, generics, unresolved_turbofish, - span, + location, ) } @@ -500,7 +503,7 @@ impl<'context> Elaborator<'context> { item_generic_kinds: Vec, generics: Vec, unresolved_turbofish: Option>, - span: Span, + location: Location, ) -> Vec { let Some(turbofish_generics) = unresolved_turbofish else { return generics; @@ -511,7 +514,7 @@ impl<'context> Elaborator<'context> { item: format!("{item_kind} {item_name}"), expected: generics.len(), found: turbofish_generics.len(), - span, + location, }); return generics; } @@ -533,7 +536,7 @@ impl<'context> Elaborator<'context> { pub(super) fn elaborate_variable(&mut self, variable: Path) -> (ExprId, Type) { let unresolved_turbofish = variable.segments.last().unwrap().generics.clone(); - let span = variable.span; + let location = variable.location; let (expr, item) = self.resolve_variable(variable); let definition_id = expr.id; @@ -547,7 +550,7 @@ impl<'context> Elaborator<'context> { // Resolve any generics if we the variable we have resolved is a function // and if the turbofish operator was used. let generics = if let Some(DefinitionKind::Function(func_id)) = &definition_kind { - self.resolve_function_turbofish_generics(func_id, unresolved_turbofish, span) + self.resolve_function_turbofish_generics(func_id, unresolved_turbofish, location) } else { None }; @@ -567,7 +570,7 @@ impl<'context> Elaborator<'context> { let id = self.interner.push_expr(HirExpression::Ident(expr.clone(), generics.clone())); - self.interner.push_expr_location(id, span, self.file); + self.interner.push_expr_location(id, location); let typ = self.type_check_variable_with_bindings(expr, id, generics, bindings); self.interner.push_expr_type(id, typ.clone()); @@ -589,7 +592,7 @@ impl<'context> Elaborator<'context> { &struct_type, struct_generics, Some(generics.generics), - generics.span, + generics.location, ) } PathResolutionItem::TypeAliasFunction(type_alias_id, generics, _func_id) => { @@ -605,7 +608,7 @@ impl<'context> Elaborator<'context> { &type_alias, alias_generics, Some(generics.generics), - generics.span, + generics.location, ) } else { alias_generics @@ -628,7 +631,7 @@ impl<'context> Elaborator<'context> { kinds, trait_generics, Some(generics.generics), - generics.span, + generics.location, ) } _ => Vec::new(), @@ -643,7 +646,7 @@ impl<'context> Elaborator<'context> { ( HirIdent { - location: Location::new(path.span, self.file), + location: path.location, id: self.interner.trait_method_id(trait_path_resolution.method.method_id), impl_kind: ImplKind::TraitMethod(trait_path_resolution.method), }, @@ -654,7 +657,7 @@ impl<'context> Elaborator<'context> { // Otherwise, then it is referring to an Identifier // This lookup allows support of such statements: let x = foo::bar::SOME_GLOBAL + 10; // If the expression is a singular indent, we search the resolver's current scope as normal. - let span = path.span(); + let location = path.location; let ((hir_ident, var_scope_index), item) = self.get_ident_from_path(path); if hir_ident.id != DefinitionId::dummy_id() { @@ -689,8 +692,7 @@ impl<'context> Elaborator<'context> { // only local variables can be captured by closures. self.resolve_local_variable(hir_ident.clone(), var_scope_index); - let reference_location = Location::new(span, self.file); - self.interner.add_local_reference(hir_ident.id, reference_location); + self.interner.add_local_reference(hir_ident.id, location); } } } @@ -742,14 +744,13 @@ impl<'context> Elaborator<'context> { _ => 0, }); - let span = self.interner.expr_span(&expr_id); let location = self.interner.expr_location(&expr_id); // This instantiates a trait's generics as well which need to be set // when the constraint below is later solved for when the function is // finished. How to link the two? let (typ, bindings) = - self.instantiate(t, bindings, generics, function_generic_count, span, location); + self.instantiate(t, bindings, generics, function_generic_count, location); // Push any trait constraints required by this definition to the context // to be checked later when the type of this variable is further constrained. @@ -796,7 +797,6 @@ impl<'context> Elaborator<'context> { bindings: TypeBindings, turbofish_generics: Option>, function_generic_count: usize, - span: Span, location: Location, ) -> (Type, TypeBindings) { match turbofish_generics { @@ -805,9 +805,9 @@ impl<'context> Elaborator<'context> { let type_check_err = TypeCheckError::IncorrectTurbofishGenericCount { expected_count: function_generic_count, actual_count: turbofish_generics.len(), - span, + location, }; - self.errors.push((CompilationError::TypeError(type_check_err), location.file)); + self.push_err(CompilationError::TypeError(type_check_err)); typ.instantiate_with_bindings(bindings, self.interner) } else { // Fetch the count of any implicit generics on the function, such as @@ -827,20 +827,20 @@ impl<'context> Elaborator<'context> { &mut self, path: Path, ) -> ((HirIdent, usize), Option) { - let location = Location::new(path.last_ident().span(), self.file); + let location = Location::new(path.last_ident().span(), path.location.file); let error = match path.as_ident().map(|ident| self.use_variable(ident)) { Some(Ok(found)) => return (found, None), // Try to look it up as a global, but still issue the first error if we fail Some(Err(error)) => match self.lookup_global(path) { Ok((id, item)) => { - return ((HirIdent::non_trait_method(id, location), 0), Some(item)) + return ((HirIdent::non_trait_method(id, location), 0), Some(item)); } Err(_) => error, }, None => match self.lookup_global(path) { Ok((id, item)) => { - return ((HirIdent::non_trait_method(id, location), 0), Some(item)) + return ((HirIdent::non_trait_method(id, location), 0), Some(item)); } Err(error) => error, }, @@ -851,11 +851,14 @@ impl<'context> Elaborator<'context> { } pub(super) fn elaborate_type_path(&mut self, path: TypePath) -> (ExprId, Type) { - let span = path.item.span(); + let location = path.item.location(); let typ = self.resolve_type(path.typ); + let check_self_param = false; - let Some(method) = self.lookup_method(&typ, &path.item.0.contents, span, false) else { - let error = Expression::new(ExpressionKind::Error, span); + let Some(method) = + self.lookup_method(&typ, &path.item.0.contents, location, check_self_param) + else { + let error = Expression::new(ExpressionKind::Error, location); return self.elaborate_expression(error); }; @@ -864,16 +867,15 @@ impl<'context> Elaborator<'context> { .expect("Expected trait function to be a DefinitionKind::Function"); let generics = - path.turbofish.map(|turbofish| self.resolve_type_args(turbofish, func_id, span).0); + path.turbofish.map(|turbofish| self.resolve_type_args(turbofish, func_id, location).0); - let location = Location::new(span, self.file); let id = self.interner.function_definition_id(func_id); let impl_kind = match method { HirMethodReference::FuncId(_) => ImplKind::NotATraitMethod, HirMethodReference::TraitMethodId(method_id, generics, _) => { let mut constraint = - self.interner.get_trait(method_id.trait_id).as_constraint(span); + self.interner.get_trait(method_id.trait_id).as_constraint(location); constraint.trait_bound.trait_generics = generics; ImplKind::TraitMethod(TraitMethod { method_id, constraint, assumed: false }) } @@ -881,7 +883,7 @@ impl<'context> Elaborator<'context> { let ident = HirIdent { location, id, impl_kind }; let id = self.interner.push_expr(HirExpression::Ident(ident.clone(), generics.clone())); - self.interner.push_expr_location(id, location.span, location.file); + self.interner.push_expr_location(id, location); let typ = self.type_check_variable(ident, id, generics); self.interner.push_expr_type(id, typ.clone()); 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 327ae02b2046..9955d874927e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs @@ -1,17 +1,17 @@ -use noirc_errors::Spanned; +use noirc_errors::Located; -use crate::ast::{Ident, Path, ERROR_IDENT}; +use crate::ast::{ERROR_IDENT, Ident, Path}; use crate::hir::def_map::{LocalModuleId, ModuleId}; use crate::hir::scope::{Scope as GenericScope, ScopeTree as GenericScopeTree}; use crate::{ + DataType, Shared, hir::resolution::errors::ResolverError, hir_def::{ expr::{HirCapturedVar, HirIdent}, traits::Trait, }, node_interner::{DefinitionId, TraitId, TypeId}, - DataType, Shared, }; use crate::{Type, TypeAlias}; @@ -22,7 +22,7 @@ use super::{Elaborator, ResolverMeta}; type Scope = GenericScope; type ScopeTree = GenericScopeTree; -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { pub fn module_id(&self) -> ModuleId { assert_ne!(self.local_module, LocalModuleId::dummy_id(), "local_module is unset"); ModuleId { krate: self.crate_id, local_id: self.local_module } @@ -83,7 +83,7 @@ impl<'context> Elaborator<'context> { &mut self, path: Path, ) -> Result<(DefinitionId, PathResolutionItem), ResolverError> { - let span = path.span(); + let location = path.location; let item = self.resolve_path_or_error(path)?; if let Some(function) = item.function_id() { @@ -97,7 +97,7 @@ impl<'context> Elaborator<'context> { let expected = "global variable"; let got = "local variable"; - Err(ResolverError::Expected { span, expected, got }) + Err(ResolverError::Expected { location, expected, got }) } pub fn push_scope(&mut self) { @@ -121,7 +121,7 @@ impl<'context> Elaborator<'context> { if let Some(definition_info) = self.interner.try_definition(unused_var.id) { let name = &definition_info.name; if name != ERROR_IDENT && !definition_info.is_global() { - let ident = Ident(Spanned::from(unused_var.location.span, name.to_owned())); + let ident = Ident(Located::from(unused_var.location, name.to_owned())); self.push_err(ResolverError::UnusedVariable { ident }); } } @@ -138,7 +138,7 @@ impl<'context> Elaborator<'context> { /// Lookup a given trait by name/path. pub fn lookup_trait_or_error(&mut self, path: Path) -> Option<&mut Trait> { - let span = path.span(); + let location = path.location; match self.resolve_path_or_error(path) { Ok(item) => { if let PathResolutionItem::Trait(trait_id) = item { @@ -147,7 +147,7 @@ impl<'context> Elaborator<'context> { self.push_err(ResolverError::Expected { expected: "trait", got: item.description(), - span, + location, }); None } @@ -161,7 +161,7 @@ impl<'context> Elaborator<'context> { /// Lookup a given struct type by name. pub fn lookup_datatype_or_error(&mut self, path: Path) -> Option> { - let span = path.span(); + let location = path.location; match self.resolve_path_or_error(path) { Ok(item) => { if let PathResolutionItem::Type(struct_id) = item { @@ -170,7 +170,7 @@ impl<'context> Elaborator<'context> { self.push_err(ResolverError::Expected { expected: "type", got: item.description(), - span, + location, }); None } @@ -186,13 +186,13 @@ impl<'context> Elaborator<'context> { /// This will also instantiate any struct types found. pub(super) fn lookup_type_or_error(&mut self, path: Path) -> Option { let ident = path.as_ident(); - if ident.map_or(false, |i| i == SELF_TYPE_NAME) { + if ident.is_some_and(|i| i == SELF_TYPE_NAME) { if let Some(typ) = &self.self_type { return Some(typ.clone()); } } - let span = path.span; + let location = path.location; match self.resolve_path_or_error(path) { Ok(PathResolutionItem::Type(struct_id)) => { let struct_type = self.get_type(struct_id); @@ -208,7 +208,7 @@ impl<'context> Elaborator<'context> { self.push_err(ResolverError::Expected { expected: "type", got: other.description(), - span, + location, }); None } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs index 3379db4aa668..f00b2a87b1ed 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs @@ -1,6 +1,7 @@ -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use crate::{ + DataType, Type, ast::{ AssignStatement, Expression, ForLoopStatement, ForRange, Ident, ItemVisibility, LValue, LetStatement, Path, Statement, StatementKind, WhileStatement, @@ -17,12 +18,11 @@ use crate::{ stmt::{HirAssignStatement, HirForStatement, HirLValue, HirLetStatement, HirStatement}, }, node_interner::{DefinitionId, DefinitionKind, GlobalId, StmtId}, - DataType, Type, }; -use super::{lints, Elaborator, Loop}; +use super::{Elaborator, Loop, lints}; -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { fn elaborate_statement_value(&mut self, statement: Statement) -> (HirStatement, Type) { self.elaborate_statement_value_with_target_type(statement, None) } @@ -36,10 +36,10 @@ impl<'context> Elaborator<'context> { StatementKind::Let(let_stmt) => self.elaborate_local_let(let_stmt), StatementKind::Assign(assign) => self.elaborate_assign(assign), StatementKind::For(for_stmt) => self.elaborate_for(for_stmt), - StatementKind::Loop(block, span) => self.elaborate_loop(block, span), + StatementKind::Loop(block, location) => self.elaborate_loop(block, location), StatementKind::While(while_) => self.elaborate_while(while_), - StatementKind::Break => self.elaborate_jump(true, statement.span), - StatementKind::Continue => self.elaborate_jump(false, statement.span), + StatementKind::Break => self.elaborate_jump(true, statement.location), + StatementKind::Continue => self.elaborate_jump(false, statement.location), StatementKind::Comptime(statement) => self.elaborate_comptime_statement(*statement), StatementKind::Expression(expr) => { let (expr, typ) = self.elaborate_expression_with_target_type(expr, target_type); @@ -51,7 +51,7 @@ impl<'context> Elaborator<'context> { } StatementKind::Interned(id) => { let kind = self.interner.get_statement_kind(id); - let statement = Statement { kind: kind.clone(), span: statement.span }; + let statement = Statement { kind: kind.clone(), location: statement.location }; self.elaborate_statement_value_with_target_type(statement, target_type) } StatementKind::Error => (HirStatement::Error, Type::Error), @@ -67,11 +67,11 @@ impl<'context> Elaborator<'context> { statement: Statement, target_type: Option<&Type>, ) -> (StmtId, Type) { - let span = statement.span; + let location = statement.location; let (hir_statement, typ) = self.elaborate_statement_value_with_target_type(statement, target_type); let id = self.interner.push_stmt(hir_statement); - self.interner.push_stmt_location(id, span, self.file); + self.interner.push_stmt_location(id, location); (id, typ) } @@ -91,15 +91,19 @@ impl<'context> Elaborator<'context> { let type_contains_unspecified = let_stmt.r#type.contains_unspecified(); let annotated_type = self.resolve_inferred_type(let_stmt.r#type); - let expr_span = let_stmt.expression.span; + let pattern_location = let_stmt.pattern.location(); + let expr_location = let_stmt.expression.location; let (expression, expr_type) = self.elaborate_expression_with_target_type(let_stmt.expression, Some(&annotated_type)); // Require the top-level of a global's type to be fully-specified if type_contains_unspecified && global_id.is_some() { - let span = expr_span; let expected_type = annotated_type.clone(); - let error = ResolverError::UnspecifiedGlobalType { span, expected_type }; + let error = ResolverError::UnspecifiedGlobalType { + pattern_location, + expr_location, + expected_type, + }; self.push_err(error); } @@ -110,11 +114,11 @@ impl<'context> Elaborator<'context> { // Now check if LHS is the same type as the RHS // Importantly, we do not coerce any types implicitly - self.unify_with_coercions(&expr_type, &annotated_type, expression, expr_span, || { + self.unify_with_coercions(&expr_type, &annotated_type, expression, expr_location, || { TypeCheckError::TypeMismatch { expected_typ: annotated_type.to_string(), expr_typ: expr_type.to_string(), - expr_span, + expr_location, } }); @@ -146,20 +150,23 @@ impl<'context> Elaborator<'context> { } pub(super) fn elaborate_assign(&mut self, assign: AssignStatement) -> (HirStatement, Type) { - let expr_span = assign.expression.span; + let expr_location = assign.expression.location; let (expression, expr_type) = self.elaborate_expression(assign.expression); let (lvalue, lvalue_type, mutable) = self.elaborate_lvalue(assign.lvalue); if !mutable { - let (name, span) = self.get_lvalue_name_and_span(&lvalue); - self.push_err(TypeCheckError::VariableMustBeMutable { name, span }); + let (_, name, location) = self.get_lvalue_error_info(&lvalue); + self.push_err(TypeCheckError::VariableMustBeMutable { name, location }); + } else { + let (id, name, location) = self.get_lvalue_error_info(&lvalue); + self.check_can_mutate_lambda_capture(id, name, location); } - self.unify_with_coercions(&expr_type, &lvalue_type, expression, expr_span, || { + self.unify_with_coercions(&expr_type, &lvalue_type, expression, expr_location, || { TypeCheckError::TypeMismatchWithSource { actual: expr_type.clone(), expected: lvalue_type.clone(), - span: expr_span, + location: expr_location, source: Source::Assignment, } }); @@ -173,14 +180,14 @@ impl<'context> Elaborator<'context> { ForRange::Range(bounds) => bounds.into_half_open(), ForRange::Array(_) => { let for_stmt = - for_loop.range.into_for(for_loop.identifier, for_loop.block, for_loop.span); + for_loop.range.into_for(for_loop.identifier, for_loop.block, for_loop.location); return self.elaborate_statement_value(for_stmt); } }; - let start_span = start.span; - let end_span = end.span; + let start_location = start.location; + let end_location = end.location; let (start_range, start_range_type) = self.elaborate_expression(start); let (end_range, end_range_type) = self.elaborate_expression(end); @@ -202,11 +209,10 @@ impl<'context> Elaborator<'context> { ); // Check that start range and end range have the same types - let range_span = start_span.merge(end_span); self.unify(&start_range_type, &end_range_type, || TypeCheckError::TypeMismatch { expected_typ: start_range_type.to_string(), expr_typ: end_range_type.to_string(), - expr_span: range_span, + expr_location: end_location, }); let expected_type = self.polymorphic_integer(); @@ -214,18 +220,18 @@ impl<'context> Elaborator<'context> { self.unify(&start_range_type, &expected_type, || TypeCheckError::TypeCannotBeUsed { typ: start_range_type.clone(), place: "for loop", - span: range_span, + location: start_location, }); self.interner.push_definition_type(identifier.id, start_range_type); - let block_span = block.type_span(); + let block_location = block.type_location(); let (block, block_type) = self.elaborate_expression(block); self.unify(&block_type, &Type::Unit, || TypeCheckError::TypeMismatch { expected_typ: Type::Unit.to_string(), expr_typ: block_type.to_string(), - expr_span: block_span, + expr_location: block_location, }); self.pop_scope(); @@ -240,24 +246,24 @@ impl<'context> Elaborator<'context> { pub(super) fn elaborate_loop( &mut self, block: Expression, - span: noirc_errors::Span, + location: Location, ) -> (HirStatement, Type) { let in_constrained_function = self.in_constrained_function(); if in_constrained_function { - self.push_err(ResolverError::LoopInConstrainedFn { span }); + self.push_err(ResolverError::LoopInConstrainedFn { location }); } let old_loop = std::mem::take(&mut self.current_loop); self.current_loop = Some(Loop { is_for: false, has_break: false }); self.push_scope(); - let block_span = block.type_span(); + let block_location = block.type_location(); let (block, block_type) = self.elaborate_expression(block); self.unify(&block_type, &Type::Unit, || TypeCheckError::TypeMismatch { expected_typ: Type::Unit.to_string(), expr_typ: block_type.to_string(), - expr_span: block_span, + expr_location: block_location, }); self.pop_scope(); @@ -265,7 +271,7 @@ impl<'context> Elaborator<'context> { let last_loop = std::mem::replace(&mut self.current_loop, old_loop).expect("Expected a loop"); if !last_loop.has_break { - self.push_err(ResolverError::LoopWithoutBreak { span }); + self.push_err(ResolverError::LoopWithoutBreak { location }); } let statement = HirStatement::Loop(block); @@ -276,29 +282,31 @@ impl<'context> Elaborator<'context> { pub(super) fn elaborate_while(&mut self, while_: WhileStatement) -> (HirStatement, Type) { let in_constrained_function = self.in_constrained_function(); if in_constrained_function { - self.push_err(ResolverError::WhileInConstrainedFn { span: while_.while_keyword_span }); + self.push_err(ResolverError::WhileInConstrainedFn { + location: while_.while_keyword_location, + }); } let old_loop = std::mem::take(&mut self.current_loop); self.current_loop = Some(Loop { is_for: false, has_break: false }); self.push_scope(); - let condition_span = while_.condition.type_span(); + let location = while_.condition.type_location(); let (condition, cond_type) = self.elaborate_expression(while_.condition); self.unify(&cond_type, &Type::Bool, || TypeCheckError::TypeMismatch { expected_typ: Type::Bool.to_string(), expr_typ: cond_type.to_string(), - expr_span: condition_span, + expr_location: location, }); - let block_span = while_.body.type_span(); + let block_location = while_.body.type_location(); let (block, block_type) = self.elaborate_expression(while_.body); self.unify(&block_type, &Type::Unit, || TypeCheckError::TypeMismatch { expected_typ: Type::Unit.to_string(), expr_typ: block_type.to_string(), - expr_span: block_span, + expr_location: block_location, }); self.pop_scope(); @@ -310,11 +318,11 @@ impl<'context> Elaborator<'context> { (statement, Type::Unit) } - fn elaborate_jump(&mut self, is_break: bool, span: noirc_errors::Span) -> (HirStatement, Type) { + fn elaborate_jump(&mut self, is_break: bool, location: Location) -> (HirStatement, Type) { let in_constrained_function = self.in_constrained_function(); if in_constrained_function { - self.push_err(ResolverError::JumpInConstrainedFn { is_break, span }); + self.push_err(ResolverError::JumpInConstrainedFn { is_break, location }); } if let Some(current_loop) = &mut self.current_loop { @@ -322,27 +330,27 @@ impl<'context> Elaborator<'context> { current_loop.has_break = true; } } else { - self.push_err(ResolverError::JumpOutsideLoop { is_break, span }); + self.push_err(ResolverError::JumpOutsideLoop { is_break, location }); } let expr = if is_break { HirStatement::Break } else { HirStatement::Continue }; (expr, self.interner.next_type_variable()) } - fn get_lvalue_name_and_span(&self, lvalue: &HirLValue) -> (String, Span) { + fn get_lvalue_error_info(&self, lvalue: &HirLValue) -> (DefinitionId, String, Location) { match lvalue { HirLValue::Ident(name, _) => { - let span = name.location.span; + let location = name.location; if let Some(definition) = self.interner.try_definition(name.id) { - (definition.name.clone(), span) + (name.id, definition.name.clone(), location) } else { - ("(undeclared variable)".into(), span) + (DefinitionId::dummy_id(), "(undeclared variable)".into(), location) } } - HirLValue::MemberAccess { object, .. } => self.get_lvalue_name_and_span(object), - HirLValue::Index { array, .. } => self.get_lvalue_name_and_span(array), - HirLValue::Dereference { lvalue, .. } => self.get_lvalue_name_and_span(lvalue), + HirLValue::MemberAccess { object, .. } => self.get_lvalue_error_info(object), + HirLValue::Index { array, .. } => self.get_lvalue_error_info(array), + HirLValue::Dereference { lvalue, .. } => self.get_lvalue_error_info(lvalue), } } @@ -350,8 +358,8 @@ impl<'context> Elaborator<'context> { match lvalue { LValue::Ident(ident) => { let mut mutable = true; - let span = ident.span(); - let path = Path::from_single(ident.0.contents, span); + let location = ident.location(); + let path = Path::from_single(ident.0.contents, location); let ((ident, scope_index), _) = self.get_ident_from_path(path); self.resolve_local_variable(ident.clone(), scope_index); @@ -365,7 +373,7 @@ impl<'context> Elaborator<'context> { if definition.comptime && !self.in_comptime_context() { self.push_err(ResolverError::MutatingComptimeInNonComptimeContext { name: definition.name.clone(), - span: ident.location.span, + location: ident.location, }); } } @@ -374,19 +382,17 @@ impl<'context> Elaborator<'context> { typ.follow_bindings() }; - let reference_location = Location::new(span, self.file); - self.interner.add_local_reference(ident.id, reference_location); + self.interner.add_local_reference(ident.id, location); (HirLValue::Ident(ident.clone(), typ.clone()), typ, mutable) } - LValue::MemberAccess { object, field_name, span } => { + LValue::MemberAccess { object, field_name, location } => { let (object, lhs_type, mut mutable) = self.elaborate_lvalue(*object); let mut object = Box::new(object); let field_name = field_name.clone(); let object_ref = &mut object; let mutable_ref = &mut mutable; - let location = Location::new(span, self.file); let dereference_lhs = move |_: &mut Self, _, element_type| { // We must create a temporary value first to move out of object_ref before @@ -403,7 +409,12 @@ impl<'context> Elaborator<'context> { let name = &field_name.0.contents; let (object_type, field_index) = self - .check_field_access(&lhs_type, name, field_name.span(), Some(dereference_lhs)) + .check_field_access( + &lhs_type, + name, + field_name.location(), + Some(dereference_lhs), + ) .unwrap_or((Type::Error, 0)); let field_index = Some(field_index); @@ -412,16 +423,15 @@ impl<'context> Elaborator<'context> { HirLValue::MemberAccess { object, field_name, field_index, typ, location }; (lvalue, object_type, mutable) } - LValue::Index { array, index, span } => { - let expr_span = index.span; + LValue::Index { array, index, location } => { + let expr_location = index.location; let (index, index_type) = self.elaborate_expression(index); - let location = Location::new(span, self.file); let expected = self.polymorphic_integer_or_field(); self.unify(&index_type, &expected, || TypeCheckError::TypeMismatch { expected_typ: "an integer".to_owned(), expr_typ: index_type.to_string(), - expr_span, + expr_location, }); let (mut lvalue, mut lvalue_type, mut mutable) = self.elaborate_lvalue(*array); @@ -442,19 +452,22 @@ impl<'context> Elaborator<'context> { Type::Slice(elem_type) => *elem_type, Type::Error => Type::Error, Type::String(_) => { - let (_lvalue_name, lvalue_span) = self.get_lvalue_name_and_span(&lvalue); - self.push_err(TypeCheckError::StringIndexAssign { span: lvalue_span }); + let (_id, _lvalue_name, lvalue_location) = + self.get_lvalue_error_info(&lvalue); + self.push_err(TypeCheckError::StringIndexAssign { + location: lvalue_location, + }); Type::Error } Type::TypeVariable(_) => { - self.push_err(TypeCheckError::TypeAnnotationsNeededForIndex { span }); + self.push_err(TypeCheckError::TypeAnnotationsNeededForIndex { location }); Type::Error } other => { self.push_err(TypeCheckError::TypeMismatch { expected_typ: "array".to_string(), expr_typ: other.to_string(), - expr_span: span, + expr_location: location, }); Type::Error } @@ -464,10 +477,9 @@ impl<'context> Elaborator<'context> { let array_type = typ.clone(); (HirLValue::Index { array, index, typ, location }, array_type, mutable) } - LValue::Dereference(lvalue, span) => { + LValue::Dereference(lvalue, location) => { let (lvalue, reference_type, _) = self.elaborate_lvalue(*lvalue); let lvalue = Box::new(lvalue); - let location = Location::new(span, self.file); let element_type = Type::type_variable(self.interner.next_type_variable_id()); let expected_type = Type::MutableReference(Box::new(element_type.clone())); @@ -475,7 +487,7 @@ impl<'context> Elaborator<'context> { self.unify(&reference_type, &expected_type, || TypeCheckError::TypeMismatch { expected_typ: expected_type.to_string(), expr_typ: reference_type.to_string(), - expr_span: span, + expr_location: location, }); // Dereferences are always mutable since we already type checked against a &mut T @@ -483,8 +495,8 @@ impl<'context> Elaborator<'context> { let lvalue = HirLValue::Dereference { lvalue, element_type, location }; (lvalue, typ, true) } - LValue::Interned(id, span) => { - let lvalue = self.interner.get_lvalue(id, span).clone(); + LValue::Interned(id, location) => { + let lvalue = self.interner.get_lvalue(id, location).clone(); self.elaborate_lvalue(lvalue) } } @@ -495,7 +507,7 @@ impl<'context> Elaborator<'context> { &mut self, lhs_type: &Type, field_name: &str, - span: Span, + location: Location, dereference_lhs: Option, ) -> Option<(Type, usize)> { let lhs_type = lhs_type.follow_bindings(); @@ -504,10 +516,9 @@ impl<'context> Elaborator<'context> { Type::DataType(s, args) => { let s = s.borrow(); if let Some((field, visibility, index)) = s.get_field(field_name, args) { - let reference_location = Location::new(span, self.file); - self.interner.add_struct_member_reference(s.id, index, reference_location); + self.interner.add_struct_member_reference(s.id, index, location); - self.check_struct_field_visibility(&s, field_name, visibility, span); + self.check_struct_field_visibility(&s, field_name, visibility, location); return Some((field, index)); } @@ -522,7 +533,7 @@ impl<'context> Elaborator<'context> { index, lhs_type, length, - span, + location, }); return None; } @@ -536,12 +547,12 @@ impl<'context> Elaborator<'context> { return self.check_field_access( element, field_name, - span, + location, Some(dereference_lhs), ); } else { let (element, index) = - self.check_field_access(element, field_name, span, dereference_lhs)?; + self.check_field_access(element, field_name, location, dereference_lhs)?; return Some((Type::MutableReference(Box::new(element)), index)); } } @@ -551,12 +562,12 @@ impl<'context> Elaborator<'context> { // If we get here the type has no field named 'access.rhs'. // Now we specialize the error message based on whether we know the object type in question yet. if let Type::TypeVariable(..) = &lhs_type { - self.push_err(TypeCheckError::TypeAnnotationsNeededForFieldAccess { span }); + self.push_err(TypeCheckError::TypeAnnotationsNeededForFieldAccess { location }); } else if lhs_type != Type::Error { self.push_err(TypeCheckError::AccessUnknownMember { lhs_type, field_name: field_name.to_string(), - span, + location, }); } @@ -568,7 +579,7 @@ impl<'context> Elaborator<'context> { struct_type: &DataType, field_name: &str, visibility: ItemVisibility, - span: Span, + location: Location, ) { if self.silence_field_visibility_errors > 0 { return; @@ -576,18 +587,18 @@ impl<'context> Elaborator<'context> { if !struct_member_is_visible(struct_type.id, visibility, self.module_id(), self.def_maps) { self.push_err(ResolverError::PathResolutionError(PathResolutionError::Private( - Ident::new(field_name.to_string(), span), + Ident::new(field_name.to_string(), location), ))); } } fn elaborate_comptime_statement(&mut self, statement: Statement) -> (HirStatement, Type) { - let span = statement.span; + let location = statement.location; let (hir_statement, _typ) = self.elaborate_in_comptime_context(|this| this.elaborate_statement(statement)); let mut interpreter = self.setup_interpreter(); let value = interpreter.evaluate_statement(hir_statement); - let (expr, typ) = self.inline_comptime_value(value, span); + let (expr, typ) = self.inline_comptime_value(value, location); let location = self.interner.id_location(hir_statement); self.debug_comptime(location, |interner| expr.to_display_ast(interner).kind); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/trait_impls.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/trait_impls.rs index aa27ac29fa67..63befb67b093 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/trait_impls.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/trait_impls.rs @@ -1,23 +1,25 @@ use crate::{ + ResolvedGeneric, ast::{Ident, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, graph::CrateId, - hir::def_collector::{dc_crate::UnresolvedTraitImpl, errors::DefCollectorErrorKind}, + hir::def_collector::{ + dc_crate::{CompilationError, UnresolvedTraitImpl}, + errors::DefCollectorErrorKind, + }, node_interner::TraitImplId, - ResolvedGeneric, }; use crate::{ + Type, hir::def_collector::errors::DuplicateType, hir_def::traits::{TraitConstraint, TraitFunction}, node_interner::{FuncId, TraitId}, - Type, }; -use noirc_errors::Location; use rustc_hash::FxHashSet as HashSet; use super::Elaborator; -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { pub(super) fn collect_trait_impl_methods( &mut self, trait_id: TraitId, @@ -25,7 +27,6 @@ impl<'context> Elaborator<'context> { trait_impl_where_clause: &[TraitConstraint], ) { self.local_module = trait_impl.module_id; - self.file = trait_impl.file_id; let impl_id = trait_impl.impl_id.expect("impl_id should be set in define_function_metas"); @@ -59,7 +60,7 @@ impl<'context> Elaborator<'context> { let func_id = self.interner.push_empty_fn(); let module = self.module_id(); - let location = Location::new(default_impl.def.span, trait_impl.file_id); + let location = default_impl.def.location; self.interner.push_function(func_id, &default_impl.def, module, location); self.define_function_meta(&mut default_impl_clone, func_id, None); func_ids_in_trait.insert(func_id); @@ -72,7 +73,7 @@ impl<'context> Elaborator<'context> { self.push_err(DefCollectorErrorKind::TraitMissingMethod { trait_name: self.interner.get_trait(trait_id).name.clone(), method_name: method.name.clone(), - trait_impl_span: trait_impl.object_type.span, + trait_impl_location: trait_impl.object_type.location, }); } } else { @@ -105,14 +106,17 @@ impl<'context> Elaborator<'context> { let the_trait = self.interner.get_trait_mut(trait_id); the_trait.set_methods(methods); + let trait_name = the_trait.name.clone(); + // Emit MethodNotInTrait error for methods in the impl block that // don't have a corresponding method signature defined in the trait for (_, func_id, func) in &trait_impl.methods.functions { if !func_ids_in_trait.contains(func_id) { - let trait_name = the_trait.name.clone(); + let trait_name = trait_name.clone(); let impl_method = func.name_ident().clone(); let error = DefCollectorErrorKind::MethodNotInTrait { trait_name, impl_method }; - self.errors.push((error.into(), self.file)); + let error: CompilationError = error.into(); + self.push_err(error); } } @@ -200,9 +204,9 @@ impl<'context> Elaborator<'context> { constraint_typ: override_trait_constraint.typ, constraint_name: the_trait.name.0.contents.clone(), constraint_generics: override_trait_constraint.trait_bound.trait_generics, - constraint_span: override_trait_constraint.trait_bound.span, + constraint_location: override_trait_constraint.trait_bound.location, trait_method_name: method.name.0.contents.clone(), - trait_method_span: method.location.span, + trait_method_location: method.location, }); } } @@ -214,7 +218,6 @@ impl<'context> Elaborator<'context> { trait_impl: &UnresolvedTraitImpl, ) { self.local_module = trait_impl.module_id; - self.file = trait_impl.file_id; let object_crate = match &trait_impl.resolved_object_type { Some(Type::DataType(struct_type, _)) => struct_type.borrow().id.krate(), @@ -224,7 +227,7 @@ impl<'context> Elaborator<'context> { let the_trait = self.interner.get_trait(trait_id); if self.crate_id != the_trait.crate_id && self.crate_id != object_crate { self.push_err(DefCollectorErrorKind::TraitImplOrphaned { - span: trait_impl.object_type.span, + location: trait_impl.object_type.location, }); } } @@ -235,12 +238,12 @@ impl<'context> Elaborator<'context> { ) -> Vec<(Ident, UnresolvedType)> { let mut associated_types = Vec::new(); for (name, _, expr) in trait_impl.associated_constants.drain(..) { - let span = expr.span; - let typ = match UnresolvedTypeExpression::from_expr(expr, span) { - Ok(expr) => UnresolvedTypeData::Expression(expr).with_span(span), + let location = expr.location; + let typ = match UnresolvedTypeExpression::from_expr(expr, location) { + Ok(expr) => UnresolvedTypeData::Expression(expr).with_location(location), Err(error) => { self.push_err(error); - UnresolvedTypeData::Error.with_span(span) + UnresolvedTypeData::Error.with_location(location) } }; associated_types.push((name, typ)); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs index bbc1214de9ed..a931dde93de5 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs @@ -1,9 +1,10 @@ use std::{collections::BTreeMap, rc::Rc}; use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use crate::{ + ResolvedGeneric, Type, TypeBindings, ast::{ BlockExpression, FunctionDefinition, FunctionKind, FunctionReturnType, Ident, ItemVisibility, NoirFunction, TraitItem, UnresolvedGeneric, UnresolvedGenerics, @@ -15,12 +16,11 @@ use crate::{ traits::{ResolvedTraitBound, TraitFunction}, }, node_interner::{DependencyId, FuncId, NodeInterner, ReferenceId, TraitId}, - ResolvedGeneric, Type, TypeBindings, }; use super::Elaborator; -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { pub fn collect_traits(&mut self, traits: &mut BTreeMap) { for (trait_id, unresolved_trait) in traits { self.local_module = unresolved_trait.module_id; @@ -97,7 +97,6 @@ impl<'context> Elaborator<'context> { unresolved_trait: &UnresolvedTrait, ) -> Vec { self.local_module = unresolved_trait.module_id; - self.file = self.def_maps[&self.crate_id].file_id(unresolved_trait.module_id); let mut functions = vec![]; @@ -117,15 +116,15 @@ impl<'context> Elaborator<'context> { self.recover_generics(|this| { let the_trait = this.interner.get_trait(trait_id); let self_typevar = the_trait.self_type_typevar.clone(); - let name_span = the_trait.name.span(); + let name_location = the_trait.name.location(); this.add_existing_generic( &UnresolvedGeneric::Variable(Ident::from("Self")), - name_span, + name_location, &ResolvedGeneric { name: Rc::new("Self".to_owned()), type_var: self_typevar, - span: name_span, + location: name_location, }, ); @@ -274,6 +273,7 @@ impl<'context> Elaborator<'context> { pub(crate) fn check_trait_impl_method_matches_declaration( interner: &mut NodeInterner, function: FuncId, + noir_function: &NoirFunction, ) -> Vec { let meta = interner.function_meta(&function); let modifiers = interner.function_modifiers(&function); @@ -297,9 +297,9 @@ pub(crate) fn check_trait_impl_method_matches_declaration( if trait_info.generics.len() != impl_.trait_generics.len() { let expected = trait_info.generics.len(); let found = impl_.trait_generics.len(); - let span = impl_.ident.span(); + let location = impl_.ident.location(); let item = trait_info.name.to_string(); - errors.push(TypeCheckError::GenericCountMismatch { item, expected, found, span }); + errors.push(TypeCheckError::GenericCountMismatch { item, expected, found, location }); } // Substitute each generic on the trait with the corresponding generic on the impl @@ -319,17 +319,17 @@ pub(crate) fn check_trait_impl_method_matches_declaration( if modifiers.is_unconstrained != trait_fn_modifiers.is_unconstrained { let expected = trait_fn_modifiers.is_unconstrained; - let span = meta.name.location.span; + let location = meta.name.location; let item = method_name.to_string(); - errors.push(TypeCheckError::UnconstrainedMismatch { item, expected, span }); + errors.push(TypeCheckError::UnconstrainedMismatch { item, expected, location }); } if trait_fn_meta.direct_generics.len() != meta.direct_generics.len() { let expected = trait_fn_meta.direct_generics.len(); let found = meta.direct_generics.len(); - let span = meta.name.location.span; + let location = meta.name.location; let item = method_name.to_string(); - errors.push(TypeCheckError::GenericCountMismatch { item, expected, found, span }); + errors.push(TypeCheckError::GenericCountMismatch { item, expected, found, location }); } // Substitute each generic on the trait function with the corresponding generic on the impl function @@ -350,7 +350,9 @@ pub(crate) fn check_trait_impl_method_matches_declaration( definition_type, method_name, &meta.parameters, - meta.name.location.span, + &meta.return_type, + noir_function, + meta.name.location, &trait_info.name.0.contents, &mut errors, ); @@ -359,12 +361,15 @@ pub(crate) fn check_trait_impl_method_matches_declaration( errors } +#[allow(clippy::too_many_arguments)] fn check_function_type_matches_expected_type( expected: &Type, actual: &Type, method_name: &str, actual_parameters: &Parameters, - span: Span, + actual_return_type: &FunctionReturnType, + noir_function: &NoirFunction, + location: Location, trait_name: &str, errors: &mut Vec, ) { @@ -382,11 +387,16 @@ fn check_function_type_matches_expected_type( if params_a.len() == params_b.len() { for (i, (a, b)) in params_a.iter().zip(params_b.iter()).enumerate() { if a.try_unify(b, &mut bindings).is_err() { + let parameter_location = noir_function.def.parameters.get(i); + let parameter_location = parameter_location.map(|param| param.typ.location); + let parameter_location = + parameter_location.unwrap_or_else(|| actual_parameters.0[i].0.location()); + errors.push(TypeCheckError::TraitMethodParameterTypeMismatch { method_name: method_name.to_string(), expected_typ: a.to_string(), actual_typ: b.to_string(), - parameter_span: actual_parameters.0[i].0.span(), + parameter_location, parameter_index: i + 1, }); } @@ -396,7 +406,7 @@ fn check_function_type_matches_expected_type( errors.push(TypeCheckError::TypeMismatch { expected_typ: ret_a.to_string(), expr_typ: ret_b.to_string(), - expr_span: span, + expr_location: actual_return_type.location(), }); } } else { @@ -405,7 +415,7 @@ fn check_function_type_matches_expected_type( expected_num_parameters: params_a.len(), trait_name: trait_name.to_string(), method_name: method_name.to_string(), - span, + location, }); } } @@ -416,6 +426,10 @@ fn check_function_type_matches_expected_type( if !bindings.is_empty() { let expected_typ = expected.to_string(); let expr_typ = actual.to_string(); - errors.push(TypeCheckError::TypeMismatch { expected_typ, expr_typ, expr_span: span }); + errors.push(TypeCheckError::TypeMismatch { + expected_typ, + expr_typ, + expr_location: location, + }); } } 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 53d0860ebf12..9b717f9fca33 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs @@ -2,10 +2,11 @@ use std::{borrow::Cow, rc::Rc}; use im::HashSet; use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use rustc_hash::FxHashMap as HashMap; use crate::{ + Generics, Kind, ResolvedGeneric, Type, TypeBinding, TypeBindings, UnificationError, ast::{ AsTraitPath, BinaryOpKind, GenericTypeArgs, Ident, IntegerBitSize, Path, PathKind, Signedness, UnaryOp, UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, @@ -13,11 +14,11 @@ use crate::{ }, hir::{ def_collector::dc_crate::CompilationError, - def_map::{fully_qualified_module_path, ModuleDefId}, + def_map::{ModuleDefId, fully_qualified_module_path}, resolution::{errors::ResolverError, import::PathResolutionError}, type_check::{ - generics::{Generic, TraitGenerics}, NoMatchingImplFoundError, Source, TypeCheckError, + generics::{Generic, TraitGenerics}, }, }, hir_def::{ @@ -30,14 +31,14 @@ use crate::{ traits::{NamedType, ResolvedTraitBound, Trait, TraitConstraint}, }, node_interner::{ - DependencyId, ExprId, FuncId, GlobalValue, ImplSearchErrorKind, NodeInterner, TraitId, - TraitImplKind, TraitMethodId, + DependencyId, ExprId, FuncId, GlobalValue, ImplSearchErrorKind, TraitId, TraitImplKind, + TraitMethodId, }, + signed_field::SignedField, token::SecondaryAttribute, - Generics, Kind, ResolvedGeneric, Type, TypeBinding, TypeBindings, UnificationError, }; -use super::{lints, path_resolution::PathResolutionItem, Elaborator, UnsafeBlockStatus}; +use super::{Elaborator, UnsafeBlockStatus, lints, path_resolution::PathResolutionItem}; pub const SELF_TYPE_NAME: &str = "Self"; @@ -47,13 +48,13 @@ pub(super) struct TraitPathResolution { pub(super) errors: Vec, } -impl<'context> Elaborator<'context> { +impl Elaborator<'_> { /// Translates an UnresolvedType to a Type with a `TypeKind::Normal` pub(crate) fn resolve_type(&mut self, typ: UnresolvedType) -> Type { - let span = typ.span; + let location = typ.location; let resolved_type = self.resolve_type_inner(typ, &Kind::Normal); if resolved_type.is_nested_slice() { - self.push_err(ResolverError::NestedSlices { span }); + self.push_err(ResolverError::NestedSlices { location }); } resolved_type } @@ -63,11 +64,11 @@ impl<'context> Elaborator<'context> { pub fn resolve_type_inner(&mut self, typ: UnresolvedType, kind: &Kind) -> Type { use crate::ast::UnresolvedTypeData::*; - let span = typ.span; - let (named_path_span, is_self_type_name, is_synthetic) = + let location = typ.location; + let (named_path_location, is_self_type_name, is_synthetic) = if let Named(ref named_path, _, synthetic) = typ.typ { ( - Some(named_path.last_ident().span()), + Some(named_path.last_ident().location()), named_path.last_ident().is_self_type_name(), synthetic, ) @@ -79,38 +80,38 @@ impl<'context> Elaborator<'context> { FieldElement => Type::FieldElement, Array(size, elem) => { let elem = Box::new(self.resolve_type_inner(*elem, kind)); - let size = self.convert_expression_type(size, &Kind::u32(), span); + let size = self.convert_expression_type(size, &Kind::u32(), location); Type::Array(Box::new(size), elem) } Slice(elem) => { let elem = Box::new(self.resolve_type_inner(*elem, kind)); Type::Slice(elem) } - Expression(expr) => self.convert_expression_type(expr, kind, span), + Expression(expr) => self.convert_expression_type(expr, kind, location), Integer(sign, bits) => Type::Integer(sign, bits), Bool => Type::Bool, String(size) => { - let resolved_size = self.convert_expression_type(size, &Kind::u32(), span); + let resolved_size = self.convert_expression_type(size, &Kind::u32(), location); Type::String(Box::new(resolved_size)) } FormatString(size, fields) => { - let resolved_size = self.convert_expression_type(size, &Kind::u32(), span); + let resolved_size = self.convert_expression_type(size, &Kind::u32(), location); let fields = self.resolve_type_inner(*fields, kind); Type::FmtString(Box::new(resolved_size), Box::new(fields)) } Quoted(quoted) => { let in_function = matches!(self.current_item, Some(DependencyId::Function(_))); if in_function && !self.in_comptime_context() { - let span = typ.span; + let location = typ.location; let typ = quoted.to_string(); - self.push_err(ResolverError::ComptimeTypeInRuntimeCode { span, typ }); + self.push_err(ResolverError::ComptimeTypeInRuntimeCode { location, typ }); } Type::Quoted(quoted) } Unit => Type::Unit, Unspecified => { - let span = typ.span; - self.push_err(TypeCheckError::UnspecifiedType { span }); + let location = typ.location; + self.push_err(TypeCheckError::UnspecifiedType { location }); Type::Error } Error => Type::Error, @@ -123,7 +124,7 @@ impl<'context> Elaborator<'context> { Function(args, ret, env, unconstrained) => { let args = vecmap(args, |arg| self.resolve_type_inner(arg, kind)); let ret = Box::new(self.resolve_type_inner(*ret, kind)); - let env_span = env.span; + let env_location = env.location; let env = Box::new(self.resolve_type_inner(*env, kind)); @@ -134,7 +135,7 @@ impl<'context> Elaborator<'context> { _ => { self.push_err(ResolverError::InvalidClosureEnvironment { typ: *env, - span: env_span, + location: env_location, }); Type::Error } @@ -148,11 +149,11 @@ impl<'context> Elaborator<'context> { AsTraitPath(path) => self.resolve_as_trait_path(*path), Interned(id) => { let typ = self.interner.get_unresolved_type_data(id).clone(); - return self.resolve_type_inner(UnresolvedType { typ, span }, kind); + return self.resolve_type_inner(UnresolvedType { typ, location }, kind); } }; - let location = Location::new(named_path_span.unwrap_or(typ.span), self.file); + let location = named_path_location.unwrap_or(typ.location); match resolved_type { Type::DataType(ref data_type, _) => { // Record the location of the type reference @@ -173,11 +174,11 @@ impl<'context> Elaborator<'context> { if !kind.unifies(&resolved_type.kind()) { let expected_typ_err = CompilationError::TypeError(TypeCheckError::TypeKindMismatch { - expected_kind: kind.to_string(), - expr_kind: resolved_type.kind().to_string(), - expr_span: span, + expected_kind: kind.clone(), + expr_kind: resolved_type.kind(), + expr_location: location, }); - self.errors.push((expected_typ_err, self.file)); + self.push_err(expected_typ_err); return Type::Error; } @@ -223,7 +224,9 @@ impl<'context> Elaborator<'context> { if name == SELF_TYPE_NAME { if let Some(self_type) = self.self_type.clone() { if !args.is_empty() { - self.push_err(ResolverError::GenericsOnSelfType { span: path.span() }); + self.push_err(ResolverError::GenericsOnSelfType { + location: path.location, + }); } return self_type; } @@ -232,16 +235,16 @@ impl<'context> Elaborator<'context> { } } else if let Some(typ) = self.lookup_associated_type_on_self(&path) { if !args.is_empty() { - self.push_err(ResolverError::GenericsOnAssociatedType { span: path.span() }); + self.push_err(ResolverError::GenericsOnAssociatedType { location: path.location }); } return typ; } - let span = path.span(); + let location = path.location; if let Some(type_alias) = self.lookup_type_alias(path.clone()) { let id = type_alias.borrow().id; - let (args, _) = self.resolve_type_args(args, id, path.span()); + let (args, _) = self.resolve_type_args(args, id, location); if let Some(item) = self.current_item { self.interner.add_type_alias_dependency(item, id); @@ -249,7 +252,7 @@ impl<'context> Elaborator<'context> { // Collecting Type Alias references [Location]s to be used by LSP in order // to resolve the definition of the type alias - self.interner.add_type_alias_ref(id, Location::new(span, self.file)); + self.interner.add_type_alias_ref(id, location); // Because there is no ordering to when type aliases (and other globals) are resolved, // it is possible for one to refer to an Error type and issue no error if it is set @@ -263,7 +266,7 @@ impl<'context> Elaborator<'context> { Some(data_type) => { if self.resolving_ids.contains(&data_type.borrow().id) { self.push_err(ResolverError::SelfReferentialType { - span: data_type.borrow().name.span(), + location: data_type.borrow().name.location(), }); return Type::Error; @@ -277,11 +280,11 @@ impl<'context> Elaborator<'context> { .any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) { self.push_err(ResolverError::AbiAttributeOutsideContract { - span: data_type.borrow().name.span(), + location: data_type.borrow().name.location(), }); } - let (args, _) = self.resolve_type_args(args, data_type.borrow(), span); + let (args, _) = self.resolve_type_args(args, data_type.borrow(), location); if let Some(current_item) = self.current_item { let dependency_id = data_type.borrow().id; @@ -297,11 +300,11 @@ impl<'context> Elaborator<'context> { fn resolve_trait_as_type(&mut self, path: Path, args: GenericTypeArgs) -> Type { // Fetch information needed from the trait as the closure for resolving all the `args` // requires exclusive access to `self` - let span = path.span; + let location = path.location; let trait_as_type_info = self.lookup_trait_or_error(path).map(|t| t.id); if let Some(id) = trait_as_type_info { - let (ordered, named) = self.resolve_type_args(args, id, span); + let (ordered, named) = self.resolve_type_args(args, id, location); let name = self.interner.get_trait(id).name.to_string(); let generics = TraitGenerics { ordered, named }; Type::TraitAsType(id, Rc::new(name), generics) @@ -316,25 +319,25 @@ impl<'context> Elaborator<'context> { &mut self, args: GenericTypeArgs, item: TraitId, - span: Span, + location: Location, ) -> (Vec, Vec) { - self.resolve_type_args_inner(args, item, span, false) + self.resolve_type_args_inner(args, item, location, false) } pub(super) fn resolve_type_args( &mut self, args: GenericTypeArgs, item: impl Generic, - span: Span, + location: Location, ) -> (Vec, Vec) { - self.resolve_type_args_inner(args, item, span, true) + self.resolve_type_args_inner(args, item, location, true) } pub(super) fn resolve_type_args_inner( &mut self, mut args: GenericTypeArgs, item: impl Generic, - span: Span, + location: Location, allow_implicit_named_args: bool, ) -> (Vec, Vec) { let expected_kinds = item.generics(self.interner); @@ -344,9 +347,9 @@ impl<'context> Elaborator<'context> { item: item.item_name(self.interner), expected: expected_kinds.len(), found: args.ordered_args.len(), - span, + location, }); - let error_type = UnresolvedTypeData::Error.with_span(span); + let error_type = UnresolvedTypeData::Error.with_location(location); args.ordered_args.resize(expected_kinds.len(), error_type); } @@ -360,12 +363,12 @@ impl<'context> Elaborator<'context> { associated = self.resolve_associated_type_args( args.named_args, item, - span, + location, allow_implicit_named_args, ); } else if !args.named_args.is_empty() { let item_kind = item.item_kind(); - self.push_err(ResolverError::NamedTypeArgs { span, item_kind }); + self.push_err(ResolverError::NamedTypeArgs { location, item_kind }); } (ordered, associated) @@ -375,7 +378,7 @@ impl<'context> Elaborator<'context> { &mut self, args: Vec<(Ident, UnresolvedType)>, item: impl Generic, - span: Span, + location: Location, allow_implicit_named_args: bool, ) -> Vec { let mut seen_args = HashMap::default(); @@ -389,8 +392,8 @@ impl<'context> Elaborator<'context> { required_args.iter().position(|item| item.name.as_ref() == &name.0.contents); let Some(index) = index else { - if let Some(prev_span) = seen_args.get(&name.0.contents).copied() { - self.push_err(TypeCheckError::DuplicateNamedTypeArg { name, prev_span }); + if let Some(prev_location) = seen_args.get(&name.0.contents).copied() { + self.push_err(TypeCheckError::DuplicateNamedTypeArg { name, prev_location }); } else { let item = item.item_name(self.interner); self.push_err(TypeCheckError::NoSuchNamedTypeArg { name, item }); @@ -400,7 +403,7 @@ impl<'context> Elaborator<'context> { // Remove the argument from the required list so we remember that we already have it let expected = required_args.remove(index); - seen_args.insert(name.0.contents.clone(), name.span()); + seen_args.insert(name.0.contents.clone(), name.location()); let typ = self.resolve_type_inner(typ, &expected.kind()); resolved.push(NamedType { name, typ }); @@ -412,12 +415,12 @@ impl<'context> Elaborator<'context> { let name = generic.name.clone(); if allow_implicit_named_args { - let name = Ident::new(name.as_ref().clone(), span); + let name = Ident::new(name.as_ref().clone(), location); let typ = self.interner.next_type_variable(); resolved.push(NamedType { name, typ }); } else { let item = item.item_name(self.interner); - self.push_err(TypeCheckError::MissingNamedTypeArg { item, span, name }); + self.push_err(TypeCheckError::MissingNamedTypeArg { item, location, name }); } } @@ -442,7 +445,7 @@ impl<'context> Elaborator<'context> { self.interner.add_global_dependency(current_item, id); } - let reference_location = Location::new(path.span(), self.file); + let reference_location = path.location; self.interner.add_global_reference(id, reference_location); let kind = self .interner @@ -461,26 +464,33 @@ impl<'context> Elaborator<'context> { }; let rhs = stmt.expression; - let span = self.interner.expr_span(&rhs); + let location = self.interner.expr_location(&rhs); let GlobalValue::Resolved(global_value) = &self.interner.get_global(id).value else { - self.push_err(ResolverError::UnevaluatedGlobalType { span }); + self.push_err(ResolverError::UnevaluatedGlobalType { location }); return None; }; let Some(global_value) = global_value.to_field_element() else { let global_value = global_value.clone(); if global_value.is_integral() { - self.push_err(ResolverError::NegativeGlobalType { span, global_value }); + self.push_err(ResolverError::NegativeGlobalType { location, global_value }); } else { - self.push_err(ResolverError::NonIntegralGlobalType { span, global_value }); + self.push_err(ResolverError::NonIntegralGlobalType { + location, + global_value, + }); } return None; }; - let Ok(global_value) = kind.ensure_value_fits(global_value, span) else { - self.push_err(ResolverError::GlobalLargerThanKind { span, global_value, kind }); + let Ok(global_value) = kind.ensure_value_fits(global_value, location) else { + self.push_err(ResolverError::GlobalLargerThanKind { + location, + global_value, + kind, + }); return None; }; @@ -494,42 +504,38 @@ impl<'context> Elaborator<'context> { &mut self, length: UnresolvedTypeExpression, expected_kind: &Kind, - span: Span, + location: Location, ) -> Type { match length { UnresolvedTypeExpression::Variable(path) => { let typ = self.resolve_named_type(path, GenericTypeArgs::default()); - self.check_kind(typ, expected_kind, span) + self.check_kind(typ, expected_kind, location) } UnresolvedTypeExpression::Constant(int, _span) => { Type::Constant(int, expected_kind.clone()) } - UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, span) => { - let (lhs_span, rhs_span) = (lhs.span(), rhs.span()); - let lhs = self.convert_expression_type(*lhs, expected_kind, lhs_span); - let rhs = self.convert_expression_type(*rhs, expected_kind, rhs_span); + UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, location) => { + let (lhs_location, rhs_location) = (lhs.location(), rhs.location()); + let lhs = self.convert_expression_type(*lhs, expected_kind, lhs_location); + let rhs = self.convert_expression_type(*rhs, expected_kind, rhs_location); match (lhs, rhs) { (Type::Constant(lhs, lhs_kind), Type::Constant(rhs, rhs_kind)) => { if !lhs_kind.unifies(&rhs_kind) { self.push_err(TypeCheckError::TypeKindMismatch { - expected_kind: lhs_kind.to_string(), - expr_kind: rhs_kind.to_string(), - expr_span: span, + expected_kind: lhs_kind, + expr_kind: rhs_kind, + expr_location: location, }); return Type::Error; } - match op.function(lhs, rhs, &lhs_kind, span) { + match op.function(lhs, rhs, &lhs_kind, location) { Ok(result) => Type::Constant(result, lhs_kind), Err(err) => { let err = Box::new(err); - self.push_err(ResolverError::BinaryOpError { - lhs, - op, - rhs, - err, - span, - }); + let error = + ResolverError::BinaryOpError { lhs, op, rhs, err, location }; + self.push_err(error); Type::Error } } @@ -543,17 +549,17 @@ impl<'context> Elaborator<'context> { } UnresolvedTypeExpression::AsTraitPath(path) => { let typ = self.resolve_as_trait_path(*path); - self.check_kind(typ, expected_kind, span) + self.check_kind(typ, expected_kind, location) } } } - fn check_kind(&mut self, typ: Type, expected_kind: &Kind, span: Span) -> Type { + fn check_kind(&mut self, typ: Type, expected_kind: &Kind, location: Location) -> Type { if !typ.kind().unifies(expected_kind) { self.push_err(TypeCheckError::TypeKindMismatch { - expected_kind: expected_kind.to_string(), - expr_kind: typ.kind().to_string(), - expr_span: span, + expected_kind: expected_kind.clone(), + expr_kind: typ.kind(), + expr_location: location, }); return Type::Error; } @@ -561,19 +567,20 @@ impl<'context> Elaborator<'context> { } fn resolve_as_trait_path(&mut self, path: AsTraitPath) -> Type { - let span = path.trait_path.span; + let location = path.trait_path.location; let Some(trait_id) = self.resolve_trait_by_path(path.trait_path.clone()) else { // Error should already be pushed in the None case return Type::Error; }; - let (ordered, named) = self.resolve_type_args(path.trait_generics.clone(), trait_id, span); + let (ordered, named) = + self.resolve_type_args(path.trait_generics.clone(), trait_id, location); let object_type = self.resolve_type(path.typ.clone()); match self.interner.lookup_trait_implementation(&object_type, trait_id, &ordered, &named) { Ok(impl_kind) => self.get_associated_type_from_trait_impl(path, impl_kind), Err(constraints) => { - self.push_trait_constraint_error(&object_type, constraints, span); + self.push_trait_constraint_error(&object_type, constraints, location); Type::Error } } @@ -622,7 +629,7 @@ impl<'context> Elaborator<'context> { if name == SELF_TYPE_NAME { let the_trait = self.interner.get_trait(trait_id); let method = the_trait.find_method(method.0.contents.as_str())?; - let constraint = the_trait.as_constraint(path.span); + let constraint = the_trait.as_constraint(path.location); return Some(TraitPathResolution { method: TraitMethod { method_id: method, constraint, assumed: true }, item: None, @@ -643,7 +650,7 @@ impl<'context> Elaborator<'context> { let meta = self.interner.try_function_meta(&func_id)?; let the_trait = self.interner.get_trait(meta.trait_id?); let method = the_trait.find_method(path.last_name())?; - let constraint = the_trait.as_constraint(path.span); + let constraint = the_trait.as_constraint(path.location); Some(TraitPathResolution { method: TraitMethod { method_id: method, constraint, assumed: false }, item: Some(path_resolution.item), @@ -691,7 +698,7 @@ impl<'context> Elaborator<'context> { } let mut path = path.clone(); - let span = path.span(); + let location = path.location; let last_segment = path.pop(); let before_last_segment = path.last_segment(); @@ -716,7 +723,7 @@ impl<'context> Elaborator<'context> { } let (hir_method_reference, error) = - self.get_trait_method_in_scope(&trait_methods, method_name, last_segment.span); + self.get_trait_method_in_scope(&trait_methods, method_name, last_segment.location); let hir_method_reference = hir_method_reference?; let func_id = hir_method_reference.func_id(self.interner)?; let HirMethodReference::TraitMethodId(trait_method_id, _, _) = hir_method_reference else { @@ -725,7 +732,7 @@ impl<'context> Elaborator<'context> { let trait_id = trait_method_id.trait_id; let trait_ = self.interner.get_trait(trait_id); - let mut constraint = trait_.as_constraint(span); + let mut constraint = trait_.as_constraint(location); constraint.typ = typ; let method = TraitMethod { method_id: trait_method_id, constraint, assumed: false }; @@ -759,7 +766,7 @@ impl<'context> Elaborator<'context> { make_error: impl FnOnce() -> TypeCheckError, ) { if let Err(UnificationError) = actual.unify(expected) { - self.errors.push((make_error().into(), self.file)); + self.push_err(make_error()); } } @@ -770,12 +777,12 @@ impl<'context> Elaborator<'context> { &mut self, actual: &Type, expected: &Type, - file: fm::FileId, make_error: impl FnOnce() -> TypeCheckError, ) { let mut bindings = TypeBindings::new(); if actual.try_unify(expected, &mut bindings).is_err() { - self.errors.push((make_error().into(), file)); + let error: CompilationError = make_error().into(); + self.push_err(error); } } @@ -785,19 +792,19 @@ impl<'context> Elaborator<'context> { actual: &Type, expected: &Type, expression: ExprId, - span: Span, + location: Location, make_error: impl FnOnce() -> TypeCheckError, ) { let mut errors = Vec::new(); actual.unify_with_coercions( expected, expression, - span, + location, self.interner, &mut errors, make_error, ); - self.errors.extend(errors.into_iter().map(|error| (error.into(), self.file))); + self.push_errors(errors.into_iter().map(|error| error.into())); } /// Return a fresh integer or field type variable and log it @@ -847,7 +854,7 @@ impl<'context> Elaborator<'context> { trait_method_id: None, })); self.interner.push_expr_type(object, element.as_ref().clone()); - self.interner.push_expr_location(object, location.span, location.file); + self.interner.push_expr_location(object, location); // Recursively dereference to allow for converting &mut &mut T to T self.insert_auto_dereferences(object, *element) @@ -885,24 +892,24 @@ impl<'context> Elaborator<'context> { &mut self, fn_params: &[Type], fn_ret: &Type, - callsite_args: &[(Type, ExprId, Span)], - span: Span, + callsite_args: &[(Type, ExprId, Location)], + location: Location, ) -> Type { if fn_params.len() != callsite_args.len() { self.push_err(TypeCheckError::ParameterCountMismatch { expected: fn_params.len(), found: callsite_args.len(), - span, + location, }); return Type::Error; } - for (param, (arg, arg_expr_id, arg_span)) in fn_params.iter().zip(callsite_args) { - self.unify_with_coercions(arg, param, *arg_expr_id, *arg_span, || { + for (param, (arg, arg_expr_id, arg_location)) in fn_params.iter().zip(callsite_args) { + self.unify_with_coercions(arg, param, *arg_expr_id, *arg_location, || { TypeCheckError::TypeMismatch { expected_typ: param.to_string(), expr_typ: arg.to_string(), - expr_span: *arg_span, + expr_location: *arg_location, } }); } @@ -913,15 +920,15 @@ impl<'context> Elaborator<'context> { pub(super) fn bind_function_type( &mut self, function: Type, - args: Vec<(Type, ExprId, Span)>, - span: Span, + args: Vec<(Type, ExprId, Location)>, + location: Location, ) -> Type { // Could do a single unification for the entire function type, but matching beforehand // lets us issue a more precise error on the individual argument that fails to type check. match function { Type::TypeVariable(binding) if binding.kind() == Kind::Normal => { if let TypeBinding::Bound(typ) = &*binding.borrow() { - return self.bind_function_type(typ.clone(), args, span); + return self.bind_function_type(typ.clone(), args, location); } let ret = self.interner.next_type_variable(); @@ -931,7 +938,7 @@ impl<'context> Elaborator<'context> { Type::Function(args, Box::new(ret.clone()), Box::new(env_type), false); let expected_kind = expected.kind(); - if let Err(error) = binding.try_bind(expected, &expected_kind, span) { + if let Err(error) = binding.try_bind(expected, &expected_kind, location) { self.push_err(error); } ret @@ -939,11 +946,11 @@ impl<'context> Elaborator<'context> { // The closure env is ignored on purpose: call arguments never place // constraints on closure environments. Type::Function(parameters, ret, _env, _unconstrained) => { - self.bind_function_type_impl(¶meters, &ret, &args, span) + self.bind_function_type_impl(¶meters, &ret, &args, location) } Type::Error => Type::Error, found => { - self.push_err(TypeCheckError::ExpectedFunction { found, span }); + self.push_err(TypeCheckError::ExpectedFunction { found, location }); Type::Error } } @@ -954,12 +961,13 @@ impl<'context> Elaborator<'context> { from_expr_id: &ExprId, from: &Type, to: &Type, - span: Span, + location: Location, ) -> Type { let from_follow_bindings = from.follow_bindings(); + use HirExpression::Literal; let from_value_opt = match self.interner.expression(from_expr_id) { - HirExpression::Literal(HirLiteral::Integer(int, false)) => Some(int), + Literal(HirLiteral::Integer(SignedField { field, is_negative: false })) => Some(field), // TODO(https://github.com/noir-lang/noir/issues/6247): // handle negative literals @@ -976,7 +984,7 @@ impl<'context> Elaborator<'context> { let expected = self.polymorphic_integer_or_field(); self.unify(from, &expected, || TypeCheckError::InvalidCast { from: from.clone(), - span, + location, reason: "casting from a non-integral type is unsupported".into(), }); true @@ -984,7 +992,7 @@ impl<'context> Elaborator<'context> { Type::Error => return Type::Error, from => { let reason = "casting from this type is unsupported".into(); - self.push_err(TypeCheckError::InvalidCast { from, span, reason }); + self.push_err(TypeCheckError::InvalidCast { from, location, reason }); return Type::Error; } }; @@ -999,9 +1007,11 @@ impl<'context> Elaborator<'context> { if from_is_polymorphic && from_value > to_maximum_size { let from = from.clone(); let to = to.clone(); - let reason = format!("casting untyped value ({from_value}) to a type with a maximum size ({to_maximum_size}) that's smaller than it"); + let reason = format!( + "casting untyped value ({from_value}) to a type with a maximum size ({to_maximum_size}) that's smaller than it" + ); // we warn that the 'to' type is too small for the value - self.push_err(TypeCheckError::DownsizingCast { from, to, span, reason }); + self.push_err(TypeCheckError::DownsizingCast { from, to, location, reason }); } } @@ -1011,7 +1021,7 @@ impl<'context> Elaborator<'context> { Type::Bool => Type::Bool, Type::Error => Type::Error, _ => { - self.push_err(TypeCheckError::UnsupportedCast { span }); + self.push_err(TypeCheckError::UnsupportedCast { location }); Type::Error } } @@ -1026,7 +1036,7 @@ impl<'context> Elaborator<'context> { lhs_type: &Type, rhs_type: &Type, op: &HirBinaryOp, - span: Span, + location: Location, ) -> Result<(Type, bool), TypeCheckError> { use Type::*; @@ -1035,17 +1045,17 @@ impl<'context> Elaborator<'context> { (Error, _) | (_, Error) => Ok((Bool, false)), (Alias(alias, args), other) | (other, Alias(alias, args)) => { let alias = alias.borrow().get_type(args); - self.comparator_operand_type_rules(&alias, other, op, span) + self.comparator_operand_type_rules(&alias, other, op, location) } // Matches on TypeVariable must be first to follow any type // bindings. (TypeVariable(var), other) | (other, TypeVariable(var)) => { - if let TypeBinding::Bound(ref binding) = &*var.borrow() { - return self.comparator_operand_type_rules(other, binding, op, span); + if let TypeBinding::Bound(binding) = &*var.borrow() { + return self.comparator_operand_type_rules(other, binding, op, location); } - let use_impl = self.bind_type_variables_for_infix(lhs_type, op, rhs_type, span); + let use_impl = self.bind_type_variables_for_infix(lhs_type, op, rhs_type, location); Ok((Bool, use_impl)) } (Integer(sign_x, bit_width_x), Integer(sign_y, bit_width_y)) => { @@ -1053,14 +1063,14 @@ impl<'context> Elaborator<'context> { return Err(TypeCheckError::IntegerSignedness { sign_x: *sign_x, sign_y: *sign_y, - span, + location, }); } if bit_width_x != bit_width_y { return Err(TypeCheckError::IntegerBitWidth { bit_width_x: *bit_width_x, bit_width_y: *bit_width_y, - span, + location, }); } Ok((Bool, false)) @@ -1069,7 +1079,7 @@ impl<'context> Elaborator<'context> { if op.kind.is_valid_for_field_type() { Ok((Bool, false)) } else { - Err(TypeCheckError::FieldComparison { span }) + Err(TypeCheckError::FieldComparison { location }) } } @@ -1080,7 +1090,7 @@ impl<'context> Elaborator<'context> { self.unify(lhs, rhs, || TypeCheckError::TypeMismatchWithSource { expected: lhs.clone(), actual: rhs.clone(), - span: op.location.span, + location: op.location, source: Source::Binary, }); Ok((Bool, true)) @@ -1096,13 +1106,13 @@ impl<'context> Elaborator<'context> { lhs_type: &Type, op: &HirBinaryOp, rhs_type: &Type, - span: Span, + location: Location, ) -> bool { self.unify(lhs_type, rhs_type, || TypeCheckError::TypeMismatchWithSource { expected: lhs_type.clone(), actual: rhs_type.clone(), source: Source::Binary, - span, + location, }); let use_impl = !lhs_type.is_numeric_value(); @@ -1117,9 +1127,9 @@ impl<'context> Elaborator<'context> { use crate::ast::BinaryOpKind::*; use TypeCheckError::*; self.unify(lhs_type, &target, || match op.kind { - Less | LessEqual | Greater | GreaterEqual => FieldComparison { span }, - And | Or | Xor | ShiftRight | ShiftLeft => FieldBitwiseOp { span }, - Modulo => FieldModulo { span }, + Less | LessEqual | Greater | GreaterEqual => FieldComparison { location }, + And | Or | Xor | ShiftRight | ShiftLeft => FieldBitwiseOp { location }, + Modulo => FieldModulo { location }, other => unreachable!("Operator {other:?} should be valid for Field"), }); } @@ -1136,10 +1146,10 @@ impl<'context> Elaborator<'context> { lhs_type: &Type, op: &HirBinaryOp, rhs_type: &Type, - span: Span, + location: Location, ) -> Result<(Type, bool), TypeCheckError> { if op.kind.is_comparator() { - return self.comparator_operand_type_rules(lhs_type, rhs_type, op, span); + return self.comparator_operand_type_rules(lhs_type, rhs_type, op, location); } use Type::*; @@ -1148,7 +1158,7 @@ impl<'context> Elaborator<'context> { (Error, _) | (_, Error) => Ok((Error, false)), (Alias(alias, args), other) | (other, Alias(alias, args)) => { let alias = alias.borrow().get_type(args); - self.infix_operand_type_rules(&alias, op, other, span) + self.infix_operand_type_rules(&alias, op, other, location) } // Matches on TypeVariable must be first so that we follow any type @@ -1158,26 +1168,26 @@ impl<'context> Elaborator<'context> { self.unify( rhs_type, &Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight), - || TypeCheckError::InvalidShiftSize { span }, + || TypeCheckError::InvalidShiftSize { location }, ); let use_impl = if lhs_type.is_numeric_value() { let integer_type = self.polymorphic_integer(); - self.bind_type_variables_for_infix(lhs_type, op, &integer_type, span) + self.bind_type_variables_for_infix(lhs_type, op, &integer_type, location) } else { true }; return Ok((lhs_type.clone(), use_impl)); } - if let TypeBinding::Bound(ref binding) = &*int.borrow() { - return self.infix_operand_type_rules(binding, op, other, span); + if let TypeBinding::Bound(binding) = &*int.borrow() { + return self.infix_operand_type_rules(binding, op, other, location); } - let use_impl = self.bind_type_variables_for_infix(lhs_type, op, rhs_type, span); + let use_impl = self.bind_type_variables_for_infix(lhs_type, op, rhs_type, location); Ok((other.clone(), use_impl)) } (Integer(sign_x, bit_width_x), Integer(sign_y, bit_width_y)) => { if op.kind == BinaryOpKind::ShiftLeft || op.kind == BinaryOpKind::ShiftRight { if *sign_y != Signedness::Unsigned || *bit_width_y != IntegerBitSize::Eight { - return Err(TypeCheckError::InvalidShiftSize { span }); + return Err(TypeCheckError::InvalidShiftSize { location }); } return Ok((Integer(*sign_x, *bit_width_x), false)); } @@ -1185,14 +1195,14 @@ impl<'context> Elaborator<'context> { return Err(TypeCheckError::IntegerSignedness { sign_x: *sign_x, sign_y: *sign_y, - span, + location, }); } if bit_width_x != bit_width_y { return Err(TypeCheckError::IntegerBitWidth { bit_width_x: *bit_width_x, bit_width_y: *bit_width_y, - span, + location, }); } Ok((Integer(*sign_x, *bit_width_x), false)) @@ -1201,9 +1211,9 @@ impl<'context> Elaborator<'context> { (FieldElement, FieldElement) => { if !op.kind.is_valid_for_field_type() { if op.kind == BinaryOpKind::Modulo { - return Err(TypeCheckError::FieldModulo { span }); + return Err(TypeCheckError::FieldModulo { location }); } else { - return Err(TypeCheckError::FieldBitwiseOp { span }); + return Err(TypeCheckError::FieldBitwiseOp { location }); } } Ok((FieldElement, false)) @@ -1216,12 +1226,12 @@ impl<'context> Elaborator<'context> { if rhs == &Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight) { return Ok((lhs.clone(), true)); } - return Err(TypeCheckError::InvalidShiftSize { span }); + return Err(TypeCheckError::InvalidShiftSize { location }); } self.unify(lhs, rhs, || TypeCheckError::TypeMismatchWithSource { expected: lhs.clone(), actual: rhs.clone(), - span: op.location.span, + location: op.location, source: Source::Binary, }); Ok((lhs.clone(), true)) @@ -1237,7 +1247,7 @@ impl<'context> Elaborator<'context> { &mut self, op: &UnaryOp, rhs_type: &Type, - span: Span, + location: Location, ) -> Result<(Type, bool), TypeCheckError> { use Type::*; @@ -1248,14 +1258,14 @@ impl<'context> Elaborator<'context> { Error => Ok((Error, false)), Alias(alias, args) => { let alias = alias.borrow().get_type(args); - self.prefix_operand_type_rules(op, &alias, span) + self.prefix_operand_type_rules(op, &alias, location) } // Matches on TypeVariable must be first so that we follow any type // bindings. TypeVariable(int) => { - if let TypeBinding::Bound(ref binding) = &*int.borrow() { - return self.prefix_operand_type_rules(op, binding, span); + if let TypeBinding::Bound(binding) = &*int.borrow() { + return self.prefix_operand_type_rules(op, binding, location); } // The `!` prefix operator is not valid for Field, so if this is a numeric @@ -1263,7 +1273,10 @@ impl<'context> Elaborator<'context> { if matches!(op, crate::ast::UnaryOp::Not) && rhs_type.is_numeric_value() { let integer_type = Type::polymorphic_integer(self.interner); self.unify(rhs_type, &integer_type, || { - TypeCheckError::InvalidUnaryOp { kind: rhs_type.to_string(), span } + TypeCheckError::InvalidUnaryOp { + kind: rhs_type.to_string(), + location, + } }); } @@ -1273,7 +1286,7 @@ impl<'context> Elaborator<'context> { if *op == UnaryOp::Minus && *sign_x == Signedness::Unsigned { return Err(TypeCheckError::InvalidUnaryOp { kind: rhs_type.to_string(), - span, + location, }); } Ok((Integer(*sign_x, *bit_width_x), false)) @@ -1281,7 +1294,7 @@ impl<'context> Elaborator<'context> { // The result of a Field is always a witness FieldElement => { if *op == UnaryOp::Not { - return Err(TypeCheckError::FieldNot { span }); + return Err(TypeCheckError::FieldNot { location }); } Ok((FieldElement, false)) } @@ -1300,7 +1313,7 @@ impl<'context> Elaborator<'context> { self.unify(rhs_type, &expected, || TypeCheckError::TypeMismatch { expr_typ: rhs_type.to_string(), expected_typ: expected.to_string(), - expr_span: span, + expr_location: location, }); Ok((element_type, false)) } @@ -1318,7 +1331,7 @@ impl<'context> Elaborator<'context> { expr_id: ExprId, trait_method_id: TraitMethodId, object_type: &Type, - span: Span, + location: Location, ) { let the_trait = self.interner.get_trait(trait_method_id.trait_id); @@ -1333,7 +1346,7 @@ impl<'context> Elaborator<'context> { self.unify(object_type, expected_object_type, || TypeCheckError::TypeMismatch { expected_typ: expected_object_type.to_string(), expr_typ: object_type.to_string(), - expr_span: span, + expr_location: location, }); } other => { @@ -1369,7 +1382,7 @@ impl<'context> Elaborator<'context> { mut access: HirMemberAccess, expr_id: ExprId, lhs_type: Type, - span: Span, + location: Location, ) -> Type { let access_lhs = &mut access.lhs; @@ -1384,13 +1397,15 @@ impl<'context> Elaborator<'context> { this.interner.push_expr_type(*access_lhs, element); let old_location = this.interner.id_location(old_lhs); - this.interner.push_expr_location(*access_lhs, span, old_location.file); + let location = Location::new(location.span, old_location.file); + this.interner.push_expr_location(*access_lhs, location); }; // If this access is just a field offset, we want to avoid dereferencing let dereference_lhs = (!access.is_offset).then_some(dereference_lhs); - match self.check_field_access(&lhs_type, &access.rhs.0.contents, span, dereference_lhs) { + match self.check_field_access(&lhs_type, &access.rhs.0.contents, location, dereference_lhs) + { Some((element_type, index)) => { self.interner.set_field_index(expr_id, index); // We must update `access` in case we added any dereferences to it @@ -1405,8 +1420,8 @@ impl<'context> Elaborator<'context> { &mut self, object_type: &Type, method_name: &str, - span: Span, - has_self_arg: bool, + location: Location, + check_self_param: bool, ) -> Option { match object_type.follow_bindings() { // TODO: We should allow method calls on `impl Trait`s eventually. @@ -1415,17 +1430,17 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::UnresolvedMethodCall { method_name: method_name.to_string(), object_type: object_type.clone(), - span, + location, }); None } Type::NamedGeneric(_, _) => { - self.lookup_method_in_trait_constraints(object_type, method_name, span) + self.lookup_method_in_trait_constraints(object_type, method_name, location) } // Mutable references to another type should resolve to methods of their element type. // This may be a data type or a primitive type. Type::MutableReference(element) => { - self.lookup_method(&element, method_name, span, has_self_arg) + self.lookup_method(&element, method_name, location, check_self_param) } // If we fail to resolve the object to a data type, we have no way of type @@ -1434,11 +1449,16 @@ impl<'context> Elaborator<'context> { // The type variable must be unbound at this point since follow_bindings was called Type::TypeVariable(var) if var.kind() == Kind::Normal => { - self.push_err(TypeCheckError::TypeAnnotationsNeededForMethodCall { span }); + self.push_err(TypeCheckError::TypeAnnotationsNeededForMethodCall { location }); None } - other => self.lookup_type_or_primitive_method(&other, method_name, span, has_self_arg), + other => self.lookup_type_or_primitive_method( + &other, + method_name, + location, + check_self_param, + ), } } @@ -1446,31 +1466,31 @@ impl<'context> Elaborator<'context> { &mut self, object_type: &Type, method_name: &str, - span: Span, - has_self_arg: bool, + location: Location, + check_self_param: bool, ) -> Option { // First search in the type methods. If there is one, that's the one. if let Some(method_id) = - self.interner.lookup_direct_method(object_type, method_name, has_self_arg) + self.interner.lookup_direct_method(object_type, method_name, check_self_param) { return Some(HirMethodReference::FuncId(method_id)); } // Next lookup all matching trait methods. let trait_methods = - self.interner.lookup_trait_methods(object_type, method_name, has_self_arg); + self.interner.lookup_trait_methods(object_type, method_name, check_self_param); // If there's at least one matching trait method we need to see if only one is in scope. if !trait_methods.is_empty() { - return self.return_trait_method_in_scope(&trait_methods, method_name, span); + return self.return_trait_method_in_scope(&trait_methods, method_name, location); } // If we couldn't find any trait methods, search in // impls for all types `T`, e.g. `impl Foo for T` let generic_methods = - self.interner.lookup_generic_methods(object_type, method_name, has_self_arg); + self.interner.lookup_generic_methods(object_type, method_name, check_self_param); if !generic_methods.is_empty() { - return self.return_trait_method_in_scope(&generic_methods, method_name, span); + return self.return_trait_method_in_scope(&generic_methods, method_name, location); } if let Type::DataType(datatype, _) = object_type { @@ -1487,13 +1507,13 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::CannotInvokeStructFieldFunctionType { method_name: method_name.to_string(), object_type: object_type.clone(), - span, + location, }); } else { self.push_err(TypeCheckError::UnresolvedMethodCall { method_name: method_name.to_string(), object_type: object_type.clone(), - span, + location, }); } None @@ -1501,7 +1521,7 @@ impl<'context> Elaborator<'context> { // 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) + self.lookup_method_in_trait_constraints(object_type, method_name, location) } } @@ -1511,9 +1531,9 @@ impl<'context> Elaborator<'context> { &mut self, trait_methods: &[(FuncId, TraitId)], method_name: &str, - span: Span, + location: Location, ) -> Option { - let (method, error) = self.get_trait_method_in_scope(trait_methods, method_name, span); + let (method, error) = self.get_trait_method_in_scope(trait_methods, method_name, location); if let Some(error) = error { self.push_err(error); } @@ -1524,7 +1544,7 @@ impl<'context> Elaborator<'context> { &mut self, trait_methods: &[(FuncId, TraitId)], method_name: &str, - span: Span, + location: Location, ) -> (Option, Option) { let module_id = self.module_id(); let module_data = self.get_module(module_id); @@ -1560,9 +1580,9 @@ impl<'context> Elaborator<'context> { let trait_ = self.interner.get_trait(trait_id); let trait_name = self.fully_qualified_trait_path(trait_); let method = - self.trait_hir_method_reference(trait_id, trait_methods, method_name, span); + self.trait_hir_method_reference(trait_id, trait_methods, method_name, location); let error = PathResolutionError::TraitMethodNotInScope { - ident: Ident::new(method_name.into(), span), + ident: Ident::new(method_name.into(), location), trait_name, }; return (Some(method), Some(error)); @@ -1573,7 +1593,7 @@ impl<'context> Elaborator<'context> { }); let method = None; let error = PathResolutionError::UnresolvedWithPossibleTraitsToImport { - ident: Ident::new(method_name.into(), span), + ident: Ident::new(method_name.into(), location), traits, }; return (method, Some(error)); @@ -1587,14 +1607,15 @@ impl<'context> Elaborator<'context> { }); let method = None; let error = PathResolutionError::MultipleTraitsInScope { - ident: Ident::new(method_name.into(), span), + ident: Ident::new(method_name.into(), location), traits, }; return (method, Some(error)); } let trait_id = traits_in_scope[0].0; - let method = self.trait_hir_method_reference(trait_id, trait_methods, method_name, span); + let method = + self.trait_hir_method_reference(trait_id, trait_methods, method_name, location); let error = None; (Some(method), error) } @@ -1604,7 +1625,7 @@ impl<'context> Elaborator<'context> { trait_id: TraitId, trait_methods: &[(FuncId, TraitId)], method_name: &str, - span: Span, + location: Location, ) -> HirMethodReference { // If we find a single trait impl method, return it so we don't have to later determine the impl if trait_methods.len() == 1 { @@ -1614,7 +1635,7 @@ impl<'context> Elaborator<'context> { // Return a TraitMethodId with unbound generics. These will later be bound by the type-checker. let trait_ = self.interner.get_trait(trait_id); - let generics = trait_.get_trait_generics(span); + let generics = trait_.get_trait_generics(location); let trait_method_id = trait_.find_method(method_name).unwrap(); HirMethodReference::TraitMethodId(trait_method_id, generics, false) } @@ -1623,7 +1644,7 @@ impl<'context> Elaborator<'context> { &mut self, object_type: &Type, method_name: &str, - span: Span, + location: Location, ) -> Option { let func_id = match self.current_item { Some(DependencyId::Function(id)) => id, @@ -1635,7 +1656,7 @@ impl<'context> Elaborator<'context> { if let Some(trait_id) = func_meta.trait_id { if Some(object_type) == self.self_type.as_ref() { let the_trait = self.interner.get_trait(trait_id); - let constraint = the_trait.as_constraint(the_trait.name.span()); + let constraint = the_trait.as_constraint(the_trait.name.location()); if let Some(HirMethodReference::TraitMethodId(method_id, generics, _)) = self .lookup_method_in_trait( the_trait, @@ -1670,7 +1691,7 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::UnresolvedMethodCall { method_name: method_name.to_string(), object_type: object_type.clone(), - span, + location, }); None @@ -1719,8 +1740,8 @@ impl<'context> Elaborator<'context> { &mut self, call: &HirCallExpression, func_type: Type, - args: Vec<(Type, ExprId, Span)>, - span: Span, + args: Vec<(Type, ExprId, Location)>, + location: Location, ) -> Type { self.run_lint(|elaborator| { lints::deprecated_function(elaborator.interner, call.func).map(Into::into) @@ -1741,7 +1762,7 @@ impl<'context> Elaborator<'context> { if crossing_runtime_boundary { match self.unsafe_block_status { UnsafeBlockStatus::NotInUnsafeBlock => { - self.push_err(TypeCheckError::Unsafe { span }); + self.push_err(TypeCheckError::Unsafe { location }); } UnsafeBlockStatus::InUnsafeBlockWithoutUnconstrainedCalls => { self.unsafe_block_status = UnsafeBlockStatus::InUnsafeBlockWithConstrainedCalls; @@ -1755,7 +1776,7 @@ impl<'context> Elaborator<'context> { elaborator.interner, &called_func_id, is_current_func_constrained, - span, + location, ) .map(Into::into) }); @@ -1767,11 +1788,11 @@ impl<'context> Elaborator<'context> { } } - let return_type = self.bind_function_type(func_type, args, span); + let return_type = self.bind_function_type(func_type, args, location); if crossing_runtime_boundary { self.run_lint(|_| { - lints::unconstrained_function_return(&return_type, span).map(Into::into) + lints::unconstrained_function_return(&return_type, location).map(Into::into) }); } @@ -1817,9 +1838,8 @@ impl<'context> Elaborator<'context> { if matches!(expected_object_type.follow_bindings(), Type::MutableReference(_)) { if !matches!(actual_type, Type::MutableReference(_)) { - if let Err(error) = verify_mutable_reference(self.interner, *object) { - self.push_err(TypeCheckError::ResolverError(error)); - } + let location = self.interner.id_location(*object); + self.check_can_mutate(*object, location); let new_type = Type::MutableReference(Box::new(actual_type)); *object_type = new_type.clone(); @@ -1830,8 +1850,6 @@ impl<'context> Elaborator<'context> { // If that didn't work, then wrap the whole expression in an `&mut` *object = new_object.unwrap_or_else(|| { - let location = self.interner.id_location(*object); - let new_object = self.interner.push_expr(HirExpression::Prefix(HirPrefixExpression { operator: UnaryOp::MutableReference, @@ -1839,7 +1857,7 @@ impl<'context> Elaborator<'context> { trait_method_id: None, })); self.interner.push_expr_type(new_object, new_type); - self.interner.push_expr_location(new_object, location.span, location.file); + self.interner.push_expr_location(new_object, location); new_object }); } @@ -1854,10 +1872,10 @@ impl<'context> Elaborator<'context> { } pub fn type_check_function_body(&mut self, body_type: Type, meta: &FuncMeta, body_id: ExprId) { - let (expr_span, empty_function) = self.function_info(body_id); + let (expr_location, empty_function) = self.function_info(body_id); let declared_return_type = meta.return_type(); - let func_span = self.interner.expr_span(&body_id); // XXX: We could be more specific and return the span of the last stmt, however stmts do not have spans yet + let func_location = self.interner.expr_location(&body_id); // XXX: We could be more specific and return the span of the last stmt, however stmts do not have spans yet if let Type::TraitAsType(trait_id, _, generics) = declared_return_type { if self .interner @@ -1872,46 +1890,52 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::TypeMismatchWithSource { expected: declared_return_type.clone(), actual: body_type, - span: func_span, - source: Source::Return(meta.return_type.clone(), expr_span), + location: func_location, + source: Source::Return(meta.return_type.clone(), expr_location), }); } } else { - self.unify_with_coercions(&body_type, declared_return_type, body_id, func_span, || { - let mut error = TypeCheckError::TypeMismatchWithSource { - expected: declared_return_type.clone(), - actual: body_type.clone(), - span: func_span, - source: Source::Return(meta.return_type.clone(), expr_span), - }; + self.unify_with_coercions( + &body_type, + declared_return_type, + body_id, + func_location, + || { + let mut error = TypeCheckError::TypeMismatchWithSource { + expected: declared_return_type.clone(), + actual: body_type.clone(), + location: func_location, + source: Source::Return(meta.return_type.clone(), expr_location), + }; - if empty_function { - error = error.add_context( + if empty_function { + error = error.add_context( "implicitly returns `()` as its body has no tail or `return` expression", ); - } - error - }); + } + error + }, + ); } } - fn function_info(&self, function_body_id: ExprId) -> (noirc_errors::Span, bool) { - let (expr_span, empty_function) = + fn function_info(&self, function_body_id: ExprId) -> (noirc_errors::Location, bool) { + let (expr_location, empty_function) = if let HirExpression::Block(block) = self.interner.expression(&function_body_id) { let last_stmt = block.statements().last(); - let mut span = self.interner.expr_span(&function_body_id); + let mut location = self.interner.expr_location(&function_body_id); if let Some(last_stmt) = last_stmt { if let HirStatement::Expression(expr) = self.interner.statement(last_stmt) { - span = self.interner.expr_span(&expr); + location = self.interner.expr_location(&expr); } } - (span, last_stmt.is_none()) + (location, last_stmt.is_none()) } else { - (self.interner.expr_span(&function_body_id), false) + (self.interner.expr_location(&function_body_id), false) }; - (expr_span, empty_function) + (expr_location, empty_function) } #[allow(clippy::too_many_arguments)] @@ -1923,7 +1947,7 @@ impl<'context> Elaborator<'context> { associated_types: &[NamedType], function_ident_id: ExprId, select_impl: bool, - span: Span, + location: Location, ) { match self.interner.lookup_trait_implementation( object_type, @@ -1936,7 +1960,7 @@ impl<'context> Elaborator<'context> { self.interner.select_impl_for_expression(function_ident_id, impl_kind); } } - Err(error) => self.push_trait_constraint_error(object_type, error, span), + Err(error) => self.push_trait_constraint_error(object_type, error, location), } } @@ -1944,25 +1968,24 @@ impl<'context> Elaborator<'context> { &mut self, object_type: &Type, error: ImplSearchErrorKind, - span: Span, + location: Location, ) { match error { ImplSearchErrorKind::TypeAnnotationsNeededOnObjectType => { - self.push_err(TypeCheckError::TypeAnnotationsNeededForMethodCall { span }); + self.push_err(TypeCheckError::TypeAnnotationsNeededForMethodCall { location }); } ImplSearchErrorKind::Nested(constraints) => { - if let Some(error) = NoMatchingImplFoundError::new(self.interner, constraints, span) + if let Some(error) = + NoMatchingImplFoundError::new(self.interner, constraints, location) { self.push_err(TypeCheckError::NoMatchingImplFound(error)); } } ImplSearchErrorKind::MultipleMatching(candidates) => { let object_type = object_type.clone(); - self.push_err(TypeCheckError::MultipleMatchingImpls { - object_type, - span, - candidates, - }); + let err = + TypeCheckError::MultipleMatchingImpls { object_type, location, candidates }; + self.push_err(err); } } } @@ -1975,14 +1998,14 @@ impl<'context> Elaborator<'context> { assert_eq!(unresolved_generics.len(), generics.len()); for (unresolved_generic, generic) in unresolved_generics.iter().zip(generics) { - self.add_existing_generic(unresolved_generic, unresolved_generic.span(), generic); + self.add_existing_generic(unresolved_generic, unresolved_generic.location(), generic); } } pub fn add_existing_generic( &mut self, unresolved_generic: &UnresolvedGeneric, - span: Span, + location: Location, resolved_generic: &ResolvedGeneric, ) { let name = &unresolved_generic.ident().0.contents; @@ -1990,8 +2013,8 @@ impl<'context> Elaborator<'context> { if let Some(generic) = self.find_generic(name) { self.push_err(ResolverError::DuplicateDefinition { name: name.clone(), - first_span: generic.span, - second_span: span, + first_location: generic.location, + second_location: location, }); } else { self.generics.push(resolved_generic.clone()); @@ -2109,29 +2132,3 @@ fn bind_generic(param: &ResolvedGeneric, arg: &Type, bindings: &mut TypeBindings bindings.insert(param.type_var.id(), (param.type_var.clone(), param.kind(), arg.clone())); } } - -/// Gives an error if a user tries to create a mutable reference -/// to an immutable variable. -fn verify_mutable_reference(interner: &NodeInterner, rhs: ExprId) -> Result<(), ResolverError> { - match interner.expression(&rhs) { - HirExpression::MemberAccess(member_access) => { - verify_mutable_reference(interner, member_access.lhs) - } - HirExpression::Index(_) => { - let span = interner.expr_span(&rhs); - Err(ResolverError::MutableReferenceToArrayElement { span }) - } - HirExpression::Ident(ident, _) => { - if let Some(definition) = interner.try_definition(ident.id) { - if !definition.mutable { - return Err(ResolverError::MutableReferenceToImmutableVariable { - span: interner.expr_span(&rhs), - variable: definition.name.clone(), - }); - } - } - Ok(()) - } - _ => Ok(()), - } -} 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 982ad3d2e1f8..56f9a694c9bf 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/unquote.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/unquote.rs @@ -1,11 +1,11 @@ use crate::{ ast::Path, - token::{SpannedToken, Token, Tokens}, + token::{LocatedToken, Token, Tokens}, }; use super::Elaborator; -impl<'a> Elaborator<'a> { +impl Elaborator<'_> { /// Go through the given tokens looking for a '$' token followed by a variable to unquote. /// Each time these two tokens are found, they are replaced by a new UnquoteMarker token /// containing the ExprId of the resolved variable to unquote. @@ -20,17 +20,18 @@ impl<'a> Elaborator<'a> { if is_unquote { if let Some(next) = tokens.next() { - let span = next.to_span(); + let location = next.location(); match next.into_token() { Token::Ident(name) => { // Don't want the leading `$` anymore new_tokens.pop(); - let path = Path::from_single(name, span); + let path = Path::from_single(name, location); let (expr_id, _) = self.elaborate_variable(path); - new_tokens.push(SpannedToken::new(Token::UnquoteMarker(expr_id), span)); + new_tokens + .push(LocatedToken::new(Token::UnquoteMarker(expr_id), location)); } - other_next => new_tokens.push(SpannedToken::new(other_next, span)), + other_next => new_tokens.push(LocatedToken::new(other_next, location)), } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/graph/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/graph/mod.rs index c007d6792bd1..0ce744aded5a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/graph/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/graph/mod.rs @@ -87,7 +87,7 @@ impl FromStr for CrateName { #[cfg(test)] mod crate_name { - use super::{CrateName, CHARACTER_BLACK_LIST}; + use super::{CHARACTER_BLACK_LIST, CrateName}; #[test] fn it_rejects_empty_string() { @@ -379,7 +379,7 @@ mod tests { use super::{CrateGraph, FileId}; fn dummy_file_ids(n: usize) -> Vec { - use fm::{FileMap, FILE_EXTENSION}; + use fm::{FILE_EXTENSION, FileMap}; let mut fm = FileMap::default(); let mut vec_ids = Vec::with_capacity(n); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/display.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/display.rs index aa2f7d99fab9..c0283d9701b4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/display.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/display.rs @@ -1,30 +1,30 @@ -use std::{fmt::Display, rc::Rc}; +use std::fmt::Display; use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ + Type, ast::{ ArrayLiteral, AsTraitPath, AssignStatement, BlockExpression, CallExpression, CastExpression, ConstrainExpression, ConstructorExpression, Expression, ExpressionKind, ForBounds, ForLoopStatement, ForRange, GenericTypeArgs, IfExpression, IndexExpression, InfixExpression, LValue, Lambda, LetStatement, Literal, MatchExpression, MemberAccessExpression, MethodCallExpression, Pattern, PrefixExpression, Statement, - StatementKind, UnresolvedType, UnresolvedTypeData, WhileStatement, + StatementKind, UnresolvedType, UnresolvedTypeData, UnsafeExpression, WhileStatement, }, hir_def::traits::TraitConstraint, node_interner::{InternedStatementKind, NodeInterner}, - token::{Keyword, Token}, - Type, + token::{Keyword, LocatedToken, Token}, }; use super::{ - value::{ExprValue, TypedExpr}, Value, + value::{ExprValue, TypedExpr}, }; pub(super) fn display_quoted( - tokens: &[Token], + tokens: &[LocatedToken], indent: usize, interner: &NodeInterner, f: &mut std::fmt::Formatter<'_>, @@ -44,16 +44,16 @@ pub(super) fn display_quoted( } struct TokensPrettyPrinter<'tokens, 'interner> { - tokens: &'tokens [Token], + tokens: &'tokens [LocatedToken], interner: &'interner NodeInterner, indent: usize, } -impl<'tokens, 'interner> Display for TokensPrettyPrinter<'tokens, 'interner> { +impl Display for TokensPrettyPrinter<'_, '_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut token_printer = TokenPrettyPrinter::new(self.interner, self.indent); for token in self.tokens { - token_printer.print(token, f)?; + token_printer.print(token.token(), f)?; } // If the printer refrained from printing a token right away, this will make it do it @@ -63,9 +63,8 @@ impl<'tokens, 'interner> Display for TokensPrettyPrinter<'tokens, 'interner> { } } -pub(super) fn tokens_to_string(tokens: Rc>, interner: &NodeInterner) -> String { - let tokens: Vec = tokens.iter().cloned().collect(); - TokensPrettyPrinter { tokens: &tokens, interner, indent: 0 }.to_string() +pub(super) fn tokens_to_string(tokens: &[LocatedToken], interner: &NodeInterner) -> String { + TokensPrettyPrinter { tokens, interner, indent: 0 }.to_string() } /// Tries to print tokens in a way that it'll be easier for the user to understand a @@ -180,7 +179,7 @@ impl<'interner> TokenPrettyPrinter<'interner> { self.print_value(&value, f) } Token::InternedLValue(id) => { - let value = Value::lvalue(LValue::Interned(*id, Span::default())); + let value = Value::lvalue(LValue::Interned(*id, Location::dummy())); self.print_value(&value, f) } Token::InternedUnresolvedTypeData(id) => { @@ -188,7 +187,7 @@ impl<'interner> TokenPrettyPrinter<'interner> { self.print_value(&value, f) } Token::InternedPattern(id) => { - let value = Value::pattern(Pattern::Interned(*id, Span::default())); + let value = Value::pattern(Pattern::Interned(*id, Location::dummy())); self.print_value(&value, f) } Token::UnquoteMarker(id) => { @@ -229,8 +228,7 @@ impl<'interner> TokenPrettyPrinter<'interner> { if last_was_alphanumeric { write!(f, " ")?; } - let tokens = vecmap(&tokens.0, |spanned_token| spanned_token.clone().into_token()); - display_quoted(&tokens, self.indent, self.interner, f) + display_quoted(&tokens.0, self.indent, self.interner, f) } Token::Colon => { write!(f, "{token} ") @@ -329,7 +327,7 @@ pub struct ValuePrinter<'value, 'interner> { interner: &'interner NodeInterner, } -impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { +impl Display for ValuePrinter<'_, '_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self.value { Value::Unit => write!(f, "()"), @@ -347,6 +345,7 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { Value::U16(value) => write!(f, "{value}"), Value::U32(value) => write!(f, "{value}"), Value::U64(value) => write!(f, "{value}"), + Value::U128(value) => write!(f, "{value}"), Value::String(value) => write!(f, "{value}"), Value::CtString(value) => write!(f, "{value}"), Value::FormatString(value, _) => write!(f, "{value}"), @@ -466,12 +465,12 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { }, Value::TypedExpr(TypedExpr::ExprId(id)) => { let hir_expr = self.interner.expression(id); - let expr = hir_expr.to_display_ast(self.interner, Span::default()); + let expr = hir_expr.to_display_ast(self.interner, Location::dummy()); write!(f, "{}", expr.kind) } Value::TypedExpr(TypedExpr::StmtId(id)) => { let hir_statement = self.interner.statement(id); - let stmt = hir_statement.to_display_ast(self.interner, Span::default()); + let stmt = hir_statement.to_display_ast(self.interner, Location::dummy()); write!(f, "{}", stmt.kind) } Value::UnresolvedType(typ) => { @@ -495,7 +494,7 @@ pub struct TokenPrinter<'token, 'interner> { interner: &'interner NodeInterner, } -impl<'token, 'interner> Display for TokenPrinter<'token, 'interner> { +impl Display for TokenPrinter<'_, '_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self.token { Token::QuotedType(id) => { @@ -510,7 +509,7 @@ impl<'token, 'interner> Display for TokenPrinter<'token, 'interner> { value.display(self.interner).fmt(f) } Token::InternedLValue(id) => { - let value = Value::lvalue(LValue::Interned(*id, Span::default())); + let value = Value::lvalue(LValue::Interned(*id, Location::dummy())); value.display(self.interner).fmt(f) } Token::InternedUnresolvedTypeData(id) => { @@ -518,7 +517,7 @@ impl<'token, 'interner> Display for TokenPrinter<'token, 'interner> { value.display(self.interner).fmt(f) } Token::InternedPattern(id) => { - let value = Value::pattern(Pattern::Interned(*id, Span::default())); + let value = Value::pattern(Pattern::Interned(*id, Location::dummy())); value.display(self.interner).fmt(f) } Token::UnquoteMarker(id) => { @@ -540,7 +539,10 @@ fn display_trait_constraint(interner: &NodeInterner, trait_constraint: &TraitCon // Returns a new Expression where all Interned and Resolved expressions have been turned into non-interned ExpressionKind. fn remove_interned_in_expression(interner: &NodeInterner, expr: Expression) -> Expression { - Expression { kind: remove_interned_in_expression_kind(interner, expr.kind), span: expr.span } + Expression { + kind: remove_interned_in_expression_kind(interner, expr.kind), + location: expr.location, + } } // Returns a new ExpressionKind where all Interned and Resolved expressions have been turned into non-interned ExpressionKind. @@ -643,10 +645,13 @@ fn remove_interned_in_expression_kind( vecmap(block.statements, |stmt| remove_interned_in_statement(interner, stmt)); ExpressionKind::Comptime(BlockExpression { statements }, span) } - ExpressionKind::Unsafe(block, span) => { + ExpressionKind::Unsafe(UnsafeExpression { block, unsafe_keyword_location }) => { let statements = vecmap(block.statements, |stmt| remove_interned_in_statement(interner, stmt)); - ExpressionKind::Unsafe(BlockExpression { statements }, span) + ExpressionKind::Unsafe(UnsafeExpression { + block: BlockExpression { statements }, + unsafe_keyword_location, + }) } ExpressionKind::AsTraitPath(mut path) => { path.typ = remove_interned_in_unresolved_type(interner, path.typ); @@ -663,7 +668,7 @@ fn remove_interned_in_expression_kind( } ExpressionKind::Resolved(id) => { let expr = interner.expression(&id); - expr.to_display_ast(interner, Span::default()).kind + expr.to_display_ast(interner, Location::dummy()).kind } ExpressionKind::Interned(id) => { let expr = interner.get_expression_kind(id).clone(); @@ -695,7 +700,7 @@ fn remove_interned_in_literal(interner: &NodeInterner, literal: Literal) -> Lite Literal::Array(remove_interned_in_array_literal(interner, array_literal)) } Literal::Bool(_) - | Literal::Integer(_, _) + | Literal::Integer(_) | Literal::Str(_) | Literal::RawStr(_, _) | Literal::FmtStr(_, _) @@ -724,7 +729,7 @@ fn remove_interned_in_array_literal( fn remove_interned_in_statement(interner: &NodeInterner, statement: Statement) -> Statement { Statement { kind: remove_interned_in_statement_kind(interner, statement.kind), - span: statement.span, + location: statement.location, } } @@ -769,7 +774,7 @@ fn remove_interned_in_statement_kind( StatementKind::While(while_) => StatementKind::While(WhileStatement { condition: remove_interned_in_expression(interner, while_.condition), body: remove_interned_in_expression(interner, while_.body), - while_keyword_span: while_.while_keyword_span, + while_keyword_location: while_.while_keyword_location, }), StatementKind::Comptime(statement) => { StatementKind::Comptime(Box::new(remove_interned_in_statement(interner, *statement))) @@ -789,15 +794,15 @@ fn remove_interned_in_statement_kind( fn remove_interned_in_lvalue(interner: &NodeInterner, lvalue: LValue) -> LValue { match lvalue { LValue::Ident(_) => lvalue, - LValue::MemberAccess { object, field_name, span } => LValue::MemberAccess { + LValue::MemberAccess { object, field_name, location: span } => LValue::MemberAccess { object: Box::new(remove_interned_in_lvalue(interner, *object)), field_name, - span, + location: span, }, - LValue::Index { array, index, span } => LValue::Index { + LValue::Index { array, index, location: span } => LValue::Index { array: Box::new(remove_interned_in_lvalue(interner, *array)), index: remove_interned_in_expression(interner, index), - span, + location: span, }, LValue::Dereference(lvalue, span) => { LValue::Dereference(Box::new(remove_interned_in_lvalue(interner, *lvalue)), span) @@ -815,7 +820,7 @@ fn remove_interned_in_unresolved_type( ) -> UnresolvedType { UnresolvedType { typ: remove_interned_in_unresolved_type_data(interner, typ.typ), - span: typ.span, + location: typ.location, } } 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 64297a240621..27440069c023 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,16 +2,16 @@ use std::fmt::Display; use std::rc::Rc; use crate::{ + Type, ast::TraitBound, hir::{ def_collector::dc_crate::CompilationError, type_check::{NoMatchingImplFoundError, TypeCheckError}, }, parser::ParserError, - Type, + signed_field::SignedField, }; -use acvm::{acir::AcirField, BlackBoxResolutionError, FieldElement}; -use fm::FileId; +use acvm::BlackBoxResolutionError; use noirc_errors::{CustomDiagnostic, Location}; /// The possible errors that can halt the interpreter. @@ -35,7 +35,7 @@ pub enum InterpreterError { location: Location, }, IntegerOutOfRangeForType { - value: FieldElement, + value: SignedField, typ: Type, location: Location, }, @@ -157,7 +157,7 @@ pub enum InterpreterError { error: Box, tokens: String, rule: &'static str, - file: FileId, + location: Location, }, UnsupportedTopLevelItemUnquote { item: String, @@ -172,7 +172,6 @@ pub enum InterpreterError { }, NoMatchingImplFound { error: NoMatchingImplFoundError, - file: FileId, }, ImplMethodTypeMismatch { expected: Type, @@ -274,12 +273,7 @@ impl From for CompilationError { } impl InterpreterError { - pub fn into_compilation_error_pair(self) -> (CompilationError, fm::FileId) { - let location = self.get_location(); - (CompilationError::InterpreterError(self), location.file) - } - - pub fn get_location(&self) -> Location { + pub fn location(&self) -> Location { match self { InterpreterError::ArgumentCountMismatch { location, .. } | InterpreterError::TypeMismatch { location, .. } @@ -339,12 +333,8 @@ impl InterpreterError { | InterpreterError::GlobalsDependencyCycle { location } | InterpreterError::LoopHaltedForUiResponsiveness { location } => *location, - InterpreterError::FailedToParseMacro { error, file, .. } => { - Location::new(error.span(), *file) - } - InterpreterError::NoMatchingImplFound { error, file } => { - Location::new(error.span, *file) - } + InterpreterError::FailedToParseMacro { error, .. } => error.location(), + InterpreterError::NoMatchingImplFound { error } => error.location, InterpreterError::Break | InterpreterError::Continue => { panic!("Tried to get the location of Break/Continue error!") } @@ -360,7 +350,7 @@ impl InterpreterError { let diagnostic = CustomDiagnostic::simple_info( "`comptime` expression ran:".to_string(), format!("After evaluation: {}", formatted_result), - location.span, + location, ); InterpreterError::DebugEvaluateComptime { diagnostic, location } } @@ -379,66 +369,62 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let few_many = if actual < expected { "few" } else { "many" }; let secondary = format!("Too {few_many} arguments"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::TypeMismatch { expected, actual, location } => { let msg = format!("Expected `{expected}` but a value of type `{actual}` was given"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NonComptimeVarReferenced { name, location } => { let msg = format!("Non-comptime variable `{name}` referenced in comptime code"); let secondary = "Non-comptime variables can't be used in comptime code".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::VariableNotInScope { location } => { let msg = "Variable not in scope".to_string(); let secondary = "Could not find variable".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::IntegerOutOfRangeForType { value, typ, location } => { - let int = match value.try_into_u128() { - Some(int) => int.to_string(), - None => value.to_string(), - }; - let msg = format!("{int} is outside the range of the {typ} type"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + let msg = format!("{value} is outside the range of the {typ} type"); + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::ErrorNodeEncountered { location } => { let msg = "Internal Compiler Error: Error node encountered".to_string(); let secondary = "This is a bug, please report this if found!".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonFunctionCalled { typ, location } => { let msg = "Only functions may be called".to_string(); let secondary = format!("Expression has type {typ}"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonBoolUsedInIf { typ, location } => { let msg = format!("Expected a `bool` but found `{typ}`"); let secondary = "If conditions must be a boolean value".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonBoolUsedInWhile { typ, location } => { let msg = format!("Expected a `bool` but found `{typ}`"); let secondary = "While conditions must be a boolean value".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonBoolUsedInConstrain { typ, location } => { let msg = format!("Expected a `bool` but found `{typ}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::FailingConstraint { message, location, call_stack } => { let (primary, secondary) = match message { Some(msg) => (msg.clone(), "Assertion failed".into()), None => ("Assertion failed".into(), String::new()), }; - let diagnostic = CustomDiagnostic::simple_error(primary, secondary, location.span); + let diagnostic = CustomDiagnostic::simple_error(primary, secondary, *location); diagnostic.with_call_stack(call_stack.into_iter().copied().collect()) } InterpreterError::NoMethodFound { name, typ, location } => { let msg = format!("No method named `{name}` found for type `{typ}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NonIntegerUsedInLoop { typ, location } => { let msg = format!("Non-integer type `{typ}` used in for loop"); @@ -447,97 +433,97 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { } else { String::new() }; - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonPointerDereferenced { typ, location } => { let msg = format!("Only references may be dereferenced, but found `{typ}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NonTupleOrStructInMemberAccess { typ, location } => { let msg = format!("The type `{typ}` has no fields to access"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NonArrayIndexed { typ, location } => { let msg = format!("Expected an array or slice but found a(n) {typ}"); let secondary = "Only arrays or slices may be indexed".into(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonIntegerUsedAsIndex { typ, location } => { let msg = format!("Expected an integer but found a(n) {typ}"); let secondary = "Only integers may be indexed. Note that this excludes `field`s".into(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonIntegerIntegerLiteral { typ, location } => { let msg = format!("This integer literal somehow has the type `{typ}`"); let secondary = "This is likely a bug".into(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonIntegerArrayLength { typ, err, location } => { let msg = format!("Non-integer array length: `{typ}`"); let secondary = if let Some(err) = err { - format!("Array lengths must be integers, but evaluating `{typ}` resulted in `{err}`") + format!( + "Array lengths must be integers, but evaluating `{typ}` resulted in `{err}`" + ) } else { "Array lengths must be integers".to_string() }; - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::NonNumericCasted { typ, location } => { let msg = "Only numeric types may be casted".into(); let secondary = format!("`{typ}` is non-numeric"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::IndexOutOfBounds { index, length, location } => { let msg = format!("{index} is out of bounds for the array of length {length}"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::ExpectedStructToHaveField { typ, field_name, location } => { let msg = format!("The type `{typ}` has no field named `{field_name}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::TypeUnsupported { typ, location } => { let msg = format!("The type `{typ}` is currently unsupported in comptime expressions"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::InvalidValueForUnary { typ, operator, location } => { let msg = format!("`{typ}` cannot be used with unary {operator}"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::InvalidValuesForBinary { lhs, rhs, operator, location } => { let msg = format!("No implementation for `{lhs}` {operator} `{rhs}`",); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::CastToNonNumericType { typ, location } => { let msg = format!("Cannot cast to non-numeric type `{typ}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::QuoteInRuntimeCode { location } => { let msg = "`quote` may only be used in comptime code".into(); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NonStructInConstructor { typ, location } => { let msg = format!("`{typ}` is not a struct type"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NonEnumInConstructor { typ, location } => { let msg = format!("`{typ}` is not an enum type"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::CannotInlineMacro { value, typ, location } => { let msg = format!("Cannot inline values of type `{typ}` into this position"); let secondary = format!("Cannot inline value `{value}`"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::UnquoteFoundDuringEvaluation { location } => { let msg = "Unquote found during comptime evaluation".into(); let secondary = "This is a bug".into(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::DebugEvaluateComptime { diagnostic, .. } => diagnostic.clone(), - InterpreterError::FailedToParseMacro { error, tokens, rule, file: _ } => { - let message = format!("Failed to parse macro's token stream into {rule}"); - + InterpreterError::FailedToParseMacro { error, tokens, rule, location } => { // If it's less than 48 chars, the error message fits in a single line (less than 80 chars total) let token_stream = if tokens.len() <= 48 && !tokens.contains('\n') { format!("The resulting token stream was: {tokens}") @@ -549,12 +535,13 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let push_the_problem_on_the_library_author = "To avoid this error in the future, try adding input validation to your macro. Erroring out early with an `assert` can be a good way to provide a user-friendly error message".into(); - let mut diagnostic = CustomDiagnostic::from(error.as_ref()); - // Swap the parser's primary note to become the secondary note so that it is - // more clear this error originates from failing to parse a macro. - let secondary = std::mem::take(&mut diagnostic.message); - diagnostic.add_secondary(secondary, error.span()); - diagnostic.message = message; + let mut diagnostic = CustomDiagnostic::from(&**error); + + // Given more prominence to where the parser error happened, but still show that it's + // because of a failure to parse a macro's token stream, and where that happens. + let message = format!("Failed to parse macro's token stream into {rule}"); + diagnostic.add_secondary(message, *location); + diagnostic.add_note(token_stream); diagnostic.add_note(push_the_problem_on_the_library_author); diagnostic @@ -563,7 +550,7 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let msg = "Unsupported statement type to unquote".into(); let secondary = "Only functions, structs, globals, and impls can be unquoted here".into(); - let mut error = CustomDiagnostic::simple_error(msg, secondary, location.span); + let mut error = CustomDiagnostic::simple_error(msg, secondary, *location); error.add_note(format!("Unquoted item was:\n{item}")); error } @@ -571,63 +558,63 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let msg = format!("Comptime dependency cycle while resolving `{function}`"); let secondary = "This function uses comptime code internally which calls into itself".into(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::Unimplemented { item, location } => { let msg = format!("{item} is currently unimplemented"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::InvalidInComptimeContext { item, location, explanation } => { let msg = format!("{item} is invalid in comptime context"); - CustomDiagnostic::simple_error(msg, explanation.clone(), location.span) + CustomDiagnostic::simple_error(msg, explanation.clone(), *location) } InterpreterError::BreakNotInLoop { location } => { let msg = "There is no loop to break out of!".into(); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::ContinueNotInLoop { location } => { let msg = "There is no loop to continue!".into(); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NoImpl { location } => { let msg = "No impl found due to prior type error".into(); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::ImplMethodTypeMismatch { expected, actual, location } => { let msg = format!( "Impl method type {actual} does not unify with trait method type {expected}" ); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::BlackBoxError(error, location) => { - CustomDiagnostic::simple_error(error.to_string(), String::new(), location.span) + CustomDiagnostic::simple_error(error.to_string(), String::new(), *location) } InterpreterError::FailedToResolveTraitBound { trait_bound, location } => { let msg = format!("Failed to resolve trait bound `{trait_bound}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::NoMatchingImplFound { error, .. } => error.into(), InterpreterError::Break => unreachable!("Uncaught InterpreterError::Break"), InterpreterError::Continue => unreachable!("Uncaught InterpreterError::Continue"), InterpreterError::TraitDefinitionMustBeAPath { location } => { let msg = "Trait definition arguments must be a variable or path".to_string(); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::FailedToResolveTraitDefinition { location } => { let msg = "Failed to resolve to a trait definition".to_string(); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::FunctionAlreadyResolved { location } => { let msg = "Function already resolved".to_string(); let secondary = "The function was previously called at compile-time or is in another crate" .to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::MultipleMatchingImpls { object_type, candidates, location } => { let message = format!("Multiple trait impls match the object type `{object_type}`"); let secondary = "Ambiguous impl".to_string(); - let mut error = CustomDiagnostic::simple_error(message, secondary, location.span); + let mut error = CustomDiagnostic::simple_error(message, secondary, *location); for (i, candidate) in candidates.iter().enumerate() { error.add_note(format!("Candidate {}: `{candidate}`", i + 1)); } @@ -637,7 +624,7 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let mut error = CustomDiagnostic::simple_error( "Object type is unknown in method call".to_string(), "Type must be known by this point to know which method to call".to_string(), - location.span, + *location, ); let message = "Try adding a type annotation for the object type before this method call"; @@ -649,19 +636,19 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { "Quoted value in index {index} of this slice is not a valid field name" ); let secondary = format!("`{value}` is not a valid field name for `set_fields`"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::InvalidAttribute { attribute, location } => { let msg = format!("`{attribute}` is not a valid attribute"); let secondary = "Note that this method expects attribute contents, without the leading `#[` or trailing `]`".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::GenericNameShouldBeAnIdent { name, location } => { let msg = "Generic name needs to be a valid identifier (one word beginning with a letter)" .to_string(); let secondary = format!("`{name}` is not a valid identifier"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::DuplicateGeneric { name, @@ -671,47 +658,76 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { } => { let msg = format!("`{struct_name}` already has a generic named `{name}`"); let secondary = format!("`{name}` added here a second time"); - let mut error = - CustomDiagnostic::simple_error(msg, secondary, duplicate_location.span); + let mut error = CustomDiagnostic::simple_error(msg, secondary, *duplicate_location); let existing_msg = format!("`{name}` was previously defined here"); - error.add_secondary_with_file( - existing_msg, - existing_location.span, - existing_location.file, - ); + error.add_secondary(existing_msg, *existing_location); error } InterpreterError::CannotResolveExpression { location, expression } => { let msg = format!("Cannot resolve expression `{expression}`"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::CannotSetFunctionBody { location, expression } => { let msg = format!("`{expression}` is not a valid function body"); - CustomDiagnostic::simple_error(msg, String::new(), location.span) + CustomDiagnostic::simple_error(msg, String::new(), *location) } InterpreterError::UnknownArrayLength { length, err, location } => { let msg = format!("Could not determine array length `{length}`"); let secondary = format!("Evaluating the length failed with: `{err}`"); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::CannotInterpretFormatStringWithErrors { location } => { let msg = "Cannot interpret format string with errors".to_string(); let secondary = "Some of the variables to interpolate could not be evaluated".to_string(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::GlobalsDependencyCycle { location } => { let msg = "This global recursively depends on itself".to_string(); let secondary = String::new(); - CustomDiagnostic::simple_error(msg, secondary, location.span) + CustomDiagnostic::simple_error(msg, secondary, *location) } InterpreterError::LoopHaltedForUiResponsiveness { location } => { let msg = "This loop took too much time to execute so it was halted for UI responsiveness" .to_string(); let secondary = "This error doesn't happen in normal executions of `nargo`".to_string(); - CustomDiagnostic::simple_warning(msg, secondary, location.span) + CustomDiagnostic::simple_warning(msg, secondary, *location) + } + } + } +} + +/// Comptime errors always wrap another error to show it together with a +/// comptime call or macro "something" that eventually led to that error. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ComptimeError { + ErrorRunningAttribute { error: Box, location: Location }, + ErrorAddingItemToModule { error: Box, location: Location }, +} + +impl ComptimeError { + pub fn location(&self) -> Location { + match self { + ComptimeError::ErrorRunningAttribute { location, .. } + | ComptimeError::ErrorAddingItemToModule { location, .. } => *location, + } + } +} + +impl<'a> From<&'a ComptimeError> for CustomDiagnostic { + fn from(error: &'a ComptimeError) -> Self { + match error { + ComptimeError::ErrorRunningAttribute { error, location } => { + let mut diagnostic = CustomDiagnostic::from(&**error); + diagnostic.add_secondary("While running this function attribute".into(), *location); + diagnostic + } + ComptimeError::ErrorAddingItemToModule { error, location } => { + let mut diagnostic = CustomDiagnostic::from(&**error); + diagnostic.add_secondary("While interpreting `Module::add_item`".into(), *location); + diagnostic } } } 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 05a0435b4503..4a4835c8bf73 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 @@ -1,12 +1,13 @@ +use fm::FileId; use iter_extended::vecmap; -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Located, Location, Span}; use crate::ast::{ ArrayLiteral, AssignStatement, BlockExpression, CallExpression, CastExpression, ConstrainKind, ConstructorExpression, ExpressionKind, ForLoopStatement, ForRange, GenericTypeArgs, Ident, IfExpression, IndexExpression, InfixExpression, LValue, Lambda, Literal, MatchExpression, - MemberAccessExpression, MethodCallExpression, Path, PathKind, PathSegment, Pattern, - PrefixExpression, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, WhileStatement, + MemberAccessExpression, MethodCallExpression, Path, PathSegment, Pattern, PrefixExpression, + UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, UnsafeExpression, WhileStatement, }; use crate::ast::{ConstrainExpression, Expression, Statement, StatementKind}; use crate::hir_def::expr::{ @@ -15,6 +16,7 @@ use crate::hir_def::expr::{ use crate::hir_def::stmt::{HirLValue, HirPattern, HirStatement}; use crate::hir_def::types::{Type, TypeBinding}; use crate::node_interner::{DefinitionId, ExprId, NodeInterner, StmtId}; +use crate::signed_field::SignedField; // TODO: // - Full path for idents & types @@ -24,7 +26,7 @@ use crate::node_interner::{DefinitionId, ExprId, NodeInterner, StmtId}; // - Type::TypeVariable has no equivalent in the Ast impl HirStatement { - pub fn to_display_ast(&self, interner: &NodeInterner, span: Span) -> Statement { + pub fn to_display_ast(&self, interner: &NodeInterner, location: Location) -> Statement { let kind = match self { HirStatement::Let(let_stmt) => { let pattern = let_stmt.pattern.to_display_ast(interner); @@ -43,13 +45,15 @@ impl HirStatement { for_stmt.end_range.to_display_ast(interner), ), block: for_stmt.block.to_display_ast(interner), - span, + location, }), - HirStatement::Loop(block) => StatementKind::Loop(block.to_display_ast(interner), span), + HirStatement::Loop(block) => { + StatementKind::Loop(block.to_display_ast(interner), location) + } HirStatement::While(condition, block) => StatementKind::While(WhileStatement { condition: condition.to_display_ast(interner), body: block.to_display_ast(interner), - while_keyword_span: span, + while_keyword_location: location, }), HirStatement::Break => StatementKind::Break, HirStatement::Continue => StatementKind::Continue, @@ -63,7 +67,7 @@ impl HirStatement { } }; - Statement { kind, span } + Statement { kind, location } } } @@ -71,32 +75,32 @@ impl StmtId { /// Convert to AST for display (some details lost) pub fn to_display_ast(self, interner: &NodeInterner) -> Statement { let statement = interner.statement(&self); - let span = interner.statement_span(self); + let location = interner.statement_location(self); - statement.to_display_ast(interner, span) + statement.to_display_ast(interner, location) } } impl HirExpression { /// Convert to AST for display (some details lost) - pub fn to_display_ast(&self, interner: &NodeInterner, span: Span) -> Expression { + pub fn to_display_ast(&self, interner: &NodeInterner, location: Location) -> Expression { let kind = match self { HirExpression::Ident(ident, generics) => { - ident.to_display_expr(interner, generics, span) + ident.to_display_expr(interner, generics, location) } HirExpression::Literal(HirLiteral::Array(array)) => { - let array = array.to_display_ast(interner, span); + let array = array.to_display_ast(interner, location); ExpressionKind::Literal(Literal::Array(array)) } HirExpression::Literal(HirLiteral::Slice(array)) => { - let array = array.to_display_ast(interner, span); + let array = array.to_display_ast(interner, location); ExpressionKind::Literal(Literal::Slice(array)) } HirExpression::Literal(HirLiteral::Bool(value)) => { ExpressionKind::Literal(Literal::Bool(*value)) } - HirExpression::Literal(HirLiteral::Integer(value, sign)) => { - ExpressionKind::Literal(Literal::Integer(*value, *sign)) + HirExpression::Literal(HirLiteral::Integer(value)) => { + ExpressionKind::Literal(Literal::Integer(*value)) } HirExpression::Literal(HirLiteral::Str(string)) => { ExpressionKind::Literal(Literal::Str(string.clone())) @@ -113,7 +117,7 @@ impl HirExpression { })), HirExpression::Infix(infix) => ExpressionKind::Infix(Box::new(InfixExpression { lhs: infix.lhs.to_display_ast(interner), - operator: Spanned::from(infix.operator.location.span, infix.operator.kind), + operator: Located::from(infix.operator.location, infix.operator.kind), rhs: infix.rhs.to_display_ast(interner), })), HirExpression::Index(index) => ExpressionKind::Index(Box::new(IndexExpression { @@ -122,16 +126,14 @@ impl HirExpression { })), HirExpression::Constructor(constructor) => { let type_name = constructor.r#type.borrow().name.to_string(); - let type_name = Path::from_single(type_name, span); + let type_name = Path::from_single(type_name, location); let fields = vecmap(constructor.fields.clone(), |(name, expr): (Ident, ExprId)| { (name, expr.to_display_ast(interner)) }); - let struct_type = None; ExpressionKind::Constructor(Box::new(ConstructorExpression { typ: UnresolvedType::from_path(type_name), fields, - struct_type, })) } HirExpression::MemberAccess(access) => { @@ -170,7 +172,7 @@ impl HirExpression { ExpressionKind::Constrain(ConstrainExpression { kind: ConstrainKind::Assert, arguments, - span, + location, }) } HirExpression::Cast(cast) => { @@ -183,7 +185,7 @@ impl HirExpression { consequence: if_expr.consequence.to_display_ast(interner), alternative: if_expr.alternative.map(|expr| expr.to_display_ast(interner)), })), - HirExpression::Match(match_expr) => match_expr.to_display_ast(interner, span), + HirExpression::Match(match_expr) => match_expr.to_display_ast(interner, location), HirExpression::Tuple(fields) => { ExpressionKind::Tuple(vecmap(fields, |field| field.to_display_ast(interner))) } @@ -197,11 +199,12 @@ impl HirExpression { } HirExpression::Error => ExpressionKind::Error, HirExpression::Comptime(block) => { - ExpressionKind::Comptime(block.to_display_ast(interner), span) - } - HirExpression::Unsafe(block) => { - ExpressionKind::Unsafe(block.to_display_ast(interner), span) + ExpressionKind::Comptime(block.to_display_ast(interner), location) } + HirExpression::Unsafe(block) => ExpressionKind::Unsafe(UnsafeExpression { + block: block.to_display_ast(interner), + unsafe_keyword_location: location, + }), HirExpression::Quote(block) => ExpressionKind::Quote(block.clone()), // A macro was evaluated here: return the quoted result @@ -211,51 +214,53 @@ impl HirExpression { HirExpression::EnumConstructor(constructor) => { let typ = constructor.r#type.borrow(); let variant = &typ.variant_at(constructor.variant_index); - let segment1 = PathSegment { ident: typ.name.clone(), span, generics: None }; - let segment2 = PathSegment { ident: variant.name.clone(), span, generics: None }; - let path = Path { segments: vec![segment1, segment2], kind: PathKind::Plain, span }; - let func = Box::new(Expression::new(ExpressionKind::Variable(path), span)); + let segment1 = PathSegment { ident: typ.name.clone(), location, generics: None }; + let segment2 = + PathSegment { ident: variant.name.clone(), location, generics: None }; + let path = Path::plain(vec![segment1, segment2], location); + let func = Box::new(Expression::new(ExpressionKind::Variable(path), location)); let arguments = vecmap(&constructor.arguments, |arg| arg.to_display_ast(interner)); let call = CallExpression { func, arguments, is_macro_call: false }; ExpressionKind::Call(Box::new(call)) } }; - Expression::new(kind, span) + Expression::new(kind, location) } } impl HirMatch { - fn to_display_ast(&self, interner: &NodeInterner, span: Span) -> ExpressionKind { + fn to_display_ast(&self, interner: &NodeInterner, location: Location) -> ExpressionKind { match self { HirMatch::Success(expr) => expr.to_display_ast(interner).kind, - HirMatch::Failure => ExpressionKind::Error, + HirMatch::Failure { .. } => ExpressionKind::Error, HirMatch::Guard { cond, body, otherwise } => { let condition = cond.to_display_ast(interner); let consequence = body.to_display_ast(interner); let alternative = - Some(Expression::new(otherwise.to_display_ast(interner, span), span)); + Some(Expression::new(otherwise.to_display_ast(interner, location), location)); ExpressionKind::If(Box::new(IfExpression { condition, consequence, alternative })) } HirMatch::Switch(variable, cases, default) => { let location = interner.definition(*variable).location; let ident = HirIdent::non_trait_method(*variable, location); - let expression = ident.to_display_expr(interner, &None, location.span); - let expression = Expression::new(expression, location.span); + let expression = ident.to_display_expr(interner, &None, location); + let expression = Expression::new(expression, location); let mut rules = vecmap(cases, |case| { let args = vecmap(&case.arguments, |arg| arg.to_display_ast(interner)); let constructor = case.constructor.to_display_ast(args); - let constructor = Expression::new(constructor, span); - let branch = case.body.to_display_ast(interner, span); - (constructor, Expression::new(branch, span)) + let constructor = Expression::new(constructor, location); + let branch = case.body.to_display_ast(interner, location); + (constructor, Expression::new(branch, location)) }); if let Some(case) = default { - let kind = ExpressionKind::Variable(Path::from_single("_".to_string(), span)); - let pattern = Expression::new(kind, span); - let branch = Expression::new(case.to_display_ast(interner, span), span); + let kind = + ExpressionKind::Variable(Path::from_single("_".to_string(), location)); + let pattern = Expression::new(kind, location); + let branch = Expression::new(case.to_display_ast(interner, location), location); rules.push((pattern, branch)); } @@ -268,12 +273,9 @@ impl HirMatch { impl DefinitionId { fn to_display_ast(self, interner: &NodeInterner) -> Expression { let location = interner.definition(self).location; - let kind = HirIdent::non_trait_method(self, location).to_display_expr( - interner, - &None, - location.span, - ); - Expression::new(kind, location.span) + let kind = + HirIdent::non_trait_method(self, location).to_display_expr(interner, &None, location); + Expression::new(kind, location) } } @@ -283,9 +285,7 @@ impl Constructor { Constructor::True => ExpressionKind::Literal(Literal::Bool(true)), Constructor::False => ExpressionKind::Literal(Literal::Bool(false)), Constructor::Unit => ExpressionKind::Literal(Literal::Unit), - Constructor::Int(value) => { - ExpressionKind::Literal(Literal::Integer(value.field, value.is_negative)) - } + Constructor::Int(value) => ExpressionKind::Literal(Literal::Integer(*value)), Constructor::Tuple(_) => ExpressionKind::Tuple(arguments), Constructor::Variant(typ, index) => { let typ = typ.follow_bindings_shallow(); @@ -301,9 +301,9 @@ impl Constructor { return ExpressionKind::Error; }; - let span = name.span(); + let location = name.location(); let name = ExpressionKind::Variable(Path::from_ident(name)); - let func = Box::new(Expression::new(name, span)); + let func = Box::new(Expression::new(name, location)); let is_macro_call = false; ExpressionKind::Call(Box::new(CallExpression { func, arguments, is_macro_call })) } @@ -319,8 +319,8 @@ impl ExprId { pub fn to_display_ast(self, interner: &NodeInterner) -> Expression { let expression = interner.expression(&self); // TODO: empty 0 span - let span = interner.try_expr_span(&self).unwrap_or_else(|| Span::empty(0)); - expression.to_display_ast(interner, span) + let location = interner.try_id_location(self).unwrap_or_else(Location::dummy); + expression.to_display_ast(interner, location) } } @@ -331,11 +331,11 @@ impl HirPattern { HirPattern::Identifier(ident) => Pattern::Identifier(ident.to_display_ast(interner)), HirPattern::Mutable(pattern, location) => { let pattern = Box::new(pattern.to_display_ast(interner)); - Pattern::Mutable(pattern, location.span, false) + Pattern::Mutable(pattern, *location, false) } HirPattern::Tuple(patterns, location) => { let patterns = vecmap(patterns, |pattern| pattern.to_display_ast(interner)); - Pattern::Tuple(patterns, location.span) + Pattern::Tuple(patterns, *location) } HirPattern::Struct(typ, patterns, location) => { let patterns = vecmap(patterns, |(name, pattern)| { @@ -352,8 +352,8 @@ impl HirPattern { other => other.to_string(), }; // The name span is lost here - let path = Path::from_single(name, location.span); - Pattern::Struct(path, patterns, location.span) + let path = Path::from_single(name, *location); + Pattern::Struct(path, patterns, *location) } } } @@ -363,14 +363,14 @@ impl HirIdent { /// Convert to AST for display (some details lost) fn to_display_ast(&self, interner: &NodeInterner) -> Ident { let name = interner.definition_name(self.id).to_owned(); - Ident(Spanned::from(self.location.span, name)) + Ident(Located::from(self.location, name)) } fn to_display_expr( &self, interner: &NodeInterner, generics: &Option>, - span: Span, + location: Location, ) -> ExpressionKind { let ident = self.to_display_ast(interner); let segment = PathSegment { @@ -378,10 +378,10 @@ impl HirIdent { generics: generics .as_ref() .map(|option| option.iter().map(|generic| generic.to_display_ast()).collect()), - span, + location, }; - let path = Path { segments: vec![segment], kind: crate::ast::PathKind::Plain, span }; + let path = Path::plain(vec![segment], location); ExpressionKind::Variable(path) } @@ -439,7 +439,8 @@ impl Type { TypeBinding::Bound(typ) => return typ.to_display_ast(), TypeBinding::Unbound(id, type_var_kind) => { let name = format!("var_{:?}_{}", type_var_kind, id); - let path = Path::from_single(name, Span::empty(0)); + let path = + Path::from_single(name, Location::new(Span::empty(0), FileId::dummy())); let expression = UnresolvedTypeExpression::Variable(path); UnresolvedTypeData::Expression(expression) } @@ -450,11 +451,11 @@ impl Type { (named_type.name.clone(), named_type.typ.to_display_ast()) }); let generics = GenericTypeArgs { ordered_args, named_args, kinds: Vec::new() }; - let name = Path::from_single(name.as_ref().clone(), Span::default()); + let name = Path::from_single(name.as_ref().clone(), Location::dummy()); UnresolvedTypeData::TraitAsType(name, generics) } Type::NamedGeneric(_var, name) => { - let name = Path::from_single(name.as_ref().clone(), Span::default()); + let name = Path::from_single(name.as_ref().clone(), Location::dummy()); UnresolvedTypeData::Named(name, GenericTypeArgs::default(), true) } Type::CheckedCast { to, .. } => to.to_display_ast().typ, @@ -479,23 +480,23 @@ impl Type { Type::InfixExpr(lhs, op, rhs, _) => { let lhs = Box::new(lhs.to_type_expression()); let rhs = Box::new(rhs.to_type_expression()); - let span = Span::default(); - let expr = UnresolvedTypeExpression::BinaryOperation(lhs, *op, rhs, span); + let location = Location::dummy(); + let expr = UnresolvedTypeExpression::BinaryOperation(lhs, *op, rhs, location); UnresolvedTypeData::Expression(expr) } }; - UnresolvedType { typ, span: Span::default() } + UnresolvedType { typ, location: Location::dummy() } } /// Convert to AST for display (some details lost) fn to_type_expression(&self) -> UnresolvedTypeExpression { - let span = Span::default(); + let location = Location::dummy(); match self.follow_bindings() { - Type::Constant(length, _kind) => UnresolvedTypeExpression::Constant(length, span), + Type::Constant(length, _kind) => UnresolvedTypeExpression::Constant(length, location), Type::NamedGeneric(_var, name) => { - let path = Path::from_single(name.as_ref().clone(), span); + let path = Path::from_single(name.as_ref().clone(), location); UnresolvedTypeExpression::Variable(path) } // TODO: This should be turned into a proper error. @@ -511,16 +512,16 @@ impl HirLValue { HirLValue::Ident(ident, _) => LValue::Ident(ident.to_display_ast(interner)), HirLValue::MemberAccess { object, field_name, field_index: _, typ: _, location } => { let object = Box::new(object.to_display_ast(interner)); - LValue::MemberAccess { object, field_name: field_name.clone(), span: location.span } + LValue::MemberAccess { object, field_name: field_name.clone(), location: *location } } HirLValue::Index { array, index, typ: _, location } => { let array = Box::new(array.to_display_ast(interner)); let index = index.to_display_ast(interner); - LValue::Index { array, index, span: location.span } + LValue::Index { array, index, location: *location } } HirLValue::Dereference { lvalue, element_type: _, location } => { let lvalue = Box::new(lvalue.to_display_ast(interner)); - LValue::Dereference(lvalue, location.span) + LValue::Dereference(lvalue, *location) } } } @@ -528,7 +529,7 @@ impl HirLValue { impl HirArrayLiteral { /// Convert to AST for display (some details lost) - fn to_display_ast(&self, interner: &NodeInterner, span: Span) -> ArrayLiteral { + fn to_display_ast(&self, interner: &NodeInterner, location: Location) -> ArrayLiteral { match self { HirArrayLiteral::Standard(elements) => { ArrayLiteral::Standard(vecmap(elements, |element| element.to_display_ast(interner))) @@ -537,11 +538,13 @@ impl HirArrayLiteral { let repeated_element = Box::new(repeated_element.to_display_ast(interner)); let length = match length { Type::Constant(length, _kind) => { - let literal = Literal::Integer(*length, false); + let literal = Literal::Integer(SignedField::positive(*length)); let expr_kind = ExpressionKind::Literal(literal); - Box::new(Expression::new(expr_kind, span)) + Box::new(Expression::new(expr_kind, location)) } - other => panic!("Cannot convert non-constant type for repeated array literal from Hir -> Ast: {other:?}"), + other => panic!( + "Cannot convert non-constant type for repeated array literal from Hir -> Ast: {other:?}" + ), }; ArrayLiteral::Repeated { repeated_element, length } } 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 279d176ad33b..5c87e70949a1 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -2,13 +2,13 @@ use std::collections::VecDeque; use std::{collections::hash_map::Entry, rc::Rc}; use acvm::blackbox_solver::BigIntSolverWithId; -use acvm::{acir::AcirField, FieldElement}; -use fm::FileId; +use acvm::{FieldElement, acir::AcirField}; use im::Vector; use iter_extended::try_vecmap; use noirc_errors::Location; use rustc_hash::FxHashMap as HashMap; +use crate::TypeVariable; use crate::ast::{BinaryOpKind, FunctionKind, IntegerBitSize, Signedness, UnaryOp}; use crate::elaborator::Elaborator; use crate::graph::CrateId; @@ -21,9 +21,10 @@ use crate::monomorphization::{ undo_instantiation_bindings, }; use crate::node_interner::GlobalValue; +use crate::signed_field::SignedField; use crate::token::{FmtStrFragment, Tokens}; -use crate::TypeVariable; use crate::{ + Shared, Type, TypeBinding, TypeBindings, hir_def::{ expr::{ HirArrayLiteral, HirBlockExpression, HirCallExpression, HirCastExpression, @@ -38,11 +39,10 @@ use crate::{ types::Kind, }, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, NodeInterner, StmtId}, - Shared, Type, TypeBinding, TypeBindings, }; use super::errors::{IResult, InterpreterError}; -use super::value::{unwrap_rc, Closure, Value}; +use super::value::{Closure, Value, unwrap_rc}; mod builtin; mod foreign; @@ -67,9 +67,6 @@ pub struct Interpreter<'local, 'interner> { /// Stateful bigint calculator. bigint_solver: BigIntSolverWithId, - - /// Use pedantic ACVM solving - pedantic_solving: bool, } #[allow(unused)] @@ -78,8 +75,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { elaborator: &'local mut Elaborator<'interner>, crate_id: CrateId, current_function: Option, - pedantic_solving: bool, ) -> Self { + let pedantic_solving = elaborator.pedantic_solving(); let bigint_solver = BigIntSolverWithId::with_pedantic_solving(pedantic_solving); Self { elaborator, @@ -88,7 +85,6 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { bound_generics: Vec::new(), in_loop: false, bigint_solver, - pedantic_solving, } } @@ -222,11 +218,10 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { fn elaborate_in_module( &mut self, module: ModuleId, - file: FileId, f: impl FnOnce(&mut Elaborator) -> T, ) -> T { self.unbind_generics_from_previous_function(); - let result = self.elaborator.elaborate_item_from_comptime_in_module(module, file, f); + let result = self.elaborator.elaborate_item_from_comptime_in_module(module, f); self.rebind_generics_from_previous_function(); result } @@ -623,9 +618,12 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Err(InterpreterError::NonIntegerArrayLength { typ, err: None, location }) } TypeBinding::Bound(binding) => { - let span = self.elaborator.interner.id_location(id).span; + let location = self.elaborator.interner.id_location(id); binding - .evaluate_to_field_element(&Kind::Numeric(numeric_typ.clone()), span) + .evaluate_to_field_element( + &Kind::Numeric(numeric_typ.clone()), + location, + ) .map_err(|err| { let typ = Type::TypeVariable(type_variable.clone()); let err = Some(Box::new(err)); @@ -635,7 +633,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } }?; - self.evaluate_integer(value, false, id) + self.evaluate_integer(value.into(), id) } } } @@ -644,9 +642,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { match literal { HirLiteral::Unit => Ok(Value::Unit), HirLiteral::Bool(value) => Ok(Value::Bool(value)), - HirLiteral::Integer(value, is_negative) => { - self.evaluate_integer(value, is_negative, id) - } + HirLiteral::Integer(value) => self.evaluate_integer(value, id), HirLiteral::Str(string) => Ok(Value::String(Rc::new(string))), HirLiteral::FmtStr(fragments, captures, _length) => { self.evaluate_format_string(fragments, captures, id) @@ -674,7 +670,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { FmtStrFragment::String(string) => { result.push_str(&string); } - FmtStrFragment::Interpolation(_, span) => { + FmtStrFragment::Interpolation(..) => { if let Some(value) = values.pop_front() { // When interpolating a quoted value inside a format string, we don't include the // surrounding `quote {` ... `}` as if we are unquoting the quoted value inside the string. @@ -683,8 +679,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { if index > 0 { result.push(' '); } - result - .push_str(&token.display(self.elaborator.interner).to_string()); + result.push_str( + &token.token().display(self.elaborator.interner).to_string(), + ); } } else { result.push_str(&value.display(self.elaborator.interner).to_string()); @@ -705,103 +702,85 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Ok(Value::FormatString(Rc::new(result), typ)) } - fn evaluate_integer( - &self, - value: FieldElement, - is_negative: bool, - id: ExprId, - ) -> IResult { + fn evaluate_integer(&self, value: SignedField, id: ExprId) -> IResult { let typ = self.elaborator.interner.id_type(id).follow_bindings(); let location = self.elaborator.interner.expr_location(&id); if let Type::FieldElement = &typ { - let value = if is_negative { -value } else { value }; - Ok(Value::Field(value)) + Ok(Value::Field(value.into())) } else if let Type::Integer(sign, bit_size) = &typ { match (sign, bit_size) { (Signedness::Unsigned, IntegerBitSize::One) => { return Err(InterpreterError::TypeUnsupported { typ, location }); } (Signedness::Unsigned, IntegerBitSize::Eight) => { - let value: u8 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( - InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; - let value = if is_negative { 0u8.wrapping_sub(value) } else { value }; + let value = value.try_to_unsigned().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::U8(value)) } (Signedness::Unsigned, IntegerBitSize::Sixteen) => { - let value: u16 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( - InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; - let value = if is_negative { 0u16.wrapping_sub(value) } else { value }; + let value = value.try_to_unsigned().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::U16(value)) } (Signedness::Unsigned, IntegerBitSize::ThirtyTwo) => { - let value: u32 = - value.try_to_u32().ok_or(InterpreterError::IntegerOutOfRangeForType { - value, - typ, - location, - })?; - let value = if is_negative { 0u32.wrapping_sub(value) } else { value }; + let value = value.try_to_unsigned().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::U32(value)) } (Signedness::Unsigned, IntegerBitSize::SixtyFour) => { - let value: u64 = - value.try_to_u64().ok_or(InterpreterError::IntegerOutOfRangeForType { - value, - typ, - location, - })?; - let value = if is_negative { 0u64.wrapping_sub(value) } else { value }; + let value = value.try_to_unsigned().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::U64(value)) } + (Signedness::Unsigned, IntegerBitSize::HundredTwentyEight) => { + let value: u128 = value.try_to_unsigned().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; + Ok(Value::U128(value)) + } (Signedness::Signed, IntegerBitSize::One) => { return Err(InterpreterError::TypeUnsupported { typ, location }); } (Signedness::Signed, IntegerBitSize::Eight) => { - let value: i8 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( - InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; - let value = if is_negative { -value } else { value }; + let value = value.try_to_signed().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::I8(value)) } (Signedness::Signed, IntegerBitSize::Sixteen) => { - let value: i16 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( - InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; - let value = if is_negative { -value } else { value }; + let value = value.try_to_signed().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::I16(value)) } (Signedness::Signed, IntegerBitSize::ThirtyTwo) => { - let value: i32 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( - InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; - let value = if is_negative { -value } else { value }; + let value = value.try_to_signed().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::I32(value)) } (Signedness::Signed, IntegerBitSize::SixtyFour) => { - let value: i64 = - value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( - InterpreterError::IntegerOutOfRangeForType { value, typ, location }, - )?; - let value = if is_negative { -value } else { value }; + let value = value.try_to_signed().ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; Ok(Value::I64(value)) } + (Signedness::Signed, IntegerBitSize::HundredTwentyEight) => { + return Err(InterpreterError::TypeUnsupported { typ, location }); + } } } else if let Type::TypeVariable(variable) = &typ { if variable.is_integer_or_field() { - Ok(Value::Field(value)) + Ok(Value::Field(value.into())) } else if variable.is_integer() { - let value: u64 = value - .try_to_u64() + let value = value + .try_to_unsigned() .ok_or(InterpreterError::IntegerOutOfRangeForType { value, typ, location })?; - let value = if is_negative { 0u64.wrapping_sub(value) } else { value }; Ok(Value::U64(value)) } else { Err(InterpreterError::NonIntegerIntegerLiteral { typ, location }) @@ -844,8 +823,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { HirArrayLiteral::Repeated { repeated_element, length } => { let element = self.evaluate(repeated_element)?; - let span = self.elaborator.interner.id_location(id).span; - match length.evaluate_to_u32(span) { + let location = self.elaborator.interner.id_location(id); + match length.evaluate_to_u32(location) { Ok(length) => { let elements = (0..length).map(|_| element.clone()).collect(); Ok(Value::Array(elements, typ)) @@ -1248,6 +1227,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Value::Field(value) => { value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or_else(|| { let typ = Type::default_int_type(); + let value = SignedField::positive(value); InterpreterError::IntegerOutOfRangeForType { value, typ, location } })? } @@ -1377,11 +1357,11 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } fn unify_without_binding(&mut self, actual: &Type, expected: &Type, location: Location) { - self.elaborator.unify_without_applying_bindings(actual, expected, location.file, || { + self.elaborator.unify_without_applying_bindings(actual, expected, || { TypeCheckError::TypeMismatch { expected_typ: expected.to_string(), expr_typ: actual.to_string(), - expr_span: location.span, + expr_location: location, } }); } @@ -1399,10 +1379,11 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let typ = object.get_type().follow_bindings(); let method_name = &call.method.0.contents; + let check_self_param = true; let method = self .elaborator - .lookup_method(&typ, method_name, location.span, true) + .lookup_method(&typ, method_name, location, check_self_param) .and_then(|method| method.func_id(self.elaborator.interner)); if let Some(method) = method { @@ -1495,6 +1476,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Signedness::Unsigned, IntegerBitSize::SixtyFour) => { cast_to_int!(lhs, to_u128, u64, U64) } + (Signedness::Unsigned, IntegerBitSize::HundredTwentyEight) => { + cast_to_int!(lhs, to_u128, u128, U128) + } (Signedness::Signed, IntegerBitSize::One) => { Err(InterpreterError::TypeUnsupported { typ: typ.clone(), location }) } @@ -1508,6 +1492,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Signedness::Signed, IntegerBitSize::SixtyFour) => { cast_to_int!(lhs, to_i128, i64, I64) } + (Signedness::Signed, IntegerBitSize::HundredTwentyEight) => { + todo!() + } }, Type::Bool => Ok(Value::Bool(!lhs.is_zero() || lhs_is_negative)), typ => Err(InterpreterError::CastToNonNumericType { typ, location }), 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 ff46592f9ed1..34a5535f63c2 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 @@ -12,23 +12,24 @@ use builtin_helpers::{ }; use im::Vector; use iter_extended::{try_vecmap, vecmap}; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use num_bigint::BigUint; use rustc_hash::FxHashMap as HashMap; use crate::{ + Kind, QuotedType, ResolvedGeneric, Shared, Type, TypeVariable, ast::{ ArrayLiteral, BlockExpression, ConstrainKind, Expression, ExpressionKind, ForRange, FunctionKind, FunctionReturnType, Ident, IntegerBitSize, ItemVisibility, LValue, Literal, Pattern, Signedness, Statement, StatementKind, UnaryOp, UnresolvedType, UnresolvedTypeData, - Visibility, + UnsafeExpression, Visibility, }, - elaborator::Elaborator, + elaborator::{ElaborateReason, Elaborator}, hir::{ comptime::{ + InterpreterError, Value, errors::IResult, value::{ExprValue, TypedExpr}, - InterpreterError, Value, }, def_collector::dc_crate::CollectedItems, def_map::ModuleDefId, @@ -40,8 +41,7 @@ use crate::{ }, node_interner::{DefinitionKind, NodeInterner, TraitImplKind}, parser::{Parser, StatementOrExpressionOrLValue}, - token::{Attribute, Token}, - Kind, QuotedType, ResolvedGeneric, Shared, Type, TypeVariable, + token::{Attribute, LocatedToken, Token}, }; use self::builtin_helpers::{eq_item, get_array, get_ctstring, get_str, get_u8, hash_item, lex}; @@ -49,7 +49,7 @@ use super::Interpreter; pub(crate) mod builtin_helpers; -impl<'local, 'context> Interpreter<'local, 'context> { +impl Interpreter<'_, '_> { pub(super) fn call_builtin( &mut self, name: &str, @@ -247,7 +247,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "unresolved_type_is_bool" => unresolved_type_is_bool(interner, arguments, location), "unresolved_type_is_field" => unresolved_type_is_field(interner, arguments, location), "unresolved_type_is_unit" => unresolved_type_is_unit(interner, arguments, location), - "zeroed" => Ok(zeroed(return_type, location.span)), + "zeroed" => Ok(zeroed(return_type, location)), _ => { let item = format!("Comptime evaluation for builtin function '{name}'"); Err(InterpreterError::Unimplemented { item, location }) @@ -385,7 +385,7 @@ fn struct_def_add_attribute( let attribute_location = attribute.1; let attribute = get_str(interner, attribute)?; let attribute = format!("#[{}]", attribute); - let mut parser = Parser::for_str(&attribute); + let mut parser = Parser::for_str(&attribute, attribute_location.file); let Some((Attribute::Secondary(attribute), _span)) = parser.parse_attribute() else { return Err(InterpreterError::InvalidAttribute { attribute: attribute.to_string(), @@ -411,7 +411,7 @@ fn struct_def_add_generic( let generic_location = generic.1; let generic = get_str(interner, generic)?; - let mut tokens = lex(&generic); + let mut tokens = lex(&generic, location); if tokens.len() != 1 { return Err(InterpreterError::GenericNameShouldBeAnIdent { name: generic, @@ -419,7 +419,7 @@ fn struct_def_add_generic( }); } - let Token::Ident(generic_name) = tokens.remove(0) else { + let Token::Ident(generic_name) = tokens.remove(0).into_token() else { return Err(InterpreterError::GenericNameShouldBeAnIdent { name: generic, location: generic_location, @@ -436,7 +436,7 @@ fn struct_def_add_generic( return Err(InterpreterError::DuplicateGeneric { name, struct_name: the_struct.name.to_string(), - existing_location: Location::new(generic.span, the_struct.location.file), + existing_location: generic.location, duplicate_location: generic_location, }); } @@ -444,9 +444,8 @@ fn struct_def_add_generic( let type_var_kind = Kind::Normal; let type_var = TypeVariable::unbound(interner.next_type_variable_id(), type_var_kind); - let span = generic_location.span; let typ = Type::NamedGeneric(type_var.clone(), name.clone()); - let new_generic = ResolvedGeneric { name, type_var, span }; + let new_generic = ResolvedGeneric { name, type_var, location: generic_location }; the_struct.generics.push(new_generic); Ok(Value::Type(typ)) @@ -509,7 +508,7 @@ fn struct_def_generics( Kind::Numeric(numeric_type) => Some(Value::Type(*numeric_type)), _ => None, }; - let numeric_type = option(option_typ.clone(), numeric_type, location.span); + let numeric_type = option(option_typ.clone(), numeric_type, location); Value::Tuple(vec![Value::Type(generic_as_named), numeric_type]) }) .collect(); @@ -562,7 +561,10 @@ fn struct_def_fields( if actual != expected { let s = if expected == 1 { "" } else { "s" }; let was_were = if actual == 1 { "was" } else { "were" }; - let message = Some(format!("`StructDefinition::fields` expected {expected} generic{s} for `{}` but {actual} {was_were} given", struct_def.name)); + let message = Some(format!( + "`StructDefinition::fields` expected {expected} generic{s} for `{}` but {actual} {was_were} given", + struct_def.name + )); let location = args_location; let call_stack = call_stack.clone(); return Err(InterpreterError::FailingConstraint { message, location, call_stack }); @@ -572,7 +574,8 @@ fn struct_def_fields( if let Some(struct_fields) = struct_def.get_fields(&generic_args) { for (field_name, field_type) in struct_fields { - let name = Value::Quoted(Rc::new(vec![Token::Ident(field_name)])); + let token = LocatedToken::new(Token::Ident(field_name), location); + let name = Value::Quoted(Rc::new(vec![token])); fields.push_back(Value::Tuple(vec![name, Value::Type(field_type)])); } } @@ -602,7 +605,8 @@ fn struct_def_fields_as_written( if let Some(struct_fields) = struct_def.get_fields_as_written() { for field in struct_fields { - let name = Value::Quoted(Rc::new(vec![Token::Ident(field.name.to_string())])); + let token = LocatedToken::new(Token::Ident(field.name.to_string()), location); + let name = Value::Quoted(Rc::new(vec![token])); let typ = Value::Type(field.typ); fields.push_back(Value::Tuple(vec![name, typ])); } @@ -638,7 +642,8 @@ fn struct_def_name( let the_struct = interner.get_type(struct_id); let name = Token::Ident(the_struct.borrow().name.to_string()); - Ok(Value::Quoted(Rc::new(vec![name]))) + let token = LocatedToken::new(name, location); + Ok(Value::Quoted(Rc::new(vec![token]))) } /// fn set_fields(self, new_fields: [(Quoted, Type)]) {} @@ -669,11 +674,11 @@ fn struct_def_set_fields( let name_tokens = get_quoted((name_value.clone(), field_location))?; let typ = get_type((typ, field_location))?; - match name_tokens.first() { + match name_tokens.first().map(|t| t.token()) { Some(Token::Ident(name)) if name_tokens.len() == 1 => { Ok(hir_def::types::StructField { visibility: ItemVisibility::Public, - name: Ident::new(name.clone(), field_location.span), + name: Ident::new(name.clone(), field_location), typ, }) } @@ -812,7 +817,7 @@ fn quoted_as_expr( }, ); - Ok(option(return_type, value, location.span)) + Ok(option(return_type, value, location)) } // fn as_module(quoted: Quoted) -> Option @@ -835,7 +840,7 @@ fn quoted_as_module( module.map(Value::ModuleDefinition) }); - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn as_trait_constraint(quoted: Quoted) -> TraitConstraint @@ -953,11 +958,7 @@ fn to_le_radix( None => 0, }; // The only built-ins that use these either return `[u1; N]` or `[u8; N]` - if return_type_is_bits { - Value::U1(digit != 0) - } else { - Value::U8(digit) - } + if return_type_is_bits { Value::U1(digit != 0) } else { Value::U8(digit) } }); let result_type = Type::Array( @@ -1003,7 +1004,7 @@ fn type_as_constant( // Prefer to use `evaluate_to_u32` over matching on `Type::Constant` // since arithmetic generics may be `Type::InfixExpr`s which evaluate to // constants but are not actually the `Type::Constant` variant. - match typ.evaluate_to_u32(location.span) { + match typ.evaluate_to_u32(location) { Ok(constant) => Ok(Some(Value::U32(constant))), Err(err) => { // Evaluating to a non-constant returns 'None' in user code @@ -1040,11 +1041,7 @@ fn type_as_mutable_reference( location: Location, ) -> IResult { type_as(arguments, return_type, location, |typ| { - if let Type::MutableReference(typ) = typ { - Some(Value::Type(*typ)) - } else { - None - } + if let Type::MutableReference(typ) = typ { Some(Value::Type(*typ)) } else { None } }) } @@ -1055,11 +1052,7 @@ fn type_as_slice( location: Location, ) -> IResult { type_as(arguments, return_type, location, |typ| { - if let Type::Slice(slice_type) = typ { - Some(Value::Type(*slice_type)) - } else { - None - } + if let Type::Slice(slice_type) = typ { Some(Value::Type(*slice_type)) } else { None } }) } @@ -1070,11 +1063,7 @@ fn type_as_str( location: Location, ) -> IResult { type_as(arguments, return_type, location, |typ| { - if let Type::String(n) = typ { - Some(Value::Type(*n)) - } else { - None - } + if let Type::String(n) = typ { Some(Value::Type(*n)) } else { None } }) } @@ -1147,7 +1136,7 @@ where let option_value = f(typ)?; - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn type_eq(_first: Type, _second: Type) -> bool @@ -1182,7 +1171,7 @@ fn type_get_trait_impl( _ => None, }; - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn implements(self, constraint: TraitConstraint) -> bool @@ -1303,7 +1292,7 @@ fn typed_expr_as_function_definition( } else { None }; - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn get_type(self) -> Option @@ -1317,15 +1306,11 @@ fn typed_expr_get_type( let typed_expr = get_typed_expr(self_argument)?; let option_value = if let TypedExpr::ExprId(expr_id) = typed_expr { let typ = interner.id_type(expr_id); - if typ == Type::Error { - None - } else { - Some(Value::Type(typ)) - } + if typ == Type::Error { None } else { Some(Value::Type(typ)) } } else { None }; - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn as_mutable_reference(self) -> Option @@ -1408,16 +1393,16 @@ where let typ = get_unresolved_type(interner, value)?; let option_value = f(typ); - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn zeroed() -> T -fn zeroed(return_type: Type, span: Span) -> Value { +fn zeroed(return_type: Type, location: Location) -> Value { match return_type { Type::FieldElement => Value::Field(0u128.into()), Type::Array(length_type, elem) => { - if let Ok(length) = length_type.evaluate_to_u32(span) { - let element = zeroed(elem.as_ref().clone(), span); + if let Ok(length) = length_type.evaluate_to_u32(location) { + let element = zeroed(elem.as_ref().clone(), location); let array = std::iter::repeat(element).take(length as usize).collect(); Value::Array(array, Type::Array(length_type, elem)) } else { @@ -1432,15 +1417,17 @@ fn zeroed(return_type: Type, span: Span) -> Value { (Signedness::Unsigned, IntegerBitSize::Sixteen) => Value::U16(0), (Signedness::Unsigned, IntegerBitSize::ThirtyTwo) => Value::U32(0), (Signedness::Unsigned, IntegerBitSize::SixtyFour) => Value::U64(0), + (Signedness::Unsigned, IntegerBitSize::HundredTwentyEight) => Value::U128(0), (Signedness::Signed, IntegerBitSize::One) => Value::I8(0), (Signedness::Signed, IntegerBitSize::Eight) => Value::I8(0), (Signedness::Signed, IntegerBitSize::Sixteen) => Value::I16(0), (Signedness::Signed, IntegerBitSize::ThirtyTwo) => Value::I32(0), (Signedness::Signed, IntegerBitSize::SixtyFour) => Value::I64(0), + (Signedness::Signed, IntegerBitSize::HundredTwentyEight) => todo!(), }, Type::Bool => Value::Bool(false), Type::String(length_type) => { - if let Ok(length) = length_type.evaluate_to_u32(span) { + if let Ok(length) = length_type.evaluate_to_u32(location) { Value::String(Rc::new("\0".repeat(length as usize))) } else { // Assume we can resolve the length later @@ -1448,7 +1435,7 @@ fn zeroed(return_type: Type, span: Span) -> Value { } } Type::FmtString(length_type, captures) => { - let length = length_type.evaluate_to_u32(span); + let length = length_type.evaluate_to_u32(location); let typ = Type::FmtString(length_type, captures); if let Ok(length) = length { Value::FormatString(Rc::new("\0".repeat(length as usize)), typ) @@ -1458,7 +1445,7 @@ fn zeroed(return_type: Type, span: Span) -> Value { } } Type::Unit => Value::Unit, - Type::Tuple(fields) => Value::Tuple(vecmap(fields, |field| zeroed(field, span))), + Type::Tuple(fields) => Value::Tuple(vecmap(fields, |field| zeroed(field, location))), Type::DataType(data_type, generics) => { let typ = data_type.borrow(); @@ -1466,7 +1453,7 @@ fn zeroed(return_type: Type, span: Span) -> Value { let mut values = HashMap::default(); for (field_name, field_type) in fields { - let field_value = zeroed(field_type, span); + let field_value = zeroed(field_type, location); values.insert(Rc::new(field_name), field_value); } @@ -1480,7 +1467,7 @@ fn zeroed(return_type: Type, span: Span) -> Value { if !variants.is_empty() { // is_empty & swap_remove let us avoid a .clone() we'd need if we did .get(0) let (_name, params) = variants.swap_remove(0); - args = vecmap(params, |param| zeroed(param, span)); + args = vecmap(params, |param| zeroed(param, location)); } drop(typ); @@ -1490,14 +1477,14 @@ fn zeroed(return_type: Type, span: Span) -> Value { Value::Zeroed(Type::DataType(data_type, generics)) } } - Type::Alias(alias, generics) => zeroed(alias.borrow().get_type(&generics), span), - Type::CheckedCast { to, .. } => zeroed(*to, span), + Type::Alias(alias, generics) => zeroed(alias.borrow().get_type(&generics), location), + Type::CheckedCast { to, .. } => zeroed(*to, location), typ @ Type::Function(..) => { // Using Value::Zeroed here is probably safer than using FuncId::dummy_id() or similar Value::Zeroed(typ) } Type::MutableReference(element) => { - let element = zeroed(*element, span); + let element = zeroed(*element, location); Value::Pointer(Shared::new(element), false) } // Optimistically assume we can resolve this type later or that the value is unused @@ -1561,7 +1548,7 @@ fn expr_as_assert( let option_type = tuple_types.pop().unwrap(); let message = message.map(|msg| Value::expression(msg.kind)); - let message = option(option_type, message, location.span); + let message = option(option_type, message, location); Some(Value::Tuple(vec![predicate, message])) } else { @@ -1607,7 +1594,7 @@ fn expr_as_assert_eq( let option_type = tuple_types.pop().unwrap(); let message = message.map(|message| Value::expression(message.kind)); - let message = option(option_type, message, location.span); + let message = option(option_type, message, location); Some(Value::Tuple(vec![lhs, rhs, message])) } else { @@ -1770,7 +1757,7 @@ fn expr_as_constructor( let typ = Value::UnresolvedType(constructor.typ.typ); let fields = constructor.fields.into_iter(); let fields = fields.map(|(name, value)| { - Value::Tuple(vec![quote_ident(&name), Value::expression(value.kind)]) + Value::Tuple(vec![quote_ident(&name, location), Value::expression(value.kind)]) }); let fields = fields.collect(); let fields_type = Type::Slice(Box::new(Type::Tuple(vec![ @@ -1783,7 +1770,7 @@ fn expr_as_constructor( None }; - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn as_for(self) -> Option<(Quoted, Expr, Expr)> @@ -1796,8 +1783,9 @@ fn expr_as_for( expr_as(interner, arguments, return_type, location, |expr| { if let ExprValue::Statement(StatementKind::For(for_statement)) = expr { if let ForRange::Array(array) = for_statement.range { - let identifier = - Value::Quoted(Rc::new(vec![Token::Ident(for_statement.identifier.0.contents)])); + let token = Token::Ident(for_statement.identifier.0.contents); + let token = LocatedToken::new(token, location); + let identifier = Value::Quoted(Rc::new(vec![token])); let array = Value::expression(array.kind); let body = Value::expression(for_statement.block.kind); Some(Value::Tuple(vec![identifier, array, body])) @@ -1821,8 +1809,9 @@ fn expr_as_for_range( if let ExprValue::Statement(StatementKind::For(for_statement)) = expr { if let ForRange::Range(bounds) = for_statement.range { let (from, to) = bounds.into_half_open(); - let identifier = - Value::Quoted(Rc::new(vec![Token::Ident(for_statement.identifier.0.contents)])); + let token = Token::Ident(for_statement.identifier.0.contents); + let token = LocatedToken::new(token, location); + let identifier = Value::Quoted(Rc::new(vec![token])); let from = Value::expression(from.kind); let to = Value::expression(to.kind); let body = Value::expression(for_statement.block.kind); @@ -1877,7 +1866,7 @@ fn expr_as_if( let alternative = option( alternative_option_type, if_expr.alternative.map(|e| Value::expression(e.kind)), - location.span, + location, ); Some(Value::Tuple(vec![ @@ -1918,14 +1907,12 @@ fn expr_as_integer( location: Location, ) -> IResult { expr_as(interner, arguments, return_type.clone(), location, |expr| match expr { - ExprValue::Expression(ExpressionKind::Literal(Literal::Integer(field, sign))) => { - Some(Value::Tuple(vec![Value::Field(field), Value::Bool(sign)])) + ExprValue::Expression(ExpressionKind::Literal(Literal::Integer(field))) => { + Some(Value::Tuple(vec![Value::Field(field.field), Value::Bool(field.is_negative)])) } ExprValue::Expression(ExpressionKind::Resolved(id)) => { - if let HirExpression::Literal(HirLiteral::Integer(field, sign)) = - interner.expression(&id) - { - Some(Value::Tuple(vec![Value::Field(field), Value::Bool(sign)])) + if let HirExpression::Literal(HirLiteral::Integer(field)) = interner.expression(&id) { + Some(Value::Tuple(vec![Value::Field(field.field), Value::Bool(field.is_negative)])) } else { None } @@ -1966,7 +1953,7 @@ fn expr_as_lambda( } else { Some(Value::UnresolvedType(typ.typ)) }; - let typ = option(option_unresolved_type.clone(), typ, location.span); + let typ = option(option_unresolved_type.clone(), typ, location); Value::Tuple(vec![pattern, typ]) }) .collect(); @@ -1985,7 +1972,7 @@ fn expr_as_lambda( Some(return_type) }; let return_type = return_type.map(Value::UnresolvedType); - let return_type = option(option_unresolved_type, return_type, location.span); + let return_type = option(option_unresolved_type, return_type, location); let body = Value::expression(lambda.body.kind); @@ -2019,7 +2006,7 @@ fn expr_as_let( Some(Value::UnresolvedType(let_statement.r#type.typ)) }; - let typ = option(option_type, typ, location.span); + let typ = option(option_type, typ, location); Some(Value::Tuple(vec![ Value::pattern(let_statement.pattern), @@ -2042,11 +2029,11 @@ fn expr_as_member_access( ExprValue::Expression(ExpressionKind::MemberAccess(member_access)) => { Some(Value::Tuple(vec![ Value::expression(member_access.lhs.kind), - quote_ident(&member_access.rhs), + quote_ident(&member_access.rhs, location), ])) } - ExprValue::LValue(crate::ast::LValue::MemberAccess { object, field_name, span: _ }) => { - Some(Value::Tuple(vec![Value::lvalue(*object), quote_ident(&field_name)])) + ExprValue::LValue(crate::ast::LValue::MemberAccess { object, field_name, location: _ }) => { + Some(Value::Tuple(vec![Value::lvalue(*object), quote_ident(&field_name, location)])) } _ => None, }) @@ -2063,7 +2050,7 @@ fn expr_as_method_call( if let ExprValue::Expression(ExpressionKind::MethodCall(method_call)) = expr { let object = Value::expression(method_call.object.kind); - let name = quote_ident(&method_call.method_name); + let name = quote_ident(&method_call.method_name, location); let generics = method_call.generics.unwrap_or_default().into_iter(); let generics = generics.map(|generic| Value::UnresolvedType(generic.typ)).collect(); @@ -2214,8 +2201,9 @@ fn expr_as_unsafe( location: Location, ) -> IResult { expr_as(interner, arguments, return_type, location, |expr| { - if let ExprValue::Expression(ExpressionKind::Unsafe(block_expr, _)) = expr { - Some(block_expression_to_value(block_expr)) + if let ExprValue::Expression(ExpressionKind::Unsafe(UnsafeExpression { block, .. })) = expr + { + Some(block_expression_to_value(block)) } else { None } @@ -2271,7 +2259,7 @@ where let expr_value = unwrap_expr_value(interner, expr_value); let option_value = f(expr_value); - Ok(option(return_type, option_value, location.span)) + Ok(option(return_type, option_value, location)) } // fn resolve(self, in_function: Option) -> TypedExpr @@ -2306,12 +2294,12 @@ fn expr_resolve( interpreter.elaborate_in_function(function_to_resolve_in, |elaborator| match expr_value { ExprValue::Expression(expression_kind) => { - let expr = Expression { kind: expression_kind, span: self_argument_location.span }; + let expr = Expression { kind: expression_kind, location: self_argument_location }; let (expr_id, _) = elaborator.elaborate_expression(expr); Ok(Value::TypedExpr(TypedExpr::ExprId(expr_id))) } ExprValue::Statement(statement_kind) => { - let statement = Statement { kind: statement_kind, span: self_argument_location.span }; + let statement = Statement { kind: statement_kind, location: self_argument_location }; let (stmt_id, _) = elaborator.elaborate_statement(statement); Ok(Value::TypedExpr(TypedExpr::StmtId(stmt_id))) } @@ -2380,7 +2368,7 @@ fn fmtstr_quoted_contents( ) -> IResult { let self_argument = check_one_argument(arguments, location)?; let (string, _) = get_format_string(interner, self_argument)?; - let tokens = lex(&string); + let tokens = lex(&string, location); Ok(Value::Quoted(Rc::new(tokens))) } @@ -2399,7 +2387,7 @@ fn function_def_add_attribute( let attribute_location = attribute.1; let attribute = get_str(interpreter.elaborator.interner, attribute)?; let attribute = format!("#[{}]", attribute); - let mut parser = Parser::for_str(&attribute); + let mut parser = Parser::for_str(&attribute, attribute_location.file); let Some((attribute, _span)) = parser.parse_attribute() else { return Err(InterpreterError::InvalidAttribute { attribute: attribute.to_string(), @@ -2437,7 +2425,7 @@ fn function_def_as_typed_expr( let generics = None; let hir_expr = HirExpression::Ident(hir_ident.clone(), generics.clone()); let expr_id = interpreter.elaborator.interner.push_expr(hir_expr); - interpreter.elaborator.interner.push_expr_location(expr_id, location.span, location.file); + interpreter.elaborator.interner.push_expr_location(expr_id, location); let typ = interpreter.elaborator.type_check_variable(hir_ident, expr_id, generics); interpreter.elaborator.interner.push_expr_type(expr_id, typ); Ok(Value::TypedExpr(TypedExpr::ExprId(expr_id))) @@ -2521,7 +2509,9 @@ fn function_def_name( let self_argument = check_one_argument(arguments, location)?; let func_id = get_function_def(self_argument)?; let name = interner.function_name(&func_id).to_string(); - let tokens = Rc::new(vec![Token::Ident(name)]); + let token = Token::Ident(name); + let token = LocatedToken::new(token, location); + let tokens = Rc::new(vec![token]); Ok(Value::Quoted(tokens)) } @@ -2539,7 +2529,9 @@ fn function_def_parameters( .parameters .iter() .map(|(hir_pattern, typ, _visibility)| { - let name = Value::Quoted(Rc::new(hir_pattern_to_tokens(interner, hir_pattern))); + let tokens = hir_pattern_to_tokens(interner, hir_pattern); + let tokens = vecmap(tokens, |token| LocatedToken::new(token, location)); + let name = Value::Quoted(Rc::new(tokens)); let typ = Value::Type(typ.clone()); Value::Tuple(vec![name, typ]) }) @@ -2580,10 +2572,9 @@ fn function_def_set_body( let body_argument = get_expr(interpreter.elaborator.interner, body_argument)?; let statement_kind = match body_argument { - ExprValue::Expression(expression_kind) => StatementKind::Expression(Expression { - kind: expression_kind, - span: body_location.span, - }), + ExprValue::Expression(expression_kind) => { + StatementKind::Expression(Expression { kind: expression_kind, location: body_location }) + } ExprValue::Statement(statement_kind) => statement_kind, ExprValue::LValue(lvalue) => StatementKind::Expression(lvalue.as_expression()), ExprValue::Pattern(pattern) => { @@ -2598,12 +2589,12 @@ fn function_def_set_body( } }; - let statement = Statement { kind: statement_kind, span: body_location.span }; + let statement = Statement { kind: statement_kind, location: body_location }; let body = BlockExpression { statements: vec![statement] }; let func_meta = interpreter.elaborator.interner.function_meta_mut(&func_id); func_meta.has_body = true; - func_meta.function_body = FunctionBody::Unresolved(FunctionKind::Normal, body, location.span); + func_meta.function_body = FunctionBody::Unresolved(FunctionKind::Normal, body, location); Ok(Value::Unit) } @@ -2681,7 +2672,7 @@ fn function_def_set_return_type( mutate_func_meta_type(interpreter.elaborator.interner, func_id, |func_meta| { func_meta.return_type = FunctionReturnType::Ty(UnresolvedType { typ: UnresolvedTypeData::Resolved(quoted_type_id), - span: location.span, + location, }); replace_func_meta_return_type(&mut func_meta.typ, return_type); }); @@ -2756,8 +2747,10 @@ fn module_add_item( let parser = Parser::parse_top_level_items; let top_level_statements = parse(interpreter.elaborator, item, parser, "a top-level item")?; - let module_data = interpreter.elaborator.get_module(module_id); - interpreter.elaborate_in_module(module_id, module_data.location.file, |elaborator| { + interpreter.elaborate_in_module(module_id, |elaborator| { + let previous_errors = elaborator + .push_elaborate_reason_and_take_errors(ElaborateReason::AddingItemToModule, location); + let mut generated_items = CollectedItems::default(); for top_level_statement in top_level_statements { @@ -2767,6 +2760,8 @@ fn module_add_item( if !generated_items.is_empty() { elaborator.elaborate_items(generated_items); } + + elaborator.pop_elaborate_reason(previous_errors); }); Ok(Value::Unit) @@ -2867,7 +2862,9 @@ fn module_name( let self_argument = check_one_argument(arguments, location)?; let module_id = get_module(self_argument)?; let name = &interner.module_attributes(&module_id).name; - let tokens = Rc::new(vec![Token::Ident(name.clone())]); + let token = Token::Ident(name.clone()); + let token = LocatedToken::new(token, location); + let tokens = Rc::new(vec![token]); Ok(Value::Quoted(tokens)) } @@ -2932,19 +2929,19 @@ fn trait_def_as_trait_constraint( let argument = check_one_argument(arguments, location)?; let trait_id = get_trait_def(argument)?; - let constraint = interner.get_trait(trait_id).as_constraint(location.span); + let constraint = interner.get_trait(trait_id).as_constraint(location); Ok(Value::TraitConstraint(trait_id, constraint.trait_bound.trait_generics)) } /// Creates a value that holds an `Option`. /// `option_type` must be a Type referencing the `Option` type. -pub(crate) fn option(option_type: Type, value: Option, span: Span) -> Value { +pub(crate) fn option(option_type: Type, value: Option, location: Location) -> Value { let t = extract_option_generic_type(option_type.clone()); let (is_some, value) = match value { Some(value) => (Value::Bool(true), value), - None => (Value::Bool(false), zeroed(t, span)), + None => (Value::Bool(false), zeroed(t, location)), }; let mut fields = HashMap::default(); @@ -2993,7 +2990,7 @@ fn derive_generators( _ => panic!("ICE: Should only have an array return type"), }; - let num_generators = size.evaluate_to_u32(location.span).map_err(|err| { + let num_generators = size.evaluate_to_u32(location).map_err(|err| { let err = Box::new(err); InterpreterError::UnknownArrayLength { length: *size, err, location } })?; @@ -3009,10 +3006,10 @@ fn derive_generators( let y_field_name: Rc = Rc::new("y".to_owned()); let is_infinite_field_name: Rc = Rc::new("is_infinite".to_owned()); let mut results = Vector::new(); - for gen in generators { - let x_big: BigUint = gen.x.into(); + for generator in generators { + let x_big: BigUint = generator.x.into(); let x = FieldElement::from_be_bytes_reduce(&x_big.to_bytes_be()); - let y_big: BigUint = gen.y.into(); + let y_big: BigUint = generator.y.into(); let y = FieldElement::from_be_bytes_reduce(&y_big.to_bytes_be()); let mut embedded_curve_point_fields = HashMap::default(); embedded_curve_point_fields.insert(x_field_name.clone(), Value::Field(x)); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs index 095f20b3f4c7..e552cf0c5a22 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs @@ -7,19 +7,23 @@ use noirc_errors::Location; use crate::elaborator::Elaborator; use crate::hir::comptime::display::tokens_to_string; -use crate::hir::comptime::value::add_token_spans; +use crate::hir::comptime::value::unwrap_rc; +use crate::hir::def_collector::dc_crate::CompilationError; use crate::lexer::Lexer; use crate::parser::{Parser, ParserError}; +use crate::token::LocatedToken; +use crate::{DataType, Kind, Shared}; use crate::{ + QuotedType, Type, ast::{ BlockExpression, ExpressionKind, Ident, IntegerBitSize, LValue, Pattern, Signedness, StatementKind, UnresolvedTypeData, }, hir::{ comptime::{ + Interpreter, InterpreterError, Value, errors::IResult, value::{ExprValue, TypedExpr}, - Interpreter, InterpreterError, Value, }, def_map::ModuleId, type_check::generics::TraitGenerics, @@ -30,9 +34,7 @@ use crate::{ }, node_interner::{FuncId, NodeInterner, TraitId, TraitImplId, TypeId}, token::{SecondaryAttribute, Token, Tokens}, - QuotedType, Type, }; -use crate::{DataType, Kind, Shared}; use rustc_hash::FxHashMap as HashMap; pub(crate) fn check_argument_count( @@ -110,7 +112,7 @@ pub(crate) fn get_struct_fields( _ => { let expected = DataType::new( TypeId::dummy_id(), - Ident::new(name.to_string(), location.span), + Ident::new(name.to_string(), location), location, Vec::new(), ); @@ -287,7 +289,7 @@ pub(crate) fn get_expr( Ok(ExprValue::Statement(interner.get_statement_kind(id).clone())) } ExprValue::LValue(LValue::Interned(id, _)) => { - Ok(ExprValue::LValue(interner.get_lvalue(id, location.span).clone())) + Ok(ExprValue::LValue(interner.get_lvalue(id, location).clone())) } ExprValue::Pattern(Pattern::Interned(id, _)) => { Ok(ExprValue::Pattern(interner.get_pattern(id).clone())) @@ -370,7 +372,7 @@ pub(crate) fn get_typed_expr((value, location): (Value, Location)) -> IResult IResult>> { +pub(crate) fn get_quoted((value, location): (Value, Location)) -> IResult>> { match value { Value::Quoted(tokens) => Ok(tokens), value => type_mismatch(value, Type::Quoted(QuotedType::Quoted), location), @@ -483,10 +485,11 @@ pub(super) fn check_function_not_yet_resolved( } } -pub(super) fn lex(input: &str) -> Vec { - let (tokens, _) = Lexer::lex(input); - let mut tokens: Vec<_> = tokens.0.into_iter().map(|token| token.into_token()).collect(); - if let Some(Token::EOF) = tokens.last() { +pub(super) fn lex(input: &str, location: Location) -> Vec { + let (tokens, _) = Lexer::lex(input, location.file); + let mut tokens: Vec<_> = + tokens.0.into_iter().map(|token| LocatedToken::new(token.into_token(), location)).collect(); + if let Some(Token::EOF) = tokens.last().map(|t| t.token()) { tokens.pop(); } tokens @@ -502,17 +505,18 @@ where F: FnOnce(&mut Parser<'a>) -> T, { let tokens = get_quoted((value, location))?; - let quoted = add_token_spans(tokens.clone(), location.span); + let quoted = Tokens(unwrap_rc(tokens.clone())); let (result, warnings) = parse_tokens(tokens, quoted, elaborator.interner, location, parser, rule)?; for warning in warnings { - elaborator.errors.push((warning.into(), location.file)); + let warning: CompilationError = warning.into(); + elaborator.push_err(warning); } Ok(result) } pub(super) fn parse_tokens<'a, T, F>( - tokens: Rc>, + tokens: Rc>, quoted: Tokens, interner: &NodeInterner, location: Location, @@ -524,8 +528,8 @@ where { Parser::for_tokens(quoted).parse_result(parsing_function).map_err(|mut errors| { let error = Box::new(errors.swap_remove(0)); - let tokens = tokens_to_string(tokens, interner); - InterpreterError::FailedToParseMacro { error, tokens, rule, file: location.file } + let tokens = tokens_to_string(&tokens, interner); + InterpreterError::FailedToParseMacro { error, tokens, rule, location } }) } @@ -582,12 +586,14 @@ pub(super) fn has_named_attribute(name: &str, attributes: &[SecondaryAttribute]) false } -pub(super) fn quote_ident(ident: &Ident) -> Value { - Value::Quoted(ident_to_tokens(ident)) +pub(super) fn quote_ident(ident: &Ident, location: Location) -> Value { + Value::Quoted(ident_to_tokens(ident, location)) } -pub(super) fn ident_to_tokens(ident: &Ident) -> Rc> { - Rc::new(vec![Token::Ident(ident.0.contents.clone())]) +fn ident_to_tokens(ident: &Ident, location: Location) -> Rc> { + let token = Token::Ident(ident.0.contents.clone()); + let token = LocatedToken::new(token, location); + Rc::new(vec![token]) } pub(super) fn hash_item( diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs index 0221280ae1b3..823e0297755c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs @@ -1,31 +1,31 @@ use acvm::{ + AcirField, BlackBoxResolutionError, FieldElement, acir::BlackBoxFunc, blackbox_solver::{BigIntSolverWithId, BlackBoxFunctionSolver}, - AcirField, BlackBoxResolutionError, FieldElement, }; use bn254_blackbox_solver::Bn254BlackBoxSolver; // Currently locked to only bn254! use im::Vector; use noirc_errors::Location; use crate::{ + Type, hir::comptime::{ - errors::IResult, interpreter::builtin::builtin_helpers::to_byte_array, InterpreterError, - Value, + InterpreterError, Value, errors::IResult, + interpreter::builtin::builtin_helpers::to_byte_array, }, node_interner::NodeInterner, - Type, }; use super::{ + Interpreter, builtin::builtin_helpers::{ check_arguments, check_one_argument, check_three_arguments, check_two_arguments, get_array_map, get_bool, get_field, get_fixed_array_map, get_slice_map, get_struct_field, - get_struct_fields, get_u32, get_u64, get_u8, to_byte_slice, to_field_array, to_struct, + get_struct_fields, get_u8, get_u32, get_u64, to_byte_slice, to_field_array, to_struct, }, - Interpreter, }; -impl<'local, 'context> Interpreter<'local, 'context> { +impl Interpreter<'_, '_> { pub(super) fn call_foreign( &mut self, name: &str, @@ -40,7 +40,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { arguments, return_type, location, - self.pedantic_solving, + self.elaborator.pedantic_solving(), ) } } @@ -234,7 +234,7 @@ fn blake_hash( /// signature: [u8; 64], /// message_hash: [u8; N], /// ) -> bool - +/// /// pub fn verify_signature_slice( /// public_key_x: [u8; 32], /// public_key_y: [u8; 32], @@ -422,11 +422,11 @@ mod tests { use noirc_errors::Location; use strum::IntoEnumIterator; - use crate::hir::comptime::tests::with_interpreter; + use crate::Type; use crate::hir::comptime::InterpreterError::{ ArgumentCountMismatch, InvalidInComptimeContext, Unimplemented, }; - use crate::Type; + use crate::hir::comptime::tests::with_interpreter; use super::call_foreign; 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 c7b1532c9b7e..fc4daa22edb7 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 @@ -2,12 +2,12 @@ use noirc_errors::Location; use crate::{ hir::comptime::errors::IResult, - token::{Token, Tokens}, + token::{LocatedToken, Token, Tokens}, }; use super::Interpreter; -impl<'local, 'interner> Interpreter<'local, 'interner> { +impl Interpreter<'_, '_> { /// Evaluates any expressions within UnquoteMarkers in the given token list /// and replaces the expression held by the marker with the evaluated value /// in expression form. @@ -15,17 +15,17 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { &mut self, tokens: Tokens, location: Location, - ) -> IResult> { + ) -> IResult> { let mut new_tokens = Vec::with_capacity(tokens.0.len()); for token in tokens.0 { - match token.into_token() { + match token.token() { Token::UnquoteMarker(id) => { - let value = self.evaluate(id)?; + let value = self.evaluate(*id)?; let tokens = value.into_tokens(self.elaborator.interner, location)?; new_tokens.extend(tokens); } - token => new_tokens.push(token), + _ => new_tokens.push(token), } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs index 2e2753001b48..c4a987e54195 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs @@ -5,6 +5,6 @@ mod interpreter; mod tests; mod value; -pub use errors::InterpreterError; +pub use errors::{ComptimeError, InterpreterError}; pub use interpreter::Interpreter; pub use value::Value; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs index 342d0a616a05..88a2bc8b52a8 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs @@ -7,10 +7,10 @@ use fm::{FileId, FileManager}; use noirc_arena::Index; use noirc_errors::Location; +use super::Interpreter; use super::errors::InterpreterError; use super::value::Value; -use super::Interpreter; -use crate::elaborator::Elaborator; +use crate::elaborator::{Elaborator, ElaboratorOptions}; use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector}; use crate::hir::def_collector::dc_mod::collect_defs; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleData}; @@ -23,7 +23,7 @@ use crate::parse_program; /// The stdlib is not made available as a dependency. pub(crate) fn with_interpreter( src: &str, - f: impl FnOnce(&mut Interpreter, FuncId, &[(CompilationError, FileId)]) -> T, + f: impl FnOnce(&mut Interpreter, FuncId, &[CompilationError]) -> T, ) -> T { let file = FileId::default(); @@ -48,7 +48,7 @@ pub(crate) fn with_interpreter( let krate = context.crate_graph.add_crate_root(FileId::dummy()); - let (module, errors) = parse_program(src); + let (module, errors) = parse_program(src, file); assert_eq!(errors.len(), 0); let ast = module.into_sorted(); @@ -60,13 +60,11 @@ pub(crate) fn with_interpreter( let main = context.get_main_function(&krate).expect("Expected 'main' function"); - let pedantic_solving = true; let mut elaborator = Elaborator::elaborate_and_return_self( &mut context, krate, collector.items, - None, - pedantic_solving, + ElaboratorOptions::test_default(), ); let errors = elaborator.errors.clone(); 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 a6668eae1b0a..8d07669497f6 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 @@ -3,25 +3,29 @@ use std::{borrow::Cow, rc::Rc, vec}; use acvm::FieldElement; use im::Vector; use iter_extended::{try_vecmap, vecmap}; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use strum_macros::Display; use crate::{ + Kind, QuotedType, Shared, Type, TypeBindings, ast::{ ArrayLiteral, BlockExpression, ConstructorExpression, Expression, ExpressionKind, Ident, - IntegerBitSize, LValue, Literal, Path, Pattern, Signedness, Statement, StatementKind, + IntegerBitSize, LValue, Literal, Pattern, Signedness, Statement, StatementKind, UnresolvedType, UnresolvedTypeData, }, elaborator::Elaborator, - hir::{def_map::ModuleId, type_check::generics::TraitGenerics}, + hir::{ + def_collector::dc_crate::CompilationError, def_map::ModuleId, + type_check::generics::TraitGenerics, + }, hir_def::expr::{ HirArrayLiteral, HirConstructorExpression, HirEnumConstructorExpression, HirExpression, HirIdent, HirLambda, HirLiteral, ImplKind, }, node_interner::{ExprId, FuncId, NodeInterner, StmtId, TraitId, TraitImplId, TypeId}, parser::{Item, Parser}, - token::{SpannedToken, Token, Tokens}, - Kind, QuotedType, Shared, Type, TypeBindings, + signed_field::SignedField, + token::{LocatedToken, Token, Tokens}, }; use rustc_hash::FxHashMap as HashMap; @@ -44,6 +48,7 @@ pub enum Value { U16(u16), U32(u32), U64(u64), + U128(u128), String(Rc), FormatString(Rc, Type), CtString(Rc), @@ -59,10 +64,7 @@ pub enum Value { Pointer(Shared, /* auto_deref */ bool), Array(Vector, Type), Slice(Vector, Type), - /// Quoted tokens don't have spans because otherwise inserting them in the middle of other - /// tokens can cause larger spans to be before lesser spans, causing an assert. They may also - /// be inserted into separate files entirely. - Quoted(Rc>), + Quoted(Rc>), StructDefinition(TypeId), TraitConstraint(TraitId, TraitGenerics), TraitDefinition(TraitId), @@ -130,6 +132,9 @@ impl Value { Value::U16(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::Sixteen), Value::U32(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo), Value::U64(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour), + Value::U128(_) => { + Type::Integer(Signedness::Unsigned, IntegerBitSize::HundredTwentyEight) + } Value::String(value) => { let length = Type::Constant(value.len().into(), Kind::u32()); Type::String(Box::new(length)) @@ -176,45 +181,38 @@ impl Value { let kind = match self { Value::Unit => ExpressionKind::Literal(Literal::Unit), Value::Bool(value) => ExpressionKind::Literal(Literal::Bool(value)), - Value::Field(value) => ExpressionKind::Literal(Literal::Integer(value, false)), + Value::Field(value) => { + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value))) + } Value::I8(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - ExpressionKind::Literal(Literal::Integer(value, negative)) + ExpressionKind::Literal(Literal::Integer(SignedField::from_signed(value))) } Value::I16(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - ExpressionKind::Literal(Literal::Integer(value, negative)) + ExpressionKind::Literal(Literal::Integer(SignedField::from_signed(value))) } Value::I32(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - ExpressionKind::Literal(Literal::Integer(value, negative)) + ExpressionKind::Literal(Literal::Integer(SignedField::from_signed(value))) } Value::I64(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - ExpressionKind::Literal(Literal::Integer(value, negative)) + ExpressionKind::Literal(Literal::Integer(SignedField::from_signed(value))) } Value::U1(value) => { - ExpressionKind::Literal(Literal::Integer((value as u128).into(), false)) + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value))) } Value::U8(value) => { - ExpressionKind::Literal(Literal::Integer((value as u128).into(), false)) + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value as u128))) } Value::U16(value) => { - ExpressionKind::Literal(Literal::Integer((value as u128).into(), false)) + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value as u128))) } Value::U32(value) => { - ExpressionKind::Literal(Literal::Integer((value as u128).into(), false)) + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value))) } Value::U64(value) => { - ExpressionKind::Literal(Literal::Integer((value as u128).into(), false)) + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value))) + } + Value::U128(value) => { + ExpressionKind::Literal(Literal::Integer(SignedField::positive(value))) } Value::String(value) | Value::CtString(value) => { ExpressionKind::Literal(Literal::Str(unwrap_rc(value))) @@ -228,7 +226,7 @@ impl Value { let impl_kind = ImplKind::NotATraitMethod; let ident = HirIdent { location, id, impl_kind }; let expr_id = elaborator.interner.push_expr(HirExpression::Ident(ident, None)); - elaborator.interner.push_expr_location(expr_id, location.span, location.file); + elaborator.interner.push_expr_location(expr_id, location); elaborator.interner.push_expr_type(expr_id, typ); elaborator.interner.store_instantiation_bindings(expr_id, unwrap_rc(bindings)); ExpressionKind::Resolved(expr_id) @@ -241,21 +239,21 @@ impl Value { Value::Struct(fields, typ) => { let fields = try_vecmap(fields, |(name, field)| { let field = field.into_expression(elaborator, location)?; - Ok((Ident::new(unwrap_rc(name), location.span), field)) + Ok((Ident::new(unwrap_rc(name), location), field)) })?; - let struct_type = match typ.follow_bindings_shallow().as_ref() { - Type::DataType(def, _) => Some(def.borrow().id), + let typ = match typ.follow_bindings_shallow().as_ref() { + Type::DataType(data_type, generics) => { + Type::DataType(data_type.clone(), generics.clone()) + } _ => return Err(InterpreterError::NonStructInConstructor { typ, location }), }; - // Since we've provided the struct_type, the path should be ignored. - let type_name = Path::from_single(String::new(), location.span); - ExpressionKind::Constructor(Box::new(ConstructorExpression { - typ: UnresolvedType::from_path(type_name), - fields, - struct_type, - })) + let quoted_type_id = elaborator.interner.push_quoted_type(typ); + + let typ = UnresolvedTypeData::Resolved(quoted_type_id); + let typ = UnresolvedType { typ, location }; + ExpressionKind::Constructor(Box::new(ConstructorExpression { typ, fields })) } value @ Value::Enum(..) => { let hir = value.into_hir_expression(elaborator.interner, location)?; @@ -273,29 +271,30 @@ impl Value { } Value::Quoted(tokens) => { // Wrap the tokens in '{' and '}' so that we can parse statements as well. - let mut tokens_to_parse = add_token_spans(tokens.clone(), location.span); - tokens_to_parse.0.insert(0, SpannedToken::new(Token::LeftBrace, location.span)); - tokens_to_parse.0.push(SpannedToken::new(Token::RightBrace, location.span)); + let mut tokens_to_parse = unwrap_rc(tokens.clone()); + tokens_to_parse.insert(0, LocatedToken::new(Token::LeftBrace, location)); + tokens_to_parse.push(LocatedToken::new(Token::RightBrace, location)); + + let tokens_to_parse = Tokens(tokens_to_parse); let parser = Parser::for_tokens(tokens_to_parse); return match parser.parse_result(Parser::parse_expression_or_error) { Ok((expr, warnings)) => { for warning in warnings { - elaborator.errors.push((warning.into(), location.file)); + let warning: CompilationError = warning.into(); + elaborator.push_err(warning); } Ok(expr) } Err(mut errors) => { let error = Box::new(errors.swap_remove(0)); - let file = location.file; let rule = "an expression"; - let tokens = tokens_to_string(tokens, elaborator.interner); - Err(InterpreterError::FailedToParseMacro { error, file, tokens, rule }) + let tokens = tokens_to_string(&tokens, elaborator.interner); + Err(InterpreterError::FailedToParseMacro { error, tokens, rule, location }) } }; } - Value::Expr(ref expr) => { // We need to do some shenanigans to get around the borrow checker here due to using a boxed value. @@ -314,7 +313,7 @@ impl Value { match *expr { ExprValue::Expression(expr) => expr, ExprValue::Statement(statement) => ExpressionKind::Block(BlockExpression { - statements: vec![Statement { kind: statement, span: location.span }], + statements: vec![Statement { kind: statement, location }], }), ExprValue::LValue(lvalue) => lvalue.as_expression().kind, ExprValue::Pattern(_) => unreachable!("this case is handled above"), @@ -338,7 +337,7 @@ impl Value { } }; - Ok(Expression::new(kind, location.span)) + Ok(Expression::new(kind, location)) } pub(crate) fn into_hir_expression( @@ -351,45 +350,36 @@ impl Value { let expression = match self { Value::Unit => HirExpression::Literal(HirLiteral::Unit), Value::Bool(value) => HirExpression::Literal(HirLiteral::Bool(value)), - Value::Field(value) => HirExpression::Literal(HirLiteral::Integer(value, false)), + Value::Field(value) => HirExpression::Literal(HirLiteral::Integer(value.into())), Value::I8(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - HirExpression::Literal(HirLiteral::Integer(value, negative)) + HirExpression::Literal(HirLiteral::Integer(SignedField::from_signed(value))) } Value::I16(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - HirExpression::Literal(HirLiteral::Integer(value, negative)) + HirExpression::Literal(HirLiteral::Integer(SignedField::from_signed(value))) } Value::I32(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - HirExpression::Literal(HirLiteral::Integer(value, negative)) + HirExpression::Literal(HirLiteral::Integer(SignedField::from_signed(value))) } Value::I64(value) => { - let negative = value < 0; - let value = value.abs(); - let value = (value as u128).into(); - HirExpression::Literal(HirLiteral::Integer(value, negative)) + HirExpression::Literal(HirLiteral::Integer(SignedField::from_signed(value))) } Value::U1(value) => { - HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) + HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value))) } Value::U8(value) => { - HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) + HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value as u128))) } Value::U16(value) => { - HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) + HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value as u128))) } Value::U32(value) => { - HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) + HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value))) } Value::U64(value) => { - HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) + HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value))) + } + Value::U128(value) => { + HirExpression::Literal(HirLiteral::Integer(SignedField::positive(value))) } Value::String(value) | Value::CtString(value) => { HirExpression::Literal(HirLiteral::Str(unwrap_rc(value))) @@ -403,7 +393,7 @@ impl Value { let impl_kind = ImplKind::NotATraitMethod; let ident = HirIdent { location, id, impl_kind }; let expr_id = interner.push_expr(HirExpression::Ident(ident, None)); - interner.push_expr_location(expr_id, location.span, location.file); + interner.push_expr_location(expr_id, location); interner.push_expr_type(expr_id, typ); interner.store_instantiation_bindings(expr_id, unwrap_rc(bindings)); return Ok(expr_id); @@ -416,7 +406,7 @@ impl Value { Value::Struct(fields, typ) => { let fields = try_vecmap(fields, |(name, field)| { let field = field.into_hir_expression(interner, location)?; - Ok((Ident::new(unwrap_rc(name), location.span), field)) + Ok((Ident::new(unwrap_rc(name), location), field)) })?; let (r#type, struct_generics) = match typ.follow_bindings() { @@ -458,7 +448,7 @@ impl Value { })?; HirExpression::Literal(HirLiteral::Slice(HirArrayLiteral::Standard(elements))) } - Value::Quoted(tokens) => HirExpression::Unquote(add_token_spans(tokens, location.span)), + Value::Quoted(tokens) => HirExpression::Unquote(Tokens(unwrap_rc(tokens))), Value::TypedExpr(TypedExpr::ExprId(expr_id)) => interner.expression(&expr_id), // Only convert pointers with auto_deref = true. These are mutable variables // and we don't need to wrap them in `&mut`. @@ -485,7 +475,7 @@ impl Value { }; let id = interner.push_expr(expression); - interner.push_expr_location(id, location.span, location.file); + interner.push_expr_location(id, location); interner.push_expr_type(id, typ); Ok(id) } @@ -494,74 +484,77 @@ impl Value { self, interner: &mut NodeInterner, location: Location, - ) -> IResult> { - let token = match self { + ) -> IResult> { + let tokens: Vec = match self { Value::Unit => { - return Ok(vec![Token::LeftParen, Token::RightParen]); + vec![Token::LeftParen, Token::RightParen] } Value::Quoted(tokens) => return Ok(unwrap_rc(tokens)), - Value::Type(typ) => Token::QuotedType(interner.push_quoted_type(typ)), + Value::Type(typ) => vec![Token::QuotedType(interner.push_quoted_type(typ))], Value::Expr(expr) => match *expr { ExprValue::Expression(expr) => { - Token::InternedExpr(interner.push_expression_kind(expr)) + vec![Token::InternedExpr(interner.push_expression_kind(expr))] } ExprValue::Statement(StatementKind::Expression(expr)) => { - Token::InternedExpr(interner.push_expression_kind(expr.kind)) + vec![Token::InternedExpr(interner.push_expression_kind(expr.kind))] } ExprValue::Statement(statement) => { - Token::InternedStatement(interner.push_statement_kind(statement)) + vec![Token::InternedStatement(interner.push_statement_kind(statement))] + } + ExprValue::LValue(lvalue) => { + vec![Token::InternedLValue(interner.push_lvalue(lvalue))] } - ExprValue::LValue(lvalue) => Token::InternedLValue(interner.push_lvalue(lvalue)), ExprValue::Pattern(pattern) => { - Token::InternedPattern(interner.push_pattern(pattern)) + vec![Token::InternedPattern(interner.push_pattern(pattern))] } }, Value::UnresolvedType(typ) => { - Token::InternedUnresolvedTypeData(interner.push_unresolved_type_data(typ)) + vec![Token::InternedUnresolvedTypeData(interner.push_unresolved_type_data(typ))] } Value::TraitConstraint(trait_id, generics) => { let name = Rc::new(interner.get_trait(trait_id).name.0.contents.clone()); let typ = Type::TraitAsType(trait_id, name, generics); - Token::QuotedType(interner.push_quoted_type(typ)) - } - Value::TypedExpr(TypedExpr::ExprId(expr_id)) => Token::UnquoteMarker(expr_id), - Value::U1(bool) => Token::Bool(bool), - Value::U8(value) => Token::Int((value as u128).into()), - Value::U16(value) => Token::Int((value as u128).into()), - Value::U32(value) => Token::Int((value as u128).into()), - Value::U64(value) => Token::Int((value as u128).into()), + vec![Token::QuotedType(interner.push_quoted_type(typ))] + } + Value::TypedExpr(TypedExpr::ExprId(expr_id)) => vec![Token::UnquoteMarker(expr_id)], + Value::U1(bool) => vec![Token::Bool(bool)], + Value::U8(value) => vec![Token::Int((value as u128).into())], + Value::U16(value) => vec![Token::Int((value as u128).into())], + Value::U32(value) => vec![Token::Int((value as u128).into())], + Value::U64(value) => vec![Token::Int((value as u128).into())], Value::I8(value) => { if value < 0 { - return Ok(vec![Token::Minus, Token::Int((-value as u128).into())]); + vec![Token::Minus, Token::Int((-value as u128).into())] } else { - Token::Int((value as u128).into()) + vec![Token::Int((value as u128).into())] } } Value::I16(value) => { if value < 0 { - return Ok(vec![Token::Minus, Token::Int((-value as u128).into())]); + vec![Token::Minus, Token::Int((-value as u128).into())] } else { - Token::Int((value as u128).into()) + vec![Token::Int((value as u128).into())] } } Value::I32(value) => { if value < 0 { - return Ok(vec![Token::Minus, Token::Int((-value as u128).into())]); + vec![Token::Minus, Token::Int((-value as u128).into())] } else { - Token::Int((value as u128).into()) + vec![Token::Int((value as u128).into())] } } Value::I64(value) => { if value < 0 { - return Ok(vec![Token::Minus, Token::Int((-value as u128).into())]); + vec![Token::Minus, Token::Int((-value as u128).into())] } else { - Token::Int((value as u128).into()) + vec![Token::Int((value as u128).into())] } } - Value::Field(value) => Token::Int(value), - other => Token::UnquoteMarker(other.into_hir_expression(interner, location)?), + Value::Field(value) => vec![Token::Int(value)], + other => vec![Token::UnquoteMarker(other.into_hir_expression(interner, location)?)], }; - Ok(vec![token]) + let tokens = vecmap(tokens, |token| LocatedToken::new(token, location)); + Ok(tokens) } /// Returns false for non-integral `Value`s. @@ -619,7 +612,7 @@ pub(crate) fn unwrap_rc(rc: Rc) -> T { } fn parse_tokens<'a, T, F>( - tokens: Rc>, + tokens: Rc>, elaborator: &mut Elaborator, parsing_function: F, location: Location, @@ -628,24 +621,19 @@ fn parse_tokens<'a, T, F>( where F: FnOnce(&mut Parser<'a>) -> T, { - let parser = Parser::for_tokens(add_token_spans(tokens.clone(), location.span)); + let parser = Parser::for_tokens(Tokens(unwrap_rc(tokens.clone()))); match parser.parse_result(parsing_function) { Ok((expr, warnings)) => { for warning in warnings { - elaborator.errors.push((warning.into(), location.file)); + let warning: CompilationError = warning.into(); + elaborator.push_err(warning); } Ok(expr) } Err(mut errors) => { let error = Box::new(errors.swap_remove(0)); - let file = location.file; - let tokens = tokens_to_string(tokens, elaborator.interner); - Err(InterpreterError::FailedToParseMacro { error, file, tokens, rule }) + let tokens = tokens_to_string(&tokens, elaborator.interner); + Err(InterpreterError::FailedToParseMacro { error, tokens, rule, location }) } } } - -pub(crate) fn add_token_spans(tokens: Rc>, span: Span) -> Tokens { - let tokens = unwrap_rc(tokens); - Tokens(vecmap(tokens, |token| SpannedToken::new(token, span))) -} 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 9d8c32fbc12d..81b3f8424873 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 @@ -2,7 +2,7 @@ use super::dc_mod::collect_defs; use super::errors::{DefCollectorErrorKind, DuplicateType}; use crate::elaborator::Elaborator; use crate::graph::CrateId; -use crate::hir::comptime::InterpreterError; +use crate::hir::comptime::{ComptimeError, InterpreterError}; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}; use crate::hir::resolution::errors::ResolverError; use crate::hir::type_check::TypeCheckError; @@ -11,8 +11,8 @@ use crate::token::SecondaryAttribute; use crate::usage_tracker::UnusedItem; use crate::{Generics, Type}; -use crate::hir::resolution::import::{resolve_import, ImportDirective}; use crate::hir::Context; +use crate::hir::resolution::import::{ImportDirective, resolve_import}; use crate::ast::{Expression, NoirEnumeration}; use crate::node_interner::{ @@ -22,10 +22,11 @@ use crate::node_interner::{ use crate::ast::{ ExpressionKind, Ident, ItemVisibility, LetStatement, Literal, NoirFunction, NoirStruct, - NoirTrait, NoirTypeAlias, Path, PathKind, PathSegment, UnresolvedGenerics, - UnresolvedTraitConstraint, UnresolvedType, UnsupportedNumericGenericType, + NoirTrait, NoirTypeAlias, Path, PathSegment, UnresolvedGenerics, UnresolvedTraitConstraint, + UnresolvedType, UnsupportedNumericGenericType, }; +use crate::elaborator::FrontendOptions; use crate::parser::{ParserError, SortedModule}; use noirc_errors::{CustomDiagnostic, Location, Span}; @@ -176,17 +177,42 @@ impl CollectedItems { /// Note that because these are keyed by unresolved types, the impl map is one of the few instances /// of HashMap rather than BTreeMap. For this reason, we should be careful not to iterate over it /// since it would be non-deterministic. -pub(crate) type ImplMap = - HashMap<(UnresolvedType, LocalModuleId), Vec<(UnresolvedGenerics, Span, UnresolvedFunctions)>>; +pub(crate) type ImplMap = HashMap< + (UnresolvedType, LocalModuleId), + Vec<(UnresolvedGenerics, Location, UnresolvedFunctions)>, +>; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum CompilationError { ParseError(ParserError), DefinitionError(DefCollectorErrorKind), ResolverError(ResolverError), TypeError(TypeCheckError), InterpreterError(InterpreterError), - DebugComptimeScopeNotFound(Vec), + ComptimeError(ComptimeError), + DebugComptimeScopeNotFound(Vec, Location), +} + +impl CompilationError { + /// Returns the primary location where this error happened. + pub fn location(&self) -> Location { + match self { + CompilationError::ParseError(error) => error.location(), + CompilationError::DefinitionError(error) => error.location(), + CompilationError::ResolverError(error) => error.location(), + CompilationError::TypeError(error) => error.location(), + CompilationError::InterpreterError(error) => error.location(), + CompilationError::ComptimeError(error) => error.location(), + CompilationError::DebugComptimeScopeNotFound(_, location) => *location, + } + } + + pub(crate) fn is_error(&self) -> bool { + // This is a bit expensive but not all error types have a `is_warning` method + // and it'd lead to code duplication to add them. `CompilationError::is_error` + // also isn't expected to be called too often. + CustomDiagnostic::from(self).is_error() + } } impl std::fmt::Display for CompilationError { @@ -197,7 +223,8 @@ impl std::fmt::Display for CompilationError { CompilationError::ResolverError(error) => write!(f, "{}", error), CompilationError::TypeError(error) => write!(f, "{}", error), CompilationError::InterpreterError(error) => write!(f, "{:?}", error), - CompilationError::DebugComptimeScopeNotFound(error) => write!(f, "{:?}", error), + CompilationError::DebugComptimeScopeNotFound(error, _) => write!(f, "{:?}", error), + CompilationError::ComptimeError(error) => write!(f, "{:?}", error), } } } @@ -210,15 +237,16 @@ impl<'a> From<&'a CompilationError> for CustomDiagnostic { CompilationError::ResolverError(error) => error.into(), CompilationError::TypeError(error) => error.into(), CompilationError::InterpreterError(error) => error.into(), - CompilationError::DebugComptimeScopeNotFound(error) => { + CompilationError::ComptimeError(error) => error.into(), + CompilationError::DebugComptimeScopeNotFound(error, _) => { let msg = "multiple files found matching --debug-comptime path".into(); let secondary = error.iter().fold(String::new(), |mut output, path| { let _ = writeln!(output, " {}", path.display()); output }); - // NOTE: this span is empty as it is not expected to be displayed - let dummy_span = Span::default(); - CustomDiagnostic::simple_error(msg, secondary, dummy_span) + // NOTE: this location is empty as it is not expected to be displayed + let dummy_location = Location::dummy(); + CustomDiagnostic::simple_error(msg, secondary, dummy_location) } } } @@ -282,10 +310,9 @@ impl DefCollector { context: &mut Context, ast: SortedModule, root_file_id: FileId, - debug_comptime_in_file: Option<&str>, - pedantic_solving: bool, - ) -> Vec<(CompilationError, FileId)> { - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + options: FrontendOptions, + ) -> Vec { + let mut errors: Vec = vec![]; let crate_id = def_map.krate; // Recursively resolve the dependencies @@ -296,12 +323,7 @@ impl DefCollector { let crate_graph = &context.crate_graph[crate_id]; for dep in crate_graph.dependencies.clone() { - errors.extend(CrateDefMap::collect_defs( - dep.crate_id, - context, - debug_comptime_in_file, - pedantic_solving, - )); + errors.extend(CrateDefMap::collect_defs(dep.crate_id, context, options)); let dep_def_map = context.def_map(&dep.crate_id).expect("ice: def map was just created"); @@ -358,15 +380,13 @@ impl DefCollector { for collected_import in std::mem::take(&mut def_collector.imports) { let local_module_id = collected_import.module_id; let module_id = ModuleId { krate: crate_id, local_id: local_module_id }; - let current_def_map = context.def_maps.get(&crate_id).unwrap(); - let file_id = current_def_map.file_id(local_module_id); let resolved_import = resolve_import( collected_import.path.clone(), module_id, &context.def_maps, &mut context.usage_tracker, - Some(ReferencesTracker::new(&mut context.def_interner, file_id)), + Some(ReferencesTracker::new(&mut context.def_interner)), ); match resolved_import { @@ -376,10 +396,7 @@ impl DefCollector { let has_path_resolution_error = !resolved_import.errors.is_empty(); for error in resolved_import.errors { - errors.push(( - DefCollectorErrorKind::PathResolutionError(error).into(), - file_id, - )); + errors.push(DefCollectorErrorKind::PathResolutionError(error).into()); } // Populate module namespaces according to the imports used @@ -390,14 +407,13 @@ impl DefCollector { resolved_import.namespace.iter_items() { if item_visibility < visibility { - errors.push(( + errors.push( DefCollectorErrorKind::CannotReexportItemWithLessVisibility { item_name: name.clone(), desired_visibility: visibility, } .into(), - file_id, - )); + ); } let visibility = visibility.min(item_visibility); @@ -454,34 +470,34 @@ impl DefCollector { first_def, second_def, }; - errors.push((err.into(), root_file_id)); + errors.push(err.into()); } } } Err(error) => { - let current_def_map = context.def_maps.get(&crate_id).unwrap(); - let file_id = current_def_map.file_id(collected_import.module_id); let error = DefCollectorErrorKind::PathResolutionError(error); - errors.push((error.into(), file_id)); + errors.push(error.into()); } } } - let debug_comptime_in_file = debug_comptime_in_file.and_then(|debug_comptime_in_file| { - let file = context.file_manager.find_by_path_suffix(debug_comptime_in_file); + let debug_comptime_in_file = options.debug_comptime_in_file.and_then(|file_suffix| { + let file = context.file_manager.find_by_path_suffix(file_suffix); file.unwrap_or_else(|error| { - errors.push((CompilationError::DebugComptimeScopeNotFound(error), root_file_id)); + let location = Location::new(Span::empty(0), root_file_id); + errors.push(CompilationError::DebugComptimeScopeNotFound(error, location)); None }) }); - let mut more_errors = Elaborator::elaborate( - context, - crate_id, - def_collector.items, + let cli_options = crate::elaborator::ElaboratorOptions { debug_comptime_in_file, - pedantic_solving, - ); + pedantic_solving: options.pedantic_solving, + enabled_unstable_features: options.enabled_unstable_features, + }; + + let mut more_errors = + Elaborator::elaborate(context, crate_id, def_collector.items, cli_options); errors.append(&mut more_errors); @@ -493,20 +509,18 @@ impl DefCollector { fn check_unused_items( context: &Context, crate_id: CrateId, - errors: &mut Vec<(CompilationError, FileId)>, + errors: &mut Vec, ) { let unused_imports = context.usage_tracker.unused_items().iter(); let unused_imports = unused_imports.filter(|(module_id, _)| module_id.krate == crate_id); - errors.extend(unused_imports.flat_map(|(module_id, usage_tracker)| { - let module = &context.def_maps[&crate_id].modules()[module_id.local_id.0]; + errors.extend(unused_imports.flat_map(|(_, usage_tracker)| { usage_tracker.iter().map(|(ident, unused_item)| { let ident = ident.clone(); - let error = CompilationError::ResolverError(ResolverError::UnusedItem { + CompilationError::ResolverError(ResolverError::UnusedItem { ident, item: *unused_item, - }); - (error, module.location.file) + }) }) })); } @@ -539,16 +553,12 @@ fn inject_prelude( .map(|segment| { crate::ast::PathSegment::from(crate::ast::Ident::new( segment.into(), - Span::default(), + Location::dummy(), )) }) .collect(); - let path = Path { - segments: segments.clone(), - kind: crate::ast::PathKind::Plain, - span: Span::default(), - }; + let path = Path::plain(segments.clone(), Location::dummy()); if let Ok(resolved_import) = resolve_import( path, @@ -566,14 +576,14 @@ fn inject_prelude( for path in prelude { let mut segments = segments.clone(); - segments.push(PathSegment::from(Ident::new(path.to_string(), Span::default()))); + segments.push(PathSegment::from(Ident::new(path.to_string(), Location::dummy()))); collected_imports.insert( 0, ImportDirective { visibility: ItemVisibility::Private, module_id: crate_root, - path: Path { segments, kind: PathKind::Plain, span: Span::default() }, + path: Path::plain(segments, Location::dummy()), alias: None, is_prelude: true, }, 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 59e1f2f6e329..8ba6be4fc4bd 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 @@ -4,7 +4,7 @@ use std::rc::Rc; use std::vec; use acvm::{AcirField, FieldElement}; -use fm::{FileId, FileManager, FILE_EXTENSION}; +use fm::{FILE_EXTENSION, FileId, FileManager}; use noirc_errors::{Location, Span}; use num_bigint::BigUint; use num_traits::Num; @@ -20,13 +20,13 @@ use crate::hir::resolution::errors::ResolverError; use crate::node_interner::{ModuleAttributes, NodeInterner, ReferenceId, TypeId}; use crate::token::SecondaryAttribute; use crate::usage_tracker::{UnusedItem, UsageTracker}; +use crate::{Generics, Kind, ResolvedGeneric, Type, TypeVariable}; use crate::{ graph::CrateId, hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait}, node_interner::{FunctionModifiers, TraitId, TypeAliasId}, parser::{SortedModule, SortedSubModule}, }; -use crate::{Generics, Kind, ResolvedGeneric, Type, TypeVariable}; use super::dc_crate::ModuleAttribute; use super::dc_crate::{CollectedItems, UnresolvedEnum}; @@ -37,9 +37,9 @@ use super::{ }, errors::{DefCollectorErrorKind, DuplicateType}, }; -use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleData, ModuleId, MAIN_FUNCTION}; -use crate::hir::resolution::import::ImportDirective; use crate::hir::Context; +use crate::hir::def_map::{CrateDefMap, LocalModuleId, MAIN_FUNCTION, ModuleData, ModuleId}; +use crate::hir::resolution::import::ImportDirective; /// Given a module collect all definitions into ModuleData struct ModCollector<'a> { @@ -58,9 +58,9 @@ pub fn collect_defs( module_id: LocalModuleId, crate_id: CrateId, context: &mut Context, -) -> Vec<(CompilationError, FileId)> { +) -> Vec { let mut collector = ModCollector { def_collector, file_id, module_id }; - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + let mut errors: Vec = vec![]; // First resolve the module declarations for decl in ast.module_decls { @@ -116,7 +116,7 @@ pub fn collect_defs( errors } -impl<'a> ModCollector<'a> { +impl ModCollector<'_> { fn collect_attributes( &mut self, attributes: Vec, @@ -143,7 +143,7 @@ impl<'a> ModCollector<'a> { context: &mut Context, globals: Vec<(Documented, ItemVisibility)>, crate_id: CrateId, - ) -> Vec<(CompilationError, fm::FileId)> { + ) -> Vec { let mut errors = vec![]; for (global, visibility) in globals { let (global, error) = collect_global( @@ -171,7 +171,7 @@ impl<'a> ModCollector<'a> { context: &mut Context, impls: Vec, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { + ) -> Vec { let mut errors = Vec::new(); let module_id = ModuleId { krate, local_id: self.module_id }; @@ -194,7 +194,7 @@ impl<'a> ModCollector<'a> { context: &mut Context, impls: Vec, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { + ) -> Vec { let mut errors = Vec::new(); for mut trait_impl in impls { @@ -212,19 +212,19 @@ impl<'a> ModCollector<'a> { for (_, func_id, noir_function) in &mut unresolved_functions.functions { if noir_function.def.attributes.is_test_function() { let error = DefCollectorErrorKind::TestOnAssociatedFunction { - span: noir_function.name_ident().span(), + location: noir_function.name_ident().location(), }; - errors.push((error.into(), self.file_id)); + errors.push(error.into()); } if noir_function.def.attributes.has_export() { let error = DefCollectorErrorKind::ExportOnAssociatedFunction { - span: noir_function.name_ident().span(), + location: noir_function.name_ident().location(), }; - errors.push((error.into(), self.file_id)); + errors.push(error.into()); } - let location = Location::new(noir_function.def.span, self.file_id); + let location = noir_function.def.location; context.def_interner.push_function(*func_id, &noir_function.def, module, location); } @@ -258,7 +258,7 @@ impl<'a> ModCollector<'a> { context: &mut Context, functions: Vec>, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { + ) -> Vec { let mut unresolved_functions = UnresolvedFunctions { file_id: self.file_id, functions: Vec::new(), @@ -276,7 +276,6 @@ impl<'a> ModCollector<'a> { &mut context.usage_tracker, &function.item, module, - self.file_id, function.doc_comments, &mut errors, ) else { @@ -304,7 +303,7 @@ impl<'a> ModCollector<'a> { context: &mut Context, types: Vec>, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { + ) -> Vec { let mut definition_errors = vec![]; for struct_definition in types { if let Some((id, the_struct)) = collect_struct( @@ -312,7 +311,6 @@ impl<'a> ModCollector<'a> { &mut self.def_collector.def_map, &mut context.usage_tracker, struct_definition, - self.file_id, self.module_id, krate, &mut definition_errors, @@ -331,7 +329,7 @@ impl<'a> ModCollector<'a> { context: &mut Context, types: Vec>, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { + ) -> Vec { let mut definition_errors = vec![]; for enum_definition in types { if let Some((id, the_enum)) = collect_enum( @@ -357,8 +355,8 @@ impl<'a> ModCollector<'a> { context: &mut Context, type_aliases: Vec>, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + ) -> Vec { + let mut errors: Vec = vec![]; for type_alias in type_aliases { let doc_comments = type_alias.doc_comments; let type_alias = type_alias.item; @@ -377,7 +375,6 @@ impl<'a> ModCollector<'a> { &context.def_interner, &unresolved.type_alias_def.generics, &mut errors, - self.file_id, ); let type_alias_id = @@ -406,7 +403,7 @@ impl<'a> ModCollector<'a> { first_def, second_def, }; - errors.push((err.into(), self.file_id)); + errors.push(err.into()); } self.def_collector.items.type_aliases.insert(type_alias_id, unresolved); @@ -433,20 +430,20 @@ impl<'a> ModCollector<'a> { context: &mut Context, traits: Vec>, krate: CrateId, - ) -> Vec<(CompilationError, FileId)> { - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + ) -> Vec { + let mut errors: Vec = vec![]; for trait_definition in traits { let doc_comments = trait_definition.doc_comments; let trait_definition = trait_definition.item; let name = trait_definition.name.clone(); - let location = Location::new(trait_definition.name.span(), self.file_id); + let location = trait_definition.location; // Create the corresponding module for the trait namespace let trait_id = match self.push_child_module( context, &name, ItemVisibility::Public, - Location::new(name.span(), self.file_id), + name.location(), Vec::new(), Vec::new(), false, @@ -455,7 +452,7 @@ impl<'a> ModCollector<'a> { ) { Ok(module_id) => TraitId(ModuleId { krate, local_id: module_id.local_id }), Err(error) => { - errors.push((error.into(), self.file_id)); + errors.push(error.into()); continue; } }; @@ -484,7 +481,7 @@ impl<'a> ModCollector<'a> { first_def, second_def, }; - errors.push((error.into(), self.file_id)); + errors.push(error.into()); } // Add all functions that have a default implementation in the trait @@ -573,7 +570,7 @@ impl<'a> ModCollector<'a> { first_def, second_def, }; - errors.push((error.into(), self.file_id)); + errors.push(error.into()); } } } @@ -597,7 +594,7 @@ impl<'a> ModCollector<'a> { first_def, second_def, }; - errors.push((error.into(), self.file_id)); + errors.push(error.into()); } else { let type_variable_id = context.def_interner.next_type_variable_id(); let typ = self.resolve_associated_constant_type(typ, &mut errors); @@ -608,7 +605,7 @@ impl<'a> ModCollector<'a> { type_variable_id, Kind::numeric(typ), ), - span: name.span(), + location: name.location(), }); } } @@ -626,13 +623,13 @@ impl<'a> ModCollector<'a> { first_def, second_def, }; - errors.push((error.into(), self.file_id)); + errors.push(error.into()); } else { let type_variable_id = context.def_interner.next_type_variable_id(); associated_types.push(ResolvedGeneric { name: Rc::new(name.to_string()), type_var: TypeVariable::unbound(type_variable_id, Kind::Normal), - span: name.span(), + location: name.location(), }); } } @@ -643,7 +640,6 @@ impl<'a> ModCollector<'a> { &context.def_interner, &trait_definition.generics, &mut errors, - self.file_id, ); let unresolved = UnresolvedTrait { @@ -684,8 +680,8 @@ impl<'a> ModCollector<'a> { parent_module_id: LocalModuleId, submodules: Vec>, file_id: FileId, - ) -> Vec<(CompilationError, FileId)> { - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + ) -> Vec { + let mut errors: Vec = vec![]; for submodule in submodules { let mut doc_comments = submodule.doc_comments; let submodule = submodule.item; @@ -731,7 +727,7 @@ impl<'a> ModCollector<'a> { )); } Err(error) => { - errors.push((error.into(), file_id)); + errors.push(error.into()); } }; } @@ -749,16 +745,16 @@ impl<'a> ModCollector<'a> { crate_id: CrateId, parent_file_id: FileId, parent_module_id: LocalModuleId, - ) -> Vec<(CompilationError, FileId)> { + ) -> Vec { let mut doc_comments = mod_decl.doc_comments; let mod_decl = mod_decl.item; - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + let mut errors: Vec = vec![]; let child_file_id = match find_module(&context.file_manager, self.file_id, &mod_decl.ident) { Ok(child_file_id) => child_file_id, Err(err) => { - errors.push((err.into(), self.file_id)); + errors.push(err.into()); return errors; } }; @@ -768,15 +764,15 @@ impl<'a> ModCollector<'a> { if let Some(old_location) = context.visited_files.get(&child_file_id) { let error = DefCollectorErrorKind::ModuleAlreadyPartOfCrate { mod_name: mod_decl.ident.clone(), - span: location.span, + location, }; - errors.push((error.into(), location.file)); + errors.push(error.into()); let error = DefCollectorErrorKind::ModuleOriginallyDefined { mod_name: mod_decl.ident.clone(), - span: old_location.span, + location: *old_location, }; - errors.push((error.into(), old_location.file)); + errors.push(error.into()); return errors; } @@ -786,9 +782,7 @@ impl<'a> ModCollector<'a> { let (ast, parsing_errors) = context.parsed_file_results(child_file_id); let ast = ast.into_sorted(); - errors.extend( - parsing_errors.iter().map(|e| (e.clone().into(), child_file_id)).collect::>(), - ); + errors.extend(parsing_errors.iter().map(|e| e.clone().into()).collect::>()); // Add module into def collector and get a ModuleId match self.push_child_module( @@ -833,7 +827,7 @@ impl<'a> ModCollector<'a> { )); } Err(error) => { - errors.push((error.into(), child_file_id)); + errors.push(error.into()); } } errors @@ -872,15 +866,15 @@ impl<'a> ModCollector<'a> { fn resolve_associated_constant_type( &self, typ: &UnresolvedType, - errors: &mut Vec<(CompilationError, FileId)>, + errors: &mut Vec, ) -> Type { match &typ.typ { UnresolvedTypeData::FieldElement => Type::FieldElement, UnresolvedTypeData::Integer(sign, bits) => Type::Integer(*sign, *bits), _ => { - let span = typ.span; - let error = ResolverError::AssociatedConstantsMustBeNumeric { span }; - errors.push((error.into(), self.file_id)); + let error = + ResolverError::AssociatedConstantsMustBeNumeric { location: typ.location }; + errors.push(error.into()); Type::Error } } @@ -979,9 +973,8 @@ pub fn collect_function( usage_tracker: &mut UsageTracker, function: &NoirFunction, module: ModuleId, - file: FileId, doc_comments: Vec, - errors: &mut Vec<(CompilationError, FileId)>, + errors: &mut Vec, ) -> Option { if let Some(field) = function.attributes().get_field_attribute() { if !is_native_field(&field) { @@ -1002,7 +995,7 @@ pub fn collect_function( let name = function.name_ident().clone(); let func_id = interner.push_empty_fn(); let visibility = function.def.visibility; - let location = Location::new(function.span(), file); + let location = function.location(); interner.push_function(func_id, &function.def, module, location); if interner.is_in_lsp_mode() && !function.def.is_test() { interner.register_function(func_id, &function.def); @@ -1023,7 +1016,7 @@ pub fn collect_function( first_def, second_def, }; - errors.push((error.into(), file)); + errors.push(error.into()); } Some(func_id) } @@ -1034,26 +1027,22 @@ pub fn collect_struct( def_map: &mut CrateDefMap, usage_tracker: &mut UsageTracker, struct_definition: Documented, - file_id: FileId, module_id: LocalModuleId, krate: CrateId, - definition_errors: &mut Vec<(CompilationError, FileId)>, + definition_errors: &mut Vec, ) -> Option<(TypeId, UnresolvedStruct)> { let doc_comments = struct_definition.doc_comments; let struct_definition = struct_definition.item; + let file_id = struct_definition.location.file; - check_duplicate_field_names(&struct_definition, file_id, definition_errors); + check_duplicate_field_names(&struct_definition, definition_errors); let name = struct_definition.name.clone(); let unresolved = UnresolvedStruct { file_id, module_id, struct_def: struct_definition }; - let resolved_generics = Context::resolve_generics( - interner, - &unresolved.struct_def.generics, - definition_errors, - file_id, - ); + let resolved_generics = + Context::resolve_generics(interner, &unresolved.struct_def.generics, definition_errors); // Create the corresponding module for the struct namespace let location = Location::new(name.span(), file_id); @@ -1072,13 +1061,13 @@ pub fn collect_struct( ) { Ok(module_id) => { let name = unresolved.struct_def.name.clone(); - let span = unresolved.struct_def.span; + let span = unresolved.struct_def.location.span; let attributes = unresolved.struct_def.attributes.clone(); let local_id = module_id.local_id; interner.new_type(name, span, attributes, resolved_generics, krate, local_id, file_id) } Err(error) => { - definition_errors.push((error.into(), file_id)); + definition_errors.push(error.into()); return None; } }; @@ -1113,7 +1102,7 @@ pub fn collect_struct( first_def, second_def, }; - definition_errors.push((error.into(), file_id)); + definition_errors.push(error.into()); } if interner.is_in_lsp_mode() { @@ -1132,23 +1121,19 @@ pub fn collect_enum( file_id: FileId, module_id: LocalModuleId, krate: CrateId, - definition_errors: &mut Vec<(CompilationError, FileId)>, + definition_errors: &mut Vec, ) -> Option<(TypeId, UnresolvedEnum)> { let doc_comments = enum_def.doc_comments; let enum_def = enum_def.item; - check_duplicate_variant_names(&enum_def, file_id, definition_errors); + check_duplicate_variant_names(&enum_def, definition_errors); let name = enum_def.name.clone(); let unresolved = UnresolvedEnum { file_id, module_id, enum_def }; - let resolved_generics = Context::resolve_generics( - interner, - &unresolved.enum_def.generics, - definition_errors, - file_id, - ); + let resolved_generics = + Context::resolve_generics(interner, &unresolved.enum_def.generics, definition_errors); // Create the corresponding module for the enum namespace let location = Location::new(name.span(), file_id); @@ -1167,13 +1152,13 @@ pub fn collect_enum( ) { Ok(module_id) => { let name = unresolved.enum_def.name.clone(); - let span = unresolved.enum_def.span; + let span = unresolved.enum_def.location.span; let attributes = unresolved.enum_def.attributes.clone(); let local_id = module_id.local_id; interner.new_type(name, span, attributes, resolved_generics, krate, local_id, file_id) } Err(error) => { - definition_errors.push((error.into(), file_id)); + definition_errors.push(error.into()); return None; } }; @@ -1208,7 +1193,7 @@ pub fn collect_enum( first_def, second_def, }; - definition_errors.push((error.into(), file_id)); + definition_errors.push(error.into()); } if interner.is_in_lsp_mode() { @@ -1224,7 +1209,7 @@ pub fn collect_impl( r#impl: TypeImpl, file_id: FileId, module_id: ModuleId, - errors: &mut Vec<(CompilationError, FileId)>, + errors: &mut Vec, ) { let mut unresolved_functions = UnresolvedFunctions { file_id, functions: Vec::new(), trait_id: None, self_type: None }; @@ -1235,16 +1220,16 @@ pub fn collect_impl( if method.def.attributes.is_test_function() { let error = DefCollectorErrorKind::TestOnAssociatedFunction { - span: method.name_ident().span(), + location: method.name_ident().location(), }; - errors.push((error.into(), file_id)); + errors.push(error.into()); continue; } if method.def.attributes.has_export() { let error = DefCollectorErrorKind::ExportOnAssociatedFunction { - span: method.name_ident().span(), + location: method.name_ident().location(), }; - errors.push((error.into(), file_id)); + errors.push(error.into()); } let func_id = interner.push_empty_fn(); @@ -1257,7 +1242,7 @@ pub fn collect_impl( let key = (r#impl.object_type, module_id.local_id); let methods = items.impls.entry(key).or_default(); - methods.push((r#impl.generics, r#impl.type_span, unresolved_functions)); + methods.push((r#impl.generics, r#impl.type_location, unresolved_functions)); } fn find_module( @@ -1339,11 +1324,7 @@ fn is_native_field(str: &str) -> bool { } else { BigUint::from_str_radix(str, 10) }; - if let Ok(big_num) = big_num { - big_num == FieldElement::modulus() - } else { - CHOSEN_FIELD == str - } + if let Ok(big_num) = big_num { big_num == FieldElement::modulus() } else { CHOSEN_FIELD == str } } type AssociatedTypes = Vec<(Ident, UnresolvedType)>; @@ -1401,7 +1382,7 @@ pub(crate) fn collect_global( file_id: FileId, module_id: LocalModuleId, crate_id: CrateId, -) -> (UnresolvedGlobal, Option<(CompilationError, FileId)>) { +) -> (UnresolvedGlobal, Option) { let doc_comments = global.doc_comments; let global = global.item; @@ -1435,7 +1416,7 @@ pub(crate) fn collect_global( let error = result.err().map(|(first_def, second_def)| { let err = DefCollectorErrorKind::Duplicate { typ: DuplicateType::Global, first_def, second_def }; - (err.into(), file_id) + err.into() }); interner.set_doc_comments(ReferenceId::Global(global_id), doc_comments); @@ -1446,8 +1427,7 @@ pub(crate) fn collect_global( fn check_duplicate_field_names( struct_definition: &NoirStruct, - file: FileId, - definition_errors: &mut Vec<(CompilationError, FileId)>, + definition_errors: &mut Vec, ) { let mut seen_field_names = std::collections::HashSet::new(); for field in &struct_definition.fields { @@ -1463,14 +1443,13 @@ fn check_duplicate_field_names( first_def: previous_field_name.clone(), second_def: field_name.clone(), }; - definition_errors.push((error.into(), file)); + definition_errors.push(error.into()); } } fn check_duplicate_variant_names( enum_def: &NoirEnumeration, - file: FileId, - definition_errors: &mut Vec<(CompilationError, FileId)>, + definition_errors: &mut Vec, ) { let mut seen_variant_names = std::collections::HashSet::new(); for variant in &enum_def.variants { @@ -1486,7 +1465,7 @@ fn check_duplicate_variant_names( first_def: previous_variant_name.clone(), second_def: variant_name.clone(), }; - definition_errors.push((error.into(), file)); + definition_errors.push(error.into()); } } @@ -1494,7 +1473,6 @@ fn check_duplicate_variant_names( mod find_module_tests { use super::*; - use noirc_errors::Spanned; use std::path::{Path, PathBuf}; fn add_file(file_manager: &mut FileManager, dir: &Path, file_name: &str) -> FileId { @@ -1513,7 +1491,9 @@ mod find_module_tests { anchor: FileId, mod_name: &str, ) -> Result { - let mod_name = Ident(Spanned::from_position(0, 1, mod_name.to_string())); + let span = Span::from(0..1); + let location = Location::new(span, FileId::dummy()); + let mod_name = Ident::new(mod_name.to_string(), location); super::find_module(file_manager, anchor, &mod_name) } 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 1ca62acd29b1..7f17b1e30430 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 @@ -2,9 +2,7 @@ use crate::ast::{Ident, ItemVisibility, Path, UnsupportedNumericGenericType}; use crate::hir::resolution::import::PathResolutionError; use crate::hir::type_check::generics::TraitGenerics; -use noirc_errors::CustomDiagnostic as Diagnostic; -use noirc_errors::FileDiagnostic; -use noirc_errors::Span; +use noirc_errors::{CustomDiagnostic as Diagnostic, Location}; use thiserror::Error; use std::fmt; @@ -25,7 +23,7 @@ pub enum DuplicateType { EnumVariant, } -#[derive(Error, Debug, Clone)] +#[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum DefCollectorErrorKind { #[error("Duplicate {typ}")] Duplicate { typ: DuplicateType, first_def: Ident, second_def: Ident }, @@ -38,23 +36,13 @@ pub enum DefCollectorErrorKind { #[error("Cannot re-export {item_name} because it has less visibility than this use statement")] CannotReexportItemWithLessVisibility { item_name: Ident, desired_visibility: ItemVisibility }, #[error("Non-struct type used in impl")] - NonStructTypeInImpl { span: Span }, + NonStructTypeInImpl { location: Location }, #[error("Cannot implement trait on a mutable reference type")] - MutableReferenceInTraitImpl { span: Span }, + MutableReferenceInTraitImpl { location: Location }, #[error("Impl for type `{typ}` overlaps with existing impl")] - OverlappingImpl { span: Span, typ: crate::Type }, - #[error("Previous impl defined here")] - OverlappingImplNote { span: Span }, + OverlappingImpl { typ: crate::Type, location: Location, prev_location: Location }, #[error("Cannot `impl` a type defined outside the current crate")] - ForeignImpl { span: Span, type_name: String }, - #[error("Mismatched number of generics in {location}")] - MismatchGenericCount { - actual_generic_count: usize, - expected_generic_count: usize, - location: &'static str, - origin: String, - span: Span, - }, + ForeignImpl { location: Location, type_name: String }, #[error("Method is not defined in trait")] MethodNotInTrait { trait_name: Ident, impl_method: Ident }, #[error("Only traits can be implemented")] @@ -62,35 +50,66 @@ pub enum DefCollectorErrorKind { #[error("Trait not found")] TraitNotFound { trait_path: Path }, #[error("Missing Trait method implementation")] - TraitMissingMethod { trait_name: Ident, method_name: Ident, trait_impl_span: Span }, + TraitMissingMethod { trait_name: Ident, method_name: Ident, trait_impl_location: Location }, #[error("Module is already part of the crate")] - ModuleAlreadyPartOfCrate { mod_name: Ident, span: Span }, + ModuleAlreadyPartOfCrate { mod_name: Ident, location: Location }, #[error("Module was originally declared here")] - ModuleOriginallyDefined { mod_name: Ident, span: Span }, - #[error( - "Either the type or the trait must be from the same crate as the trait implementation" - )] - TraitImplOrphaned { span: Span }, + ModuleOriginallyDefined { mod_name: Ident, location: Location }, + #[error("Either the type or the trait must be from the same crate as the trait implementation")] + TraitImplOrphaned { location: Location }, #[error("impl has stricter requirements than trait")] ImplIsStricterThanTrait { constraint_typ: crate::Type, constraint_name: String, constraint_generics: TraitGenerics, - constraint_span: Span, + constraint_location: Location, trait_method_name: String, - trait_method_span: Span, + trait_method_location: Location, }, #[error("{0}")] UnsupportedNumericGenericType(#[from] UnsupportedNumericGenericType), #[error("The `#[test]` attribute may only be used on a non-associated function")] - TestOnAssociatedFunction { span: Span }, + TestOnAssociatedFunction { location: Location }, #[error("The `#[export]` attribute may only be used on a non-associated function")] - ExportOnAssociatedFunction { span: Span }, + ExportOnAssociatedFunction { location: Location }, } impl DefCollectorErrorKind { - pub fn into_file_diagnostic(&self, file: fm::FileId) -> FileDiagnostic { - Diagnostic::from(self).in_file(file) + pub fn location(&self) -> Location { + match self { + DefCollectorErrorKind::Duplicate { second_def: ident, .. } + | DefCollectorErrorKind::UnresolvedModuleDecl { mod_name: ident, .. } + | DefCollectorErrorKind::CannotReexportItemWithLessVisibility { + item_name: ident, + .. + } + | DefCollectorErrorKind::MethodNotInTrait { impl_method: ident, .. } + | DefCollectorErrorKind::OverlappingModuleDecls { mod_name: ident, .. } => { + ident.location() + } + DefCollectorErrorKind::PathResolutionError(path_resolution_error) => { + path_resolution_error.location() + } + DefCollectorErrorKind::ImplIsStricterThanTrait { + trait_method_location: location, + .. + } + | DefCollectorErrorKind::TestOnAssociatedFunction { location } + | DefCollectorErrorKind::ExportOnAssociatedFunction { location } + | DefCollectorErrorKind::NonStructTypeInImpl { location } + | DefCollectorErrorKind::MutableReferenceInTraitImpl { location } + | DefCollectorErrorKind::OverlappingImpl { location, .. } + | DefCollectorErrorKind::ModuleAlreadyPartOfCrate { location, .. } + | DefCollectorErrorKind::ModuleOriginallyDefined { location, .. } + | DefCollectorErrorKind::TraitImplOrphaned { location } + | DefCollectorErrorKind::TraitMissingMethod { trait_impl_location: location, .. } + | DefCollectorErrorKind::ForeignImpl { location, .. } => *location, + DefCollectorErrorKind::NotATrait { not_a_trait_name: path } + | DefCollectorErrorKind::TraitNotFound { trait_path: path } => path.location, + DefCollectorErrorKind::UnsupportedNumericGenericType( + unsupported_numeric_generic_type, + ) => unsupported_numeric_generic_type.ident.location(), + } } } @@ -100,9 +119,11 @@ impl<'a> From<&'a UnsupportedNumericGenericType> for Diagnostic { let typ = &error.typ; Diagnostic::simple_error( - format!("{name} has a type of {typ}. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`."), + format!( + "{name} has a type of {typ}. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`." + ), "Unsupported numeric generic type".to_string(), - error.ident.0.span(), + error.ident.0.location(), ) } } @@ -135,35 +156,35 @@ impl<'a> From<&'a DefCollectorErrorKind> for Diagnostic { &typ, &first_def.0.contents ); { - let first_span = first_def.0.span(); - let second_span = second_def.0.span(); + let first_location = first_def.0.location(); + let second_location = second_def.0.location(); let mut diag = Diagnostic::simple_error( primary_message, - format!("First {} found here", &typ), - first_span, + format!("Second {} found here", &typ), + second_location, ); - diag.add_secondary(format!("Second {} found here", &typ), second_span); + diag.add_secondary(format!("First {} found here", &typ), first_location); diag } } DefCollectorErrorKind::UnresolvedModuleDecl { mod_name, expected_path, alternative_path } => { - let span = mod_name.0.span(); + let location = mod_name.0.location(); let mod_name = &mod_name.0.contents; Diagnostic::simple_error( format!("No module `{mod_name}` at path `{expected_path}` or `{alternative_path}`"), String::new(), - span, + location, ) } DefCollectorErrorKind::OverlappingModuleDecls { mod_name, expected_path, alternative_path } => { - let span = mod_name.0.span(); + let location = mod_name.0.location(); let mod_name = &mod_name.0.contents; Diagnostic::simple_error( format!("Overlapping modules `{mod_name}` at path `{expected_path}` and `{alternative_path}`"), String::new(), - span, + location, ) } DefCollectorErrorKind::PathResolutionError(error) => error.into(), @@ -171,68 +192,48 @@ impl<'a> From<&'a DefCollectorErrorKind> for Diagnostic { Diagnostic::simple_error( format!("cannot re-export {item_name} because it has less visibility than this use statement"), format!("consider marking {item_name} as {desired_visibility}"), - item_name.span()) + item_name.location()) } - DefCollectorErrorKind::NonStructTypeInImpl { span } => Diagnostic::simple_error( + DefCollectorErrorKind::NonStructTypeInImpl { location } => Diagnostic::simple_error( "Non-struct type used in impl".into(), "Only struct types may have implementation methods".into(), - *span, + *location, ), - DefCollectorErrorKind::MutableReferenceInTraitImpl { span } => Diagnostic::simple_error( + DefCollectorErrorKind::MutableReferenceInTraitImpl { location } => Diagnostic::simple_error( "Trait impls are not allowed on mutable reference types".into(), "Try using a struct type here instead".into(), - *span, + *location, ), - DefCollectorErrorKind::OverlappingImpl { span, typ } => { - Diagnostic::simple_error( + DefCollectorErrorKind::OverlappingImpl { location, typ, prev_location } => { + let mut diagnostic = Diagnostic::simple_error( format!("Impl for type `{typ}` overlaps with existing impl"), "Overlapping impl".into(), - *span, - ) - } - DefCollectorErrorKind::OverlappingImplNote { span } => { - // This should be a note or part of the previous error eventually. - // This must be an error to appear next to the previous OverlappingImpl - // error since we sort warnings first. - Diagnostic::simple_error( - "Previous impl defined here".into(), - "Previous impl defined here".into(), - *span, - ) + *location, + ); + diagnostic.add_secondary("Previous impl defined here".into(), *prev_location); + diagnostic } - DefCollectorErrorKind::ForeignImpl { span, type_name } => Diagnostic::simple_error( + DefCollectorErrorKind::ForeignImpl { location, type_name } => Diagnostic::simple_error( "Cannot `impl` a type that was defined outside the current crate".into(), format!("{type_name} was defined outside the current crate"), - *span, + *location, ), DefCollectorErrorKind::TraitNotFound { trait_path } => Diagnostic::simple_error( format!("Trait {trait_path} not found"), "".to_string(), - trait_path.span(), + trait_path.location, ), - DefCollectorErrorKind::MismatchGenericCount { - actual_generic_count, - expected_generic_count, - location, - origin, - span, - } => { - let plural = if *expected_generic_count == 1 { "" } else { "s" }; - let primary_message = format!( - "`{origin}` expects {expected_generic_count} generic{plural}, but {location} has {actual_generic_count}"); - Diagnostic::simple_error(primary_message, "".to_string(), *span) - } DefCollectorErrorKind::MethodNotInTrait { trait_name, impl_method } => { let trait_name = &trait_name.0.contents; - let impl_method_span = impl_method.span(); + let impl_method_location = impl_method.location(); let impl_method_name = &impl_method.0.contents; let primary_message = format!("Method with name `{impl_method_name}` is not part of trait `{trait_name}`, therefore it can't be implemented"); - Diagnostic::simple_error(primary_message, "".to_owned(), impl_method_span) + Diagnostic::simple_error(primary_message, "".to_owned(), impl_method_location) } DefCollectorErrorKind::TraitMissingMethod { trait_name, method_name, - trait_impl_span, + trait_impl_location, } => { let trait_name = &trait_name.0.contents; let impl_method_name = &method_name.0.contents; @@ -242,53 +243,53 @@ impl<'a> From<&'a DefCollectorErrorKind> for Diagnostic { Diagnostic::simple_error( primary_message, format!("Please implement {impl_method_name} here"), - *trait_impl_span, + *trait_impl_location, ) } DefCollectorErrorKind::NotATrait { not_a_trait_name } => { - let span = not_a_trait_name.span(); + let location = not_a_trait_name.location; Diagnostic::simple_error( format!("{not_a_trait_name} is not a trait, therefore it can't be implemented"), String::new(), - span, + location, ) } - DefCollectorErrorKind::ModuleAlreadyPartOfCrate { mod_name, span } => { + DefCollectorErrorKind::ModuleAlreadyPartOfCrate { mod_name, location } => { let message = format!("Module '{mod_name}' is already part of the crate"); let secondary = String::new(); - Diagnostic::simple_error(message, secondary, *span) + Diagnostic::simple_error(message, secondary, *location) } - DefCollectorErrorKind::ModuleOriginallyDefined { mod_name, span } => { + DefCollectorErrorKind::ModuleOriginallyDefined { mod_name, location } => { let message = format!("Note: {mod_name} was originally declared here"); let secondary = String::new(); - Diagnostic::simple_error(message, secondary, *span) + Diagnostic::simple_error(message, secondary, *location) } - DefCollectorErrorKind::TraitImplOrphaned { span } => Diagnostic::simple_error( + DefCollectorErrorKind::TraitImplOrphaned { location } => Diagnostic::simple_error( "Orphaned trait implementation".into(), "Either the type or the trait must be from the same crate as the trait implementation".into(), - *span, + *location, ), - DefCollectorErrorKind::ImplIsStricterThanTrait { constraint_typ, constraint_name, constraint_generics, constraint_span, trait_method_name, trait_method_span } => { + DefCollectorErrorKind::ImplIsStricterThanTrait { constraint_typ, constraint_name, constraint_generics, constraint_location, trait_method_name, trait_method_location } => { let constraint = format!("{}{}", constraint_name, constraint_generics); let mut diag = Diagnostic::simple_error( "impl has stricter requirements than trait".to_string(), format!("impl has extra requirement `{constraint_typ}: {constraint}`"), - *constraint_span, + *constraint_location, ); - diag.add_secondary(format!("definition of `{trait_method_name}` from trait"), *trait_method_span); + diag.add_secondary(format!("definition of `{trait_method_name}` from trait"), *trait_method_location); diag } DefCollectorErrorKind::UnsupportedNumericGenericType(err) => err.into(), - DefCollectorErrorKind::TestOnAssociatedFunction { span } => Diagnostic::simple_error( + DefCollectorErrorKind::TestOnAssociatedFunction { location } => Diagnostic::simple_error( "The `#[test]` attribute is disallowed on `impl` methods".into(), String::new(), - *span, + *location, ), - DefCollectorErrorKind::ExportOnAssociatedFunction { span } => Diagnostic::simple_error( + DefCollectorErrorKind::ExportOnAssociatedFunction { location } => Diagnostic::simple_error( "The `#[export]` attribute is disallowed on `impl` methods".into(), String::new(), - *span, + *location, ), } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/item_scope.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/item_scope.rs index 3ca89e56bbc4..bbc2f59c655b 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/item_scope.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/item_scope.rs @@ -1,8 +1,8 @@ -use super::{namespace::PerNs, ModuleDefId, ModuleId}; +use super::{ModuleDefId, ModuleId, namespace::PerNs}; use crate::ast::{Ident, ItemVisibility}; use crate::node_interner::{FuncId, TraitId}; -use std::collections::{hash_map::Entry, HashMap}; +use std::collections::{HashMap, hash_map::Entry}; type Scope = HashMap, (ModuleDefId, ItemVisibility, bool /*is_prelude*/)>; @@ -50,11 +50,7 @@ impl ItemScope { let is_prelude = std::mem::replace(&mut n.get_mut().2, is_prelude); let old_ident = o.key(); - if is_prelude { - Ok(()) - } else { - Err((old_ident.clone(), name)) - } + if is_prelude { Ok(()) } else { Err((old_ident.clone(), name)) } } else { trait_hashmap.insert(trait_id, (mod_def, visibility, is_prelude)); Ok(()) 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 fae891a16475..5377ee7d42a6 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 @@ -1,6 +1,7 @@ +use crate::elaborator::FrontendOptions; use crate::graph::{CrateGraph, CrateId}; -use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector}; use crate::hir::Context; +use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector}; use crate::node_interner::{FuncId, GlobalId, NodeInterner, TypeId}; use crate::parse_program; use crate::parser::{ParsedModule, ParserError}; @@ -77,15 +78,14 @@ impl CrateDefMap { pub fn collect_defs( crate_id: CrateId, context: &mut Context, - debug_comptime_in_file: Option<&str>, - pedantic_solving: bool, - ) -> Vec<(CompilationError, FileId)> { + options: FrontendOptions, + ) -> Vec { // Check if this Crate has already been compiled // XXX: There is probably a better alternative for this. // Without this check, the compiler will panic as it does not // expect the same crate to be processed twice. It would not // make the implementation wrong, if the same crate was processed twice, it just makes it slow. - let mut errors: Vec<(CompilationError, FileId)> = vec![]; + let mut errors: Vec = vec![]; if context.def_map(&crate_id).is_some() { return errors; } @@ -120,13 +120,10 @@ impl CrateDefMap { context, ast, root_file_id, - debug_comptime_in_file, - pedantic_solving, + options, )); - errors.extend( - parsing_errors.iter().map(|e| (e.clone().into(), root_file_id)).collect::>(), - ); + errors.extend(parsing_errors.iter().map(|e| e.clone().into()).collect::>()); errors } @@ -373,7 +370,7 @@ pub struct Contract { /// Given a FileId, fetch the File, from the FileManager and parse it's content pub fn parse_file(fm: &FileManager, file_id: FileId) -> (ParsedModule, Vec) { let file_source = fm.fetch_file(file_id).expect("File does not exist"); - parse_program(file_source) + parse_program(file_source, file_id) } impl std::ops::Index for CrateDefMap { 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 e85fa629d561..8f2c3c5f8675 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/mod.rs @@ -14,7 +14,7 @@ use crate::parser::ParserError; use crate::usage_tracker::UsageTracker; use crate::{Generics, Kind, ParsedModule, ResolvedGeneric, TypeVariable}; use def_collector::dc_crate::CompilationError; -use def_map::{fully_qualified_module_path, Contract, CrateDefMap}; +use def_map::{Contract, CrateDefMap, fully_qualified_module_path}; use fm::{FileId, FileManager}; use iter_extended::vecmap; use noirc_errors::Location; @@ -139,11 +139,7 @@ impl Context<'_, '_> { let parent = def_map.get_module_path_with_separator(module_id.local_id.0, module.parent, "::"); - if parent.is_empty() { - name.into() - } else { - format!("{parent}::{name}") - } + if parent.is_empty() { name.into() } else { format!("{parent}::{name}") } } /// Returns a fully-qualified path to the given [StructId] from the given [CrateId]. This function also @@ -232,26 +228,25 @@ impl Context<'_, '_> { pub(crate) fn resolve_generics( interner: &NodeInterner, generics: &UnresolvedGenerics, - errors: &mut Vec<(CompilationError, FileId)>, - file_id: FileId, + errors: &mut Vec, ) -> Generics { vecmap(generics, |generic| { // Map the generic to a fresh type variable let id = interner.next_type_variable_id(); let type_var_kind = generic.kind().unwrap_or_else(|err| { - errors.push((err.into(), file_id)); + errors.push(err.into()); // When there's an error, unify with any other kinds Kind::Any }); let type_var = TypeVariable::unbound(id, type_var_kind); let ident = generic.ident(); - let span = ident.0.span(); + let location = ident.0.location(); // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); - ResolvedGeneric { name, type_var, span } + ResolvedGeneric { name, type_var, location } }) } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs index 5af0cf41a627..bc1c519ed5d8 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -1,9 +1,10 @@ use acvm::FieldElement; pub use noirc_errors::Span; -use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic, Location}; +use noirc_errors::{CustomDiagnostic as Diagnostic, Location}; use thiserror::Error; use crate::{ + Kind, Type, ast::{Ident, UnsupportedNumericGenericType}, hir::{ comptime::{InterpreterError, Value}, @@ -11,7 +12,6 @@ use crate::{ }, parser::ParserError, usage_tracker::UnusedItem, - Kind, Type, }; use super::import::PathResolutionError; @@ -27,166 +27,257 @@ pub enum PubPosition { #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum ResolverError { #[error("Duplicate definition")] - DuplicateDefinition { name: String, first_span: Span, second_span: Span }, + DuplicateDefinition { name: String, first_location: Location, second_location: Location }, #[error("Unused variable")] UnusedVariable { ident: Ident }, #[error("Unused {}", item.item_type())] UnusedItem { ident: Ident, item: UnusedItem }, #[error("Unconditional recursion")] - UnconditionalRecursion { name: String, span: Span }, + UnconditionalRecursion { name: String, location: Location }, #[error("Could not find variable in this scope")] - VariableNotDeclared { name: String, span: Span }, + VariableNotDeclared { name: String, location: Location }, #[error("path is not an identifier")] - PathIsNotIdent { span: Span }, + PathIsNotIdent { location: Location }, #[error("could not resolve path")] PathResolutionError(#[from] PathResolutionError), #[error("Expected")] - Expected { span: Span, expected: &'static str, got: &'static str }, + Expected { location: Location, expected: &'static str, got: &'static str }, #[error("Duplicate field in constructor")] DuplicateField { field: Ident }, #[error("No such field in struct")] NoSuchField { field: Ident, struct_definition: Ident }, #[error("Missing fields from struct")] - MissingFields { span: Span, missing_fields: Vec, struct_definition: Ident }, + MissingFields { location: Location, missing_fields: Vec, struct_definition: Ident }, #[error("Unneeded 'mut', pattern is already marked as mutable")] - UnnecessaryMut { first_mut: Span, second_mut: Span }, + UnnecessaryMut { first_mut: Location, second_mut: Location }, #[error("Unneeded 'pub', function is not the main method")] UnnecessaryPub { ident: Ident, position: PubPosition }, #[error("Required 'pub', main function must return public value")] NecessaryPub { ident: Ident }, #[error("Missing expression for declared constant")] - MissingRhsExpr { name: String, span: Span }, + MissingRhsExpr { name: String, location: Location }, #[error("Expression invalid in an array length context")] - InvalidArrayLengthExpr { span: Span }, + InvalidArrayLengthExpr { location: Location }, #[error("Integer too large to be evaluated in an array length context")] - IntegerTooLarge { span: Span }, + IntegerTooLarge { location: Location }, #[error("No global or generic type parameter found with the given name")] NoSuchNumericTypeVariable { path: crate::ast::Path }, #[error("Closures cannot capture mutable variables")] - CapturedMutableVariable { span: Span }, + CapturedMutableVariable { location: Location }, #[error("Test functions are not allowed to have any parameters")] - TestFunctionHasParameters { span: Span }, + TestFunctionHasParameters { location: Location }, #[error("Only struct types can be used in constructor expressions")] - NonStructUsedInConstructor { typ: String, span: Span }, + NonStructUsedInConstructor { typ: String, location: Location }, #[error("Only struct types can have generics")] - NonStructWithGenerics { span: Span }, + NonStructWithGenerics { location: Location }, #[error("Cannot apply generics on Self type")] - GenericsOnSelfType { span: Span }, + GenericsOnSelfType { location: Location }, #[error("Cannot apply generics on an associated type")] - GenericsOnAssociatedType { span: Span }, + GenericsOnAssociatedType { location: Location }, #[error("{0}")] ParserError(Box), - #[error("Cannot create a mutable reference to {variable}, it was declared to be immutable")] - MutableReferenceToImmutableVariable { variable: String, span: Span }, - #[error("Mutable references to array indices are unsupported")] - MutableReferenceToArrayElement { span: Span }, #[error("Closure environment must be a tuple or unit type")] - InvalidClosureEnvironment { typ: Type, span: Span }, + InvalidClosureEnvironment { typ: Type, location: Location }, #[error("Nested slices, i.e. slices within an array or slice, are not supported")] - NestedSlices { span: Span }, + NestedSlices { location: Location }, #[error("#[abi(tag)] attribute is only allowed in contracts")] - AbiAttributeOutsideContract { span: Span }, - #[error("Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library")] - LowLevelFunctionOutsideOfStdlib { ident: Ident }, + AbiAttributeOutsideContract { location: Location }, #[error( - "Usage of the `#[oracle]` function attribute is only valid on unconstrained functions" + "Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library" )] + LowLevelFunctionOutsideOfStdlib { ident: Ident }, + #[error("Usage of the `#[oracle]` function attribute is only valid on unconstrained functions")] OracleMarkedAsConstrained { ident: Ident }, #[error("Oracle functions cannot be called directly from constrained functions")] - UnconstrainedOracleReturnToConstrained { span: Span }, + UnconstrainedOracleReturnToConstrained { location: Location }, #[error("Dependency cycle found, '{item}' recursively depends on itself: {cycle} ")] - DependencyCycle { span: Span, item: String, cycle: String }, + DependencyCycle { location: Location, item: String, cycle: String }, #[error("break/continue are only allowed in unconstrained functions")] - JumpInConstrainedFn { is_break: bool, span: Span }, + JumpInConstrainedFn { is_break: bool, location: Location }, #[error("`loop` is only allowed in unconstrained functions")] - LoopInConstrainedFn { span: Span }, + LoopInConstrainedFn { location: Location }, #[error("`loop` must have at least one `break` in it")] - LoopWithoutBreak { span: Span }, + LoopWithoutBreak { location: Location }, #[error("`while` is only allowed in unconstrained functions")] - WhileInConstrainedFn { span: Span }, + WhileInConstrainedFn { location: Location }, #[error("break/continue are only allowed within loops")] - JumpOutsideLoop { is_break: bool, span: Span }, + JumpOutsideLoop { is_break: bool, location: Location }, #[error("Only `comptime` globals can be mutable")] - MutableGlobal { span: Span }, + MutableGlobal { location: Location }, #[error("Globals must have a specified type")] - UnspecifiedGlobalType { span: Span, expected_type: Type }, + UnspecifiedGlobalType { + pattern_location: Location, + expr_location: Location, + expected_type: Type, + }, #[error("Global failed to evaluate")] - UnevaluatedGlobalType { span: Span }, + UnevaluatedGlobalType { location: Location }, #[error("Globals used in a type position must be non-negative")] - NegativeGlobalType { span: Span, global_value: Value }, + NegativeGlobalType { location: Location, global_value: Value }, #[error("Globals used in a type position must be integers")] - NonIntegralGlobalType { span: Span, global_value: Value }, + NonIntegralGlobalType { location: Location, global_value: Value }, #[error("Global value `{global_value}` is larger than its kind's maximum value")] - GlobalLargerThanKind { span: Span, global_value: FieldElement, kind: Kind }, + GlobalLargerThanKind { location: Location, global_value: FieldElement, kind: Kind }, #[error("Self-referential types are not supported")] - SelfReferentialType { span: Span }, + SelfReferentialType { location: Location }, #[error("#[no_predicates] attribute is only allowed on constrained functions")] NoPredicatesAttributeOnUnconstrained { ident: Ident }, #[error("#[fold] attribute is only allowed on constrained functions")] FoldAttributeOnUnconstrained { ident: Ident }, #[error("expected type, found numeric generic parameter")] - NumericGenericUsedForType { name: String, span: Span }, + NumericGenericUsedForType { name: String, location: Location }, #[error("Invalid array length construction")] ArrayLengthInterpreter { error: InterpreterError }, #[error("The unquote operator '$' can only be used within a quote expression")] - UnquoteUsedOutsideQuote { span: Span }, - #[error("\"as trait path\" not yet implemented")] - AsTraitPathNotYetImplemented { span: Span }, + UnquoteUsedOutsideQuote { location: Location }, #[error("Invalid syntax in macro call")] - InvalidSyntaxInMacroCall { span: Span }, + InvalidSyntaxInMacroCall { location: Location }, #[error("Macros must be comptime functions")] - MacroIsNotComptime { span: Span }, + MacroIsNotComptime { location: Location }, #[error("Annotation name must refer to a comptime function")] - NonFunctionInAnnotation { span: Span }, + NonFunctionInAnnotation { location: Location }, #[error("Type `{typ}` was inserted into the generics list from a macro, but is not a generic")] - MacroResultInGenericsListNotAGeneric { span: Span, typ: Type }, + MacroResultInGenericsListNotAGeneric { location: Location, typ: Type }, #[error("Named type arguments aren't allowed in a {item_kind}")] - NamedTypeArgs { span: Span, item_kind: &'static str }, + NamedTypeArgs { location: Location, item_kind: &'static str }, #[error("Associated constants may only be a field or integer type")] - AssociatedConstantsMustBeNumeric { span: Span }, + AssociatedConstantsMustBeNumeric { location: Location }, #[error("Computing `{lhs} {op} {rhs}` failed with error {err}")] BinaryOpError { lhs: FieldElement, op: crate::BinaryTypeOperator, rhs: FieldElement, err: Box, - span: Span, + location: Location, }, #[error("`quote` cannot be used in runtime code")] - QuoteInRuntimeCode { span: Span }, + QuoteInRuntimeCode { location: Location }, #[error("Comptime-only type `{typ}` cannot be used in runtime code")] - ComptimeTypeInRuntimeCode { typ: String, span: Span }, + ComptimeTypeInRuntimeCode { typ: String, location: Location }, #[error("Comptime variable `{name}` cannot be mutated in a non-comptime context")] - MutatingComptimeInNonComptimeContext { name: String, span: Span }, + MutatingComptimeInNonComptimeContext { name: String, location: Location }, #[error("Failed to parse `{statement}` as an expression")] - InvalidInternedStatementInExpr { statement: String, span: Span }, + InvalidInternedStatementInExpr { statement: String, location: Location }, #[error("{0}")] UnsupportedNumericGenericType(#[from] UnsupportedNumericGenericType), #[error("Type `{typ}` is more private than item `{item}`")] - TypeIsMorePrivateThenItem { typ: String, item: String, span: Span }, + TypeIsMorePrivateThenItem { typ: String, item: String, location: Location }, #[error("Unable to parse attribute `{attribute}`")] - UnableToParseAttribute { attribute: String, span: Span }, + UnableToParseAttribute { attribute: String, location: Location }, #[error("Attribute function `{function}` is not a path")] - AttributeFunctionIsNotAPath { function: String, span: Span }, + AttributeFunctionIsNotAPath { function: String, location: Location }, #[error("Attribute function `{name}` is not in scope")] - AttributeFunctionNotInScope { name: String, span: Span }, - #[error("The trait `{missing_trait}` is not implemented for `{type_missing_trait}")] + AttributeFunctionNotInScope { name: String, location: Location }, + #[error("The trait `{missing_trait}` is not implemented for `{type_missing_trait}`")] TraitNotImplemented { impl_trait: String, missing_trait: String, type_missing_trait: String, - span: Span, + location: Location, missing_trait_location: Location, }, #[error("`loop` statements are not yet implemented")] - LoopNotYetSupported { span: Span }, + LoopNotYetSupported { location: Location }, #[error("Expected a trait but found {found}")] - ExpectedTrait { found: String, span: Span }, + ExpectedTrait { found: String, location: Location }, + #[error("Invalid syntax in match pattern")] + InvalidSyntaxInPattern { location: Location }, + #[error("Variable '{existing}' was already defined in the same match pattern")] + VariableAlreadyDefinedInPattern { existing: Ident, new_location: Location }, + #[error("Only integer globals can be used in match patterns")] + NonIntegerGlobalUsedInPattern { location: Location }, + #[error("Cannot match on values of type `{typ}`")] + TypeUnsupportedInMatch { typ: Type, location: Location }, + #[error("Expected a struct, enum, or literal value in pattern, but found a {item}")] + UnexpectedItemInPattern { location: Location, item: &'static str }, + #[error("Trait `{trait_name}` doesn't have a method named `{method_name}`")] + NoSuchMethodInTrait { trait_name: String, method_name: String, location: Location }, } impl ResolverError { - pub fn into_file_diagnostic(&self, file: fm::FileId) -> FileDiagnostic { - Diagnostic::from(self).in_file(file) + pub fn location(&self) -> Location { + match self { + ResolverError::DuplicateDefinition { second_location: location, .. } + | ResolverError::UnconditionalRecursion { location, .. } + | ResolverError::PathIsNotIdent { location } + | ResolverError::Expected { location, .. } + | ResolverError::VariableNotDeclared { location, .. } + | ResolverError::MissingFields { location, .. } + | ResolverError::UnnecessaryMut { second_mut: location, .. } + | ResolverError::TypeIsMorePrivateThenItem { location, .. } + | ResolverError::UnableToParseAttribute { location, .. } + | ResolverError::AttributeFunctionIsNotAPath { location, .. } + | ResolverError::AttributeFunctionNotInScope { location, .. } + | ResolverError::TraitNotImplemented { location, .. } + | ResolverError::LoopNotYetSupported { location } + | ResolverError::ExpectedTrait { location, .. } + | ResolverError::MissingRhsExpr { location, .. } + | ResolverError::InvalidArrayLengthExpr { location } + | ResolverError::IntegerTooLarge { location } + | ResolverError::CapturedMutableVariable { location } + | ResolverError::TestFunctionHasParameters { location } + | ResolverError::NonStructUsedInConstructor { location, .. } + | ResolverError::NonStructWithGenerics { location } + | ResolverError::GenericsOnSelfType { location } + | ResolverError::GenericsOnAssociatedType { location } + | ResolverError::InvalidClosureEnvironment { location, .. } + | ResolverError::NestedSlices { location } + | ResolverError::AbiAttributeOutsideContract { location } + | ResolverError::UnconstrainedOracleReturnToConstrained { location } + | ResolverError::DependencyCycle { location, .. } + | ResolverError::JumpInConstrainedFn { location, .. } + | ResolverError::LoopInConstrainedFn { location } + | ResolverError::LoopWithoutBreak { location } + | ResolverError::WhileInConstrainedFn { location } + | ResolverError::JumpOutsideLoop { location, .. } + | ResolverError::MutableGlobal { location } + | ResolverError::UnspecifiedGlobalType { pattern_location: location, .. } + | ResolverError::UnevaluatedGlobalType { location } + | ResolverError::NegativeGlobalType { location, .. } + | ResolverError::NonIntegralGlobalType { location, .. } + | ResolverError::GlobalLargerThanKind { location, .. } + | ResolverError::SelfReferentialType { location } + | ResolverError::NumericGenericUsedForType { location, .. } + | ResolverError::UnquoteUsedOutsideQuote { location } + | ResolverError::InvalidSyntaxInMacroCall { location } + | ResolverError::MacroIsNotComptime { location } + | ResolverError::NonFunctionInAnnotation { location } + | ResolverError::MacroResultInGenericsListNotAGeneric { location, .. } + | ResolverError::NamedTypeArgs { location, .. } + | ResolverError::AssociatedConstantsMustBeNumeric { location } + | ResolverError::BinaryOpError { location, .. } + | ResolverError::QuoteInRuntimeCode { location } + | ResolverError::ComptimeTypeInRuntimeCode { location, .. } + | ResolverError::MutatingComptimeInNonComptimeContext { location, .. } + | ResolverError::InvalidInternedStatementInExpr { location, .. } + | ResolverError::InvalidSyntaxInPattern { location } + | ResolverError::NonIntegerGlobalUsedInPattern { location, .. } + | ResolverError::TypeUnsupportedInMatch { location, .. } + | ResolverError::UnexpectedItemInPattern { location, .. } + | ResolverError::NoSuchMethodInTrait { location, .. } + | ResolverError::VariableAlreadyDefinedInPattern { new_location: location, .. } => { + *location + } + ResolverError::UnusedVariable { ident } + | ResolverError::UnusedItem { ident, .. } + | ResolverError::DuplicateField { field: ident } + | ResolverError::NoSuchField { field: ident, .. } + | ResolverError::UnnecessaryPub { ident, .. } + | ResolverError::NecessaryPub { ident } + | ResolverError::LowLevelFunctionOutsideOfStdlib { ident } + | ResolverError::OracleMarkedAsConstrained { ident } + | ResolverError::NoPredicatesAttributeOnUnconstrained { ident } + | ResolverError::FoldAttributeOnUnconstrained { ident } => ident.location(), + ResolverError::ArrayLengthInterpreter { error } => error.location(), + ResolverError::PathResolutionError(path_resolution_error) => { + path_resolution_error.location() + } + ResolverError::NoSuchNumericTypeVariable { path } => path.location, + ResolverError::ParserError(parser_error) => parser_error.location(), + ResolverError::UnsupportedNumericGenericType(unsupported_numeric_generic_type) => { + unsupported_numeric_generic_type.ident.location() + } + } } } @@ -196,13 +287,13 @@ impl<'a> From<&'a ResolverError> for Diagnostic { /// soundness of the generated program fn from(error: &'a ResolverError) -> Diagnostic { match error { - ResolverError::DuplicateDefinition { name, first_span, second_span } => { + ResolverError::DuplicateDefinition { name, first_location, second_location} => { let mut diag = Diagnostic::simple_error( format!("duplicate definitions of {name} found"), - "first definition found here".to_string(), - *first_span, + "second definition found here".to_string(), + *second_location, ); - diag.add_secondary("second definition found here".to_string(), *second_span); + diag.add_secondary("first definition found here".to_string(), *first_location); diag } ResolverError::UnusedVariable { ident } => { @@ -210,8 +301,8 @@ impl<'a> From<&'a ResolverError> for Diagnostic { let mut diagnostic = Diagnostic::simple_warning( format!("unused variable {name}"), - "unused variable ".to_string(), - ident.span(), + "unused variable".to_string(), + ident.location(), ); diagnostic.unnecessary = true; diagnostic @@ -225,64 +316,64 @@ impl<'a> From<&'a ResolverError> for Diagnostic { Diagnostic::simple_warning( format!("{item_type} `{name}` is never constructed"), format!("{item_type} is never constructed"), - ident.span(), + ident.location(), ) } else { Diagnostic::simple_warning( format!("unused {item_type} {name}"), format!("unused {item_type}"), - ident.span(), + ident.location(), ) }; diagnostic.unnecessary = true; diagnostic } - ResolverError::UnconditionalRecursion { name, span} => { + ResolverError::UnconditionalRecursion { name, location} => { Diagnostic::simple_warning( format!("function `{name}` cannot return without recursing"), "function cannot return without recursing".to_string(), - *span, + *location, ) } - ResolverError::VariableNotDeclared { name, span } => { + ResolverError::VariableNotDeclared { name, location } => { if name == "_" { Diagnostic::simple_error( "in expressions, `_` can only be used on the left-hand side of an assignment".to_string(), "`_` not allowed here".to_string(), - *span, + *location, ) } else { Diagnostic::simple_error( format!("cannot find `{name}` in this scope"), "not found in this scope".to_string(), - *span, + *location, ) } }, - ResolverError::PathIsNotIdent { span } => Diagnostic::simple_error( + ResolverError::PathIsNotIdent { location } => Diagnostic::simple_error( "cannot use path as an identifier".to_string(), String::new(), - *span, + *location, ), ResolverError::PathResolutionError(error) => error.into(), - ResolverError::Expected { span, expected, got } => Diagnostic::simple_error( + ResolverError::Expected { location, expected, got } => Diagnostic::simple_error( format!("expected {expected} got {got}"), String::new(), - *span, + *location, ), ResolverError::DuplicateField { field } => Diagnostic::simple_error( format!("duplicate field {field}"), String::new(), - field.span(), + field.location(), ), ResolverError::NoSuchField { field, struct_definition } => { Diagnostic::simple_error( format!("no such field {field} defined in struct {struct_definition}"), String::new(), - field.span(), + field.location(), ) } - ResolverError::MissingFields { span, missing_fields, struct_definition } => { + ResolverError::MissingFields { location, missing_fields, struct_definition } => { let plural = if missing_fields.len() != 1 { "s" } else { "" }; let remaining_fields_names = match &missing_fields[..] { [field1] => field1.clone(), @@ -301,7 +392,7 @@ impl<'a> From<&'a ResolverError> for Diagnostic { Diagnostic::simple_error( format!("missing field{plural} {remaining_fields_names} in struct {struct_definition}"), String::new(), - *span, + *location, ) } ResolverError::UnnecessaryMut { first_mut, second_mut } => { @@ -322,7 +413,7 @@ impl<'a> From<&'a ResolverError> for Diagnostic { let mut diag = Diagnostic::simple_warning( format!("unnecessary pub keyword on {position} for function {name}"), format!("unnecessary pub {position}"), - ident.0.span(), + ident.0.location(), ); diag.add_note("The `pub` keyword only has effects on arguments to the entry-point function of a program. Thus, adding it to other function parameters can be deceiving and should be removed".to_owned()); @@ -334,192 +425,188 @@ impl<'a> From<&'a ResolverError> for Diagnostic { let mut diag = Diagnostic::simple_error( format!("missing pub keyword on return type of function {name}"), "missing pub on return type".to_string(), - ident.0.span(), + ident.0.location(), ); diag.add_note("The `pub` keyword is mandatory for the entry-point function return type because the verifier cannot retrieve private witness and thus the function will not be able to return a 'priv' value".to_owned()); diag } - ResolverError::MissingRhsExpr { name, span } => Diagnostic::simple_error( + ResolverError::MissingRhsExpr { name, location } => Diagnostic::simple_error( format!( "no expression specifying the value stored by the constant variable {name}" ), "expected expression to be stored for let statement".to_string(), - *span, + *location, ), - ResolverError::InvalidArrayLengthExpr { span } => Diagnostic::simple_error( + ResolverError::InvalidArrayLengthExpr { location } => Diagnostic::simple_error( "Expression invalid in an array-length context".into(), "Array-length expressions can only have simple integer operations and any variables used must be global constants".into(), - *span, + *location, ), - ResolverError::IntegerTooLarge { span } => Diagnostic::simple_error( + ResolverError::IntegerTooLarge { location } => Diagnostic::simple_error( "Integer too large to be evaluated to an array-length".into(), "Array-lengths may be a maximum size of usize::MAX, including intermediate calculations".into(), - *span, + *location, ), ResolverError::NoSuchNumericTypeVariable { path } => Diagnostic::simple_error( format!("Cannot find a global or generic type parameter named `{path}`"), "Only globals or generic type parameters are allowed to be used as an array type's length".to_string(), - path.span(), + path.location, ), - ResolverError::CapturedMutableVariable { span } => Diagnostic::simple_error( + ResolverError::CapturedMutableVariable { location } => Diagnostic::simple_error( "Closures cannot capture mutable variables".into(), "Mutable variable".into(), - *span, + *location, ), - ResolverError::TestFunctionHasParameters { span } => Diagnostic::simple_error( + ResolverError::TestFunctionHasParameters { location } => Diagnostic::simple_error( "Test functions cannot have any parameters".into(), "Try removing the parameters or moving the test into a wrapper function".into(), - *span, + *location, ), - ResolverError::NonStructUsedInConstructor { typ, span } => Diagnostic::simple_error( + ResolverError::NonStructUsedInConstructor { typ, location } => Diagnostic::simple_error( "Only struct types can be used in constructor expressions".into(), format!("{typ} has no fields to construct it with"), - *span, + *location, ), - ResolverError::NonStructWithGenerics { span } => Diagnostic::simple_error( + ResolverError::NonStructWithGenerics { location } => Diagnostic::simple_error( "Only struct types can have generic arguments".into(), "Try removing the generic arguments".into(), - *span, + *location, ), - ResolverError::GenericsOnSelfType { span } => Diagnostic::simple_error( + ResolverError::GenericsOnSelfType { location } => Diagnostic::simple_error( "Cannot apply generics to Self type".into(), "Use an explicit type name or apply the generics at the start of the impl instead".into(), - *span, + *location, ), - ResolverError::GenericsOnAssociatedType { span } => Diagnostic::simple_error( + ResolverError::GenericsOnAssociatedType { location } => Diagnostic::simple_error( "Generic Associated Types (GATs) are currently unsupported in Noir".into(), "Cannot apply generics to an associated type".into(), - *span, + *location, ), ResolverError::ParserError(error) => error.as_ref().into(), - ResolverError::MutableReferenceToImmutableVariable { variable, span } => { - Diagnostic::simple_error(format!("Cannot mutably reference the immutable variable {variable}"), format!("{variable} is immutable"), *span) - }, - ResolverError::MutableReferenceToArrayElement { span } => { - Diagnostic::simple_error("Mutable references to array elements are currently unsupported".into(), "Try storing the element in a fresh variable first".into(), *span) - }, - ResolverError::InvalidClosureEnvironment { span, typ } => Diagnostic::simple_error( + ResolverError::InvalidClosureEnvironment { location, typ } => Diagnostic::simple_error( format!("{typ} is not a valid closure environment type"), - "Closure environment must be a tuple or unit type".to_string(), *span), - ResolverError::NestedSlices { span } => Diagnostic::simple_error( + "Closure environment must be a tuple or unit type".to_string(), *location), + ResolverError::NestedSlices { location } => Diagnostic::simple_error( "Nested slices, i.e. slices within an array or slice, are not supported".into(), "Try to use a constant sized array or BoundedVec instead".into(), - *span, + *location, ), - ResolverError::AbiAttributeOutsideContract { span } => { + ResolverError::AbiAttributeOutsideContract { location } => { Diagnostic::simple_error( "#[abi(tag)] attributes can only be used in contracts".to_string(), "misplaced #[abi(tag)] attribute".to_string(), - *span, + *location, ) }, ResolverError::LowLevelFunctionOutsideOfStdlib { ident } => Diagnostic::simple_error( "Definition of low-level function outside of standard library".into(), "Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library".into(), - ident.span(), + ident.location(), ), ResolverError::OracleMarkedAsConstrained { ident } => Diagnostic::simple_error( error.to_string(), "Oracle functions must have the `unconstrained` keyword applied".into(), - ident.span(), + ident.location(), ), - ResolverError::UnconstrainedOracleReturnToConstrained { span } => Diagnostic::simple_error( + ResolverError::UnconstrainedOracleReturnToConstrained { location } => Diagnostic::simple_error( error.to_string(), "This oracle call must be wrapped in a call to another unconstrained function before being returned to a constrained runtime".into(), - *span, + *location, ), - ResolverError::DependencyCycle { span, item, cycle } => { + ResolverError::DependencyCycle { location, item, cycle } => { Diagnostic::simple_error( "Dependency cycle found".into(), format!("'{item}' recursively depends on itself: {cycle}"), - *span, + *location, ) }, - ResolverError::JumpInConstrainedFn { is_break, span } => { + ResolverError::JumpInConstrainedFn { is_break, location } => { let item = if *is_break { "break" } else { "continue" }; Diagnostic::simple_error( format!("{item} is only allowed in unconstrained functions"), "Constrained code must always have a known number of loop iterations".into(), - *span, + *location, ) }, - ResolverError::LoopInConstrainedFn { span } => { + ResolverError::LoopInConstrainedFn { location } => { Diagnostic::simple_error( "`loop` is only allowed in unconstrained functions".into(), "Constrained code must always have a known number of loop iterations".into(), - *span, + *location, ) }, - ResolverError::LoopWithoutBreak { span } => { + ResolverError::LoopWithoutBreak { location } => { Diagnostic::simple_error( "`loop` must have at least one `break` in it".into(), "Infinite loops are disallowed".into(), - *span, + *location, ) }, - ResolverError::WhileInConstrainedFn { span } => { + ResolverError::WhileInConstrainedFn { location } => { Diagnostic::simple_error( "`while` is only allowed in unconstrained functions".into(), "Constrained code must always have a known number of loop iterations".into(), - *span, + *location, ) }, - ResolverError::JumpOutsideLoop { is_break, span } => { + ResolverError::JumpOutsideLoop { is_break, location } => { let item = if *is_break { "break" } else { "continue" }; Diagnostic::simple_error( format!("{item} is only allowed within loops"), "".into(), - *span, + *location, ) }, - ResolverError::MutableGlobal { span } => { + ResolverError::MutableGlobal { location } => { Diagnostic::simple_error( "Only `comptime` globals may be mutable".into(), String::new(), - *span, + *location, ) }, - ResolverError::UnspecifiedGlobalType { span, expected_type } => { - Diagnostic::simple_error( + ResolverError::UnspecifiedGlobalType { pattern_location, expr_location, expected_type } => { + let mut diagnostic = Diagnostic::simple_error( "Globals must have a specified type".to_string(), - format!("Inferred type is `{expected_type}`"), - *span, - ) + String::new(), + *pattern_location, + ); + diagnostic.add_secondary(format!("Inferred type is `{expected_type}`"), *expr_location); + diagnostic }, - ResolverError::UnevaluatedGlobalType { span } => { + ResolverError::UnevaluatedGlobalType { location } => { Diagnostic::simple_error( "Global failed to evaluate".to_string(), String::new(), - *span, + *location, ) } - ResolverError::NegativeGlobalType { span, global_value } => { + ResolverError::NegativeGlobalType { location, global_value } => { Diagnostic::simple_error( "Globals used in a type position must be non-negative".to_string(), format!("But found value `{global_value:?}`"), - *span, + *location, ) } - ResolverError::NonIntegralGlobalType { span, global_value } => { + ResolverError::NonIntegralGlobalType { location, global_value } => { Diagnostic::simple_error( "Globals used in a type position must be integers".to_string(), format!("But found value `{global_value:?}`"), - *span, + *location, ) } - ResolverError::GlobalLargerThanKind { span, global_value, kind } => { + ResolverError::GlobalLargerThanKind { location, global_value, kind } => { Diagnostic::simple_error( format!("Global value `{global_value}` is larger than its kind's maximum value"), format!("Global's kind inferred to be `{kind}`"), - *span, + *location, ) } - ResolverError::SelfReferentialType { span } => { + ResolverError::SelfReferentialType { location } => { Diagnostic::simple_error( "Self-referential types are not supported".into(), "".into(), - *span, + *location, ) }, ResolverError::NoPredicatesAttributeOnUnconstrained { ident } => { @@ -528,7 +615,7 @@ impl<'a> From<&'a ResolverError> for Diagnostic { let mut diag = Diagnostic::simple_error( format!("misplaced #[no_predicates] attribute on unconstrained function {name}. Only allowed on constrained functions"), "misplaced #[no_predicates] attribute".to_string(), - ident.0.span(), + ident.0.location(), ); diag.add_note("The `#[no_predicates]` attribute specifies to the compiler whether it should diverge from auto-inlining constrained functions".to_owned()); @@ -540,159 +627,191 @@ impl<'a> From<&'a ResolverError> for Diagnostic { let mut diag = Diagnostic::simple_error( format!("misplaced #[fold] attribute on unconstrained function {name}. Only allowed on constrained functions"), "misplaced #[fold] attribute".to_string(), - ident.0.span(), + ident.0.location(), ); diag.add_note("The `#[fold]` attribute specifies whether a constrained function should be treated as a separate circuit rather than inlined into the program entry point".to_owned()); diag } - ResolverError::NumericGenericUsedForType { name, span } => { + ResolverError::NumericGenericUsedForType { name, location } => { Diagnostic::simple_error( format!("expected type, found numeric generic parameter {name}"), String::from("not a type"), - *span, + *location, ) } ResolverError::ArrayLengthInterpreter { error } => Diagnostic::from(error), - ResolverError::UnquoteUsedOutsideQuote { span } => { + ResolverError::UnquoteUsedOutsideQuote { location } => { Diagnostic::simple_error( "The unquote operator '$' can only be used within a quote expression".into(), "".into(), - *span, - ) - }, - ResolverError::AsTraitPathNotYetImplemented { span } => { - Diagnostic::simple_error( - "\"as trait path\" not yet implemented".into(), - "".into(), - *span, + *location, ) }, - ResolverError::InvalidSyntaxInMacroCall { span } => { + ResolverError::InvalidSyntaxInMacroCall { location } => { Diagnostic::simple_error( "Invalid syntax in macro call".into(), "Macro calls must call a comptime function directly, they cannot use higher-order functions".into(), - *span, + *location, ) }, - ResolverError::MacroIsNotComptime { span } => { + ResolverError::MacroIsNotComptime { location } => { Diagnostic::simple_error( "This macro call is to a non-comptime function".into(), "Macro calls must be to comptime functions".into(), - *span, + *location, ) }, - ResolverError::NonFunctionInAnnotation { span } => { + ResolverError::NonFunctionInAnnotation { location } => { Diagnostic::simple_error( "Unknown annotation".into(), "The name of an annotation must refer to a comptime function".into(), - *span, + *location, ) }, - ResolverError::MacroResultInGenericsListNotAGeneric { span, typ } => { + ResolverError::MacroResultInGenericsListNotAGeneric { location, typ } => { Diagnostic::simple_error( format!("Type `{typ}` was inserted into a generics list from a macro, but it is not a generic"), format!("Type `{typ}` is not a generic"), - *span, + *location, ) } - ResolverError::NamedTypeArgs { span, item_kind } => { + ResolverError::NamedTypeArgs { location, item_kind } => { Diagnostic::simple_error( format!("Named type arguments aren't allowed on a {item_kind}"), "Named type arguments are only allowed for associated types on traits".to_string(), - *span, + *location, ) } - ResolverError::AssociatedConstantsMustBeNumeric { span } => { + ResolverError::AssociatedConstantsMustBeNumeric { location } => { Diagnostic::simple_error( "Associated constants may only be a field or integer type".to_string(), "Only numeric constants are allowed".to_string(), - *span, + *location, ) } - ResolverError::BinaryOpError { lhs, op, rhs, err, span } => { + ResolverError::BinaryOpError { lhs, op, rhs, err, location } => { Diagnostic::simple_error( format!("Computing `{lhs} {op} {rhs}` failed with error {err}"), String::new(), - *span, + *location, ) } - ResolverError::QuoteInRuntimeCode { span } => { + ResolverError::QuoteInRuntimeCode { location } => { Diagnostic::simple_error( "`quote` cannot be used in runtime code".to_string(), "Wrap this in a `comptime` block or function to use it".to_string(), - *span, + *location, ) }, - ResolverError::ComptimeTypeInRuntimeCode { typ, span } => { + ResolverError::ComptimeTypeInRuntimeCode { typ, location } => { Diagnostic::simple_error( format!("Comptime-only type `{typ}` cannot be used in runtime code"), "Comptime-only type used here".to_string(), - *span, + *location, ) }, - ResolverError::MutatingComptimeInNonComptimeContext { name, span } => { + ResolverError::MutatingComptimeInNonComptimeContext { name, location } => { Diagnostic::simple_error( format!("Comptime variable `{name}` cannot be mutated in a non-comptime context"), format!("`{name}` mutated here"), - *span, + *location, ) }, - ResolverError::InvalidInternedStatementInExpr { statement, span } => { + ResolverError::InvalidInternedStatementInExpr { statement, location } => { Diagnostic::simple_error( format!("Failed to parse `{statement}` as an expression"), "The statement was used from a macro here".to_string(), - *span, + *location, ) }, ResolverError::UnsupportedNumericGenericType(err) => err.into(), - ResolverError::TypeIsMorePrivateThenItem { typ, item, span } => { + ResolverError::TypeIsMorePrivateThenItem { typ, item, location } => { Diagnostic::simple_error( format!("Type `{typ}` is more private than item `{item}`"), String::new(), - *span, + *location, ) }, - ResolverError::UnableToParseAttribute { attribute, span } => { + ResolverError::UnableToParseAttribute { attribute, location } => { Diagnostic::simple_error( format!("Unable to parse attribute `{attribute}`"), "Attribute should be a function or function call".into(), - *span, + *location, ) }, - ResolverError::AttributeFunctionIsNotAPath { function, span } => { + ResolverError::AttributeFunctionIsNotAPath { function, location } => { Diagnostic::simple_error( format!("Attribute function `{function}` is not a path"), "An attribute's function should be a single identifier or a path".into(), - *span, + *location, ) }, - ResolverError::AttributeFunctionNotInScope { name, span } => { + ResolverError::AttributeFunctionNotInScope { name, location } => { Diagnostic::simple_error( format!("Attribute function `{name}` is not in scope"), String::new(), - *span, + *location, ) }, - ResolverError::TraitNotImplemented { impl_trait, missing_trait: the_trait, type_missing_trait: typ, span, missing_trait_location} => { + ResolverError::TraitNotImplemented { impl_trait, missing_trait: the_trait, type_missing_trait: typ, location, missing_trait_location} => { let mut diagnostic = Diagnostic::simple_error( format!("The trait bound `{typ}: {the_trait}` is not satisfied"), - format!("The trait `{the_trait}` is not implemented for `{typ}") - , *span); - diagnostic.add_secondary_with_file(format!("required by this bound in `{impl_trait}"), missing_trait_location.span, missing_trait_location.file); + format!("The trait `{the_trait}` is not implemented for `{typ}`") + , *location); + diagnostic.add_secondary(format!("required by this bound in `{impl_trait}`"), *missing_trait_location); diagnostic }, - ResolverError::LoopNotYetSupported { span } => { + ResolverError::LoopNotYetSupported { location } => { let msg = "`loop` statements are not yet implemented".to_string(); - Diagnostic::simple_error(msg, String::new(), *span) + Diagnostic::simple_error(msg, String::new(), *location) } - ResolverError::ExpectedTrait { found, span } => { + ResolverError::ExpectedTrait { found, location } => { Diagnostic::simple_error( format!("Expected a trait, found {found}"), String::new(), - *span) + *location) } + ResolverError::InvalidSyntaxInPattern { location } => { + Diagnostic::simple_error( + "Invalid syntax in match pattern".into(), + "Only literal, constructor, and variable patterns are allowed".into(), + *location) + }, + ResolverError::VariableAlreadyDefinedInPattern { existing, new_location } => { + let message = format!("Variable `{existing}` was already defined in the same match pattern"); + let secondary = format!("`{existing}` redefined here"); + let mut error = Diagnostic::simple_error(message, secondary, *new_location); + error.add_secondary(format!("`{existing}` was previously defined here"), existing.location()); + error + }, + ResolverError::NonIntegerGlobalUsedInPattern { location } => { + let message = "Only integer or boolean globals can be used in match patterns".to_string(); + let secondary = "This global is not an integer or boolean".to_string(); + Diagnostic::simple_error(message, secondary, *location) + }, + ResolverError::TypeUnsupportedInMatch { typ, location } => { + Diagnostic::simple_error( + format!("Cannot match on values of type `{typ}`"), + String::new(), + *location, + ) + }, + ResolverError::UnexpectedItemInPattern { item, location } => { + Diagnostic::simple_error( + format!("Expected a struct, enum, or literal pattern, but found a {item}"), + String::new(), + *location, + ) + }, + ResolverError::NoSuchMethodInTrait { trait_name, method_name, location } => { + Diagnostic::simple_error( + format!("Trait `{trait_name}` has no method named `{method_name}`"), + String::new(), + *location, + ) + }, } } } 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 11b694aa61be..16e25b804651 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 @@ -1,5 +1,5 @@ use iter_extended::vecmap; -use noirc_errors::{CustomDiagnostic, Span}; +use noirc_errors::{CustomDiagnostic, Location}; use thiserror::Error; use crate::graph::CrateId; @@ -44,12 +44,14 @@ pub enum PathResolutionError { #[error("{0} is private and not visible from the current module")] Private(Ident), #[error("There is no super module")] - NoSuper(Span), + NoSuper(Location), #[error("turbofish (`::<_>`) not allowed on {item}")] - TurbofishNotAllowedOnItem { item: String, span: Span }, + TurbofishNotAllowedOnItem { item: String, location: Location }, #[error("{ident} is a {kind}, not a module")] NotAModule { ident: Ident, kind: &'static str }, - #[error("trait `{trait_name}` which provides `{ident}` is implemented but not in scope, please import it")] + #[error( + "trait `{trait_name}` which provides `{ident}` is implemented but not in scope, please import it" + )] TraitMethodNotInScope { ident: Ident, trait_name: String }, #[error("Could not resolve '{ident}' in path")] UnresolvedWithPossibleTraitsToImport { ident: Ident, traits: Vec }, @@ -57,6 +59,23 @@ pub enum PathResolutionError { MultipleTraitsInScope { ident: Ident, traits: Vec }, } +impl PathResolutionError { + pub fn location(&self) -> Location { + match self { + PathResolutionError::NoSuper(location) + | PathResolutionError::TurbofishNotAllowedOnItem { location, .. } => *location, + PathResolutionError::Unresolved(ident) + | PathResolutionError::Private(ident) + | PathResolutionError::NotAModule { ident, .. } + | PathResolutionError::TraitMethodNotInScope { ident, .. } + | PathResolutionError::MultipleTraitsInScope { ident, .. } + | PathResolutionError::UnresolvedWithPossibleTraitsToImport { ident, .. } => { + ident.location() + } + } + } +} + #[derive(Debug)] pub struct ResolvedImport { // The symbol which we have resolved to @@ -75,43 +94,48 @@ impl<'a> From<&'a PathResolutionError> for CustomDiagnostic { fn from(error: &'a PathResolutionError) -> Self { match &error { PathResolutionError::Unresolved(ident) => { - CustomDiagnostic::simple_error(error.to_string(), String::new(), ident.span()) + CustomDiagnostic::simple_error(error.to_string(), String::new(), ident.location()) } // This will be upgraded to an error in future versions PathResolutionError::Private(ident) => CustomDiagnostic::simple_warning( error.to_string(), format!("{ident} is private"), - ident.span(), + ident.location(), ), - PathResolutionError::NoSuper(span) => { - CustomDiagnostic::simple_error(error.to_string(), String::new(), *span) + PathResolutionError::NoSuper(location) => { + CustomDiagnostic::simple_error(error.to_string(), String::new(), *location) } - PathResolutionError::TurbofishNotAllowedOnItem { item: _, span } => { - CustomDiagnostic::simple_error(error.to_string(), String::new(), *span) + PathResolutionError::TurbofishNotAllowedOnItem { item: _, location } => { + CustomDiagnostic::simple_error(error.to_string(), String::new(), *location) } PathResolutionError::NotAModule { ident, kind: _ } => { - CustomDiagnostic::simple_error(error.to_string(), String::new(), ident.span()) + CustomDiagnostic::simple_error(error.to_string(), String::new(), ident.location()) } PathResolutionError::TraitMethodNotInScope { ident, .. } => { - CustomDiagnostic::simple_warning(error.to_string(), String::new(), ident.span()) + CustomDiagnostic::simple_warning(error.to_string(), String::new(), ident.location()) } PathResolutionError::UnresolvedWithPossibleTraitsToImport { ident, traits } => { - let traits = vecmap(traits, |trait_name| format!("`{}`", trait_name)); + let mut traits = vecmap(traits, |trait_name| format!("`{}`", trait_name)); + traits.sort(); CustomDiagnostic::simple_error( error.to_string(), - format!("The following traits which provide `{ident}` are implemented but not in scope: {}", traits.join(", ")), - ident.span(), + format!( + "The following traits which provide `{ident}` are implemented but not in scope: {}", + traits.join(", ") + ), + ident.location(), ) } PathResolutionError::MultipleTraitsInScope { ident, traits } => { - let traits = vecmap(traits, |trait_name| format!("`{}`", trait_name)); + let mut traits = vecmap(traits, |trait_name| format!("`{}`", trait_name)); + traits.sort(); CustomDiagnostic::simple_error( error.to_string(), format!( "All these trait which provide `{ident}` are implemented and in scope: {}", traits.join(", ") ), - ident.span(), + ident.location(), ) } } @@ -162,7 +186,7 @@ struct PathResolutionTargetResolver<'def_maps, 'references_tracker> { references_tracker: Option>, } -impl<'def_maps, 'references_tracker> PathResolutionTargetResolver<'def_maps, 'references_tracker> { +impl PathResolutionTargetResolver<'_, '_> { fn resolve(&mut self, path: Path) -> Result<(Path, ModuleId), PathResolutionError> { match path.kind { PathKind::Crate => self.resolve_crate_path(path), @@ -214,8 +238,8 @@ impl<'def_maps, 'references_tracker> PathResolutionTargetResolver<'def_maps, 're .ok_or_else(|| PathResolutionError::Unresolved(crate_name.to_owned()))?; if let Some(references_tracker) = &mut self.references_tracker { - let span = crate_name.span(); - references_tracker.add_reference(ModuleDefId::ModuleId(*dep_module), span, false); + let location = crate_name.location(); + references_tracker.add_reference(ModuleDefId::ModuleId(*dep_module), location, false); } // Now the path can be solved starting from the second segment as a plain path @@ -227,9 +251,7 @@ impl<'def_maps, 'references_tracker> PathResolutionTargetResolver<'def_maps, 're fn resolve_super_path(&mut self, path: Path) -> Result<(Path, ModuleId), PathResolutionError> { let Some(parent_module_id) = get_module(self.def_maps, self.importing_module).parent else { - let span_start = path.span.start(); - let span = Span::from(span_start..span_start + 5); // 5 == "super".len() - return Err(PathResolutionError::NoSuper(span)); + return Err(PathResolutionError::NoSuper(path.kind_location)); }; let current_module = @@ -297,7 +319,7 @@ impl<'def_maps, 'usage_tracker, 'references_tracker> Some((typ, visibility, _)) => (typ, visibility), }; - self.add_reference(typ, last_segment.span, last_segment.ident.is_self_type_name()); + self.add_reference(typ, last_segment.location, last_segment.ident.is_self_type_name()); // In the type namespace, only Mod can be used in a path. current_module_id = match typ { @@ -339,7 +361,7 @@ impl<'def_maps, 'usage_tracker, 'references_tracker> let (module_def_id, visibility, _) = current_ns.values.or(current_ns.types).expect("Found empty namespace"); - self.add_reference(module_def_id, path.segments.last().unwrap().ident.span(), false); + self.add_reference(module_def_id, path.segments.last().unwrap().ident.location(), false); if !self.item_in_module_is_visible(current_module_id, visibility) { errors.push(PathResolutionError::Private(path.last_ident())); @@ -348,9 +370,14 @@ impl<'def_maps, 'usage_tracker, 'references_tracker> Ok(ResolvedImport { namespace: current_ns, errors }) } - fn add_reference(&mut self, reference_id: ModuleDefId, span: Span, is_self_type_name: bool) { + fn add_reference( + &mut self, + reference_id: ModuleDefId, + location: Location, + is_self_type_name: bool, + ) { if let Some(references_tracker) = &mut self.references_tracker { - references_tracker.add_reference(reference_id, span, is_self_type_name); + references_tracker.add_reference(reference_id, location, is_self_type_name); } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/visibility.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/visibility.rs index c592175ffcb6..84badde9d35a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/visibility.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/visibility.rs @@ -1,6 +1,6 @@ +use crate::Type; use crate::graph::CrateId; use crate::node_interner::{FuncId, NodeInterner, TraitId, TypeId}; -use crate::Type; use std::collections::BTreeMap; @@ -61,7 +61,7 @@ pub(crate) fn module_descendent_of_target( def_map.modules[current.0] .parent - .map_or(false, |parent| module_descendent_of_target(def_map, target, parent)) + .is_some_and(|parent| module_descendent_of_target(def_map, target, parent)) } /// Returns true if `target` is a struct and its parent is `current`. 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 d29e1aa43397..e68e70253a36 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 @@ -1,8 +1,9 @@ +use std::collections::BTreeSet; use std::rc::Rc; use acvm::FieldElement; use noirc_errors::CustomDiagnostic as Diagnostic; -use noirc_errors::Span; +use noirc_errors::Location; use thiserror::Error; use crate::ast::{ @@ -13,6 +14,10 @@ use crate::hir_def::expr::HirBinaryOp; use crate::hir_def::traits::TraitConstraint; use crate::hir_def::types::{BinaryTypeOperator, Kind, Type}; use crate::node_interner::NodeInterner; +use crate::signed_field::SignedField; + +/// Rust also only shows 3 maximum, even for short patterns. +pub const MAX_MISSING_CASES: usize = 3; #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum Source { @@ -31,190 +36,226 @@ pub enum Source { #[error("{0}")] BinOp(BinaryOpKind), #[error("Return")] - Return(FunctionReturnType, Span), + Return(FunctionReturnType, Location), } #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum TypeCheckError { #[error("Operator {op:?} cannot be used in a {place:?}")] - OpCannotBeUsed { op: HirBinaryOp, place: &'static str, span: Span }, + OpCannotBeUsed { op: HirBinaryOp, place: &'static str, location: Location }, #[error("Division by zero: {lhs} / {rhs}")] - DivisionByZero { lhs: FieldElement, rhs: FieldElement, span: Span }, + DivisionByZero { lhs: FieldElement, rhs: FieldElement, location: Location }, #[error("Modulo on Field elements: {lhs} % {rhs}")] - ModuloOnFields { lhs: FieldElement, rhs: FieldElement, span: Span }, - #[error("The value `{expr:?}` cannot fit into `{ty}` which has range `{range}`")] - OverflowingAssignment { expr: FieldElement, ty: Type, range: String, span: Span }, + ModuloOnFields { lhs: FieldElement, rhs: FieldElement, location: Location }, + #[error("The value `{expr}` cannot fit into `{ty}` which has range `{range}`")] + OverflowingAssignment { expr: SignedField, ty: Type, range: String, location: Location }, #[error( "The value `{value}` cannot fit into `{kind}` which has a maximum size of `{maximum_size}`" )] - OverflowingConstant { value: FieldElement, kind: Kind, maximum_size: FieldElement, span: Span }, + OverflowingConstant { + value: FieldElement, + kind: Kind, + maximum_size: FieldElement, + location: Location, + }, #[error("Evaluating `{op}` on `{lhs}`, `{rhs}` failed")] - FailingBinaryOp { op: BinaryTypeOperator, lhs: i128, rhs: i128, span: Span }, + FailingBinaryOp { op: BinaryTypeOperator, lhs: i128, rhs: i128, location: Location }, #[error("Type {typ:?} cannot be used in a {place:?}")] - TypeCannotBeUsed { typ: Type, place: &'static str, span: Span }, + TypeCannotBeUsed { typ: Type, place: &'static str, location: Location }, #[error("Expected type {expected_typ:?} is not the same as {expr_typ:?}")] - TypeMismatch { expected_typ: String, expr_typ: String, expr_span: Span }, + TypeMismatch { expected_typ: String, expr_typ: String, expr_location: Location }, #[error("Expected type {expected} is not the same as {actual}")] - TypeMismatchWithSource { expected: Type, actual: Type, span: Span, source: Source }, + TypeMismatchWithSource { expected: Type, actual: Type, location: Location, source: Source }, #[error("Expected type {expected_kind:?} is not the same as {expr_kind:?}")] - TypeKindMismatch { expected_kind: String, expr_kind: String, expr_span: Span }, + TypeKindMismatch { expected_kind: Kind, expr_kind: Kind, expr_location: Location }, #[error("Evaluating {to} resulted in {to_value}, but {from_value} was expected")] TypeCanonicalizationMismatch { to: Type, from: Type, to_value: FieldElement, from_value: FieldElement, - span: Span, + location: Location, }, #[error("Expected {expected:?} found {found:?}")] - ArityMisMatch { expected: usize, found: usize, span: Span }, + ArityMisMatch { expected: usize, found: usize, location: Location }, #[error("Return type in a function cannot be public")] - PublicReturnType { typ: Type, span: Span }, + PublicReturnType { typ: Type, location: Location }, #[error("Cannot cast type {from}, 'as' is only for primitive field or integer types")] - InvalidCast { from: Type, span: Span, reason: String }, + InvalidCast { from: Type, location: Location, reason: String }, #[error("Casting value of type {from} to a smaller type ({to})")] - DownsizingCast { from: Type, to: Type, span: Span, reason: String }, + DownsizingCast { from: Type, to: Type, location: Location, reason: String }, #[error("Expected a function, but found a(n) {found}")] - ExpectedFunction { found: Type, span: Span }, + ExpectedFunction { found: Type, location: Location }, #[error("Type {lhs_type} has no member named {field_name}")] - AccessUnknownMember { lhs_type: Type, field_name: String, span: Span }, + AccessUnknownMember { lhs_type: Type, field_name: String, location: Location }, #[error("Function expects {expected} parameters but {found} were given")] - ParameterCountMismatch { expected: usize, found: usize, span: Span }, + ParameterCountMismatch { expected: usize, found: usize, location: Location }, #[error("{} expects {} or {} parameters but {found} were given", kind, kind.required_arguments_count(), kind.required_arguments_count() + 1)] - AssertionParameterCountMismatch { kind: ConstrainKind, found: usize, span: Span }, + AssertionParameterCountMismatch { kind: ConstrainKind, found: usize, location: Location }, #[error("{item} expects {expected} generics but {found} were given")] - GenericCountMismatch { item: String, expected: usize, found: usize, span: Span }, + GenericCountMismatch { item: String, expected: usize, found: usize, location: Location }, #[error("{item} has incompatible `unconstrained`")] - UnconstrainedMismatch { item: String, expected: bool, span: Span }, + UnconstrainedMismatch { item: String, expected: bool, location: Location }, #[error("Only integer and Field types may be casted to")] - UnsupportedCast { span: Span }, + UnsupportedCast { location: Location }, #[error("Index {index} is out of bounds for this tuple {lhs_type} of length {length}")] - TupleIndexOutOfBounds { index: usize, lhs_type: Type, length: usize, span: Span }, + TupleIndexOutOfBounds { index: usize, lhs_type: Type, length: usize, location: Location }, #[error("Variable `{name}` must be mutable to be assigned to")] - VariableMustBeMutable { name: String, span: Span }, + VariableMustBeMutable { name: String, location: Location }, #[error("Cannot mutate immutable variable `{name}`")] - CannotMutateImmutableVariable { name: String, span: Span }, + CannotMutateImmutableVariable { name: String, location: Location }, + #[error("Variable {name} captured in lambda must be a mutable reference")] + MutableCaptureWithoutRef { name: String, location: Location }, + #[error("Mutable references to array indices are unsupported")] + MutableReferenceToArrayElement { location: Location }, #[error("No method named '{method_name}' found for type '{object_type}'")] - UnresolvedMethodCall { method_name: String, object_type: Type, span: Span }, + UnresolvedMethodCall { method_name: String, object_type: Type, location: Location }, #[error("Cannot invoke function field '{method_name}' on type '{object_type}' as a method")] - CannotInvokeStructFieldFunctionType { method_name: String, object_type: Type, span: Span }, + CannotInvokeStructFieldFunctionType { + method_name: String, + object_type: Type, + location: Location, + }, #[error("Integers must have the same signedness LHS is {sign_x:?}, RHS is {sign_y:?}")] - IntegerSignedness { sign_x: Signedness, sign_y: Signedness, span: Span }, + IntegerSignedness { sign_x: Signedness, sign_y: Signedness, location: Location }, #[error("Integers must have the same bit width LHS is {bit_width_x}, RHS is {bit_width_y}")] - IntegerBitWidth { bit_width_x: IntegerBitSize, bit_width_y: IntegerBitSize, span: Span }, + IntegerBitWidth { bit_width_x: IntegerBitSize, bit_width_y: IntegerBitSize, location: Location }, #[error("{kind} cannot be used in an infix operation")] - InvalidInfixOp { kind: &'static str, span: Span }, + InvalidInfixOp { kind: &'static str, location: Location }, #[error("{kind} cannot be used in a unary operation")] - InvalidUnaryOp { kind: String, span: Span }, - #[error("Bitwise operations are invalid on Field types. Try casting the operands to a sized integer type first.")] - FieldBitwiseOp { span: Span }, + InvalidUnaryOp { kind: String, location: Location }, + #[error( + "Bitwise operations are invalid on Field types. Try casting the operands to a sized integer type first." + )] + FieldBitwiseOp { location: Location }, #[error("Integer cannot be used with type {typ}")] - IntegerTypeMismatch { typ: Type, span: Span }, - #[error("Cannot use an integer and a Field in a binary operation, try converting the Field into an integer first")] - IntegerAndFieldBinaryOperation { span: Span }, + IntegerTypeMismatch { typ: Type, location: Location }, + #[error( + "Cannot use an integer and a Field in a binary operation, try converting the Field into an integer first" + )] + IntegerAndFieldBinaryOperation { location: Location }, #[error("Cannot do modulo on Fields, try casting to an integer first")] - FieldModulo { span: Span }, + FieldModulo { location: Location }, #[error("Cannot do not (`!`) on Fields, try casting to an integer first")] - FieldNot { span: Span }, + FieldNot { location: Location }, #[error("Fields cannot be compared, try casting to an integer first")] - FieldComparison { span: Span }, - #[error("The bit count in a bit-shift operation must fit in a u8, try casting the right hand side into a u8 first")] - InvalidShiftSize { span: Span }, - #[error("The number of bits to use for this bitwise operation is ambiguous. Either the operand's type or return type should be specified")] - AmbiguousBitWidth { span: Span }, + FieldComparison { location: Location }, + #[error( + "The bit count in a bit-shift operation must fit in a u8, try casting the right hand side into a u8 first" + )] + InvalidShiftSize { location: Location }, + #[error( + "The number of bits to use for this bitwise operation is ambiguous. Either the operand's type or return type should be specified" + )] + AmbiguousBitWidth { location: Location }, #[error("Error with additional context")] Context { err: Box, ctx: &'static str }, #[error("Array is not homogeneous")] NonHomogeneousArray { - first_span: Span, + first_location: Location, first_type: String, first_index: usize, - second_span: Span, + second_location: Location, second_type: String, second_index: usize, }, #[error("Object type is unknown in method call")] - TypeAnnotationsNeededForMethodCall { span: Span }, + TypeAnnotationsNeededForMethodCall { location: Location }, #[error("Object type is unknown in field access")] - TypeAnnotationsNeededForFieldAccess { span: Span }, + TypeAnnotationsNeededForFieldAccess { location: Location }, #[error("Multiple trait impls may apply to this object type")] - MultipleMatchingImpls { object_type: Type, candidates: Vec, span: Span }, + MultipleMatchingImpls { object_type: Type, candidates: Vec, location: Location }, #[error("use of deprecated function {name}")] - CallDeprecated { name: String, note: Option, span: Span }, + CallDeprecated { name: String, note: Option, location: Location }, #[error("{0}")] ResolverError(ResolverError), #[error("Unused expression result of type {expr_type}")] - UnusedResultError { expr_type: Type, expr_span: Span }, + UnusedResultError { expr_type: Type, expr_location: Location }, #[error("Expected type {expected_typ:?} is not the same as {actual_typ:?}")] TraitMethodParameterTypeMismatch { method_name: String, expected_typ: String, actual_typ: String, - parameter_span: Span, + parameter_location: Location, parameter_index: usize, }, #[error("No matching impl found")] NoMatchingImplFound(NoMatchingImplFoundError), - #[error("Constraint for `{typ}: {trait_name}` is not needed, another matching impl is already in scope")] - UnneededTraitConstraint { trait_name: String, typ: Type, span: Span }, + #[error( + "Constraint for `{typ}: {trait_name}` is not needed, another matching impl is already in scope" + )] + UnneededTraitConstraint { trait_name: String, typ: Type, location: Location }, #[error( "Expected {expected_count} generic(s) from this function, but {actual_count} were provided" )] - IncorrectTurbofishGenericCount { expected_count: usize, actual_count: usize, span: Span }, + IncorrectTurbofishGenericCount { + expected_count: usize, + actual_count: usize, + location: Location, + }, #[error( "Cannot pass a mutable reference from a constrained runtime to an unconstrained runtime" )] - ConstrainedReferenceToUnconstrained { span: Span }, + ConstrainedReferenceToUnconstrained { location: Location }, #[error( "Cannot pass a mutable reference from a unconstrained runtime to an constrained runtime" )] - UnconstrainedReferenceToConstrained { span: Span }, + UnconstrainedReferenceToConstrained { location: Location }, #[error("Slices cannot be returned from an unconstrained runtime to a constrained runtime")] - UnconstrainedSliceReturnToConstrained { span: Span }, - #[error("Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block")] - Unsafe { span: Span }, + UnconstrainedSliceReturnToConstrained { location: Location }, + #[error( + "Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block" + )] + Unsafe { location: Location }, #[error("Converting an unconstrained fn to a non-unconstrained fn is unsafe")] - UnsafeFn { span: Span }, + UnsafeFn { location: Location }, #[error("Expected a constant, but found `{typ}`")] - NonConstantEvaluated { typ: Type, span: Span }, + NonConstantEvaluated { typ: Type, location: Location }, #[error("Slices must have constant length")] - NonConstantSliceLength { span: Span }, + NonConstantSliceLength { location: Location }, #[error("Only sized types may be used in the entry point to a program")] - InvalidTypeForEntryPoint { span: Span }, + InvalidTypeForEntryPoint { location: Location }, #[error("Mismatched number of parameters in trait implementation")] MismatchTraitImplNumParameters { actual_num_parameters: usize, expected_num_parameters: usize, trait_name: String, method_name: String, - span: Span, + location: Location, }, #[error("Strings do not support indexed assignment")] - StringIndexAssign { span: Span }, + StringIndexAssign { location: Location }, #[error("Macro calls may only return `Quoted` values")] - MacroReturningNonExpr { typ: Type, span: Span }, + MacroReturningNonExpr { typ: Type, location: Location }, #[error("`{name}` has already been specified")] - DuplicateNamedTypeArg { name: Ident, prev_span: Span }, + DuplicateNamedTypeArg { name: Ident, prev_location: Location }, #[error("`{item}` has no associated type named `{name}`")] NoSuchNamedTypeArg { name: Ident, item: String }, #[error("`{item}` is missing the associated type `{name}`")] - MissingNamedTypeArg { name: Rc, item: String, span: Span }, + MissingNamedTypeArg { name: Rc, item: String, location: Location }, #[error("Internal compiler error: type unspecified for value")] - UnspecifiedType { span: Span }, + UnspecifiedType { location: Location }, #[error("Binding `{typ}` here to the `_` inside would create a cyclic type")] - CyclicType { typ: Type, span: Span }, + CyclicType { typ: Type, location: Location }, #[error("Type annotations required before indexing this array or slice")] - TypeAnnotationsNeededForIndex { span: Span }, + TypeAnnotationsNeededForIndex { location: Location }, #[error("Unnecessary `unsafe` block")] - UnnecessaryUnsafeBlock { span: Span }, + UnnecessaryUnsafeBlock { location: Location }, #[error("Unnecessary `unsafe` block")] - NestedUnsafeBlock { span: Span }, + NestedUnsafeBlock { location: Location }, + #[error("Unreachable match case")] + UnreachableCase { location: Location }, + #[error("Missing cases")] + MissingCases { cases: BTreeSet, location: Location }, + /// This error is used for types like integers which have too many variants to enumerate + #[error("Missing cases: `{typ}` is non-empty")] + MissingManyCases { typ: String, location: Location }, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct NoMatchingImplFoundError { pub(crate) constraints: Vec<(Type, String)>, - pub span: Span, + pub location: Location, } impl TypeCheckError { @@ -225,69 +266,182 @@ impl TypeCheckError { pub(crate) fn is_non_constant_evaluated(&self) -> bool { matches!(self, TypeCheckError::NonConstantEvaluated { .. }) } + + pub fn location(&self) -> Location { + match self { + TypeCheckError::OpCannotBeUsed { location, .. } + | TypeCheckError::DivisionByZero { location, .. } + | TypeCheckError::ModuloOnFields { location, .. } + | TypeCheckError::OverflowingAssignment { location, .. } + | TypeCheckError::OverflowingConstant { location, .. } + | TypeCheckError::FailingBinaryOp { location, .. } + | TypeCheckError::TypeCannotBeUsed { location, .. } + | TypeCheckError::TypeMismatch { expr_location: location, .. } + | TypeCheckError::TypeMismatchWithSource { location, .. } + | TypeCheckError::TypeKindMismatch { expr_location: location, .. } + | TypeCheckError::TypeCanonicalizationMismatch { location, .. } + | TypeCheckError::ArityMisMatch { location, .. } + | TypeCheckError::PublicReturnType { location, .. } + | TypeCheckError::InvalidCast { location, .. } + | TypeCheckError::DownsizingCast { location, .. } + | TypeCheckError::ExpectedFunction { location, .. } + | TypeCheckError::AccessUnknownMember { location, .. } + | TypeCheckError::ParameterCountMismatch { location, .. } + | TypeCheckError::AssertionParameterCountMismatch { location, .. } + | TypeCheckError::GenericCountMismatch { location, .. } + | TypeCheckError::UnconstrainedMismatch { location, .. } + | TypeCheckError::UnsupportedCast { location } + | TypeCheckError::TupleIndexOutOfBounds { location, .. } + | TypeCheckError::VariableMustBeMutable { location, .. } + | TypeCheckError::CannotMutateImmutableVariable { location, .. } + | TypeCheckError::MutableCaptureWithoutRef { location, .. } + | TypeCheckError::MutableReferenceToArrayElement { location } + | TypeCheckError::UnresolvedMethodCall { location, .. } + | TypeCheckError::CannotInvokeStructFieldFunctionType { location, .. } + | TypeCheckError::IntegerSignedness { location, .. } + | TypeCheckError::IntegerBitWidth { location, .. } + | TypeCheckError::InvalidInfixOp { location, .. } + | TypeCheckError::InvalidUnaryOp { location, .. } + | TypeCheckError::FieldBitwiseOp { location } + | TypeCheckError::IntegerTypeMismatch { location, .. } + | TypeCheckError::IntegerAndFieldBinaryOperation { location } + | TypeCheckError::FieldModulo { location } + | TypeCheckError::FieldNot { location } + | TypeCheckError::FieldComparison { location } + | TypeCheckError::InvalidShiftSize { location } + | TypeCheckError::AmbiguousBitWidth { location } + | TypeCheckError::NonHomogeneousArray { first_location: location, .. } + | TypeCheckError::TypeAnnotationsNeededForMethodCall { location } + | TypeCheckError::TypeAnnotationsNeededForFieldAccess { location } + | TypeCheckError::MultipleMatchingImpls { location, .. } + | TypeCheckError::CallDeprecated { location, .. } + | TypeCheckError::UnusedResultError { expr_location: location, .. } + | TypeCheckError::TraitMethodParameterTypeMismatch { + parameter_location: location, + .. + } + | TypeCheckError::UnneededTraitConstraint { location, .. } + | TypeCheckError::IncorrectTurbofishGenericCount { location, .. } + | TypeCheckError::ConstrainedReferenceToUnconstrained { location } + | TypeCheckError::UnconstrainedReferenceToConstrained { location } + | TypeCheckError::UnconstrainedSliceReturnToConstrained { location } + | TypeCheckError::Unsafe { location } + | TypeCheckError::UnsafeFn { location } + | TypeCheckError::NonConstantEvaluated { location, .. } + | TypeCheckError::NonConstantSliceLength { location } + | TypeCheckError::InvalidTypeForEntryPoint { location } + | TypeCheckError::MismatchTraitImplNumParameters { location, .. } + | TypeCheckError::StringIndexAssign { location } + | TypeCheckError::MacroReturningNonExpr { location, .. } + | TypeCheckError::MissingNamedTypeArg { location, .. } + | TypeCheckError::UnspecifiedType { location } + | TypeCheckError::CyclicType { location, .. } + | TypeCheckError::TypeAnnotationsNeededForIndex { location } + | TypeCheckError::UnnecessaryUnsafeBlock { location } + | TypeCheckError::UnreachableCase { location } + | TypeCheckError::MissingCases { location, .. } + | TypeCheckError::MissingManyCases { location, .. } + | TypeCheckError::NestedUnsafeBlock { location } => *location, + + TypeCheckError::DuplicateNamedTypeArg { name: ident, .. } + | TypeCheckError::NoSuchNamedTypeArg { name: ident, .. } => ident.location(), + + TypeCheckError::NoMatchingImplFound(no_matching_impl_found_error) => { + no_matching_impl_found_error.location + } + TypeCheckError::Context { err, .. } => err.location(), + TypeCheckError::ResolverError(resolver_error) => resolver_error.location(), + } + } } impl<'a> From<&'a TypeCheckError> for Diagnostic { fn from(error: &'a TypeCheckError) -> Diagnostic { match error { - TypeCheckError::TypeCannotBeUsed { typ, place, span } => Diagnostic::simple_error( + TypeCheckError::TypeCannotBeUsed { typ, place, location } => Diagnostic::simple_error( format!("The type {} cannot be used in a {}", &typ, place), String::new(), - *span, + *location, ), TypeCheckError::Context { err, ctx } => { let mut diag = Diagnostic::from(err.as_ref()); diag.add_note(ctx.to_string()); diag } - TypeCheckError::OpCannotBeUsed { op, place, span } => Diagnostic::simple_error( + TypeCheckError::OpCannotBeUsed { op, place, location } => Diagnostic::simple_error( format!("The operator {op:?} cannot be used in a {place}"), String::new(), - *span, + *location, ), - TypeCheckError::DivisionByZero { lhs, rhs, span } => Diagnostic::simple_error( + TypeCheckError::DivisionByZero { lhs, rhs, location } => Diagnostic::simple_error( format!("Division by zero: {lhs} / {rhs}"), String::new(), - *span, + *location, ), - TypeCheckError::ModuloOnFields { lhs, rhs, span } => Diagnostic::simple_error( + TypeCheckError::ModuloOnFields { lhs, rhs, location } => Diagnostic::simple_error( format!("Modulo on Field elements: {lhs} % {rhs}"), String::new(), - *span, + *location, ), - TypeCheckError::TypeMismatch { expected_typ, expr_typ, expr_span } => { + TypeCheckError::TypeMismatch { expected_typ, expr_typ, expr_location } => { Diagnostic::simple_error( format!("Expected type {expected_typ}, found type {expr_typ}"), String::new(), - *expr_span, + *expr_location, ) } - TypeCheckError::TypeKindMismatch { expected_kind, expr_kind, expr_span } => { - Diagnostic::simple_error( - format!("Expected kind {expected_kind}, found kind {expr_kind}"), - String::new(), - *expr_span, - ) + TypeCheckError::TypeKindMismatch { expected_kind, expr_kind, expr_location } => { + // Try to improve the error message for some kind combinations + match (expected_kind, expr_kind) { + (Kind::Normal, Kind::Numeric(_)) => { + Diagnostic::simple_error( + "Expected type, found numeric generic".into(), + "not a type".into(), + *expr_location, + ) + } + (Kind::Numeric(typ), Kind::Normal) => { + Diagnostic::simple_error( + "Type provided when a numeric generic was expected".into(), + format!("the numeric generic is not of type `{typ}`"), + *expr_location, + ) + } + (Kind::Numeric(expected_type), Kind::Numeric(found_type)) => { + Diagnostic::simple_error( + format!("The numeric generic is not of type `{expected_type}`"), + format!("expected `{expected_type}`, found `{found_type}`"), + *expr_location, + ) + } + _ => { + Diagnostic::simple_error( + format!("Expected kind {expected_kind}, found kind {expr_kind}"), + String::new(), + *expr_location, + ) + } + } } - TypeCheckError::TypeCanonicalizationMismatch { to, from, to_value, from_value, span } => { + TypeCheckError::TypeCanonicalizationMismatch { to, from, to_value, from_value, location } => { Diagnostic::simple_error( format!("Evaluating {to} resulted in {to_value}, but {from_value} was expected"), format!("from evaluating {from} without simplifications"), - *span, + *location, ) } - TypeCheckError::TraitMethodParameterTypeMismatch { method_name, expected_typ, actual_typ, parameter_index, parameter_span } => { + TypeCheckError::TraitMethodParameterTypeMismatch { method_name, expected_typ, actual_typ, parameter_index, parameter_location } => { Diagnostic::simple_error( format!("Parameter #{parameter_index} of method `{method_name}` must be of type {expected_typ}, not {actual_typ}"), String::new(), - *parameter_span, + *parameter_location, ) } TypeCheckError::NonHomogeneousArray { - first_span, + first_location, first_type, first_index, - second_span, + second_location, second_type, second_index, } => { @@ -296,114 +450,122 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { "Non homogeneous array, different element types found at indices ({first_index},{second_index})" ), format!("Found type {first_type}"), - *first_span, + *first_location, ); - diag.add_secondary(format!("but then found type {second_type}"), *second_span); + diag.add_secondary(format!("but then found type {second_type}"), *second_location); diag } - TypeCheckError::ArityMisMatch { expected, found, span } => { + TypeCheckError::ArityMisMatch { expected, found, location } => { let plural = if *expected == 1 { "" } else { "s" }; let msg = format!("Expected {expected} argument{plural}, but found {found}"); - Diagnostic::simple_error(msg, String::new(), *span) + Diagnostic::simple_error(msg, String::new(), *location) } - TypeCheckError::ParameterCountMismatch { expected, found, span } => { + TypeCheckError::ParameterCountMismatch { expected, found, location } => { let empty_or_s = if *expected == 1 { "" } else { "s" }; let was_or_were = if *found == 1 { "was" } else { "were" }; let msg = format!("Function expects {expected} parameter{empty_or_s} but {found} {was_or_were} given"); - Diagnostic::simple_error(msg, String::new(), *span) + Diagnostic::simple_error(msg, String::new(), *location) } - TypeCheckError::AssertionParameterCountMismatch { kind, found, span } => { + TypeCheckError::AssertionParameterCountMismatch { kind, found, location } => { let was_or_were = if *found == 1 { "was" } else { "were" }; let min = kind.required_arguments_count(); let max = min + 1; let msg = format!("{kind} expects {min} or {max} parameters but {found} {was_or_were} given"); - Diagnostic::simple_error(msg, String::new(), *span) + Diagnostic::simple_error(msg, String::new(), *location) } - TypeCheckError::GenericCountMismatch { item, expected, found, span } => { + TypeCheckError::GenericCountMismatch { item, expected, found, location } => { let empty_or_s = if *expected == 1 { "" } else { "s" }; let was_or_were = if *found == 1 { "was" } else { "were" }; let msg = format!("{item} expects {expected} generic{empty_or_s} but {found} {was_or_were} given"); - Diagnostic::simple_error(msg, String::new(), *span) + Diagnostic::simple_error(msg, String::new(), *location) } - TypeCheckError::UnconstrainedMismatch { item, expected, span } => { + TypeCheckError::UnconstrainedMismatch { item, expected, location } => { let msg = if *expected { format!("{item} is expected to be unconstrained") } else { format!("{item} is not expected to be unconstrained") }; - Diagnostic::simple_error(msg, String::new(), *span) + Diagnostic::simple_error(msg, String::new(), *location) } - TypeCheckError::InvalidCast { span, reason, .. } => { - Diagnostic::simple_error(error.to_string(), reason.clone(), *span) + TypeCheckError::InvalidCast { location, reason, .. } => { + Diagnostic::simple_error(error.to_string(), reason.clone(), *location) } - TypeCheckError::DownsizingCast { span, reason, .. } => { - Diagnostic::simple_warning(error.to_string(), reason.clone(), *span) + TypeCheckError::DownsizingCast { location, reason, .. } => { + Diagnostic::simple_warning(error.to_string(), reason.clone(), *location) } - TypeCheckError::ExpectedFunction { span, .. } - | TypeCheckError::AccessUnknownMember { span, .. } - | TypeCheckError::UnsupportedCast { span } - | TypeCheckError::TupleIndexOutOfBounds { span, .. } - | TypeCheckError::VariableMustBeMutable { span, .. } - | TypeCheckError::CannotMutateImmutableVariable { span, .. } - | TypeCheckError::UnresolvedMethodCall { span, .. } - | TypeCheckError::IntegerSignedness { span, .. } - | TypeCheckError::IntegerBitWidth { span, .. } - | TypeCheckError::InvalidInfixOp { span, .. } - | TypeCheckError::InvalidUnaryOp { span, .. } - | TypeCheckError::FieldBitwiseOp { span, .. } - | TypeCheckError::IntegerTypeMismatch { span, .. } - | TypeCheckError::FieldComparison { span, .. } - | TypeCheckError::AmbiguousBitWidth { span, .. } - | TypeCheckError::IntegerAndFieldBinaryOperation { span } - | TypeCheckError::OverflowingAssignment { span, .. } - | TypeCheckError::OverflowingConstant { span, .. } - | TypeCheckError::FailingBinaryOp { span, .. } - | TypeCheckError::FieldModulo { span } - | TypeCheckError::FieldNot { span } - | TypeCheckError::ConstrainedReferenceToUnconstrained { span } - | TypeCheckError::UnconstrainedReferenceToConstrained { span } - | TypeCheckError::UnconstrainedSliceReturnToConstrained { span } - | TypeCheckError::NonConstantEvaluated { span, .. } - | TypeCheckError::NonConstantSliceLength { span } - | TypeCheckError::StringIndexAssign { span } - | TypeCheckError::InvalidShiftSize { span } => { - Diagnostic::simple_error(error.to_string(), String::new(), *span) + TypeCheckError::ExpectedFunction { location, .. } + | TypeCheckError::AccessUnknownMember { location, .. } + | TypeCheckError::UnsupportedCast { location } + | TypeCheckError::TupleIndexOutOfBounds { location, .. } + | TypeCheckError::VariableMustBeMutable { location, .. } + | TypeCheckError::CannotMutateImmutableVariable { location, .. } + | TypeCheckError::UnresolvedMethodCall { location, .. } + | TypeCheckError::IntegerSignedness { location, .. } + | TypeCheckError::IntegerBitWidth { location, .. } + | TypeCheckError::InvalidInfixOp { location, .. } + | TypeCheckError::InvalidUnaryOp { location, .. } + | TypeCheckError::FieldBitwiseOp { location, .. } + | TypeCheckError::IntegerTypeMismatch { location, .. } + | TypeCheckError::FieldComparison { location, .. } + | TypeCheckError::AmbiguousBitWidth { location, .. } + | TypeCheckError::IntegerAndFieldBinaryOperation { location } + | TypeCheckError::OverflowingAssignment { location, .. } + | TypeCheckError::OverflowingConstant { location, .. } + | TypeCheckError::FailingBinaryOp { location, .. } + | TypeCheckError::FieldModulo { location } + | TypeCheckError::FieldNot { location } + | TypeCheckError::ConstrainedReferenceToUnconstrained { location } + | TypeCheckError::UnconstrainedReferenceToConstrained { location } + | TypeCheckError::UnconstrainedSliceReturnToConstrained { location } + | TypeCheckError::NonConstantEvaluated { location, .. } + | TypeCheckError::NonConstantSliceLength { location } + | TypeCheckError::StringIndexAssign { location } + | TypeCheckError::InvalidShiftSize { location } => { + Diagnostic::simple_error(error.to_string(), String::new(), *location) } - TypeCheckError::PublicReturnType { typ, span } => Diagnostic::simple_error( + TypeCheckError::MutableCaptureWithoutRef { name, location } => Diagnostic::simple_error( + format!("Mutable variable {name} captured in lambda must be a mutable reference"), + "Use '&mut' instead of 'mut' to capture a mutable variable.".to_string(), + *location, + ), + TypeCheckError::MutableReferenceToArrayElement { location } => { + Diagnostic::simple_error("Mutable references to array elements are currently unsupported".into(), "Try storing the element in a fresh variable first".into(), *location) + }, + TypeCheckError::PublicReturnType { typ, location } => Diagnostic::simple_error( "Functions cannot declare a public return type".to_string(), format!("return type is {typ}"), - *span, + *location, ), - TypeCheckError::TypeAnnotationsNeededForMethodCall { span } => { + TypeCheckError::TypeAnnotationsNeededForMethodCall { location } => { let mut error = Diagnostic::simple_error( "Object type is unknown in method call".to_string(), "Type must be known by this point to know which method to call".to_string(), - *span, + *location, ); error.add_note("Try adding a type annotation for the object type before this method call".to_string()); error }, - TypeCheckError::TypeAnnotationsNeededForFieldAccess { span } => { + TypeCheckError::TypeAnnotationsNeededForFieldAccess { location } => { let mut error = Diagnostic::simple_error( "Object type is unknown in field access".to_string(), "Type must be known by this point".to_string(), - *span, + *location, ); error.add_note("Try adding a type annotation for the object type before this expression".to_string()); error }, - TypeCheckError::MultipleMatchingImpls { object_type, candidates, span } => { + TypeCheckError::MultipleMatchingImpls { object_type, candidates, location } => { let message = format!("Multiple trait impls match the object type `{object_type}`"); let secondary = "Ambiguous impl".to_string(); - let mut error = Diagnostic::simple_error(message, secondary, *span); + let mut error = Diagnostic::simple_error(message, secondary, *location); for (i, candidate) in candidates.iter().enumerate() { error.add_note(format!("Candidate {}: `{candidate}`", i + 1)); } error }, TypeCheckError::ResolverError(error) => error.into(), - TypeCheckError::TypeMismatchWithSource { expected, actual, span, source } => { + TypeCheckError::TypeMismatchWithSource { expected, actual, location, source } => { let message = match source { Source::Binary => format!("Types in a binary operation should match, but found {expected} and {actual}"), Source::Assignment => { @@ -414,127 +576,157 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { Source::StringLen => format!("Can only compare strings of the same length. Here LHS is of length {expected}, and RHS is {actual}"), Source::Comparison => format!("Unsupported types for comparison: {expected} and {actual}"), Source::BinOp(kind) => format!("Unsupported types for operator `{kind}`: {expected} and {actual}"), - Source::Return(ret_ty, expr_span) => { - let ret_ty_span = match ret_ty.clone() { - FunctionReturnType::Default(span) => span, - FunctionReturnType::Ty(ty) => ty.span, + Source::Return(ret_ty, expr_location) => { + let ret_ty_location = match ret_ty.clone() { + FunctionReturnType::Default(location) => location, + FunctionReturnType::Ty(ty) => ty.location, }; - let mut diagnostic = Diagnostic::simple_error(format!("expected type {expected}, found type {actual}"), format!("expected {expected} because of return type"), ret_ty_span); + let mut diagnostic = Diagnostic::simple_error(format!("expected type {expected}, found type {actual}"), format!("expected {expected} because of return type"), ret_ty_location); if let FunctionReturnType::Default(_) = ret_ty { diagnostic.add_note(format!("help: try adding a return type: `-> {actual}`")); } - diagnostic.add_secondary(format!("{actual} returned here"), *expr_span); + diagnostic.add_secondary(format!("{actual} returned here"), *expr_location); return diagnostic }, }; - Diagnostic::simple_error(message, String::new(), *span) + Diagnostic::simple_error(message, String::new(), *location) } - TypeCheckError::CallDeprecated { span, ref note, .. } => { + TypeCheckError::CallDeprecated { location, note, .. } => { let primary_message = error.to_string(); let secondary_message = note.clone().unwrap_or_default(); - let mut diagnostic = Diagnostic::simple_warning(primary_message, secondary_message, *span); + let mut diagnostic = Diagnostic::simple_warning(primary_message, secondary_message, *location); diagnostic.deprecated = true; diagnostic } - TypeCheckError::UnusedResultError { expr_type, expr_span } => { + TypeCheckError::UnusedResultError { expr_type, expr_location } => { let msg = format!("Unused expression result of type {expr_type}"); - Diagnostic::simple_warning(msg, String::new(), *expr_span) + Diagnostic::simple_warning(msg, String::new(), *expr_location) } TypeCheckError::NoMatchingImplFound(error) => error.into(), - TypeCheckError::UnneededTraitConstraint { trait_name, typ, span } => { + TypeCheckError::UnneededTraitConstraint { trait_name, typ, location } => { let msg = format!("Constraint for `{typ}: {trait_name}` is not needed, another matching impl is already in scope"); - Diagnostic::simple_warning(msg, "Unnecessary trait constraint in where clause".into(), *span) + Diagnostic::simple_warning(msg, "Unnecessary trait constraint in where clause".into(), *location) } - TypeCheckError::InvalidTypeForEntryPoint { span } => Diagnostic::simple_error( + TypeCheckError::InvalidTypeForEntryPoint { location } => Diagnostic::simple_error( "Only sized types may be used in the entry point to a program".to_string(), - "Slices, references, or any type containing them may not be used in main, contract functions, or foldable functions".to_string(), *span), + "Slices, references, or any type containing them may not be used in main, contract functions, or foldable functions".to_string(), *location), TypeCheckError::MismatchTraitImplNumParameters { expected_num_parameters, actual_num_parameters, trait_name, method_name, - span, + location, } => { let plural = if *expected_num_parameters == 1 { "" } else { "s" }; let primary_message = format!( "`{trait_name}::{method_name}` expects {expected_num_parameters} parameter{plural}, but this method has {actual_num_parameters}"); - Diagnostic::simple_error(primary_message, "".to_string(), *span) + Diagnostic::simple_error(primary_message, "".to_string(), *location) } - TypeCheckError::IncorrectTurbofishGenericCount { expected_count, actual_count, span } => { + TypeCheckError::IncorrectTurbofishGenericCount { expected_count, actual_count, location } => { let expected_plural = if *expected_count == 1 { "" } else { "s" }; let actual_plural = if *actual_count == 1 { "was" } else { "were" }; let msg = format!("Expected {expected_count} generic{expected_plural} from this function, but {actual_count} {actual_plural} provided"); - Diagnostic::simple_error(msg, "".into(), *span) + Diagnostic::simple_error(msg, "".into(), *location) }, - TypeCheckError::MacroReturningNonExpr { typ, span } => { + TypeCheckError::MacroReturningNonExpr { typ, location } => { let mut error = Diagnostic::simple_error( format!("Expected macro call to return a `Quoted` but found a(n) `{typ}`"), "Macro calls must return quoted values, otherwise there is no code to insert.".into(), - *span, + *location, ); - error.add_secondary("Hint: remove the `!` from the end of the function name.".to_string(), *span); + error.add_secondary("Hint: remove the `!` from the end of the function name.".to_string(), *location); error }, - TypeCheckError::DuplicateNamedTypeArg { name, prev_span } => { + TypeCheckError::DuplicateNamedTypeArg { name, prev_location } => { let msg = format!("`{name}` has already been specified"); - let mut error = Diagnostic::simple_error(msg.to_string(), "".to_string(), name.span()); - error.add_secondary(format!("`{name}` previously specified here"), *prev_span); + let mut error = Diagnostic::simple_error(msg.to_string(), "".to_string(), name.location()); + error.add_secondary(format!("`{name}` previously specified here"), *prev_location); error }, TypeCheckError::NoSuchNamedTypeArg { name, item } => { let msg = format!("`{item}` has no associated type named `{name}`"); - Diagnostic::simple_error(msg.to_string(), "".to_string(), name.span()) + Diagnostic::simple_error(msg.to_string(), "".to_string(), name.location()) }, - TypeCheckError::MissingNamedTypeArg { name, item, span } => { + TypeCheckError::MissingNamedTypeArg { name, item, location } => { let msg = format!("`{item}` is missing the associated type `{name}`"); - Diagnostic::simple_error(msg.to_string(), "".to_string(), *span) + Diagnostic::simple_error(msg.to_string(), "".to_string(), *location) }, - TypeCheckError::Unsafe { span } => { - Diagnostic::simple_error(error.to_string(), String::new(), *span) + TypeCheckError::Unsafe { location } => { + Diagnostic::simple_error(error.to_string(), String::new(), *location) } - TypeCheckError::UnsafeFn { span } => { - Diagnostic::simple_error(error.to_string(), String::new(), *span) + TypeCheckError::UnsafeFn { location } => { + Diagnostic::simple_error(error.to_string(), String::new(), *location) } - TypeCheckError::UnspecifiedType { span } => { - Diagnostic::simple_error(error.to_string(), String::new(), *span) + TypeCheckError::UnspecifiedType { location } => { + Diagnostic::simple_error(error.to_string(), String::new(), *location) } - TypeCheckError::CyclicType { typ: _, span } => { - Diagnostic::simple_error(error.to_string(), "Cyclic types have unlimited size and are prohibited in Noir".into(), *span) + TypeCheckError::CyclicType { typ: _, location } => { + Diagnostic::simple_error(error.to_string(), "Cyclic types have unlimited size and are prohibited in Noir".into(), *location) } - TypeCheckError::CannotInvokeStructFieldFunctionType { method_name, object_type, span } => { + TypeCheckError::CannotInvokeStructFieldFunctionType { method_name, object_type, location } => { Diagnostic::simple_error( format!("Cannot invoke function field '{method_name}' on type '{object_type}' as a method"), format!("to call the function stored in '{method_name}', surround the field access with parentheses: '(', ')'"), - *span, + *location, ) }, - TypeCheckError::TypeAnnotationsNeededForIndex { span } => { + TypeCheckError::TypeAnnotationsNeededForIndex { location } => { Diagnostic::simple_error( "Type annotations required before indexing this array or slice".into(), "Type annotations needed before this point, can't decide if this is an array or slice".into(), - *span, + *location, ) }, - TypeCheckError::UnnecessaryUnsafeBlock { span } => { + TypeCheckError::UnnecessaryUnsafeBlock { location } => { Diagnostic::simple_warning( "Unnecessary `unsafe` block".into(), "".into(), - *span, + *location, ) }, - TypeCheckError::NestedUnsafeBlock { span } => { + TypeCheckError::NestedUnsafeBlock { location } => { Diagnostic::simple_warning( "Unnecessary `unsafe` block".into(), "Because it's nested inside another `unsafe` block".into(), - *span, + *location, ) }, + TypeCheckError::UnreachableCase { location } => { + Diagnostic::simple_warning( + "Unreachable match case".into(), + "This pattern is redundant with one or more prior patterns".into(), + *location, + ) + }, + TypeCheckError::MissingCases { cases, location } => { + let s = if cases.len() == 1 { "" } else { "s" }; + + let mut not_shown = String::new(); + let mut shown_cases = cases.iter() + .map(|s| format!("`{s}`")) + .take(MAX_MISSING_CASES) + .collect::>(); + + if cases.len() > MAX_MISSING_CASES { + shown_cases.truncate(MAX_MISSING_CASES); + not_shown = format!(", and {} more not shown", cases.len() - MAX_MISSING_CASES); + } + + let shown_cases = shown_cases.join(", "); + let msg = format!("Missing case{s}: {shown_cases}{not_shown}"); + Diagnostic::simple_error(msg, String::new(), *location) + }, + TypeCheckError::MissingManyCases { typ, location } => { + let msg = format!("Missing cases: `{typ}` is non-empty"); + let secondary = "Try adding a match-all pattern: `_`".to_string(); + Diagnostic::simple_error(msg, secondary, *location) + }, } } } @@ -542,15 +734,15 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { impl<'a> From<&'a NoMatchingImplFoundError> for Diagnostic { fn from(error: &'a NoMatchingImplFoundError) -> Self { let constraints = &error.constraints; - let span = error.span; + let location = error.location; assert!(!constraints.is_empty()); let msg = format!("No matching impl found for `{}: {}`", constraints[0].0, constraints[0].1); - let mut diagnostic = Diagnostic::from_message(&msg); + let mut diagnostic = Diagnostic::from_message(&msg, location.file); let secondary = format!("No impl for `{}: {}`", constraints[0].0, constraints[0].1); - diagnostic.add_secondary(secondary, span); + diagnostic.add_secondary(secondary, location); // These must be notes since secondaries are unordered for (typ, trait_name) in &constraints[1..] { @@ -565,7 +757,7 @@ impl NoMatchingImplFoundError { pub fn new( interner: &NodeInterner, failing_constraints: Vec, - span: Span, + location: Location, ) -> Option { // Don't show any errors where try_get_trait returns None. // This can happen if a trait is used that was never declared. @@ -578,6 +770,6 @@ impl NoMatchingImplFoundError { }) .collect::>>()?; - Some(Self { constraints, span }) + Some(Self { constraints, location }) } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/generics.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/generics.rs index f823b495040a..29baee8c30a4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/generics.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/generics.rs @@ -3,9 +3,9 @@ use std::cell::Ref; use iter_extended::vecmap; use crate::{ + DataType, ResolvedGeneric, Type, hir_def::traits::NamedType, node_interner::{FuncId, NodeInterner, TraitId, TypeAliasId}, - DataType, ResolvedGeneric, Type, }; /// Represents something that can be generic over type variables diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/mod.rs index f45b68dd818c..c7a7890fcc01 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -2,4 +2,4 @@ mod errors; pub mod generics; pub use self::errors::Source; -pub use errors::{NoMatchingImplFoundError, TypeCheckError}; +pub use errors::{MAX_MISSING_CASES, NoMatchingImplFoundError, TypeCheckError}; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs index 74028fa38093..25e055188c2b 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs @@ -1,14 +1,15 @@ -use acvm::FieldElement; use fm::FileId; +use iter_extended::vecmap; use noirc_errors::Location; +use crate::Shared; use crate::ast::{BinaryOp, BinaryOpKind, Ident, UnaryOp}; use crate::hir::type_check::generics::TraitGenerics; use crate::node_interner::{ DefinitionId, DefinitionKind, ExprId, FuncId, NodeInterner, StmtId, TraitMethodId, }; +use crate::signed_field::SignedField; use crate::token::{FmtStrFragment, Tokens}; -use crate::Shared; use super::stmt::HirPattern; use super::traits::{ResolvedTraitBound, TraitConstraint}; @@ -115,7 +116,7 @@ pub enum HirLiteral { Array(HirArrayLiteral), Slice(HirArrayLiteral), Bool(bool), - Integer(FieldElement, bool), //true for negative integer and false for positive + Integer(SignedField), Str(String), FmtStr(Vec, Vec, u32 /* length */), Unit, @@ -249,13 +250,10 @@ impl HirMethodReference { } HirMethodReference::TraitMethodId(method_id, trait_generics, assumed) => { let id = interner.trait_method_id(method_id); + let trait_id = method_id.trait_id; let constraint = TraitConstraint { typ: object_type, - trait_bound: ResolvedTraitBound { - trait_id: method_id.trait_id, - trait_generics, - span: location.span, - }, + trait_bound: ResolvedTraitBound { trait_id, trait_generics, location }, }; (id, ImplKind::TraitMethod(TraitMethod { method_id, constraint, assumed })) @@ -263,7 +261,7 @@ impl HirMethodReference { }; let func_var = HirIdent { location, id, impl_kind }; let func = interner.push_expr(HirExpression::Ident(func_var.clone(), generics)); - interner.push_expr_location(func, location.span, location.file); + interner.push_expr_location(func, location); (func, func_var) } } @@ -361,15 +359,13 @@ pub enum HirMatch { /// Jump directly to ExprId Success(ExprId), - Failure, + /// A Failure node in the match. `missing_case` is true if this node is the result of a missing + /// case of the match for which we should later reconstruct an example of. + Failure { missing_case: bool }, /// Run `body` if the given expression is true. /// Otherwise continue with the given decision tree. - Guard { - cond: ExprId, - body: ExprId, - otherwise: Box, - }, + Guard { cond: ExprId, body: ExprId, otherwise: Box }, /// Switch on the given variable with the given cases to test. /// The final argument is an optional match-all case to take if @@ -390,50 +386,7 @@ impl Case { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct SignedField { - pub field: FieldElement, - pub is_negative: bool, -} - -impl SignedField { - pub fn new(field: FieldElement, is_negative: bool) -> Self { - Self { field, is_negative } - } -} - -impl std::ops::Neg for SignedField { - type Output = Self; - - fn neg(mut self) -> Self::Output { - self.is_negative = !self.is_negative; - self - } -} - -impl std::cmp::PartialOrd for SignedField { - fn partial_cmp(&self, other: &Self) -> Option { - if self.is_negative != other.is_negative { - if self.is_negative { - return Some(std::cmp::Ordering::Less); - } else { - return Some(std::cmp::Ordering::Greater); - } - } - self.field.partial_cmp(&other.field) - } -} - -impl std::fmt::Display for SignedField { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.is_negative { - write!(f, "-")?; - } - write!(f, "{}", self.field) - } -} - -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum Constructor { True, False, @@ -483,6 +436,41 @@ impl Constructor { _ => false, } } + + /// Return all the constructors of this type from one constructor. Intended to be used + /// for error reporting in cases where there are at least 2 constructors. + pub(crate) fn all_constructors(&self) -> Vec<(Constructor, /*arg count:*/ usize)> { + match self { + Constructor::True | Constructor::False => { + vec![(Constructor::True, 0), (Constructor::False, 0)] + } + Constructor::Unit => vec![(Constructor::Unit, 0)], + Constructor::Tuple(args) => vec![(self.clone(), args.len())], + Constructor::Variant(typ, _) => { + let typ = typ.follow_bindings(); + let Type::DataType(def, generics) = &typ else { + unreachable!( + "Constructor::Variant should have a DataType type, but found {typ:?}" + ); + }; + + let def_ref = def.borrow(); + if let Some(variants) = def_ref.get_variants(generics) { + vecmap(variants.into_iter().enumerate(), |(i, (_, fields))| { + (Constructor::Variant(typ.clone(), i), fields.len()) + }) + } else + /* def is a struct */ + { + let field_count = def_ref.fields_raw().map(|fields| fields.len()).unwrap_or(0); + vec![(Constructor::Variant(typ.clone(), 0), field_count)] + } + } + + // Nothing great to return for these + Constructor::Int(_) | Constructor::Range(..) => Vec::new(), + } + } } impl std::fmt::Display for Constructor { 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 75bb4f505417..b84e511bd447 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 @@ -171,7 +171,7 @@ pub struct FuncMeta { #[derive(Debug, Clone)] pub enum FunctionBody { - Unresolved(FunctionKind, BlockExpression, Span), + Unresolved(FunctionKind, BlockExpression, Location), Resolving, Resolved, } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/stmt.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/stmt.rs index b0e004349032..21db5971bf0f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -1,8 +1,8 @@ use super::expr::HirIdent; +use crate::Type; use crate::ast::Ident; use crate::node_interner::{ExprId, StmtId}; use crate::token::SecondaryAttribute; -use crate::Type; use noirc_errors::{Location, Span}; /// A HirStatement is the result of performing name resolution on diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/traits.rs index a80c25492a35..a344b2769133 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/traits.rs @@ -1,13 +1,13 @@ use iter_extended::vecmap; use rustc_hash::FxHashMap as HashMap; +use crate::ResolvedGeneric; use crate::ast::{Ident, ItemVisibility, NoirFunction}; use crate::hir::type_check::generics::TraitGenerics; -use crate::ResolvedGeneric; use crate::{ + Generics, Type, TypeBindings, TypeVariable, graph::CrateId, node_interner::{FuncId, TraitId, TraitMethodId}, - Generics, Type, TypeBindings, TypeVariable, }; use fm::FileId; use noirc_errors::{Location, Span}; @@ -121,7 +121,7 @@ impl TraitConstraint { pub struct ResolvedTraitBound { pub trait_id: TraitId, pub trait_generics: TraitGenerics, - pub span: Span, + pub location: Location, } impl ResolvedTraitBound { @@ -186,10 +186,10 @@ impl Trait { (ordered, named) } - pub fn get_trait_generics(&self, span: Span) -> TraitGenerics { + pub fn get_trait_generics(&self, location: Location) -> TraitGenerics { let ordered = vecmap(&self.generics, |generic| generic.clone().as_named_generic()); let named = vecmap(&self.associated_types, |generic| { - let name = Ident::new(generic.name.to_string(), span); + let name = Ident::new(generic.name.to_string(), location); NamedType { name, typ: generic.clone().as_named_generic() } }); TraitGenerics { ordered, named } @@ -197,11 +197,11 @@ impl Trait { /// Returns a TraitConstraint for this trait using Self as the object /// type and the uninstantiated generics for any trait generics. - pub fn as_constraint(&self, span: Span) -> TraitConstraint { - let trait_generics = self.get_trait_generics(span); + pub fn as_constraint(&self, location: Location) -> TraitConstraint { + let trait_generics = self.get_trait_generics(location); TraitConstraint { typ: Type::TypeVariable(self.self_type_typevar.clone()), - trait_bound: ResolvedTraitBound { trait_generics, trait_id: self.id, span }, + trait_bound: ResolvedTraitBound { trait_generics, trait_id: self.id, location }, } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs index 2afaced32c71..1f4b21cb9a90 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs @@ -12,11 +12,12 @@ use acvm::{AcirField, FieldElement}; use crate::{ ast::{IntegerBitSize, ItemVisibility}, - hir::type_check::{generics::TraitGenerics, TypeCheckError}, + hir::type_check::{TypeCheckError, generics::TraitGenerics}, node_interner::{ExprId, NodeInterner, TraitId, TypeAliasId}, + signed_field::{AbsU128, SignedField}, }; use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use noirc_printable_type::PrintableType; use crate::{ @@ -249,11 +250,7 @@ impl Kind { } pub(crate) fn unify(&self, other: &Kind) -> Result<(), UnificationError> { - if self.unifies(other) { - Ok(()) - } else { - Err(UnificationError) - } + if self.unifies(other) { Ok(()) } else { Err(UnificationError) } } /// Returns the default type this type variable should be bound to if it is still unbound @@ -278,7 +275,7 @@ impl Kind { pub(crate) fn ensure_value_fits( &self, value: FieldElement, - span: Span, + location: Location, ) -> Result { match self.integral_maximum_size() { None => Ok(value), @@ -287,7 +284,7 @@ impl Kind { value, kind: self.clone(), maximum_size, - span, + location, } }), } @@ -395,7 +392,7 @@ pub type Generics = Vec; pub struct ResolvedGeneric { pub name: Rc, pub type_var: TypeVariable, - pub span: Span, + pub location: Location, } impl ResolvedGeneric { @@ -854,12 +851,17 @@ impl TypeVariable { *self.1.borrow_mut() = TypeBinding::Bound(typ); } - pub fn try_bind(&self, binding: Type, kind: &Kind, span: Span) -> Result<(), TypeCheckError> { + pub fn try_bind( + &self, + binding: Type, + kind: &Kind, + location: Location, + ) -> Result<(), TypeCheckError> { if !binding.kind().unifies(kind) { return Err(TypeCheckError::TypeKindMismatch { - expected_kind: format!("{}", kind), - expr_kind: format!("{}", binding.kind()), - expr_span: span, + expected_kind: kind.clone(), + expr_kind: binding.kind(), + expr_location: location, }); } @@ -871,7 +873,7 @@ impl TypeVariable { }; if binding.occurs(id) { - Err(TypeCheckError::CyclicType { span, typ: binding }) + Err(TypeCheckError::CyclicType { location, typ: binding }) } else { *self.1.borrow_mut() = TypeBinding::Bound(binding); Ok(()) @@ -1053,11 +1055,7 @@ impl std::fmt::Display for Type { let this = self.canonicalize_checked(); // Prevent infinite recursion - if this != *self { - write!(f, "{this}") - } else { - write!(f, "({lhs} {op} {rhs})") - } + if this != *self { write!(f, "{this}") } else { write!(f, "({lhs} {op} {rhs})") } } } } @@ -1159,15 +1157,15 @@ impl Type { } pub fn is_field(&self) -> bool { - matches!(self.follow_bindings(), Type::FieldElement) + matches!(self.follow_bindings_shallow().as_ref(), Type::FieldElement) } pub fn is_bool(&self) -> bool { - matches!(self.follow_bindings(), Type::Bool) + matches!(self.follow_bindings_shallow().as_ref(), Type::Bool) } pub fn is_integer(&self) -> bool { - matches!(self.follow_bindings(), Type::Integer(_, _)) + matches!(self.follow_bindings_shallow().as_ref(), Type::Integer(_, _)) } /// If value_level, only check for Type::FieldElement, @@ -1245,6 +1243,10 @@ impl Type { } } + pub(crate) fn is_mutable_ref(&self) -> bool { + matches!(self.follow_bindings_shallow().as_ref(), Type::MutableReference(_)) + } + /// True if this type can be used as a parameter to `main` or a contract function. /// This is only false for unsized types like slices or slices that do not make sense /// as a program input such as named generics or mutable references. @@ -1457,8 +1459,8 @@ impl Type { Type::NamedGeneric(var, _) => var.kind(), Type::Constant(_, kind) => kind.clone(), Type::TypeVariable(var) => match &*var.borrow() { - TypeBinding::Bound(ref typ) => typ.kind(), - TypeBinding::Unbound(_, ref type_var_kind) => type_var_kind.clone(), + TypeBinding::Bound(typ) => typ.kind(), + TypeBinding::Unbound(_, type_var_kind) => type_var_kind.clone(), }, Type::InfixExpr(lhs, _op, rhs, _) => lhs.infix_kind(rhs), Type::Alias(def, generics) => def.borrow().get_type(generics).kind(), @@ -1486,11 +1488,7 @@ impl Type { fn infix_kind(&self, other: &Self) -> Kind { let self_kind = self.kind(); let other_kind = other.kind(); - if self_kind.unifies(&other_kind) { - self_kind - } else { - Kind::numeric(Type::Error) - } + if self_kind.unifies(&other_kind) { self_kind } else { Kind::numeric(Type::Error) } } /// Creates an `InfixExpr`. @@ -1541,7 +1539,7 @@ impl Type { Type::FieldElement | Type::Integer { .. } | Type::Bool => 1, Type::Array(size, typ) => { let length = size - .evaluate_to_u32(location.span) + .evaluate_to_u32(*location) .expect("Cannot have variable sized arrays as a parameter to main"); let typ = typ.as_ref(); length * typ.field_count(location) @@ -1568,7 +1566,7 @@ impl Type { fields.iter().fold(0, |acc, field_typ| acc + field_typ.field_count(location)) } Type::String(size) => size - .evaluate_to_u32(location.span) + .evaluate_to_u32(*location) .expect("Cannot have variable sized strings as a parameter to main"), Type::FmtString(_, _) | Type::Unit @@ -1658,8 +1656,8 @@ impl Type { typ.try_bind_to_polymorphic_int(var, bindings, only_integer) } // Avoid infinitely recursive bindings - TypeBinding::Unbound(ref id, _) if *id == target_id => Ok(()), - TypeBinding::Unbound(ref new_target_id, Kind::IntegerOrField) => { + TypeBinding::Unbound(id, _) if *id == target_id => Ok(()), + TypeBinding::Unbound(new_target_id, Kind::IntegerOrField) => { let type_var_kind = Kind::IntegerOrField; if only_integer { let var_clone = var.clone(); @@ -1682,7 +1680,7 @@ impl Type { bindings.insert(target_id, (var.clone(), Kind::Integer, this.clone())); Ok(()) } - TypeBinding::Unbound(new_target_id, ref type_var_kind) => { + TypeBinding::Unbound(new_target_id, type_var_kind) => { let var_clone = var.clone(); // Bind to the most specific type variable kind let clone_kind = @@ -1934,8 +1932,8 @@ impl Type { } (Constant(value, kind), other) | (other, Constant(value, kind)) => { - let dummy_span = Span::default(); - if let Ok(other_value) = other.evaluate_to_field_element(kind, dummy_span) { + let dummy_location = Location::dummy(); + if let Ok(other_value) = other.evaluate_to_field_element(kind, dummy_location) { if *value == other_value && kind.unifies(&other.kind()) { Ok(()) } else { @@ -2012,7 +2010,7 @@ impl Type { &self, expected: &Type, expression: ExprId, - span: Span, + location: Location, interner: &mut NodeInterner, errors: &mut Vec, make_error: impl FnOnce() -> TypeCheckError, @@ -2032,14 +2030,16 @@ impl Type { match self.try_fn_to_unconstrained_fn_coercion(expected) { FunctionCoercionResult::NoCoercion => errors.push(make_error()), FunctionCoercionResult::Coerced(coerced_self) => { - coerced_self - .unify_with_coercions(expected, expression, span, interner, errors, make_error); + coerced_self.unify_with_coercions( + expected, expression, location, interner, errors, make_error, + ); } FunctionCoercionResult::UnconstrainedMismatch(coerced_self) => { - errors.push(TypeCheckError::UnsafeFn { span }); + errors.push(TypeCheckError::UnsafeFn { location }); - coerced_self - .unify_with_coercions(expected, expression, span, interner, errors, make_error); + coerced_self.unify_with_coercions( + expected, expression, location, interner, errors, make_error, + ); } } } @@ -2104,8 +2104,8 @@ impl Type { /// If this type is a Type::Constant (used in array lengths), or is bound /// to a Type::Constant, return the constant as a u32. - pub fn evaluate_to_u32(&self, span: Span) -> Result { - self.evaluate_to_field_element(&Kind::u32(), span).map(|field_element| { + pub fn evaluate_to_u32(&self, location: Location) -> Result { + self.evaluate_to_field_element(&Kind::u32(), location).map(|field_element| { field_element .try_to_u32() .expect("ICE: size should have already been checked by evaluate_to_field_element") @@ -2117,17 +2117,17 @@ impl Type { pub(crate) fn evaluate_to_field_element( &self, kind: &Kind, - span: Span, + location: Location, ) -> Result { let run_simplifications = true; - self.evaluate_to_field_element_helper(kind, span, run_simplifications) + self.evaluate_to_field_element_helper(kind, location, run_simplifications) } /// evaluate_to_field_element with optional generic arithmetic simplifications pub(crate) fn evaluate_to_field_element_helper( &self, kind: &Kind, - span: Span, + location: Location, run_simplifications: bool, ) -> Result { if let Some((binding, binding_kind)) = self.get_inner_type_variable() { @@ -2135,7 +2135,7 @@ impl Type { if kind.unifies(&binding_kind) { return binding.evaluate_to_field_element_helper( &binding_kind, - span, + location, run_simplifications, ); } @@ -2146,12 +2146,12 @@ impl Type { match self.canonicalize_helper(could_be_checked_cast, run_simplifications) { Type::Constant(x, constant_kind) => { if kind.unifies(&constant_kind) { - kind.ensure_value_fits(x, span) + kind.ensure_value_fits(x, location) } else { Err(TypeCheckError::TypeKindMismatch { - expected_kind: format!("{}", constant_kind), - expr_kind: format!("{}", kind), - expr_span: span, + expected_kind: constant_kind, + expr_kind: kind.clone(), + expr_location: location, }) } } @@ -2160,31 +2160,31 @@ impl Type { if kind.unifies(&infix_kind) { let lhs_value = lhs.evaluate_to_field_element_helper( &infix_kind, - span, + location, run_simplifications, )?; let rhs_value = rhs.evaluate_to_field_element_helper( &infix_kind, - span, + location, run_simplifications, )?; - op.function(lhs_value, rhs_value, &infix_kind, span) + op.function(lhs_value, rhs_value, &infix_kind, location) } else { Err(TypeCheckError::TypeKindMismatch { - expected_kind: format!("{}", kind), - expr_kind: format!("{}", infix_kind), - expr_span: span, + expected_kind: kind.clone(), + expr_kind: infix_kind, + expr_location: location, }) } } Type::CheckedCast { from, to } => { - let to_value = to.evaluate_to_field_element(kind, span)?; + let to_value = to.evaluate_to_field_element(kind, location)?; // if both 'to' and 'from' evaluate to a constant, // return None unless they match let skip_simplifications = false; if let Ok(from_value) = - from.evaluate_to_field_element_helper(kind, span, skip_simplifications) + from.evaluate_to_field_element_helper(kind, location, skip_simplifications) { if to_value == from_value { Ok(to_value) @@ -2196,14 +2196,14 @@ impl Type { from, to_value, from_value, - span, + location, }) } } else { Ok(to_value) } } - other => Err(TypeCheckError::NonConstantEvaluated { typ: other, span }), + other => Err(TypeCheckError::NonConstantEvaluated { typ: other, location }), } } @@ -2287,7 +2287,11 @@ impl Type { ) -> (Type, TypeBindings) { match self { Type::Forall(typevars, typ) => { - assert_eq!(types.len() + implicit_generic_count, typevars.len(), "Turbofish operator used with incorrect generic count which was not caught by name resolution"); + assert_eq!( + types.len() + implicit_generic_count, + typevars.len(), + "Turbofish operator used with incorrect generic count which was not caught by name resolution" + ); let bindings = (0..implicit_generic_count).map(|_| interner.next_type_variable()).chain(types); @@ -2724,7 +2728,8 @@ impl Type { if sign == &Signedness::Signed { max_bit_size -= 1; } - Some(((1u128 << max_bit_size) - 1).into()) + let max = if max_bit_size == 128 { u128::MAX } else { (1u128 << max_bit_size) - 1 }; + Some(max.into()) } Type::Bool => Some(FieldElement::one()), Type::TypeVariable(var) => { @@ -2761,6 +2766,36 @@ impl Type { | Type::Error => None, } } + + pub(crate) fn integral_minimum_size(&self) -> Option { + match self.follow_bindings_shallow().as_ref() { + Type::FieldElement => None, + Type::Integer(sign, num_bits) => { + if *sign == Signedness::Unsigned { + return Some(SignedField::zero()); + } + + let max_bit_size = num_bits.bit_size() - 1; + Some(if max_bit_size == 128 { + SignedField::negative(i128::MIN.abs_u128()) + } else { + SignedField::negative(1u128 << max_bit_size) + }) + } + Type::Bool => Some(SignedField::zero()), + Type::TypeVariable(var) => { + let binding = &var.1; + match &*binding.borrow() { + TypeBinding::Unbound(_, type_var_kind) => match type_var_kind { + Kind::Any | Kind::Normal | Kind::Integer | Kind::IntegerOrField => None, + Kind::Numeric(typ) => typ.integral_minimum_size(), + }, + TypeBinding::Bound(typ) => typ.integral_minimum_size(), + } + } + _ => None, + } + } } /// Wraps a given `expression` in `expression.as_slice()` @@ -2781,14 +2816,14 @@ fn convert_array_expression_to_slice( let argument = interner.expression(&expression); let argument = interner.push_expr(argument); interner.push_expr_type(argument, array_type.clone()); - interner.push_expr_location(argument, location.span, location.file); + interner.push_expr_location(argument, location); let arguments = vec![argument]; let is_macro_call = false; let call = HirExpression::Call(HirCallExpression { func, arguments, location, is_macro_call }); interner.replace_expr(&expression, call); - interner.push_expr_location(func, location.span, location.file); + interner.push_expr_location(func, location); interner.push_expr_type(expression, target_type.clone()); let func_type = @@ -2803,7 +2838,7 @@ impl BinaryTypeOperator { a: FieldElement, b: FieldElement, kind: &Kind, - span: Span, + location: Location, ) -> Result { match kind.follow_bindings().integral_maximum_size() { None => match self { @@ -2812,16 +2847,16 @@ impl BinaryTypeOperator { BinaryTypeOperator::Multiplication => Ok(a * b), BinaryTypeOperator::Division => (b != FieldElement::zero()) .then(|| a / b) - .ok_or(TypeCheckError::DivisionByZero { lhs: a, rhs: b, span }), + .ok_or(TypeCheckError::DivisionByZero { lhs: a, rhs: b, location }), BinaryTypeOperator::Modulo => { - Err(TypeCheckError::ModuloOnFields { lhs: a, rhs: b, span }) + Err(TypeCheckError::ModuloOnFields { lhs: a, rhs: b, location }) } }, Some(_maximum_size) => { let a = a.to_i128(); let b = b.to_i128(); - let err = TypeCheckError::FailingBinaryOp { op: self, lhs: a, rhs: b, span }; + let err = TypeCheckError::FailingBinaryOp { op: self, lhs: a, rhs: b, location }; let result = match self { BinaryTypeOperator::Addition => a.checked_add(b).ok_or(err)?, BinaryTypeOperator::Subtraction => a.checked_sub(b).ok_or(err)?, @@ -2875,9 +2910,10 @@ impl From<&Type> for PrintableType { match value { Type::FieldElement => PrintableType::Field, Type::Array(size, typ) => { - let dummy_span = Span::default(); - let length = - size.evaluate_to_u32(dummy_span).expect("Cannot print variable sized arrays"); + let dummy_location = Location::dummy(); + let length = size + .evaluate_to_u32(dummy_location) + .expect("Cannot print variable sized arrays"); let typ = typ.as_ref(); PrintableType::Array { length, typ: Box::new(typ.into()) } } @@ -2902,16 +2938,17 @@ impl From<&Type> for PrintableType { }, Type::Bool => PrintableType::Boolean, Type::String(size) => { - let dummy_span = Span::default(); - let size = - size.evaluate_to_u32(dummy_span).expect("Cannot print variable sized strings"); + let dummy_location = Location::dummy(); + let size = size + .evaluate_to_u32(dummy_location) + .expect("Cannot print variable sized strings"); PrintableType::String { length: size } } Type::FmtString(_, _) => unreachable!("format strings cannot be printed"), Type::Error => unreachable!(), Type::Unit => PrintableType::Unit, Type::Constant(_, _) => unreachable!(), - Type::DataType(def, ref args) => { + Type::DataType(def, args) => { let data_type = def.borrow(); let name = data_type.name.to_string(); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index ce9125cd5f01..83618bf37077 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use acvm::{AcirField, FieldElement}; -use noirc_errors::Span; +use noirc_errors::Location; use crate::{BinaryTypeOperator, Type, TypeBindings, UnificationError}; @@ -60,16 +60,19 @@ impl Type { match self.follow_bindings() { Type::InfixExpr(lhs, op, rhs, inversion) => { let kind = lhs.infix_kind(&rhs); - let dummy_span = Span::default(); + let dummy_location = Location::dummy(); // evaluate_to_field_element also calls canonicalize so if we just called // `self.evaluate_to_field_element(..)` we'd get infinite recursion. if let Ok(lhs_value) = - lhs.evaluate_to_field_element_helper(&kind, dummy_span, run_simplifications) + lhs.evaluate_to_field_element_helper(&kind, dummy_location, run_simplifications) { - if let Ok(rhs_value) = - rhs.evaluate_to_field_element_helper(&kind, dummy_span, run_simplifications) - { - if let Ok(result) = op.function(lhs_value, rhs_value, &kind, dummy_span) { + if let Ok(rhs_value) = rhs.evaluate_to_field_element_helper( + &kind, + dummy_location, + run_simplifications, + ) { + if let Ok(result) = op.function(lhs_value, rhs_value, &kind, dummy_location) + { return Type::Constant(result, kind); } } @@ -139,9 +142,9 @@ impl Type { queue.push(*rhs_inner); } Type::Constant(new_constant, new_constant_kind) => { - let dummy_span = Span::default(); + let dummy_location = Location::dummy(); if let Ok(result) = - op.function(constant, new_constant, &new_constant_kind, dummy_span) + op.function(constant, new_constant, &new_constant_kind, dummy_location) { constant = result; } else { @@ -268,15 +271,14 @@ impl Type { rhs: &Type, ) -> Option<(Box, BinaryTypeOperator, FieldElement, FieldElement)> { let kind = lhs.infix_kind(rhs); - let dummy_span = Span::default(); - let rhs = rhs.evaluate_to_field_element(&kind, dummy_span).ok()?; + let dummy_location = Location::dummy(); + let rhs = rhs.evaluate_to_field_element(&kind, dummy_location).ok()?; let Type::InfixExpr(l_type, l_op, l_rhs, _) = lhs.follow_bindings() else { return None; }; - let dummy_span = Span::default(); - let l_rhs = l_rhs.evaluate_to_field_element(&kind, dummy_span).ok()?; + let l_rhs = l_rhs.evaluate_to_field_element(&kind, dummy_location).ok()?; Some((l_type, l_op, l_rhs, rhs)) } @@ -301,9 +303,9 @@ impl Type { if l_op == Subtraction { op = op.inverse()?; } - let dummy_span = Span::default(); + let dummy_location = Location::dummy(); let result = - op.function(l_const, r_const, &lhs.infix_kind(rhs), dummy_span).ok()?; + op.function(l_const, r_const, &lhs.infix_kind(rhs), dummy_location).ok()?; let constant = Type::Constant(result, lhs.infix_kind(rhs)); Some(Type::infix_expr(l_type, l_op, Box::new(constant))) } @@ -316,9 +318,9 @@ impl Type { if op == Division && (r_const == FieldElement::zero() || !divides_evenly) { None } else { - let dummy_span = Span::default(); + let dummy_location = Location::dummy(); let result = - op.function(l_const, r_const, &lhs.infix_kind(rhs), dummy_span).ok()?; + op.function(l_const, r_const, &lhs.infix_kind(rhs), dummy_location).ok()?; let constant = Box::new(Type::Constant(result, lhs.infix_kind(rhs))); Some(Type::infix_expr(l_type, l_op, constant)) } @@ -337,8 +339,8 @@ impl Type { if let Type::InfixExpr(lhs_a, op_a, rhs_a, _) = self { if let Some(inverse) = op_a.approx_inverse() { let kind = lhs_a.infix_kind(rhs_a); - let dummy_span = Span::default(); - if let Ok(rhs_a_value) = rhs_a.evaluate_to_field_element(&kind, dummy_span) { + let dummy_location = Location::dummy(); + if let Ok(rhs_a_value) = rhs_a.evaluate_to_field_element(&kind, dummy_location) { let rhs_a = Box::new(Type::Constant(rhs_a_value, kind)); let new_other = Type::inverted_infix_expr(Box::new(other.clone()), inverse, rhs_a); @@ -355,8 +357,8 @@ impl Type { if let Type::InfixExpr(lhs_b, op_b, rhs_b, inversion) = other { if let Some(inverse) = op_b.approx_inverse() { let kind = lhs_b.infix_kind(rhs_b); - let dummy_span = Span::default(); - if let Ok(rhs_b_value) = rhs_b.evaluate_to_field_element(&kind, dummy_span) { + let dummy_location = Location::dummy(); + if let Ok(rhs_b_value) = rhs_b.evaluate_to_field_element(&kind, dummy_location) { let rhs_b = Box::new(Type::Constant(rhs_b_value, kind)); let new_self = Type::InfixExpr(Box::new(self.clone()), inverse, rhs_b, !inversion); @@ -598,7 +600,7 @@ mod proptests { #[test] // Expect cases that don't resolve to constants, e.g. see // `arithmetic_generics_checked_cast_indirect_zeros` - #[should_panic(expected = "matches!(infix, Type :: Constant(..))")] + #[should_panic(expected = "matches!(infix, Type::Constant(..))")] fn instantiate_before_or_after_canonicalize(infix_type_bindings in arbitrary_infix_expr_with_bindings(10)) { let (infix, typ, bindings) = infix_type_bindings; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/errors.rs index f95ccba061a6..9adae78febde 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/errors.rs @@ -1,55 +1,55 @@ use crate::hir::def_collector::dc_crate::CompilationError; use crate::parser::ParserError; use crate::parser::ParserErrorReason; -use crate::token::SpannedToken; +use super::token::LocatedToken; use super::token::Token; use noirc_errors::CustomDiagnostic as Diagnostic; -use noirc_errors::Span; +use noirc_errors::Location; use thiserror::Error; #[derive(Error, Clone, Debug, PartialEq, Eq)] pub enum LexerErrorKind { #[error("An unexpected character {:?} was found.", found)] - UnexpectedCharacter { span: Span, expected: String, found: Option }, + UnexpectedCharacter { location: Location, expected: String, found: Option }, #[error("Internal error: Tried to lex {:?} as a double char token", found)] - NotADoubleChar { span: Span, found: Token }, + NotADoubleChar { location: Location, found: Token }, #[error("Invalid integer literal, {:?} is not a integer", found)] - InvalidIntegerLiteral { span: Span, found: String }, + InvalidIntegerLiteral { location: Location, found: String }, #[error("Integer literal is too large")] - IntegerLiteralTooLarge { span: Span, limit: String }, + IntegerLiteralTooLarge { location: Location, limit: String }, #[error("{:?} is not a valid attribute", found)] - MalformedFuncAttribute { span: Span, found: String }, + MalformedFuncAttribute { location: Location, found: String }, #[error("Malformed test attribute")] - MalformedTestAttribute { span: Span }, + MalformedTestAttribute { location: Location }, #[error("{:?} is not a valid inner attribute", found)] - InvalidInnerAttribute { span: Span, found: String }, + InvalidInnerAttribute { location: Location, found: String }, #[error("Logical and used instead of bitwise and")] - LogicalAnd { span: Span }, + LogicalAnd { location: Location }, #[error("Unterminated block comment")] - UnterminatedBlockComment { span: Span }, + UnterminatedBlockComment { location: Location }, #[error("Unterminated string literal")] - UnterminatedStringLiteral { span: Span }, + UnterminatedStringLiteral { location: Location }, #[error("Invalid format string: expected '}}', found {found:?}")] - InvalidFormatString { found: char, span: Span }, + InvalidFormatString { found: char, location: Location }, #[error("Invalid format string: expected letter or underscore, found '}}'")] - EmptyFormatStringInterpolation { span: Span }, + EmptyFormatStringInterpolation { location: Location }, #[error( "'\\{escaped}' is not a valid escape sequence. Use '\\' for a literal backslash character." )] - InvalidEscape { escaped: char, span: Span }, + InvalidEscape { escaped: char, location: Location }, #[error("Invalid quote delimiter `{delimiter}`, valid delimiters are `{{`, `[`, and `(`")] - InvalidQuoteDelimiter { delimiter: SpannedToken }, + InvalidQuoteDelimiter { delimiter: LocatedToken }, #[error("Non-ASCII characters are invalid in comments")] - NonAsciiComment { span: Span }, + NonAsciiComment { location: Location }, #[error("Expected `{end_delim}` to close this {start_delim}")] - UnclosedQuote { start_delim: SpannedToken, end_delim: Token }, + UnclosedQuote { start_delim: LocatedToken, end_delim: Token }, } impl From for ParserError { fn from(value: LexerErrorKind) -> Self { - let span = value.span(); - ParserError::with_reason(ParserErrorReason::Lexer(value), span) + let location = value.location(); + ParserError::with_reason(ParserErrorReason::Lexer(value), location) } } @@ -60,31 +60,31 @@ impl From for CompilationError { } impl LexerErrorKind { - pub fn span(&self) -> Span { + pub fn location(&self) -> Location { match self { - LexerErrorKind::UnexpectedCharacter { span, .. } => *span, - LexerErrorKind::NotADoubleChar { span, .. } => *span, - LexerErrorKind::InvalidIntegerLiteral { span, .. } => *span, - LexerErrorKind::IntegerLiteralTooLarge { span, .. } => *span, - LexerErrorKind::MalformedFuncAttribute { span, .. } => *span, - LexerErrorKind::MalformedTestAttribute { span, .. } => *span, - LexerErrorKind::InvalidInnerAttribute { span, .. } => *span, - LexerErrorKind::LogicalAnd { span } => *span, - LexerErrorKind::UnterminatedBlockComment { span } => *span, - LexerErrorKind::UnterminatedStringLiteral { span } => *span, - LexerErrorKind::InvalidFormatString { span, .. } => *span, - LexerErrorKind::EmptyFormatStringInterpolation { span, .. } => *span, - LexerErrorKind::InvalidEscape { span, .. } => *span, - LexerErrorKind::InvalidQuoteDelimiter { delimiter } => delimiter.to_span(), - LexerErrorKind::NonAsciiComment { span, .. } => *span, - LexerErrorKind::UnclosedQuote { start_delim, .. } => start_delim.to_span(), + LexerErrorKind::UnexpectedCharacter { location, .. } => *location, + LexerErrorKind::NotADoubleChar { location, .. } => *location, + LexerErrorKind::InvalidIntegerLiteral { location, .. } => *location, + LexerErrorKind::IntegerLiteralTooLarge { location, .. } => *location, + LexerErrorKind::MalformedFuncAttribute { location, .. } => *location, + LexerErrorKind::MalformedTestAttribute { location, .. } => *location, + LexerErrorKind::InvalidInnerAttribute { location, .. } => *location, + LexerErrorKind::LogicalAnd { location } => *location, + LexerErrorKind::UnterminatedBlockComment { location } => *location, + LexerErrorKind::UnterminatedStringLiteral { location } => *location, + LexerErrorKind::InvalidFormatString { location, .. } => *location, + LexerErrorKind::EmptyFormatStringInterpolation { location, .. } => *location, + LexerErrorKind::InvalidEscape { location, .. } => *location, + LexerErrorKind::InvalidQuoteDelimiter { delimiter } => delimiter.location(), + LexerErrorKind::NonAsciiComment { location, .. } => *location, + LexerErrorKind::UnclosedQuote { start_delim, .. } => start_delim.location(), } } - fn parts(&self) -> (String, String, Span) { + fn parts(&self) -> (String, String, Location) { match self { LexerErrorKind::UnexpectedCharacter { - span, + location, expected, found, } => { @@ -93,55 +93,55 @@ impl LexerErrorKind { ( "An unexpected character was found".to_string(), format!("Expected {expected}, but found {found}"), - *span, + *location, ) }, - LexerErrorKind::NotADoubleChar { span, found } => ( + LexerErrorKind::NotADoubleChar { location, found } => ( format!("Tried to parse {found} as double char"), format!( " {found:?} is not a double char, this is an internal error" ), - *span, + *location, ), - LexerErrorKind::InvalidIntegerLiteral { span, found } => ( + LexerErrorKind::InvalidIntegerLiteral { location, found } => ( "Invalid integer literal".to_string(), format!(" {found} is not an integer"), - *span, + *location, ), - LexerErrorKind::IntegerLiteralTooLarge { span, limit } => ( + LexerErrorKind::IntegerLiteralTooLarge { location, limit } => ( "Integer literal is too large".to_string(), format!("value exceeds limit of {limit}"), - *span, + *location, ), - LexerErrorKind::MalformedFuncAttribute { span, found } => ( + LexerErrorKind::MalformedFuncAttribute { location, found } => ( "Malformed function attribute".to_string(), format!(" {found} is not a valid attribute"), - *span, + *location, ), - LexerErrorKind::MalformedTestAttribute { span } => ( + LexerErrorKind::MalformedTestAttribute { location } => ( "Malformed test attribute".to_string(), "The test attribute can be written in one of these forms: `#[test]`, `#[test(should_fail)]` or `#[test(should_fail_with = \"message\")]`".to_string(), - *span, + *location, ), - LexerErrorKind::InvalidInnerAttribute { span, found } => ( + LexerErrorKind::InvalidInnerAttribute { location, found } => ( "Invalid inner attribute".to_string(), format!(" {found} is not a valid inner attribute"), - *span, + *location, ), - LexerErrorKind::LogicalAnd { span } => ( + LexerErrorKind::LogicalAnd { location } => ( "Noir has no logical-and (&&) operator since short-circuiting is much less efficient when compiling to circuits".to_string(), "Try `&` instead, or use `if` only if you require short-circuiting".to_string(), - *span, + *location, ), - LexerErrorKind::UnterminatedBlockComment { span } => ("Unterminated block comment".to_string(), "Unterminated block comment".to_string(), *span), - LexerErrorKind::UnterminatedStringLiteral { span } => - ("Unterminated string literal".to_string(), "Unterminated string literal".to_string(), *span), - LexerErrorKind::InvalidFormatString { found, span } => { + LexerErrorKind::UnterminatedBlockComment { location } => ("Unterminated block comment".to_string(), "Unterminated block comment".to_string(), *location), + LexerErrorKind::UnterminatedStringLiteral { location } => + ("Unterminated string literal".to_string(), "Unterminated string literal".to_string(), *location), + LexerErrorKind::InvalidFormatString { found, location } => { if found == &'}' { ( "Invalid format string: unmatched '}}' found".to_string(), "If you intended to print '}', you can escape it using '}}'".to_string(), - *span, + *location, ) } else { ( @@ -151,27 +151,27 @@ impl LexerErrorKind { } else { "If you intended to print '{', you can escape it using '{{'".to_string() }, - *span, + *location, ) } } - LexerErrorKind::EmptyFormatStringInterpolation { span } => { + LexerErrorKind::EmptyFormatStringInterpolation { location } => { ( "Invalid format string: expected letter or underscore, found '}}'".to_string(), "If you intended to print '{' or '}', you can escape them using '{{' and '}}' respectively".to_string(), - *span, + *location, ) } - LexerErrorKind::InvalidEscape { escaped, span } => - (format!("'\\{escaped}' is not a valid escape sequence. Use '\\' for a literal backslash character."), "Invalid escape sequence".to_string(), *span), + LexerErrorKind::InvalidEscape { escaped, location } => + (format!("'\\{escaped}' is not a valid escape sequence. Use '\\' for a literal backslash character."), "Invalid escape sequence".to_string(), *location), LexerErrorKind::InvalidQuoteDelimiter { delimiter } => { - (format!("Invalid quote delimiter `{delimiter}`"), "Valid delimiters are `{`, `[`, and `(`".to_string(), delimiter.to_span()) + (format!("Invalid quote delimiter `{delimiter}`"), "Valid delimiters are `{`, `[`, and `(`".to_string(), delimiter.location()) }, - LexerErrorKind::NonAsciiComment { span } => { - ("Non-ASCII character in comment".to_string(), "Invalid comment character: only ASCII is currently supported.".to_string(), *span) + LexerErrorKind::NonAsciiComment { location } => { + ("Non-ASCII character in comment".to_string(), "Invalid comment character: only ASCII is currently supported.".to_string(), *location) } LexerErrorKind::UnclosedQuote { start_delim, end_delim } => { - ("Unclosed `quote` expression".to_string(), format!("Expected a `{end_delim}` to close this `{start_delim}`"), start_delim.to_span()) + ("Unclosed `quote` expression".to_string(), format!("Expected a `{end_delim}` to close this `{start_delim}`"), start_delim.location()) } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs index ef5706b4d49b..630f192c1095 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs @@ -2,10 +2,11 @@ use crate::token::DocStyle; use super::{ errors::LexerErrorKind, - token::{FmtStrFragment, IntType, Keyword, SpannedToken, Token, Tokens}, + token::{FmtStrFragment, IntType, Keyword, LocatedToken, SpannedToken, Token, Tokens}, }; use acvm::{AcirField, FieldElement}; -use noirc_errors::{Position, Span}; +use fm::FileId; +use noirc_errors::{Location, Position, Span}; use num_bigint::BigInt; use num_traits::{Num, One}; use std::str::{CharIndices, FromStr}; @@ -14,6 +15,7 @@ use std::str::{CharIndices, FromStr}; /// into an iterator of `SpannedToken`. Each `Token` corresponds roughly to 1 word or operator. /// Tokens are tagged with their location in the source file (a `Span`) for use in error reporting. pub struct Lexer<'a> { + file_id: FileId, chars: CharIndices<'a>, position: Position, done: bool, @@ -24,11 +26,13 @@ pub struct Lexer<'a> { pub type SpannedTokenResult = Result; +pub type LocatedTokenResult = Result; + impl<'a> Lexer<'a> { /// Given a source file of noir code, return all the tokens in the file /// in order, along with any lexing errors that occurred. - pub fn lex(source: &'a str) -> (Tokens, Vec) { - let lexer = Lexer::new(source); + pub fn lex(source: &'a str, file_id: FileId) -> (Tokens, Vec) { + let lexer = Lexer::new(source, file_id); let mut tokens = vec![]; let mut errors = vec![]; for result in lexer { @@ -40,8 +44,9 @@ impl<'a> Lexer<'a> { (Tokens(tokens), errors) } - pub fn new(source: &'a str) -> Self { + pub fn new(source: &'a str, file_id: FileId) -> Self { Lexer { + file_id, chars: source.char_indices(), position: 0, done: false, @@ -52,6 +57,10 @@ impl<'a> Lexer<'a> { } } + pub fn new_with_dummy_file(source: &'a str) -> Self { + Self::new(source, FileId::dummy()) + } + pub fn skip_comments(mut self, flag: bool) -> Self { self.skip_comments = flag; self @@ -96,21 +105,28 @@ impl<'a> Lexer<'a> { // When we issue this error the first '&' will already be consumed // and the next token issued will be the next '&'. let span = Span::inclusive(self.position, self.position + 1); - Err(LexerErrorKind::LogicalAnd { span }) + Err(LexerErrorKind::LogicalAnd { location: self.location(span) }) } else { self.single_char_token(Token::Ampersand) } } - fn next_token(&mut self) -> SpannedTokenResult { + fn next_token(&mut self) -> LocatedTokenResult { + self.next_spanned_token().map(|token| { + let span = token.span(); + LocatedToken::new(token.into_token(), Location::new(span, self.file_id)) + }) + } + + fn next_spanned_token(&mut self) -> SpannedTokenResult { if !self.skip_comments { - return self.next_token_without_checking_comments(); + return self.next_spanned_token_without_checking_comments(); } // Read tokens and skip comments. This is done like this to avoid recursion // and hitting stack overflow when there are many comments in a row. loop { - let token = self.next_token_without_checking_comments()?; + let token = self.next_spanned_token_without_checking_comments()?; if matches!(token.token(), Token::LineComment(_, None) | Token::BlockComment(_, None)) { continue; } @@ -119,12 +135,12 @@ impl<'a> Lexer<'a> { } /// Reads the next token, which might be a comment token (these aren't skipped in this method) - fn next_token_without_checking_comments(&mut self) -> SpannedTokenResult { + fn next_spanned_token_without_checking_comments(&mut self) -> SpannedTokenResult { match self.next_char() { Some(x) if Self::is_code_whitespace(x) => { let spanned = self.eat_whitespace(x); if self.skip_whitespaces { - self.next_token_without_checking_comments() + self.next_spanned_token_without_checking_comments() } else { Ok(spanned) } @@ -261,7 +277,7 @@ impl<'a> Lexer<'a> { Ok(prev_token.into_single_span(start)) } _ => Err(LexerErrorKind::NotADoubleChar { - span: Span::single_char(self.position), + location: self.location(Span::single_char(self.position)), found: prev_token, }), } @@ -303,7 +319,7 @@ impl<'a> Lexer<'a> { 'A'..='Z' | 'a'..='z' | '_' => Ok(self.eat_word(initial_char)?), '0'..='9' => self.eat_digit(initial_char), _ => Err(LexerErrorKind::UnexpectedCharacter { - span: Span::single_char(self.position), + location: self.location(Span::single_char(self.position)), found: initial_char.into(), expected: "an alpha numeric character".to_owned(), }), @@ -322,7 +338,7 @@ impl<'a> Lexer<'a> { if !self.peek_char_is('[') { return Err(LexerErrorKind::UnexpectedCharacter { - span: Span::single_char(self.position), + location: self.location(Span::single_char(self.position)), found: self.next_char(), expected: "[".to_owned(), }); @@ -399,7 +415,7 @@ impl<'a> Lexer<'a> { let consecutive_underscores = integer_str.contains("__"); if invalid_underscore_location || consecutive_underscores { return Err(LexerErrorKind::InvalidIntegerLiteral { - span: Span::inclusive(start, end), + location: self.location(Span::inclusive(start, end)), found: integer_str, }); } @@ -416,7 +432,7 @@ impl<'a> Lexer<'a> { Ok(bigint) => { if bigint > self.max_integer { return Err(LexerErrorKind::IntegerLiteralTooLarge { - span: Span::inclusive(start, end), + location: self.location(Span::inclusive(start, end)), limit: self.max_integer.to_string(), }); } @@ -425,9 +441,9 @@ impl<'a> Lexer<'a> { } Err(_) => { return Err(LexerErrorKind::InvalidIntegerLiteral { - span: Span::inclusive(start, end), + location: self.location(Span::inclusive(start, end)), found: integer_str, - }) + }); } }; @@ -452,11 +468,16 @@ impl<'a> Lexer<'a> { Some('\\') => '\\', Some(escaped) => { let span = Span::inclusive(start, self.position); - return Err(LexerErrorKind::InvalidEscape { escaped, span }); + return Err(LexerErrorKind::InvalidEscape { + escaped, + location: self.location(span), + }); } None => { let span = Span::inclusive(start, self.position); - return Err(LexerErrorKind::UnterminatedStringLiteral { span }); + return Err(LexerErrorKind::UnterminatedStringLiteral { + location: self.location(span), + }); } }, other => other, @@ -465,7 +486,9 @@ impl<'a> Lexer<'a> { string.push(char); } else { let span = Span::inclusive(start, self.position); - return Err(LexerErrorKind::UnterminatedStringLiteral { span }); + return Err(LexerErrorKind::UnterminatedStringLiteral { + location: self.location(span), + }); } } @@ -499,11 +522,16 @@ impl<'a> Lexer<'a> { Some('\\') => '\\', Some(escaped) => { let span = Span::inclusive(start, self.position); - return Err(LexerErrorKind::InvalidEscape { escaped, span }); + return Err(LexerErrorKind::InvalidEscape { + escaped, + location: self.location(span), + }); } None => { let span = Span::inclusive(start, self.position); - return Err(LexerErrorKind::UnterminatedStringLiteral { span }); + return Err(LexerErrorKind::UnterminatedStringLiteral { + location: self.location(span), + }); } }, '{' if self.peek_char_is('{') => { @@ -521,7 +549,10 @@ impl<'a> Lexer<'a> { self.skip_until_string_end(); let span = Span::inclusive(error_position, error_position); - return Err(LexerErrorKind::InvalidFormatString { found: '}', span }); + return Err(LexerErrorKind::InvalidFormatString { + found: '}', + location: self.location(span), + }); } '{' => { found_curly = true; @@ -545,7 +576,9 @@ impl<'a> Lexer<'a> { } } else { let span = Span::inclusive(start, self.position); - return Err(LexerErrorKind::UnterminatedStringLiteral { span }); + return Err(LexerErrorKind::UnterminatedStringLiteral { + location: self.location(span), + }); } } @@ -573,7 +606,9 @@ impl<'a> Lexer<'a> { self.skip_until_string_end(); let span = Span::inclusive(error_position, error_position); - return Err(LexerErrorKind::EmptyFormatStringInterpolation { span }); + return Err(LexerErrorKind::EmptyFormatStringInterpolation { + location: self.location(span), + }); } break; @@ -594,7 +629,10 @@ impl<'a> Lexer<'a> { } let span = Span::inclusive(error_position, error_position); - return Err(LexerErrorKind::InvalidFormatString { found: other, span }); + return Err(LexerErrorKind::InvalidFormatString { + found: other, + location: self.location(span), + }); } first_char = false; other @@ -606,8 +644,9 @@ impl<'a> Lexer<'a> { length += 1; // for the closing curly brace - let interpolation_span = Span::from(interpolation_start..self.position); - fragments.push(FmtStrFragment::Interpolation(string, interpolation_span)); + let span = Span::from(interpolation_start..self.position); + let location = Location::new(span, self.file_id); + fragments.push(FmtStrFragment::Interpolation(string, location)); } let token = Token::FmtStr(fragments, length); @@ -626,11 +665,7 @@ impl<'a> Lexer<'a> { } fn eat_format_string_or_alpha_numeric(&mut self) -> SpannedTokenResult { - if self.peek_char_is('"') { - self.eat_fmt_string() - } else { - self.eat_alpha_numeric('f') - } + if self.peek_char_is('"') { self.eat_fmt_string() } else { self.eat_alpha_numeric('f') } } fn eat_raw_string(&mut self) -> SpannedTokenResult { @@ -642,7 +677,7 @@ impl<'a> Lexer<'a> { // too many hashes (unlikely in practice) // also, Rust disallows 256+ hashes as well return Err(LexerErrorKind::UnexpectedCharacter { - span: Span::single_char(start + 255), + location: self.location(Span::single_char(start + 255)), found: Some('#'), expected: "\"".to_owned(), }); @@ -650,7 +685,7 @@ impl<'a> Lexer<'a> { if !self.peek_char_is('"') { return Err(LexerErrorKind::UnexpectedCharacter { - span: Span::single_char(self.position), + location: self.location(Span::single_char(self.position)), found: self.next_char(), expected: "\"".to_owned(), }); @@ -663,7 +698,7 @@ impl<'a> Lexer<'a> { str_literal.push_str(&chars[..]); if !self.peek_char_is('"') { return Err(LexerErrorKind::UnexpectedCharacter { - span: Span::single_char(self.position), + location: self.location(Span::single_char(self.position)), found: self.next_char(), expected: "\"".to_owned(), }); @@ -769,7 +804,7 @@ impl<'a> Lexer<'a> { if !comment.is_ascii() { let span = Span::from(start..self.position); - return Err(LexerErrorKind::NonAsciiComment { span }); + return Err(LexerErrorKind::NonAsciiComment { location: self.location(span) }); } Ok(Token::LineComment(comment, doc_style).into_span(start, self.position)) @@ -814,13 +849,13 @@ impl<'a> Lexer<'a> { if depth == 0 { if !content.is_ascii() { let span = Span::from(start..self.position); - return Err(LexerErrorKind::NonAsciiComment { span }); + return Err(LexerErrorKind::NonAsciiComment { location: self.location(span) }); } Ok(Token::BlockComment(content, doc_style).into_span(start, self.position)) } else { let span = Span::inclusive(start, self.position); - Err(LexerErrorKind::UnterminatedBlockComment { span }) + Err(LexerErrorKind::UnterminatedBlockComment { location: self.location(span) }) } } @@ -834,17 +869,17 @@ impl<'a> Lexer<'a> { let whitespace = self.eat_while(initial_char.into(), Self::is_code_whitespace); SpannedToken::new(Token::Whitespace(whitespace), Span::inclusive(start, self.position)) } + + fn location(&self, span: Span) -> Location { + Location::new(span, self.file_id) + } } -impl<'a> Iterator for Lexer<'a> { - type Item = SpannedTokenResult; +impl Iterator for Lexer<'_> { + type Item = LocatedTokenResult; fn next(&mut self) -> Option { - if self.done { - None - } else { - Some(self.next_token()) - } + if self.done { None } else { Some(self.next_token()) } } } @@ -894,7 +929,7 @@ mod tests { Token::EOF, ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); @@ -905,7 +940,7 @@ mod tests { #[test] fn invalid_attribute() { let input = "#"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next().unwrap(); assert!(token.is_err()); @@ -914,7 +949,7 @@ mod tests { #[test] fn test_attribute_start() { let input = r#"#[something]"#; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next_token().unwrap(); assert_eq!(token.token(), &Token::AttributeStart { is_inner: false, is_tag: false }); @@ -923,7 +958,7 @@ mod tests { #[test] fn test_attribute_start_with_tag() { let input = r#"#['something]"#; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next_token().unwrap(); assert_eq!(token.token(), &Token::AttributeStart { is_inner: false, is_tag: true }); @@ -932,7 +967,7 @@ mod tests { #[test] fn test_inner_attribute_start() { let input = r#"#![something]"#; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next_token().unwrap(); assert_eq!(token.token(), &Token::AttributeStart { is_inner: true, is_tag: false }); @@ -941,7 +976,7 @@ mod tests { #[test] fn test_inner_attribute_start_with_tag() { let input = r#"#!['something]"#; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next_token().unwrap(); assert_eq!(token.token(), &Token::AttributeStart { is_inner: true, is_tag: true }); @@ -960,7 +995,7 @@ mod tests { Token::Int(5_i128.into()), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); assert_eq!(got, token); @@ -972,7 +1007,7 @@ mod tests { let modulus = FieldElement::modulus(); let input = modulus.to_string(); - let mut lexer = Lexer::new(&input); + let mut lexer = Lexer::new_with_dummy_file(&input); let token = lexer.next_token(); assert!( matches!(token, Err(LexerErrorKind::IntegerLiteralTooLarge { .. })), @@ -997,7 +1032,7 @@ mod tests { Token::Assign, ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); assert_eq!(got, token); @@ -1008,7 +1043,7 @@ mod tests { fn unterminated_block_comment() { let input = "/*/"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next().unwrap(); assert!(token.is_err()); @@ -1027,7 +1062,7 @@ mod tests { Token::Int(FieldElement::from(5_i128)), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let first_lexer_output = lexer.next_token().unwrap(); assert_eq!(first_lexer_output, token); @@ -1049,7 +1084,7 @@ mod tests { Token::Int(FieldElement::from(5_i128)), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let first_lexer_output = lexer.next_token().unwrap(); assert_eq!(first_lexer_output, token); @@ -1075,7 +1110,7 @@ mod tests { Token::BlockComment(" inner doc block ".into(), DocStyle::Inner.into()), ]; - let mut lexer = Lexer::new(input).skip_comments(false); + let mut lexer = Lexer::new_with_dummy_file(input).skip_comments(false); for token in expected { let first_lexer_output = lexer.next_token().unwrap(); assert_eq!(token, first_lexer_output); @@ -1097,7 +1132,7 @@ mod tests { Token::Int(FieldElement::from(5_i128)), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let first_lexer_output = lexer.next_token().unwrap(); assert_eq!(first_lexer_output, token); @@ -1113,7 +1148,7 @@ mod tests { Token::Assign, Token::Str("hello".to_string()), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); @@ -1131,7 +1166,7 @@ mod tests { Token::Assign, Token::Str("hello\n\t".to_string()), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); @@ -1142,7 +1177,7 @@ mod tests { #[test] fn test_eat_string_literal_missing_double_quote() { let input = "\"hello"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); assert!(matches!( lexer.next_token(), Err(LexerErrorKind::UnterminatedStringLiteral { .. }) @@ -1159,7 +1194,7 @@ mod tests { Token::Assign, Token::FmtStr(vec![FmtStrFragment::String("hello".to_string())], 5), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); @@ -1177,7 +1212,7 @@ mod tests { Token::Assign, Token::FmtStr(vec![FmtStrFragment::String("hello\n\t{x}".to_string())], 12), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); @@ -1188,6 +1223,7 @@ mod tests { #[test] fn test_eat_fmt_string_literal_with_interpolations() { let input = "let _word = f\"hello {world} and {_another} {vAr_123}\""; + let file = FileId::dummy(); let expected = vec![ Token::Keyword(Keyword::Let), @@ -1196,16 +1232,25 @@ mod tests { Token::FmtStr( vec![ FmtStrFragment::String("hello ".to_string()), - FmtStrFragment::Interpolation("world".to_string(), Span::from(21..26)), + FmtStrFragment::Interpolation( + "world".to_string(), + Location::new(Span::from(21..26), file), + ), FmtStrFragment::String(" and ".to_string()), - FmtStrFragment::Interpolation("_another".to_string(), Span::from(33..41)), + FmtStrFragment::Interpolation( + "_another".to_string(), + Location::new(Span::from(33..41), file), + ), FmtStrFragment::String(" ".to_string()), - FmtStrFragment::Interpolation("vAr_123".to_string(), Span::from(44..51)), + FmtStrFragment::Interpolation( + "vAr_123".to_string(), + Location::new(Span::from(44..51), file), + ), ], 38, ), ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap().into_token(); @@ -1216,7 +1261,7 @@ mod tests { #[test] fn test_eat_fmt_string_literal_missing_double_quote() { let input = "f\"hello"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); assert!(matches!( lexer.next_token(), Err(LexerErrorKind::UnterminatedStringLiteral { .. }) @@ -1226,7 +1271,7 @@ mod tests { #[test] fn test_eat_fmt_string_literal_invalid_char_in_interpolation() { let input = "f\"hello {foo.bar}\" true"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); assert!(matches!(lexer.next_token(), Err(LexerErrorKind::InvalidFormatString { .. }))); // Make sure the lexer went past the ending double quote for better recovery @@ -1237,7 +1282,7 @@ mod tests { #[test] fn test_eat_fmt_string_literal_double_quote_inside_interpolation() { let input = "f\"hello {world\" true"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); assert!(matches!(lexer.next_token(), Err(LexerErrorKind::InvalidFormatString { .. }))); // Make sure the lexer stopped parsing the string literal when it found \" inside the interpolation @@ -1248,7 +1293,7 @@ mod tests { #[test] fn test_eat_fmt_string_literal_unmatched_closing_curly() { let input = "f\"hello }\" true"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); assert!(matches!(lexer.next_token(), Err(LexerErrorKind::InvalidFormatString { .. }))); // Make sure the lexer went past the ending double quote for better recovery @@ -1259,7 +1304,7 @@ mod tests { #[test] fn test_eat_fmt_string_literal_empty_interpolation() { let input = "f\"{}\" true"; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); assert!(matches!( lexer.next_token(), Err(LexerErrorKind::EmptyFormatStringInterpolation { .. }) @@ -1281,7 +1326,7 @@ mod tests { ]; for (input, expected_token) in test_cases { - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let got = lexer.next_token().unwrap(); assert_eq!(got.token(), &expected_token); } @@ -1292,7 +1337,7 @@ mod tests { let test_cases: Vec<&str> = vec!["0x05_", "5_", "5__5", "0x5__5"]; for input in test_cases { - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); let token = lexer.next_token(); assert!( matches!(token, Err(LexerErrorKind::InvalidIntegerLiteral { .. })), @@ -1332,12 +1377,12 @@ mod tests { let int_token = Token::Int(5_i128.into()).into_single_span(int_position); let expected = vec![let_token, ident_token, assign_token, int_token]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for spanned_token in expected.into_iter() { let got = lexer.next_token().unwrap(); - assert_eq!(got.to_span(), spanned_token.to_span()); - assert_eq!(got, spanned_token); + assert_eq!(got.span(), spanned_token.span()); + assert_eq!(got.into_spanned_token(), spanned_token); } } @@ -1403,7 +1448,7 @@ mod tests { Token::Semicolon, Token::EOF, ]; - let mut lexer = Lexer::new(input); + let mut lexer = Lexer::new_with_dummy_file(input); for token in expected.into_iter() { let got = lexer.next_token().unwrap(); @@ -1481,7 +1526,7 @@ mod tests { for (token_discriminator_opt, blns_program_strs) in statements { for blns_program_str in blns_program_strs { let mut expected_token_found = false; - let mut lexer = Lexer::new(&blns_program_str); + let mut lexer = Lexer::new_with_dummy_file(&blns_program_str); let mut result_tokens = Vec::new(); loop { match lexer.next_token() { @@ -1542,7 +1587,8 @@ mod tests { ]; for (source, expected_stream_length) in cases { - let mut tokens = vecmap(Lexer::new(source), |result| result.unwrap().into_token()); + let mut tokens = + vecmap(Lexer::new_with_dummy_file(source), |result| result.unwrap().into_token()); // All examples should be a single TokenStream token followed by an EOF token. assert_eq!(tokens.len(), 2, "Unexpected token count: {tokens:?}"); @@ -1550,7 +1596,9 @@ mod tests { tokens.pop(); match tokens.pop().unwrap() { Token::Quote(stream) => assert_eq!(stream.0.len(), expected_stream_length), - other => panic!("test_quote test failure! Expected a single TokenStream token, got {other} for input `{source}`") + other => panic!( + "test_quote test failure! Expected a single TokenStream token, got {other} for input `{source}`" + ), } } } @@ -1562,7 +1610,7 @@ mod tests { for source in cases { // `quote` is not itself a keyword so if the token stream fails to // parse we don't expect any valid tokens from the quote construct - for token in Lexer::new(source) { + for token in Lexer::new_with_dummy_file(source) { assert!(token.is_err(), "Expected Err, found {token:?}"); } } @@ -1573,7 +1621,7 @@ mod tests { let cases = vec!["// 🙂", "// schön", "/* in the middle 🙂 of a comment */"]; for source in cases { - let mut lexer = Lexer::new(source); + let mut lexer = Lexer::new_with_dummy_file(source); assert!( lexer.any(|token| matches!(token, Err(LexerErrorKind::NonAsciiComment { .. }))), "Expected NonAsciiComment error" diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs index ef90ff4e5818..7367489f6251 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs @@ -1,5 +1,5 @@ use acvm::FieldElement; -use noirc_errors::{Position, Span, Spanned}; +use noirc_errors::{Located, Location, Position, Span, Spanned}; use std::fmt::{self, Display}; use crate::{ @@ -255,18 +255,18 @@ pub enum Token { pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { match token { - Token::Ident(ref s) => BorrowedToken::Ident(s), + Token::Ident(s) => BorrowedToken::Ident(s), Token::Int(n) => BorrowedToken::Int(*n), Token::Bool(b) => BorrowedToken::Bool(*b), - Token::Str(ref b) => BorrowedToken::Str(b), - Token::FmtStr(ref b, length) => BorrowedToken::FmtStr(b, *length), - Token::RawStr(ref b, hashes) => BorrowedToken::RawStr(b, *hashes), + Token::Str(b) => BorrowedToken::Str(b), + Token::FmtStr(b, length) => BorrowedToken::FmtStr(b, *length), + Token::RawStr(b, hashes) => BorrowedToken::RawStr(b, *hashes), Token::Keyword(k) => BorrowedToken::Keyword(*k), Token::AttributeStart { is_inner, is_tag } => { BorrowedToken::AttributeStart { is_inner: *is_inner, is_tag: *is_tag } } - Token::LineComment(ref s, _style) => BorrowedToken::LineComment(s, *_style), - Token::BlockComment(ref s, _style) => BorrowedToken::BlockComment(s, *_style), + Token::LineComment(s, _style) => BorrowedToken::LineComment(s, *_style), + Token::BlockComment(s, _style) => BorrowedToken::BlockComment(s, *_style), Token::Quote(stream) => BorrowedToken::Quote(stream), Token::QuotedType(id) => BorrowedToken::QuotedType(*id), Token::InternedExpr(id) => BorrowedToken::InternedExpression(*id), @@ -274,7 +274,7 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { Token::InternedLValue(id) => BorrowedToken::InternedLValue(*id), Token::InternedUnresolvedTypeData(id) => BorrowedToken::InternedUnresolvedTypeData(*id), Token::InternedPattern(id) => BorrowedToken::InternedPattern(*id), - Token::IntType(ref i) => BorrowedToken::IntType(i.clone()), + Token::IntType(i) => BorrowedToken::IntType(i.clone()), Token::Less => BorrowedToken::Less, Token::LessEqual => BorrowedToken::LessEqual, Token::Greater => BorrowedToken::Greater, @@ -312,7 +312,7 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { Token::DollarSign => BorrowedToken::DollarSign, Token::EOF => BorrowedToken::EOF, Token::Invalid(c) => BorrowedToken::Invalid(*c), - Token::Whitespace(ref s) => BorrowedToken::Whitespace(s), + Token::Whitespace(s) => BorrowedToken::Whitespace(s), Token::UnquoteMarker(id) => BorrowedToken::UnquoteMarker(*id), } } @@ -320,7 +320,7 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { #[derive(Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] pub enum FmtStrFragment { String(String), - Interpolation(String, Span), + Interpolation(String, Location), } impl Display for FmtStrFragment { @@ -339,7 +339,7 @@ impl Display for FmtStrFragment { .replace('\"', "\\\""); write!(f, "{}", string) } - FmtStrFragment::Interpolation(string, _span) => { + FmtStrFragment::Interpolation(string, _) => { write!(f, "{{{}}}", string) } } @@ -352,6 +352,63 @@ pub enum DocStyle { Inner, } +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct LocatedToken(Located); + +impl PartialEq for Token { + fn eq(&self, other: &LocatedToken) -> bool { + self == &other.0.contents + } +} +impl PartialEq for LocatedToken { + fn eq(&self, other: &Token) -> bool { + &self.0.contents == other + } +} + +impl From for Token { + fn from(spt: LocatedToken) -> Self { + spt.0.contents + } +} + +impl<'a> From<&'a LocatedToken> for &'a Token { + fn from(spt: &'a LocatedToken) -> Self { + &spt.0.contents + } +} + +impl LocatedToken { + pub fn new(token: Token, location: Location) -> LocatedToken { + LocatedToken(Located::from(location, token)) + } + pub fn location(&self) -> Location { + self.0.location() + } + pub fn span(&self) -> Span { + self.0.span() + } + pub fn token(&self) -> &Token { + &self.0.contents + } + pub fn into_token(self) -> Token { + self.0.contents + } + pub fn kind(&self) -> TokenKind { + self.token().kind() + } + pub fn into_spanned_token(self) -> SpannedToken { + let span = self.span(); + SpannedToken::new(self.into_token(), span) + } +} + +impl std::fmt::Display for LocatedToken { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.token().fmt(f) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct SpannedToken(Spanned); @@ -382,7 +439,7 @@ impl SpannedToken { pub fn new(token: Token, span: Span) -> SpannedToken { SpannedToken(Spanned::from(span, token)) } - pub fn to_span(&self) -> Span { + pub fn span(&self) -> Span { self.0.span() } pub fn token(&self) -> &Token { @@ -521,7 +578,7 @@ pub enum TokenKind { impl fmt::Display for TokenKind { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - TokenKind::Token(ref tok) => write!(f, "{tok}"), + TokenKind::Token(tok) => write!(f, "{tok}"), TokenKind::Ident => write!(f, "identifier"), TokenKind::Literal => write!(f, "literal"), TokenKind::Keyword => write!(f, "keyword"), @@ -743,11 +800,11 @@ impl Attributes { } pub fn is_foldable(&self) -> bool { - self.function().map_or(false, |func_attribute| func_attribute.is_foldable()) + self.function().is_some_and(|func_attribute| func_attribute.is_foldable()) } pub fn is_no_predicates(&self) -> bool { - self.function().map_or(false, |func_attribute| func_attribute.is_no_predicates()) + self.function().is_some_and(|func_attribute| func_attribute.is_no_predicates()) } pub fn has_varargs(&self) -> bool { @@ -869,9 +926,9 @@ impl fmt::Display for FunctionAttribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { FunctionAttribute::Test(scope) => write!(f, "#[test{scope}]"), - FunctionAttribute::Foreign(ref k) => write!(f, "#[foreign({k})]"), - FunctionAttribute::Builtin(ref k) => write!(f, "#[builtin({k})]"), - FunctionAttribute::Oracle(ref k) => write!(f, "#[oracle({k})]"), + FunctionAttribute::Foreign(k) => write!(f, "#[foreign({k})]"), + FunctionAttribute::Builtin(k) => write!(f, "#[builtin({k})]"), + FunctionAttribute::Oracle(k) => write!(f, "#[oracle({k})]"), FunctionAttribute::Fold => write!(f, "#[fold]"), FunctionAttribute::NoPredicates => write!(f, "#[no_predicates]"), FunctionAttribute::InlineAlways => write!(f, "#[inline_always]"), @@ -944,18 +1001,18 @@ impl SecondaryAttribute { pub(crate) fn contents(&self) -> String { match self { SecondaryAttribute::Deprecated(None) => "deprecated".to_string(), - SecondaryAttribute::Deprecated(Some(ref note)) => { + SecondaryAttribute::Deprecated(Some(note)) => { format!("deprecated({note:?})") } - SecondaryAttribute::Tag(ref attribute) => format!("'{}", attribute.contents), - SecondaryAttribute::Meta(ref meta) => meta.to_string(), + SecondaryAttribute::Tag(attribute) => format!("'{}", attribute.contents), + SecondaryAttribute::Meta(meta) => meta.to_string(), SecondaryAttribute::ContractLibraryMethod => "contract_library_method".to_string(), SecondaryAttribute::Export => "export".to_string(), - SecondaryAttribute::Field(ref k) => format!("field({k})"), - SecondaryAttribute::Abi(ref k) => format!("abi({k})"), + SecondaryAttribute::Field(k) => format!("field({k})"), + SecondaryAttribute::Abi(k) => format!("abi({k})"), SecondaryAttribute::Varargs => "varargs".to_string(), SecondaryAttribute::UseCallersScope => "use_callers_scope".to_string(), - SecondaryAttribute::Allow(ref k) => format!("allow({k})"), + SecondaryAttribute::Allow(k) => format!("allow({k})"), } } } @@ -970,7 +1027,7 @@ impl fmt::Display for SecondaryAttribute { pub struct MetaAttribute { pub name: Path, pub arguments: Vec, - pub span: Span, + pub location: Location, } impl Display for MetaAttribute { @@ -996,13 +1053,9 @@ pub struct CustomAttribute { impl CustomAttribute { fn name(&self) -> Option { - let mut lexer = Lexer::new(&self.contents); + let mut lexer = Lexer::new_with_dummy_file(&self.contents); let token = lexer.next()?.ok()?; - if let Token::Ident(ident) = token.into_token() { - Some(ident) - } else { - None - } + if let Token::Ident(ident) = token.into_token() { Some(ident) } else { None } } } @@ -1202,7 +1255,7 @@ impl Keyword { } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Tokens(pub Vec); +pub struct Tokens(pub Vec); #[cfg(test)] mod keywords { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lib.rs b/noir/noir-repo/compiler/noirc_frontend/src/lib.rs index 9d98b125e324..88a32b2717cc 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lib.rs @@ -9,6 +9,8 @@ #![warn(unused_crate_dependencies, unused_extern_crates)] #![warn(unreachable_pub)] #![warn(clippy::semicolon_if_nothing_returned)] +// Temporary allows. +#![allow(clippy::mutable_key_type, clippy::result_large_err)] pub mod ast; pub mod debug; @@ -20,6 +22,7 @@ pub mod monomorphization; pub mod node_interner; pub mod parser; pub mod resolve_locations; +pub mod signed_field; pub mod usage_tracker; pub mod hir; @@ -29,7 +32,7 @@ pub mod hir_def; pub use lexer::token; // Parser API -pub use parser::{parse_program, ParsedModule}; +pub use parser::{ParsedModule, parse_program, parse_program_with_dummy_file}; // Type API pub use hir_def::types::*; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs index e68a5d8c5d88..6c3925f3e41c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs @@ -1,5 +1,5 @@ use fm::FileId; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use rangemap::RangeMap; use rustc_hash::FxHashMap as HashMap; @@ -36,21 +36,19 @@ impl LocationIndices { pub struct ReferencesTracker<'a> { interner: &'a mut NodeInterner, - file_id: FileId, } impl<'a> ReferencesTracker<'a> { - pub fn new(interner: &'a mut NodeInterner, file_id: FileId) -> Self { - Self { interner, file_id } + pub fn new(interner: &'a mut NodeInterner) -> Self { + Self { interner } } pub(crate) fn add_reference( &mut self, module_def_id: ModuleDefId, - span: Span, + location: Location, is_self_type: bool, ) { - let location = Location::new(span, self.file_id); self.interner.add_module_def_id_reference(module_def_id, location, is_self_type); } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/ast.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/ast.rs index 6cd5073aadb9..da86a466fdbc 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -1,15 +1,15 @@ use std::{collections::BTreeMap, fmt::Display}; -use acvm::FieldElement; use iter_extended::vecmap; use noirc_errors::{ - debug_info::{DebugFunctions, DebugTypes, DebugVariables}, Location, + debug_info::{DebugFunctions, DebugTypes, DebugVariables}, }; use crate::{ ast::{BinaryOpKind, IntegerBitSize, Signedness, Visibility}, hir_def::expr::Constructor, + signed_field::SignedField, token::{Attributes, FunctionAttribute}, }; use crate::{hir_def::function::FunctionSignature, token::FmtStrFragment}; @@ -123,7 +123,7 @@ pub struct While { pub enum Literal { Array(ArrayLiteral), Slice(ArrayLiteral), - Integer(FieldElement, /*sign*/ bool, Type, Location), // false for positive integer and true for negative + Integer(SignedField, Type, Location), Bool(bool), Unit, Str(String), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/debug.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/debug.rs index 3b399c757063..680e78828115 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/debug.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/debug.rs @@ -1,12 +1,13 @@ use acvm::acir::AcirField; use iter_extended::vecmap; -use noirc_errors::debug_info::DebugVarId; use noirc_errors::Location; +use noirc_errors::debug_info::DebugVarId; use noirc_printable_type::PrintableType; use crate::debug::{SourceFieldId, SourceVarId}; use crate::hir_def::expr::*; use crate::node_interner::ExprId; +use crate::signed_field::SignedField; use super::ast::{Expression, Ident}; use super::{MonomorphizationError, Monomorphizer}; @@ -28,7 +29,7 @@ impl From for SourceFieldId { } } -impl<'interner> Monomorphizer<'interner> { +impl Monomorphizer<'_> { /// Patch instrumentation calls inserted for debugging. This will record /// tracked variables and their types, and assign them an ID to use at /// runtime. This ID is different from the source ID assigned at @@ -68,14 +69,14 @@ impl<'interner> Monomorphizer<'interner> { ) -> Result<(), MonomorphizationError> { let hir_arguments = vecmap(&call.arguments, |id| self.interner.expression(id)); let var_id_arg = hir_arguments.get(DEBUG_VAR_ID_ARG_SLOT); - let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id, _))) = var_id_arg else { + let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id))) = var_id_arg else { unreachable!("Missing source_var_id in __debug_var_assign call"); }; // instantiate tracked variable for the value type and associate it with // the ID used by the injected instrumentation code let var_type = self.interner.id_type(call.arguments[DEBUG_VALUE_ARG_SLOT]); - let source_var_id = source_var_id.to_u128().into(); + let source_var_id = source_var_id.field.to_u128().into(); // then update the ID used for tracking at runtime let var_id = self.debug_type_tracker.insert_var(source_var_id, &var_type); let interned_var_id = self.intern_var_id(var_id, &call.location); @@ -93,11 +94,11 @@ impl<'interner> Monomorphizer<'interner> { ) -> Result<(), MonomorphizationError> { let hir_arguments = vecmap(&call.arguments, |id| self.interner.expression(id)); let var_id_arg = hir_arguments.get(DEBUG_VAR_ID_ARG_SLOT); - let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id, _))) = var_id_arg else { + let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id))) = var_id_arg else { unreachable!("Missing source_var_id in __debug_var_drop call"); }; // update variable ID for tracked drops (ie. when the var goes out of scope) - let source_var_id = source_var_id.to_u128().into(); + let source_var_id = source_var_id.field.to_u128().into(); let var_id = self .debug_type_tracker .get_var_id(source_var_id) @@ -121,11 +122,11 @@ impl<'interner> Monomorphizer<'interner> { ) -> Result<(), MonomorphizationError> { let hir_arguments = vecmap(&call.arguments, |id| self.interner.expression(id)); let var_id_arg = hir_arguments.get(DEBUG_VAR_ID_ARG_SLOT); - let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id, _))) = var_id_arg else { + let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id))) = var_id_arg else { unreachable!("Missing source_var_id in __debug_member_assign call"); }; // update variable member assignments - let source_var_id = source_var_id.to_u128().into(); + let source_var_id = source_var_id.field.to_u128().into(); let var_type = self .debug_type_tracker @@ -134,11 +135,11 @@ impl<'interner> Monomorphizer<'interner> { .clone(); let mut cursor_type = &var_type; for i in 0..arity { - if let Some(HirExpression::Literal(HirLiteral::Integer(fe_i, i_neg))) = + if let Some(HirExpression::Literal(HirLiteral::Integer(fe_i))) = hir_arguments.get(DEBUG_MEMBER_FIELD_INDEX_ARG_SLOT + i) { - let index = fe_i.to_i128().unsigned_abs(); - if *i_neg { + let index = fe_i.field.to_i128().unsigned_abs(); + if fe_i.is_negative { // We use negative indices at instrumentation time to indicate // and reference member accesses by name which cannot be // resolved until we have a type. This strategy is also used @@ -152,15 +153,10 @@ impl<'interner> Monomorphizer<'interner> { }); cursor_type = element_type_at_index(cursor_type, field_index); - let index_id = self.interner.push_expr(HirExpression::Literal( - HirLiteral::Integer(field_index.into(), false), - )); + let integer = HirLiteral::Integer(SignedField::positive(field_index)); + let index_id = self.interner.push_expr(HirExpression::Literal(integer)); self.interner.push_expr_type(index_id, crate::Type::FieldElement); - self.interner.push_expr_location( - index_id, - call.location.span, - call.location.file, - ); + self.interner.push_expr_location(index_id, call.location); arguments[DEBUG_MEMBER_FIELD_INDEX_ARG_SLOT + i] = self.expr(index_id)?; } else { // array/string element using constant index @@ -182,10 +178,11 @@ impl<'interner> Monomorphizer<'interner> { } fn intern_var_id(&mut self, var_id: DebugVarId, location: &Location) -> ExprId { - let var_id_literal = HirLiteral::Integer((var_id.0 as u128).into(), false); + let value = SignedField::positive(var_id.0); + let var_id_literal = HirLiteral::Integer(value); let expr_id = self.interner.push_expr(HirExpression::Literal(var_id_literal)); self.interner.push_expr_type(expr_id, crate::Type::FieldElement); - self.interner.push_expr_location(expr_id, location.span, location.file); + self.interner.push_expr_location(expr_id, *location); expr_id } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs index c137a6fc90ab..93a12a46591b 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs @@ -1,8 +1,8 @@ -use noirc_errors::{CustomDiagnostic, FileDiagnostic, Location}; +use noirc_errors::{CustomDiagnostic, Location}; use crate::{ - hir::{comptime::InterpreterError, type_check::TypeCheckError}, Type, + hir::{comptime::InterpreterError, type_check::TypeCheckError}, }; #[derive(Debug)] @@ -29,23 +29,14 @@ impl MonomorphizationError { | MonomorphizationError::CheckedTransmuteFailed { location, .. } | MonomorphizationError::CheckedCastFailed { location, .. } | MonomorphizationError::NoDefaultType { location, .. } => *location, - MonomorphizationError::InterpreterError(error) => error.get_location(), + MonomorphizationError::InterpreterError(error) => error.location(), } } } -impl From for FileDiagnostic { - fn from(error: MonomorphizationError) -> FileDiagnostic { - let location = error.location(); - let call_stack = vec![location]; - let diagnostic = error.into_diagnostic(); - diagnostic.with_call_stack(call_stack).in_file(location.file) - } -} - -impl MonomorphizationError { - fn into_diagnostic(self) -> CustomDiagnostic { - let message = match &self { +impl From for CustomDiagnostic { + fn from(error: MonomorphizationError) -> CustomDiagnostic { + let message = match &error { MonomorphizationError::UnknownArrayLength { length, err, .. } => { format!("Could not determine array length `{length}`, encountered error: `{err}`") } @@ -61,7 +52,7 @@ impl MonomorphizationError { MonomorphizationError::NoDefaultType { location } => { let message = "Type annotation needed".into(); let secondary = "Could not determine type of generic argument".into(); - return CustomDiagnostic::simple_error(message, secondary, location.span); + return CustomDiagnostic::simple_error(message, secondary, *location); } MonomorphizationError::InterpreterError(error) => return error.into(), MonomorphizationError::InternalError { message, .. } => message.to_string(), @@ -69,16 +60,16 @@ impl MonomorphizationError { let message = format!("Comptime function {name} used in runtime code"); let secondary = "Comptime functions must be in a comptime block to be called".into(); - return CustomDiagnostic::simple_error(message, secondary, location.span); + return CustomDiagnostic::simple_error(message, secondary, *location); } MonomorphizationError::ComptimeTypeInRuntimeCode { typ, location } => { let message = format!("Comptime-only type `{typ}` used in runtime code"); let secondary = "Comptime type used here".into(); - return CustomDiagnostic::simple_error(message, secondary, location.span); + return CustomDiagnostic::simple_error(message, secondary, *location); } }; - let location = self.location(); - CustomDiagnostic::simple_error(message, String::new(), location.span) + let location = error.location(); + CustomDiagnostic::simple_error(message, String::new(), location) } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs index d30229ce97d5..4b0c4b683454 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -12,8 +12,10 @@ use crate::ast::{FunctionKind, IntegerBitSize, Signedness, UnaryOp, Visibility}; use crate::hir::comptime::InterpreterError; use crate::hir::type_check::{NoMatchingImplFoundError, TypeCheckError}; use crate::node_interner::{ExprId, GlobalValue, ImplSearchErrorKind}; +use crate::signed_field::SignedField; use crate::token::FmtStrFragment; use crate::{ + Kind, Type, TypeBinding, TypeBindings, debug::DebugInstrumenter, hir_def::{ expr::*, @@ -22,9 +24,8 @@ use crate::{ types, }, node_interner::{self, DefinitionKind, NodeInterner, StmtId, TraitImplKind, TraitMethodId}, - Kind, Type, TypeBinding, TypeBindings, }; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{FieldElement, acir::AcirField}; use ast::{GlobalId, While}; use fxhash::FxHashMap as HashMap; use iter_extended::{btree_map, try_vecmap, vecmap}; @@ -481,10 +482,10 @@ impl<'interner> Monomorphizer<'interner> { )) } HirExpression::Literal(HirLiteral::Bool(value)) => Literal(Bool(value)), - HirExpression::Literal(HirLiteral::Integer(value, sign)) => { + HirExpression::Literal(HirLiteral::Integer(value)) => { let location = self.interner.id_location(expr); let typ = Self::convert_type(&self.interner.id_type(expr), location)?; - Literal(Integer(value, sign, typ, location)) + Literal(Integer(value, typ, location)) } HirExpression::Literal(HirLiteral::Array(array)) => match array { HirArrayLiteral::Standard(array) => self.standard_array(expr, array, false)?, @@ -602,7 +603,9 @@ impl<'interner> Monomorphizer<'interner> { HirExpression::Lambda(lambda) => self.lambda(lambda, expr)?, HirExpression::MethodCall(hir_method_call) => { - unreachable!("Encountered HirExpression::MethodCall during monomorphization {hir_method_call:?}") + unreachable!( + "Encountered HirExpression::MethodCall during monomorphization {hir_method_call:?}" + ) } HirExpression::Error => unreachable!("Encountered Error node during monomorphization"), HirExpression::Quote(_) => unreachable!("quote expression remaining in runtime code"), @@ -646,7 +649,7 @@ impl<'interner> Monomorphizer<'interner> { let location = self.interner.expr_location(&array); let typ = Self::convert_type(&self.interner.id_type(array), location)?; - let length = length.evaluate_to_u32(location.span).map_err(|err| { + let length = length.evaluate_to_u32(location).map_err(|err| { let location = self.interner.expr_location(&array); MonomorphizationError::UnknownArrayLength { location, err, length } })?; @@ -819,7 +822,8 @@ impl<'interner> Monomorphizer<'interner> { })?; let tag_value = FieldElement::from(constructor.variant_index); - let tag = ast::Literal::Integer(tag_value, false, ast::Type::Field, location); + let tag_value = SignedField::positive(tag_value); + let tag = ast::Literal::Integer(tag_value, ast::Type::Field, location); fields.insert(0, ast::Expression::Literal(tag)); Ok(ast::Expression::Tuple(fields)) @@ -1027,7 +1031,7 @@ impl<'interner> Monomorphizer<'interner> { binding .evaluate_to_field_element( &Kind::Numeric(numeric_typ.clone()), - location.span, + location, ) .map_err(|err| MonomorphizationError::UnknownArrayLength { length: binding.clone(), @@ -1044,7 +1048,8 @@ impl<'interner> Monomorphizer<'interner> { } let typ = Self::convert_type(&typ, ident.location)?; - ast::Expression::Literal(ast::Literal::Integer(value, false, typ, location)) + let value = SignedField::positive(value); + ast::Expression::Literal(ast::Literal::Integer(value, typ, location)) } }; @@ -1078,7 +1083,9 @@ impl<'interner> Monomorphizer<'interner> { .map_err(MonomorphizationError::InterpreterError)?; (expr, is_closure) } else { - unreachable!("All global values should be resolved at compile time and before monomorphization"); + unreachable!( + "All global values should be resolved at compile time and before monomorphization" + ); }; let expr = self.expr(expr)?; @@ -1119,7 +1126,7 @@ impl<'interner> Monomorphizer<'interner> { HirType::Integer(sign, bits) => ast::Type::Integer(*sign, *bits), HirType::Bool => ast::Type::Bool, HirType::String(size) => { - let size = match size.evaluate_to_u32(location.span) { + let size = match size.evaluate_to_u32(location) { Ok(size) => size, // only default variable sizes to size 0 Err(TypeCheckError::NonConstantEvaluated { .. }) => 0, @@ -1135,7 +1142,7 @@ impl<'interner> Monomorphizer<'interner> { ast::Type::String(size) } HirType::FmtString(size, fields) => { - let size = match size.evaluate_to_u32(location.span) { + let size = match size.evaluate_to_u32(location) { Ok(size) => size, // only default variable sizes to size 0 Err(TypeCheckError::NonConstantEvaluated { .. }) => 0, @@ -1154,7 +1161,7 @@ impl<'interner> Monomorphizer<'interner> { HirType::Unit => ast::Type::Unit, HirType::Array(length, element) => { let element = Box::new(Self::convert_type(element.as_ref(), location)?); - let length = match length.evaluate_to_u32(location.span) { + let length = match length.evaluate_to_u32(location) { Ok(length) => length, Err(err) => { let length = length.as_ref().clone(); @@ -1175,7 +1182,7 @@ impl<'interner> Monomorphizer<'interner> { unreachable!("All TraitAsType should be replaced before calling convert_type"); } HirType::NamedGeneric(binding, _) => { - if let TypeBinding::Bound(ref binding) = &*binding.borrow() { + if let TypeBinding::Bound(binding) = &*binding.borrow() { return Self::convert_type(binding, location); } @@ -1191,12 +1198,12 @@ impl<'interner> Monomorphizer<'interner> { Self::convert_type(to, location)? } - HirType::TypeVariable(ref binding) => { + HirType::TypeVariable(binding) => { let type_var_kind = match &*binding.borrow() { - TypeBinding::Bound(ref binding) => { + TypeBinding::Bound(binding) => { return Self::convert_type(binding, location); } - TypeBinding::Unbound(_, ref type_var_kind) => type_var_kind.clone(), + TypeBinding::Unbound(_, type_var_kind) => type_var_kind.clone(), }; // Default any remaining unbound type variables. @@ -1321,18 +1328,18 @@ impl<'interner> Monomorphizer<'interner> { 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(ref binding) = &*binding.borrow() { + if let TypeBinding::Bound(binding) = &*binding.borrow() { return Self::check_type(binding, location); } Ok(()) } - HirType::TypeVariable(ref binding) => { + HirType::TypeVariable(binding) => { let type_var_kind = match &*binding.borrow() { TypeBinding::Bound(binding) => { return Self::check_type(binding, location); } - TypeBinding::Unbound(_, ref type_var_kind) => type_var_kind.clone(), + TypeBinding::Unbound(_, type_var_kind) => type_var_kind.clone(), }; // Default any remaining unbound type variables. @@ -1402,14 +1409,11 @@ impl<'interner> Monomorphizer<'interner> { location, }); } - let to_value = to.evaluate_to_field_element(&to.kind(), location.span); + let to_value = to.evaluate_to_field_element(&to.kind(), location); if to_value.is_ok() { let skip_simplifications = false; - let from_value = from.evaluate_to_field_element_helper( - &to.kind(), - location.span, - skip_simplifications, - ); + let from_value = + from.evaluate_to_field_element_helper(&to.kind(), location, skip_simplifications); if from_value.is_err() || from_value.unwrap() != to_value.clone().unwrap() { return Err(MonomorphizationError::CheckedCastFailed { actual: HirType::Constant(to_value.unwrap(), to.kind()), @@ -1628,12 +1632,11 @@ impl<'interner> Monomorphizer<'interner> { let location = self.interner.expr_location(expr_id); return Ok(match opcode.as_str() { "modulus_num_bits" => { - let bits = (FieldElement::max_num_bits() as u128).into(); + let bits = FieldElement::max_num_bits(); let typ = ast::Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour); - Some(ast::Expression::Literal(ast::Literal::Integer( - bits, false, typ, location, - ))) + let bits = SignedField::positive(bits); + Some(ast::Expression::Literal(ast::Literal::Integer(bits, typ, location))) } "zeroed" => { let location = self.interner.expr_location(expr_id); @@ -1697,12 +1700,8 @@ impl<'interner> Monomorphizer<'interner> { let int_type = Type::Integer(crate::ast::Signedness::Unsigned, arr_elem_bits); let bytes_as_expr = vecmap(bytes, |byte| { - Expression::Literal(Literal::Integer( - (byte as u128).into(), - false, - int_type.clone(), - location, - )) + let value = SignedField::positive(byte as u32); + Expression::Literal(Literal::Integer(value, int_type.clone(), location)) }); let typ = Type::Slice(Box::new(int_type)); @@ -1996,7 +1995,7 @@ impl<'interner> Monomorphizer<'interner> { ) -> Result { match match_expr { HirMatch::Success(id) => self.expr(id), - HirMatch::Failure => { + HirMatch::Failure { .. } => { let false_ = Box::new(ast::Expression::Literal(ast::Literal::Bool(false))); let msg = "match failure"; let msg_expr = ast::Expression::Literal(ast::Literal::Str(msg.to_string())); @@ -2062,7 +2061,8 @@ impl<'interner> Monomorphizer<'interner> { match typ { ast::Type::Field | ast::Type::Integer(..) => { let typ = typ.clone(); - ast::Expression::Literal(ast::Literal::Integer(0_u128.into(), false, typ, location)) + let zero = SignedField::positive(0u32); + ast::Expression::Literal(ast::Literal::Integer(zero, typ, location)) } ast::Type::Bool => ast::Expression::Literal(ast::Literal::Bool(false)), ast::Type::Unit => ast::Expression::Literal(ast::Literal::Unit), @@ -2080,7 +2080,9 @@ impl<'interner> Monomorphizer<'interner> { let zeroed_tuple = self.zeroed_value_of_type(fields, location); let fields_len = match &zeroed_tuple { ast::Expression::Tuple(fields) => fields.len() as u64, - _ => unreachable!("ICE: format string fields should be structured in a tuple, but got a {zeroed_tuple}"), + _ => unreachable!( + "ICE: format string fields should be structured in a tuple, but got a {zeroed_tuple}" + ), }; ast::Expression::Literal(ast::Literal::FmtStr( vec![FmtStrFragment::String("\0".repeat(*length as usize))], @@ -2217,8 +2219,8 @@ impl<'interner> Monomorphizer<'interner> { let operator = if matches!(operator.kind, Less | Greater) { Equal } else { NotEqual }; - let int_value = - ast::Literal::Integer(ordering_value, false, ast::Type::Field, location); + let ordering_value = SignedField::positive(ordering_value); + let int_value = ast::Literal::Integer(ordering_value, ast::Type::Field, location); let rhs = Box::new(ast::Expression::Literal(int_value)); let lhs = Box::new(ast::Expression::ExtractTupleField(Box::new(result), 0)); @@ -2375,10 +2377,9 @@ pub fn resolve_trait_method( } Err(ImplSearchErrorKind::Nested(constraints)) => { if let Some(error) = - NoMatchingImplFoundError::new(interner, constraints, location.span) + NoMatchingImplFoundError::new(interner, constraints, location) { - let file = location.file; - return Err(InterpreterError::NoMatchingImplFound { error, file }); + return Err(InterpreterError::NoMatchingImplFound { error }); } else { return Err(InterpreterError::NoImpl { location }); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/printer.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/printer.rs index df4340b4e0d3..bf783f50f000 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/printer.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/printer.rs @@ -105,7 +105,7 @@ impl AstPrinter { self.print_comma_separated(&array.contents, f)?; write!(f, "]") } - super::ast::Literal::Integer(x, _, _, _) => x.fmt(f), + super::ast::Literal::Integer(x, _, _) => x.fmt(f), super::ast::Literal::Bool(x) => x.fmt(f), super::ast::Literal::Str(s) => write!(f, "\"{s}\""), super::ast::Literal::FmtStr(fragments, _, _) => { 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 f995b20bc1a7..2430dca8fcad 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -6,12 +6,14 @@ use std::marker::Copy; use fm::FileId; use iter_extended::vecmap; use noirc_arena::{Arena, Index}; -use noirc_errors::{Location, Span, Spanned}; +use noirc_errors::Located; +use noirc_errors::{Location, Span}; use petgraph::algo::tarjan_scc; use petgraph::prelude::DiGraph; use petgraph::prelude::NodeIndex as PetGraphIndex; use rustc_hash::FxHashMap as HashMap; +use crate::QuotedType; use crate::ast::{ ExpressionKind, Ident, LValue, Pattern, StatementKind, UnaryOp, UnresolvedTypeData, }; @@ -25,8 +27,9 @@ use crate::hir::type_check::generics::TraitGenerics; use crate::hir_def::traits::NamedType; use crate::hir_def::traits::ResolvedTraitBound; use crate::locations::AutoImportEntry; -use crate::QuotedType; +use crate::GenericTypeVars; +use crate::Generics; use crate::ast::{BinaryOpKind, FunctionDefinition, ItemVisibility}; use crate::hir::resolution::errors::ResolverError; use crate::hir_def::expr::HirIdent; @@ -41,8 +44,6 @@ use crate::hir_def::{ }; use crate::locations::LocationIndices; use crate::token::{Attributes, SecondaryAttribute}; -use crate::GenericTypeVars; -use crate::Generics; use crate::{Shared, TypeAlias, TypeBindings, TypeVariable, TypeVariableId}; /// An arbitrary number to limit the recursion depth when searching for trait impls. @@ -358,16 +359,19 @@ pub enum ImplSearchErrorKind { /// as long as these specialized impls do not overlap. E.g. `impl Struct` and `impl Struct` #[derive(Default, Debug, Clone)] pub struct Methods { - pub direct: Vec, + pub direct: Vec, pub trait_impl_methods: Vec, } +#[derive(Debug, Clone)] +pub struct ImplMethod { + pub typ: Type, + pub method: FuncId, +} + #[derive(Debug, Clone)] pub struct TraitImplMethod { - // This type is only stored for primitive types to be able to - // select the correct static methods between multiple options keyed - // under TypeMethodKey::FieldOrInt - pub typ: Option, + pub typ: Type, pub method: FuncId, pub trait_id: TraitId, } @@ -721,9 +725,18 @@ impl NodeInterner { ExprId(self.nodes.insert(Node::Expression(expr))) } + /// Intern an expression with everything needed for it (location & Type) + /// instead of requiring they be pushed later. + pub fn push_expr_full(&mut self, expr: HirExpression, location: Location, typ: Type) -> ExprId { + let id = self.push_expr(expr); + self.push_expr_location(id, location); + self.push_expr_type(id, typ); + id + } + /// Stores the span for an interned expression. - pub fn push_expr_location(&mut self, expr_id: ExprId, span: Span, file: FileId) { - self.id_to_location.insert(expr_id.into(), Location::new(span, file)); + pub fn push_expr_location(&mut self, expr_id: ExprId, location: Location) { + self.id_to_location.insert(expr_id.into(), location); } /// Interns a HIR Function. @@ -752,7 +765,7 @@ impl NodeInterner { id: type_id, name: unresolved_trait.trait_def.name.clone(), crate_id: unresolved_trait.crate_id, - location: Location::new(unresolved_trait.trait_def.span, unresolved_trait.file_id), + location: unresolved_trait.trait_def.name.location(), generics, visibility: ItemVisibility::Private, self_type_typevar: TypeVariable::unbound(self.next_type_variable_id(), Kind::Normal), @@ -797,7 +810,7 @@ impl NodeInterner { self.type_aliases.push(Shared::new(TypeAlias::new( type_id, typ.type_alias_def.name.clone(), - Location::new(typ.type_alias_def.span, typ.file_id), + typ.type_alias_def.location, Type::Error, generics, ))); @@ -910,11 +923,11 @@ impl NodeInterner { comptime: bool, ) -> GlobalId { let statement = self.push_stmt(HirStatement::Error); - let span = name.span(); + let location = name.location(); let id = self .push_global(name, local_id, crate_id, statement, file, attributes, mutable, comptime); - self.push_stmt_location(statement, span, file); + self.push_stmt_location(statement, location); id } @@ -1089,8 +1102,8 @@ impl NodeInterner { pub fn function_ident(&self, func_id: &FuncId) -> crate::ast::Ident { let name = self.function_name(func_id).to_owned(); - let span = self.function_meta(func_id).name.location.span; - crate::ast::Ident(Spanned::from(span, name)) + let location = self.function_meta(func_id).name.location; + crate::ast::Ident(Located::from(location, name)) } pub fn function_name(&self, func_id: &FuncId) -> &str { @@ -1154,7 +1167,9 @@ impl NodeInterner { HirStatement::Let(let_stmt) => Some(let_stmt.clone()), HirStatement::Error => None, other => { - panic!("ice: all globals should correspond to a let statement in the interner: {other:?}") + panic!( + "ice: all globals should correspond to a let statement in the interner: {other:?}" + ) } }, _ => panic!("ice: all globals should correspond to a statement in the interner"), @@ -1222,8 +1237,8 @@ impl NodeInterner { self.id_location(stmt_id) } - pub fn push_stmt_location(&mut self, id: StmtId, span: Span, file: FileId) { - self.id_to_location.insert(id.into(), Location::new(span, file)); + pub fn push_stmt_location(&mut self, id: StmtId, location: Location) { + self.id_to_location.insert(id.into(), location); } pub fn get_type(&self, id: TypeId) -> Shared { @@ -1274,7 +1289,11 @@ impl NodeInterner { /// Returns the type of an item stored in the Interner or Error if it was not found. pub fn id_type(&self, index: impl Into) -> Type { - self.id_to_type.get(&index.into()).cloned().unwrap_or(Type::Error) + self.try_id_type(index).cloned().unwrap_or(Type::Error) + } + + pub fn try_id_type(&self, index: impl Into) -> Option<&Type> { + self.id_to_type.get(&index.into()) } /// Returns the type of the definition or `Type::Error` if it was not found. @@ -1401,7 +1420,9 @@ impl NodeInterner { }); if trait_id.is_none() && matches!(self_type, Type::DataType(..)) { - if let Some(existing) = self.lookup_direct_method(self_type, &method_name, true) + let check_self_param = false; + if let Some(existing) = + self.lookup_direct_method(self_type, &method_name, check_self_param) { return Some(existing); } @@ -1409,8 +1430,7 @@ impl NodeInterner { // Only remember the actual type if it's FieldOrInt, // so later we can disambiguate on calls like `u32::call`. - let typ = - if key == TypeMethodKey::FieldOrInt { Some(self_type.clone()) } else { None }; + let typ = self_type.clone(); self.methods .entry(key) .or_default() @@ -1512,7 +1532,7 @@ impl NodeInterner { trait_bound: ResolvedTraitBound { trait_id, trait_generics: TraitGenerics { ordered, named }, - span: Span::default(), + location: Location::dummy(), }, } }; @@ -1596,7 +1616,7 @@ impl NodeInterner { trait_bound: ResolvedTraitBound { trait_id, trait_generics, - span: Span::default(), + location: Location::dummy(), }, }; matching_impls.push((impl_kind.clone(), fresh_bindings, constraint)); @@ -1707,7 +1727,7 @@ impl NodeInterner { impl_id: TraitImplId, impl_generics: GenericTypeVars, trait_impl: Shared, - ) -> Result<(), (Span, FileId)> { + ) -> Result<(), Location> { self.trait_implementations.insert(impl_id, trait_impl.clone()); // Avoid adding error types to impls since they'll conflict with every other type. @@ -1759,7 +1779,7 @@ impl NodeInterner { ) { let existing_impl = self.get_trait_implementation(existing); let existing_impl = existing_impl.borrow(); - return Err((existing_impl.ident.span(), existing_impl.file)); + return Err(existing_impl.ident.location()); } for method in &trait_impl.borrow().methods { @@ -1777,18 +1797,20 @@ impl NodeInterner { } /// Looks up a method that's directly defined in the given type. + /// If `check_self_param` is `true`, only a method that has a `self` parameter with a type + /// that unifies with `typ` will be returned. pub fn lookup_direct_method( &self, typ: &Type, method_name: &str, - has_self_arg: bool, + check_self_param: bool, ) -> Option { let key = get_type_method_key(typ)?; self.methods .get(&key) .and_then(|h| h.get(method_name)) - .and_then(|methods| methods.find_direct_method(typ, has_self_arg, self)) + .and_then(|methods| methods.find_direct_method(typ, check_self_param, self)) } /// Looks up a methods that apply to the given type but are defined in traits. @@ -2029,15 +2051,14 @@ impl NodeInterner { index } - pub(crate) fn check_for_dependency_cycles(&self) -> Vec<(CompilationError, FileId)> { + pub(crate) fn check_for_dependency_cycles(&self) -> Vec { let strongly_connected_components = tarjan_scc(&self.dependency_graph); let mut errors = Vec::new(); let mut push_error = |item: String, scc: &[_], i, location: Location| { let cycle = self.get_cycle_error_string(scc, i); - let span = location.span; - let error = ResolverError::DependencyCycle { item, cycle, span }; - errors.push((error.into(), location.file)); + let error = ResolverError::DependencyCycle { item, cycle, location }; + errors.push(error.into()); }; for scc in strongly_connected_components { @@ -2146,8 +2167,8 @@ impl NodeInterner { self.push_expression_kind(lvalue.as_expression().kind) } - pub fn get_lvalue(&self, id: InternedExpressionKind, span: Span) -> LValue { - LValue::from_expression_kind(self.get_expression_kind(id).clone(), span) + pub fn get_lvalue(&self, id: InternedExpressionKind, location: Location) -> LValue { + LValue::from_expression_kind(self.get_expression_kind(id).clone(), location) .expect("Called LValue::from_expression with an invalid expression") } @@ -2310,20 +2331,21 @@ impl NodeInterner { } impl Methods { - fn add_method(&mut self, method: FuncId, typ: Option, trait_id: Option) { + fn add_method(&mut self, method: FuncId, typ: Type, trait_id: Option) { if let Some(trait_id) = trait_id { let trait_impl_method = TraitImplMethod { typ, method, trait_id }; self.trait_impl_methods.push(trait_impl_method); } else { - self.direct.push(method); + let impl_method = ImplMethod { typ, method }; + self.direct.push(impl_method); } } /// Iterate through each method, starting with the direct methods - pub fn iter(&self) -> impl Iterator, Option)> { + pub fn iter(&self) -> impl Iterator)> { let trait_impl_methods = - self.trait_impl_methods.iter().map(|m| (m.method, m.typ.as_ref(), Some(m.trait_id))); - let direct = self.direct.iter().copied().map(|func_id| (func_id, None, None)); + self.trait_impl_methods.iter().map(|m| (m.method, &m.typ, Some(m.trait_id))); + let direct = self.direct.iter().map(|method| (method.method, &method.typ, None)); direct.chain(trait_impl_methods) } @@ -2345,12 +2367,12 @@ impl Methods { pub fn find_direct_method( &self, typ: &Type, - has_self_param: bool, + check_self_param: bool, interner: &NodeInterner, ) -> Option { for method in &self.direct { - if Self::method_matches(typ, has_self_param, *method, None, interner) { - return Some(*method); + if Self::method_matches(typ, check_self_param, method.method, &method.typ, interner) { + return Some(method.method); } } @@ -2367,7 +2389,7 @@ impl Methods { for trait_impl_method in &self.trait_impl_methods { let method = trait_impl_method.method; - let method_type = trait_impl_method.typ.as_ref(); + let method_type = &trait_impl_method.typ; let trait_id = trait_impl_method.trait_id; if Self::method_matches(typ, has_self_param, method, method_type, interner) { @@ -2380,14 +2402,14 @@ impl Methods { fn method_matches( typ: &Type, - has_self_param: bool, + check_self_param: bool, method: FuncId, - method_type: Option<&Type>, + method_type: &Type, interner: &NodeInterner, ) -> bool { match interner.function_meta(&method).typ.instantiate(interner).0 { Type::Function(args, _, _, _) => { - if has_self_param { + if check_self_param { if let Some(object) = args.first() { if object.unify(typ).is_ok() { return true; @@ -2401,21 +2423,18 @@ impl Methods { } } } else { - // If we recorded the concrete type this trait impl method belongs to, - // and it matches typ, it's an exact match and we return that. - if let Some(method_type) = method_type { + // We still need to make sure the method is for the given type + // (this might be false if for example a method for `Struct` was added but + // now we are looking for a method in `Struct`) + if method_type.unify(typ).is_ok() { + return true; + } + + // Handle auto-dereferencing `&mut T` into `T` + if let Type::MutableReference(method_type) = method_type { if method_type.unify(typ).is_ok() { return true; } - - // Handle auto-dereferencing `&mut T` into `T` - if let Type::MutableReference(method_type) = method_type { - if method_type.unify(typ).is_ok() { - return true; - } - } - } else { - return true; } } } 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 8345b75dbabf..76e2958f668d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/errors.rs @@ -5,9 +5,10 @@ use crate::token::TokenKind; use small_ord_set::SmallOrdSet; use thiserror::Error; +use crate::elaborator::UnstableFeature; use iter_extended::vecmap; -use noirc_errors::CustomDiagnostic as Diagnostic; use noirc_errors::Span; +use noirc_errors::{CustomDiagnostic as Diagnostic, Location}; use super::labels::ParsingRuleLabel; @@ -61,7 +62,9 @@ pub enum ParserErrorReason { MissingSeparatingSemi, #[error("constrain keyword is deprecated")] ConstrainDeprecated, - #[error("Invalid type expression: '{0}'. Only unsigned integer constants up to `u32`, globals, generics, +, -, *, /, and % may be used in this context.")] + #[error( + "Invalid type expression: '{0}'. Only unsigned integer constants up to `u32`, globals, generics, +, -, *, /, and % may be used in this context." + )] InvalidTypeExpression(Expression), #[error("Early 'return' is unsupported")] EarlyReturn, @@ -75,8 +78,8 @@ pub enum ParserErrorReason { TraitImplVisibilityIgnored, #[error("comptime keyword is deprecated")] ComptimeDeprecated, - #[error("{0} are experimental and aren't fully supported yet")] - ExperimentalFeature(&'static str), + #[error("This requires the unstable feature '{0}' which is not enabled")] + ExperimentalFeature(UnstableFeature), #[error( "Multiple primary attributes found. Only one function attribute is allowed per function" )] @@ -132,42 +135,50 @@ pub struct ParserError { expected_labels: SmallOrdSet<[ParsingRuleLabel; 1]>, found: Token, reason: Option, - span: Span, + location: Location, } impl ParserError { - pub fn empty(found: Token, span: Span) -> ParserError { + pub fn empty(found: Token, location: Location) -> ParserError { ParserError { expected_tokens: SmallOrdSet::new(), expected_labels: SmallOrdSet::new(), found, reason: None, - span, + location, } } - pub fn expected_token(token: Token, found: Token, span: Span) -> ParserError { - let mut error = ParserError::empty(found, span); + pub fn expected_token(token: Token, found: Token, location: Location) -> ParserError { + let mut error = ParserError::empty(found, location); error.expected_tokens.insert(token); error } - pub fn expected_one_of_tokens(tokens: &[Token], found: Token, span: Span) -> ParserError { - let mut error = ParserError::empty(found, span); + pub fn expected_one_of_tokens( + tokens: &[Token], + found: Token, + location: Location, + ) -> ParserError { + let mut error = ParserError::empty(found, location); for token in tokens { error.expected_tokens.insert(token.clone()); } error } - pub fn expected_label(label: ParsingRuleLabel, found: Token, span: Span) -> ParserError { - let mut error = ParserError::empty(found, span); + pub fn expected_label( + label: ParsingRuleLabel, + found: Token, + location: Location, + ) -> ParserError { + let mut error = ParserError::empty(found, location); error.expected_labels.insert(label); error } - pub fn with_reason(reason: ParserErrorReason, span: Span) -> ParserError { - let mut error = ParserError::empty(Token::EOF, span); + pub fn with_reason(reason: ParserErrorReason, location: Location) -> ParserError { + let mut error = ParserError::empty(Token::EOF, location); error.reason = Some(reason); error } @@ -177,7 +188,11 @@ impl ParserError { } pub fn span(&self) -> Span { - self.span + self.location.span + } + + pub fn location(&self) -> Location { + self.location } pub fn reason(&self) -> Option<&ParserErrorReason> { @@ -234,7 +249,7 @@ impl<'a> From<&'a ParserError> for Diagnostic { let mut diagnostic = Diagnostic::simple_error( "Use of deprecated keyword 'constrain'".into(), "The 'constrain' keyword is deprecated. Please use the 'assert' function instead.".into(), - error.span, + error.location(), ); diagnostic.deprecated = true; diagnostic @@ -243,7 +258,7 @@ impl<'a> From<&'a ParserError> for Diagnostic { let mut diagnostic = Diagnostic::simple_warning( "Use of deprecated keyword 'comptime'".into(), "The 'comptime' keyword has been deprecated. It can be removed without affecting your program".into(), - error.span, + error.location(), ) ; diagnostic.deprecated = true; diagnostic @@ -258,46 +273,51 @@ impl<'a> From<&'a ParserError> for Diagnostic { .collect::>() .join(", ") ), - error.span, + error.location(), ), - ParserErrorReason::ExperimentalFeature(_) => { - Diagnostic::simple_warning(reason.to_string(), "".into(), error.span) + ParserErrorReason::ExperimentalFeature(feature) => { + let secondary = format!( + "Pass -Z{feature} to nargo to enable this feature at your own risk." + ); + Diagnostic::simple_error(reason.to_string(), secondary, error.location()) } ParserErrorReason::TraitVisibilityIgnored => { - Diagnostic::simple_warning(reason.to_string(), "".into(), error.span) + Diagnostic::simple_warning(reason.to_string(), "".into(), error.location()) } ParserErrorReason::TraitImplVisibilityIgnored => { - Diagnostic::simple_warning(reason.to_string(), "".into(), error.span) + Diagnostic::simple_warning(reason.to_string(), "".into(), error.location()) } ParserErrorReason::ExpectedPatternButFoundType(ty) => Diagnostic::simple_error( format!("Expected a pattern but found a type - {ty}"), format!("{ty} is a type and cannot be used as a variable name"), - error.span, + error.location(), ), ParserErrorReason::Lexer(error) => error.into(), ParserErrorReason::ExpectedMutAfterAmpersand { found } => Diagnostic::simple_error( format!("Expected `mut` after `&`, found `{found}`"), "Noir doesn't have immutable references, only mutable references".to_string(), - error.span, + error.location(), ), ParserErrorReason::MissingSafetyComment => Diagnostic::simple_warning( "Unsafe block must have a safety comment above it".into(), "The comment must start with the \"Safety: \" word".into(), - error.span, + error.location(), ), ParserErrorReason::MissingParametersForFunctionDefinition => { Diagnostic::simple_error( "Missing parameters for function definition".into(), "Add a parameter list: `()`".into(), - error.span, + error.location(), ) } ParserErrorReason::DocCommentDoesNotDocumentAnything => { let primary = "This doc comment doesn't document anything".to_string(); let secondary = "Consider changing it to a regular `//` comment".to_string(); - Diagnostic::simple_warning(primary, secondary, error.span) + Diagnostic::simple_warning(primary, secondary, error.location()) + } + other => { + Diagnostic::simple_error(format!("{other}"), String::new(), error.location()) } - other => Diagnostic::simple_error(format!("{other}"), String::new(), error.span), }, None => { if matches!( @@ -306,10 +326,10 @@ impl<'a> From<&'a ParserError> for Diagnostic { ) { let primary = "This doc comment doesn't document anything".to_string(); let secondary = "Consider changing it to a regular `//` comment".to_string(); - Diagnostic::simple_warning(primary, secondary, error.span) + Diagnostic::simple_warning(primary, secondary, error.location()) } else { let primary = error.to_string(); - Diagnostic::simple_error(primary, String::new(), error.span) + Diagnostic::simple_error(primary, String::new(), error.location()) } } } 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 c433adbfdfb1..0cecbe0814ab 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs @@ -20,8 +20,10 @@ use crate::token::SecondaryAttribute; pub use errors::ParserError; pub use errors::ParserErrorReason; -use noirc_errors::Span; -pub use parser::{parse_program, Parser, StatementOrExpressionOrLValue}; +use noirc_errors::Location; +pub use parser::{ + Parser, StatementOrExpressionOrLValue, parse_program, parse_program_with_dummy_file, +}; #[derive(Clone, Default)] pub struct SortedModule { @@ -128,7 +130,7 @@ impl ParsedModule { #[derive(Clone, Debug)] pub struct Item { pub kind: ItemKind, - pub span: Span, + pub location: Location, pub doc_comments: Vec, } 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 f4491e84471a..a5ea2ea5fe9e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs @@ -1,14 +1,15 @@ use acvm::FieldElement; +use fm::FileId; use modifiers::Modifiers; -use noirc_errors::Span; +use noirc_errors::{Location, Span}; use crate::{ ast::{Ident, ItemVisibility}, - lexer::{Lexer, SpannedTokenResult}, - token::{FmtStrFragment, IntType, Keyword, SpannedToken, Token, TokenKind, Tokens}, + lexer::{Lexer, lexer::LocatedTokenResult}, + token::{FmtStrFragment, IntType, Keyword, LocatedToken, Token, TokenKind, Tokens}, }; -use super::{labels::ParsingRuleLabel, ParsedModule, ParserError, ParserErrorReason}; +use super::{ParsedModule, ParserError, ParserErrorReason, labels::ParsingRuleLabel}; mod arguments; mod attributes; @@ -47,21 +48,25 @@ pub use statement_or_expression_or_lvalue::StatementOrExpressionOrLValue; /// of the program along with any parsing errors encountered. If the parsing errors /// Vec is non-empty, there may be Error nodes in the Ast to fill in the gaps that /// failed to parse. Otherwise the Ast is guaranteed to have 0 Error nodes. -pub fn parse_program(source_program: &str) -> (ParsedModule, Vec) { - let lexer = Lexer::new(source_program); +pub fn parse_program(source_program: &str, file_id: FileId) -> (ParsedModule, Vec) { + let lexer = Lexer::new(source_program, file_id); let mut parser = Parser::for_lexer(lexer); let program = parser.parse_program(); let errors = parser.errors; (program, errors) } +pub fn parse_program_with_dummy_file(source_program: &str) -> (ParsedModule, Vec) { + parse_program(source_program, FileId::dummy()) +} + enum TokenStream<'a> { Lexer(Lexer<'a>), Tokens(Tokens), } -impl<'a> TokenStream<'a> { - fn next(&mut self) -> Option { +impl TokenStream<'_> { + fn next(&mut self) -> Option { match self { TokenStream::Lexer(lexer) => lexer.next(), TokenStream::Tokens(tokens) => { @@ -80,10 +85,10 @@ pub struct Parser<'a> { // We always have one look-ahead token for these cases: // - check if we get `&` or `&mut` // - check if we get `>` or `>>` - token: SpannedToken, - next_token: SpannedToken, - current_token_span: Span, - previous_token_span: Span, + token: LocatedToken, + next_token: LocatedToken, + current_token_location: Location, + previous_token_location: Location, // We also keep track of comments that appear right before a token, // because `unsafe { }` requires one before it. @@ -111,18 +116,22 @@ impl<'a> Parser<'a> { Self::new(TokenStream::Tokens(tokens)) } - pub fn for_str(str: &'a str) -> Self { - Self::for_lexer(Lexer::new(str)) + pub fn for_str(str: &'a str, file_id: FileId) -> Self { + Self::for_lexer(Lexer::new(str, file_id)) + } + + pub fn for_str_with_dummy_file(str: &'a str) -> Self { + Self::for_str(str, FileId::dummy()) } fn new(tokens: TokenStream<'a>) -> Self { let mut parser = Self { errors: Vec::new(), tokens, - token: eof_spanned_token(), - next_token: eof_spanned_token(), - current_token_span: Default::default(), - previous_token_span: Default::default(), + token: eof_located_token(), + next_token: eof_located_token(), + current_token_location: Location::dummy(), + previous_token_location: Location::dummy(), current_token_comments: String::new(), next_token_comments: String::new(), statement_comments: None, @@ -163,16 +172,12 @@ impl<'a> Parser<'a> { } let all_warnings = self.errors.iter().all(|error| error.is_warning()); - if all_warnings { - Ok((item, self.errors)) - } else { - Err(self.errors) - } + if all_warnings { Ok((item, self.errors)) } else { Err(self.errors) } } /// Bumps this parser by one token. Returns the token that was previously the "current" token. - fn bump(&mut self) -> SpannedToken { - self.previous_token_span = self.current_token_span; + fn bump(&mut self) -> LocatedToken { + self.previous_token_location = self.current_token_location; let (next_next_token, next_next_token_comments) = self.read_token_internal(); let next_token = std::mem::replace(&mut self.next_token, next_next_token); let token = std::mem::replace(&mut self.token, next_token); @@ -181,7 +186,7 @@ impl<'a> Parser<'a> { std::mem::replace(&mut self.next_token_comments, next_next_token_comments); let _ = std::mem::replace(&mut self.current_token_comments, next_comments); - self.current_token_span = self.token.to_span(); + self.current_token_location = self.token.location(); token } @@ -189,20 +194,23 @@ impl<'a> Parser<'a> { let (token, comments) = self.read_token_internal(); self.token = token; self.current_token_comments = comments; - self.current_token_span = self.token.to_span(); + self.current_token_location = self.token.location(); let (token, comments) = self.read_token_internal(); self.next_token = token; self.next_token_comments = comments; } - fn read_token_internal(&mut self) -> (SpannedToken, String) { + fn read_token_internal(&mut self) -> (LocatedToken, String) { let mut last_comments = String::new(); loop { match self.tokens.next() { Some(Ok(token)) => match token.token() { Token::LineComment(comment, None) | Token::BlockComment(comment, None) => { + if !last_comments.is_empty() { + last_comments.push('\n'); + } last_comments.push_str(comment); continue; } @@ -211,17 +219,13 @@ impl<'a> Parser<'a> { } }, Some(Err(lexer_error)) => self.errors.push(lexer_error.into()), - None => return (eof_spanned_token(), last_comments), + None => return (eof_located_token(), last_comments), } } } - fn eat_kind(&mut self, kind: TokenKind) -> Option { - if self.token.kind() == kind { - Some(self.bump()) - } else { - None - } + fn eat_kind(&mut self, kind: TokenKind) -> Option { + if self.token.kind() == kind { Some(self.bump()) } else { None } } fn eat_keyword(&mut self, keyword: Keyword) -> bool { @@ -240,7 +244,7 @@ impl<'a> Parser<'a> { fn eat_ident(&mut self) -> Option { if let Some(token) = self.eat_kind(TokenKind::Ident) { match token.into_token() { - Token::Ident(ident) => Some(Ident::new(ident, self.previous_token_span)), + Token::Ident(ident) => Some(Ident::new(ident, self.previous_token_location)), _ => unreachable!(), } } else { @@ -375,7 +379,7 @@ impl<'a> Parser<'a> { fn eat_commas(&mut self) -> bool { if self.eat_comma() { while self.eat_comma() { - self.push_error(ParserErrorReason::UnexpectedComma, self.previous_token_span); + self.push_error(ParserErrorReason::UnexpectedComma, self.previous_token_location); } true } else { @@ -390,7 +394,10 @@ impl<'a> Parser<'a> { fn eat_semicolons(&mut self) -> bool { if self.eat_semicolon() { while self.eat_semicolon() { - self.push_error(ParserErrorReason::UnexpectedSemicolon, self.previous_token_span); + self.push_error( + ParserErrorReason::UnexpectedSemicolon, + self.previous_token_location, + ); } true } else { @@ -479,17 +486,35 @@ impl<'a> Parser<'a> { self.token.token() == &Token::EOF } - fn span_since(&self, start_span: Span) -> Span { - if self.current_token_span == start_span { + fn location_since(&self, start_location: Location) -> Location { + // When taking the span between locations in different files, just keep the first one + if self.current_token_location.file != start_location.file { + return start_location; + } + + let start_span = start_location.span; + + let span = if self.current_token_location.span == start_location.span { start_span } else { - let end_span = self.previous_token_span; - Span::from(start_span.start()..end_span.end()) - } + let end_span = self.previous_token_location.span; + if start_span.start() <= end_span.end() { + Span::from(start_span.start()..end_span.end()) + } else { + // TODO: workaround for now + start_span + } + }; + + Location::new(span, start_location.file) + } + + fn location_at_previous_token_end(&self) -> Location { + Location::new(self.span_at_previous_token_end(), self.previous_token_location.file) } fn span_at_previous_token_end(&self) -> Span { - Span::from(self.previous_token_span.end()..self.previous_token_span.end()) + Span::from(self.previous_token_location.span.end()..self.previous_token_location.span.end()) } fn expected_identifier(&mut self) { @@ -500,7 +525,7 @@ impl<'a> Parser<'a> { self.errors.push(ParserError::expected_token( token, self.token.token().clone(), - self.current_token_span, + self.current_token_location, )); } @@ -508,7 +533,7 @@ impl<'a> Parser<'a> { self.errors.push(ParserError::expected_one_of_tokens( tokens, self.token.token().clone(), - self.current_token_span, + self.current_token_location, )); } @@ -516,18 +541,26 @@ impl<'a> Parser<'a> { self.errors.push(ParserError::expected_label( label, self.token.token().clone(), - self.current_token_span, + self.current_token_location, )); } - fn expected_token_separating_items(&mut self, token: Token, items: &'static str, span: Span) { - self.push_error(ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items }, span); + fn expected_token_separating_items( + &mut self, + token: Token, + items: &'static str, + location: Location, + ) { + self.push_error( + ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items }, + location, + ); } fn expected_mut_after_ampersand(&mut self) { self.push_error( ParserErrorReason::ExpectedMutAfterAmpersand { found: self.token.token().clone() }, - self.current_token_span, + self.current_token_location, ); } @@ -543,20 +576,20 @@ impl<'a> Parser<'a> { ParserErrorReason::VisibilityNotFollowedByAnItem { visibility: modifiers.visibility, }, - modifiers.visibility_span, + modifiers.visibility_location, ); } } fn unconstrained_not_followed_by_an_item(&mut self, modifiers: Modifiers) { - if let Some(span) = modifiers.unconstrained { - self.push_error(ParserErrorReason::UnconstrainedNotFollowedByAnItem, span); + if let Some(location) = modifiers.unconstrained { + self.push_error(ParserErrorReason::UnconstrainedNotFollowedByAnItem, location); } } fn comptime_not_followed_by_an_item(&mut self, modifiers: Modifiers) { - if let Some(span) = modifiers.comptime { - self.push_error(ParserErrorReason::ComptimeNotFollowedByAnItem, span); + if let Some(location) = modifiers.comptime { + self.push_error(ParserErrorReason::ComptimeNotFollowedByAnItem, location); } } @@ -567,28 +600,28 @@ impl<'a> Parser<'a> { } fn mutable_not_applicable(&mut self, modifiers: Modifiers) { - if let Some(span) = modifiers.mutable { - self.push_error(ParserErrorReason::MutableNotApplicable, span); + if let Some(location) = modifiers.mutable { + self.push_error(ParserErrorReason::MutableNotApplicable, location); } } fn comptime_not_applicable(&mut self, modifiers: Modifiers) { - if let Some(span) = modifiers.comptime { - self.push_error(ParserErrorReason::ComptimeNotApplicable, span); + if let Some(location) = modifiers.comptime { + self.push_error(ParserErrorReason::ComptimeNotApplicable, location); } } fn unconstrained_not_applicable(&mut self, modifiers: Modifiers) { - if let Some(span) = modifiers.unconstrained { - self.push_error(ParserErrorReason::UnconstrainedNotApplicable, span); + if let Some(location) = modifiers.unconstrained { + self.push_error(ParserErrorReason::UnconstrainedNotApplicable, location); } } - fn push_error(&mut self, reason: ParserErrorReason, span: Span) { - self.errors.push(ParserError::with_reason(reason, span)); + fn push_error(&mut self, reason: ParserErrorReason, location: Location) { + self.errors.push(ParserError::with_reason(reason, location)); } } -fn eof_spanned_token() -> SpannedToken { - SpannedToken::new(Token::EOF, Default::default()) +fn eof_located_token() -> LocatedToken { + LocatedToken::new(Token::EOF, Location::dummy()) } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/arguments.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/arguments.rs index 380f42809a6e..6b7dd8559c8e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/arguments.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/arguments.rs @@ -1,13 +1,13 @@ use crate::{ast::Expression, token::Token}; -use super::{parse_many::separated_by_comma_until_right_paren, Parser}; +use super::{Parser, parse_many::separated_by_comma_until_right_paren}; pub(crate) struct CallArguments { pub(crate) arguments: Vec, pub(crate) is_macro_call: bool, } -impl<'a> Parser<'a> { +impl Parser<'_> { /// Arguments = '(' ArgumentsList? ')' /// /// ArgumentsList = Expression ( ',' Expression )? ','? diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/attributes.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/attributes.rs index e32e7d3cb235..32aa974fcafe 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -1,31 +1,31 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::ast::{Expression, ExpressionKind, Ident, Literal, Path}; use crate::lexer::errors::LexerErrorKind; -use crate::parser::labels::ParsingRuleLabel; use crate::parser::ParserErrorReason; +use crate::parser::labels::ParsingRuleLabel; use crate::token::{Attribute, FunctionAttribute, MetaAttribute, TestScope, Token}; use crate::token::{CustomAttribute, SecondaryAttribute}; -use super::parse_many::without_separator; use super::Parser; +use super::parse_many::without_separator; -impl<'a> Parser<'a> { +impl Parser<'_> { /// InnerAttribute = '#![' SecondaryAttribute ']' pub(super) fn parse_inner_attribute(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let is_tag = self.eat_inner_attribute_start()?; let attribute = if is_tag { - self.parse_tag_attribute(start_span) + self.parse_tag_attribute(start_location) } else { - self.parse_non_tag_attribute(start_span) + self.parse_non_tag_attribute(start_location) }; match attribute { Attribute::Function(function_attribute) => { self.errors.push( LexerErrorKind::InvalidInnerAttribute { - span: self.span_since(start_span), + location: self.location_since(start_location), found: function_attribute.to_string(), } .into(), @@ -37,7 +37,7 @@ impl<'a> Parser<'a> { } /// Attributes = Attribute* - pub(super) fn parse_attributes(&mut self) -> Vec<(Attribute, Span)> { + pub(super) fn parse_attributes(&mut self) -> Vec<(Attribute, Location)> { self.parse_many("attributes", without_separator(), Self::parse_attribute) } @@ -73,26 +73,26 @@ impl<'a> Parser<'a> { /// AttributeValue /// = Path /// | integer - pub(crate) fn parse_attribute(&mut self) -> Option<(Attribute, Span)> { - let start_span = self.current_token_span; + pub(crate) fn parse_attribute(&mut self) -> Option<(Attribute, Location)> { + let start_location = self.current_token_location; let is_tag = self.eat_attribute_start()?; let attribute = if is_tag { - self.parse_tag_attribute(start_span) + self.parse_tag_attribute(start_location) } else { - self.parse_non_tag_attribute(start_span) + self.parse_non_tag_attribute(start_location) }; - Some((attribute, self.span_since(start_span))) + Some((attribute, self.location_since(start_location))) } pub(super) fn validate_secondary_attributes( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, ) -> Vec { attributes .into_iter() - .filter_map(|(attribute, span)| match attribute { + .filter_map(|(attribute, location)| match attribute { Attribute::Function(..) => { - self.push_error(ParserErrorReason::NoFunctionAttributesAllowedOnType, span); + self.push_error(ParserErrorReason::NoFunctionAttributesAllowedOnType, location); None } Attribute::Secondary(attr) => Some(attr), @@ -100,9 +100,9 @@ impl<'a> Parser<'a> { .collect() } - fn parse_tag_attribute(&mut self, start_span: Span) -> Attribute { - let contents_start_span = self.current_token_span; - let mut contents_span = contents_start_span; + fn parse_tag_attribute(&mut self, start_location: Location) -> Attribute { + let contents_start_location = self.current_token_location; + let mut contents_location = contents_start_location; let mut contents = String::new(); let mut brackets_count = 1; // 1 because of the starting `#[` @@ -113,7 +113,7 @@ impl<'a> Parser<'a> { } else if self.at(Token::RightBracket) { brackets_count -= 1; if brackets_count == 0 { - contents_span = self.span_since(contents_start_span); + contents_location = self.location_since(contents_start_location); self.bump(); break; } @@ -125,66 +125,68 @@ impl<'a> Parser<'a> { Attribute::Secondary(SecondaryAttribute::Tag(CustomAttribute { contents, - span: self.span_since(start_span), - contents_span, + span: self.location_since(start_location).span, + contents_span: contents_location.span, })) } - fn parse_non_tag_attribute(&mut self, start_span: Span) -> Attribute { + fn parse_non_tag_attribute(&mut self, start_location: Location) -> Attribute { if matches!(&self.token.token(), Token::Keyword(..)) && (self.next_is(Token::LeftParen) || self.next_is(Token::RightBracket)) { // This is a Meta attribute with the syntax `keyword(arg1, arg2, .., argN)` - let path = Path::from_single(self.token.to_string(), self.current_token_span); + let path = Path::from_single(self.token.to_string(), self.current_token_location); self.bump(); - self.parse_meta_attribute(path, start_span) + self.parse_meta_attribute(path, start_location) } else if let Some(path) = self.parse_path_no_turbofish() { if let Some(ident) = path.as_ident() { if ident.0.contents == "test" { // The test attribute is the only secondary attribute that has `a = b` in its syntax // (`should_fail_with = "..."``) so we parse it differently. - self.parse_test_attribute(start_span) + self.parse_test_attribute(start_location) } else { // Every other attribute has the form `name(arg1, arg2, .., argN)` - self.parse_ident_attribute_other_than_test(ident, start_span) + self.parse_ident_attribute_other_than_test(ident, start_location) } } else { // This is a Meta attribute with the syntax `path(arg1, arg2, .., argN)` - self.parse_meta_attribute(path, start_span) + self.parse_meta_attribute(path, start_location) } } else { self.expected_label(ParsingRuleLabel::Path); - self.parse_tag_attribute(start_span) + self.parse_tag_attribute(start_location) } } - fn parse_meta_attribute(&mut self, name: Path, start_span: Span) -> Attribute { + fn parse_meta_attribute(&mut self, name: Path, start_location: Location) -> Attribute { let arguments = self.parse_arguments().unwrap_or_default(); self.skip_until_right_bracket(); Attribute::Secondary(SecondaryAttribute::Meta(MetaAttribute { name, arguments, - span: self.span_since(start_span), + location: self.location_since(start_location), })) } fn parse_ident_attribute_other_than_test( &mut self, ident: &Ident, - start_span: Span, + start_location: Location, ) -> Attribute { let arguments = self.parse_arguments().unwrap_or_default(); self.skip_until_right_bracket(); match ident.0.contents.as_str() { - "abi" => self.parse_single_name_attribute(ident, arguments, start_span, |name| { + "abi" => self.parse_single_name_attribute(ident, arguments, start_location, |name| { Attribute::Secondary(SecondaryAttribute::Abi(name)) }), - "allow" => self.parse_single_name_attribute(ident, arguments, start_span, |name| { + "allow" => self.parse_single_name_attribute(ident, arguments, start_location, |name| { Attribute::Secondary(SecondaryAttribute::Allow(name)) }), - "builtin" => self.parse_single_name_attribute(ident, arguments, start_span, |name| { - Attribute::Function(FunctionAttribute::Builtin(name)) - }), + "builtin" => { + self.parse_single_name_attribute(ident, arguments, start_location, |name| { + Attribute::Function(FunctionAttribute::Builtin(name)) + }) + } "deprecated" => self.parse_deprecated_attribute(ident, arguments), "contract_library_method" => { let attr = Attribute::Secondary(SecondaryAttribute::ContractLibraryMethod); @@ -194,16 +196,18 @@ impl<'a> Parser<'a> { let attr = Attribute::Secondary(SecondaryAttribute::Export); self.parse_no_args_attribute(ident, arguments, attr) } - "field" => self.parse_single_name_attribute(ident, arguments, start_span, |name| { + "field" => self.parse_single_name_attribute(ident, arguments, start_location, |name| { Attribute::Secondary(SecondaryAttribute::Field(name)) }), "fold" => { let attr = Attribute::Function(FunctionAttribute::Fold); self.parse_no_args_attribute(ident, arguments, attr) } - "foreign" => self.parse_single_name_attribute(ident, arguments, start_span, |name| { - Attribute::Function(FunctionAttribute::Foreign(name)) - }), + "foreign" => { + self.parse_single_name_attribute(ident, arguments, start_location, |name| { + Attribute::Function(FunctionAttribute::Foreign(name)) + }) + } "inline_always" => { let attr = Attribute::Function(FunctionAttribute::InlineAlways); self.parse_no_args_attribute(ident, arguments, attr) @@ -212,9 +216,11 @@ impl<'a> Parser<'a> { let attr = Attribute::Function(FunctionAttribute::NoPredicates); self.parse_no_args_attribute(ident, arguments, attr) } - "oracle" => self.parse_single_name_attribute(ident, arguments, start_span, |name| { - Attribute::Function(FunctionAttribute::Oracle(name)) - }), + "oracle" => { + self.parse_single_name_attribute(ident, arguments, start_location, |name| { + Attribute::Function(FunctionAttribute::Oracle(name)) + }) + } "use_callers_scope" => { let attr = Attribute::Secondary(SecondaryAttribute::UseCallersScope); self.parse_no_args_attribute(ident, arguments, attr) @@ -226,7 +232,7 @@ impl<'a> Parser<'a> { _ => Attribute::Secondary(SecondaryAttribute::Meta(MetaAttribute { name: Path::from_ident(ident.clone()), arguments, - span: self.span_since(start_span), + location: self.location_since(start_location), })), } } @@ -248,7 +254,7 @@ impl<'a> Parser<'a> { max: 1, found: arguments.len(), }, - ident.span(), + ident.location(), ); return Attribute::Secondary(SecondaryAttribute::Deprecated(None)); } @@ -257,7 +263,7 @@ impl<'a> Parser<'a> { let ExpressionKind::Literal(Literal::Str(message)) = argument.kind else { self.push_error( ParserErrorReason::DeprecatedAttributeExpectsAStringArgument, - argument.span, + argument.location, ); return Attribute::Secondary(SecondaryAttribute::Deprecated(None)); }; @@ -265,7 +271,7 @@ impl<'a> Parser<'a> { Attribute::Secondary(SecondaryAttribute::Deprecated(Some(message))) } - fn parse_test_attribute(&mut self, start_span: Span) -> Attribute { + fn parse_test_attribute(&mut self, start_location: Location) -> Attribute { let scope = if self.eat_left_paren() { let scope = if let Some(ident) = self.eat_ident() { match ident.0.contents.as_str() { @@ -295,7 +301,10 @@ impl<'a> Parser<'a> { scope } else { self.errors.push( - LexerErrorKind::MalformedTestAttribute { span: self.span_since(start_span) }.into(), + LexerErrorKind::MalformedTestAttribute { + location: self.location_since(start_location), + } + .into(), ); TestScope::None }; @@ -307,7 +316,7 @@ impl<'a> Parser<'a> { &mut self, ident: &Ident, mut arguments: Vec, - start_span: Span, + start_location: Location, f: F, ) -> Attribute where @@ -321,7 +330,7 @@ impl<'a> Parser<'a> { max: 1, found: arguments.len(), }, - self.current_token_span, + self.current_token_location, ); return f(String::new()); } @@ -332,10 +341,13 @@ impl<'a> Parser<'a> { f(argument.to_string()) } _ => { - let span = self.span_since(start_span); + let location = self.location_since(start_location); self.errors.push( - LexerErrorKind::MalformedFuncAttribute { span, found: argument.to_string() } - .into(), + LexerErrorKind::MalformedFuncAttribute { + location, + found: argument.to_string(), + } + .into(), ); f(String::new()) } @@ -356,7 +368,7 @@ impl<'a> Parser<'a> { max: 0, found: arguments.len(), }, - ident.span(), + ident.location(), ); } @@ -388,19 +400,19 @@ mod tests { use noirc_errors::Span; use crate::{ - parser::{parser::tests::expect_no_errors, Parser}, + parser::{Parser, parser::tests::expect_no_errors}, token::{Attribute, FunctionAttribute, SecondaryAttribute, TestScope}, }; fn parse_inner_secondary_attribute_no_errors(src: &str, expected: SecondaryAttribute) { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let attribute = parser.parse_inner_attribute(); expect_no_errors(&parser.errors); assert_eq!(attribute.unwrap(), expected); } fn parse_attribute_no_errors(src: &str, expected: Attribute) { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); assert_eq!(attribute, expected); @@ -409,7 +421,7 @@ mod tests { #[test] fn parses_inner_attribute_as_tag() { let src = "#!['hello]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let Some(SecondaryAttribute::Tag(custom)) = parser.parse_inner_attribute() else { panic!("Expected inner tag attribute"); }; @@ -422,7 +434,7 @@ mod tests { #[test] fn parses_inner_attribute_as_tag_with_nested_brackets() { let src = "#!['hello[1]]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let Some(SecondaryAttribute::Tag(custom)) = parser.parse_inner_attribute() else { panic!("Expected inner tag attribute"); }; @@ -570,7 +582,7 @@ mod tests { #[test] fn parses_meta_attribute_single_identifier_no_arguments() { let src = "#[foo]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); let Attribute::Secondary(SecondaryAttribute::Meta(meta)) = attribute else { @@ -583,7 +595,7 @@ mod tests { #[test] fn parses_meta_attribute_single_identifier_as_keyword() { let src = "#[dep]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); let Attribute::Secondary(SecondaryAttribute::Meta(meta)) = attribute else { @@ -596,7 +608,7 @@ mod tests { #[test] fn parses_meta_attribute_single_identifier_with_arguments() { let src = "#[foo(1, 2, 3)]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); let Attribute::Secondary(SecondaryAttribute::Meta(meta)) = attribute else { @@ -610,7 +622,7 @@ mod tests { #[test] fn parses_meta_attribute_path_with_arguments() { let src = "#[foo::bar(1, 2, 3)]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); let Attribute::Secondary(SecondaryAttribute::Meta(meta)) = attribute else { @@ -624,7 +636,7 @@ mod tests { #[test] fn parses_attributes() { let src = "#[test] #[deprecated]"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let mut attributes = parser.parse_attributes(); expect_no_errors(&parser.errors); assert_eq!(attributes.len(), 2); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/doc_comments.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/doc_comments.rs index 578a49641f64..0c9329d7027f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/doc_comments.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/doc_comments.rs @@ -1,8 +1,11 @@ -use crate::token::{DocStyle, Token, TokenKind}; +use crate::{ + parser::ParserErrorReason, + token::{DocStyle, Token, TokenKind}, +}; -use super::{parse_many::without_separator, Parser}; +use super::{Parser, parse_many::without_separator}; -impl<'a> Parser<'a> { +impl Parser<'_> { /// InnerDocComments = inner_doc_comment* pub(super) fn parse_inner_doc_comments(&mut self) -> Vec { self.parse_many("inner doc comments", without_separator(), Self::parse_inner_doc_comment) @@ -28,16 +31,28 @@ impl<'a> Parser<'a> { _ => unreachable!(), }) } + + /// Skips any outer doc comments but produces a warning saying that they don't document anything. + pub(super) fn warn_on_outer_doc_comments(&mut self) { + let location_before_doc_comments = self.current_token_location; + let doc_comments = self.parse_outer_doc_comments(); + if !doc_comments.is_empty() { + self.push_error( + ParserErrorReason::DocCommentDoesNotDocumentAnything, + location_before_doc_comments, + ); + } + } } #[cfg(test)] mod tests { - use crate::parser::{parser::tests::expect_no_errors, Parser}; + use crate::parser::{Parser, parser::tests::expect_no_errors}; #[test] fn parses_inner_doc_comments() { let src = "//! Hello\n//! World"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let comments = parser.parse_inner_doc_comments(); expect_no_errors(&parser.errors); assert_eq!(comments.len(), 2); @@ -48,7 +63,7 @@ mod tests { #[test] fn parses_outer_doc_comments() { let src = "/// Hello\n/// World"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let comments = parser.parse_outer_doc_comments(); expect_no_errors(&parser.errors); assert_eq!(comments.len(), 2); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/enums.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/enums.rs index 3b496a438cf3..885bfa871eeb 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/enums.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/enums.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{Documented, EnumVariant, Ident, ItemVisibility, NoirEnumeration, UnresolvedGenerics}, @@ -7,24 +7,22 @@ use crate::{ }; use super::{ - parse_many::{separated_by_comma_until_right_brace, separated_by_comma_until_right_paren}, Parser, + parse_many::{separated_by_comma_until_right_brace, separated_by_comma_until_right_paren}, }; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Enum = 'enum' identifier Generics '{' EnumVariant* '}' /// /// EnumField = OuterDocComments identifier ':' Type pub(crate) fn parse_enum( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, visibility: ItemVisibility, - start_span: Span, + start_location: Location, ) -> NoirEnumeration { let attributes = self.validate_secondary_attributes(attributes); - self.push_error(ParserErrorReason::ExperimentalFeature("Enums"), start_span); - let Some(name) = self.eat_ident() else { self.expected_identifier(); return self.empty_enum( @@ -32,7 +30,7 @@ impl<'a> Parser<'a> { attributes, visibility, Vec::new(), - start_span, + start_location, ); }; @@ -40,7 +38,7 @@ impl<'a> Parser<'a> { if !self.eat_left_brace() { self.expected_token(Token::LeftBrace); - return self.empty_enum(name, attributes, visibility, generics, start_span); + return self.empty_enum(name, attributes, visibility, generics, start_location); } let comma_separated = separated_by_comma_until_right_brace(); @@ -52,7 +50,7 @@ impl<'a> Parser<'a> { visibility, generics, variants, - span: self.span_since(start_span), + location: self.location_since(start_location), } } @@ -62,7 +60,7 @@ impl<'a> Parser<'a> { // Loop until we find an identifier, skipping anything that's not one loop { - let doc_comments_start_span = self.current_token_span; + let doc_comments_start_location = self.current_token_location; doc_comments = self.parse_outer_doc_comments(); if let Some(ident) = self.eat_ident() { @@ -73,7 +71,7 @@ impl<'a> Parser<'a> { if !doc_comments.is_empty() { self.push_error( ParserErrorReason::DocCommentDoesNotDocumentAnything, - self.span_since(doc_comments_start_span), + self.location_since(doc_comments_start_location), ); } @@ -106,7 +104,7 @@ impl<'a> Parser<'a> { attributes: Vec, visibility: ItemVisibility, generics: UnresolvedGenerics, - start_span: Span, + start_location: Location, ) -> NoirEnumeration { NoirEnumeration { name, @@ -114,7 +112,7 @@ impl<'a> Parser<'a> { visibility, generics, variants: Vec::new(), - span: self.span_since(start_span), + location: self.location_since(start_location), } } } @@ -123,17 +121,15 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{IntegerBitSize, NoirEnumeration, Signedness, UnresolvedGeneric, UnresolvedTypeData}, + parse_program_with_dummy_file, parser::{ - parser::{ - parse_program, - tests::{expect_no_errors, get_source_with_error_span}, - }, ItemKind, ParserErrorReason, + parser::tests::{expect_no_errors, get_source_with_error_span}, }, }; fn parse_enum_no_errors(src: &str) -> NoirEnumeration { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -205,7 +201,7 @@ mod tests { #[test] fn parse_empty_enum_with_doc_comments() { let src = "/// Hello\nenum Foo {}"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -219,8 +215,8 @@ mod tests { #[test] fn parse_unclosed_enum() { let src = "enum Foo {"; - let (module, errors) = parse_program(src); - assert_eq!(errors.len(), 2); + let (module, errors) = parse_program_with_dummy_file(src); + assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; let ItemKind::Enum(noir_enum) = &item.kind else { @@ -236,7 +232,7 @@ mod tests { ^^^^^^^ "; let (src, _) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let reason = errors[0].reason().unwrap(); assert!(matches!(reason, ParserErrorReason::NoFunctionAttributesAllowedOnType)); } @@ -248,7 +244,7 @@ mod tests { ^^ "; let (src, _) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -258,7 +254,8 @@ mod tests { assert_eq!("Foo", noir_enum.name.to_string()); assert_eq!(noir_enum.variants.len(), 1); - let error = &errors[1]; + assert_eq!(errors.len(), 1); + let error = &errors[0]; assert_eq!(error.to_string(), "Expected an identifier but found '42'"); } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/expression.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/expression.rs index b2ddc200ef25..d0f335414da4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -1,26 +1,26 @@ use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{ ArrayLiteral, BlockExpression, CallExpression, CastExpression, ConstrainExpression, ConstrainKind, ConstructorExpression, Expression, ExpressionKind, Ident, IfExpression, IndexExpression, Literal, MatchExpression, MemberAccessExpression, MethodCallExpression, - Statement, TypePath, UnaryOp, UnresolvedType, + Statement, TypePath, UnaryOp, UnresolvedType, UnsafeExpression, }, - parser::{labels::ParsingRuleLabel, parser::parse_many::separated_by_comma, ParserErrorReason}, + parser::{ParserErrorReason, labels::ParsingRuleLabel, parser::parse_many::separated_by_comma}, token::{Keyword, Token, TokenKind}, }; use super::{ + Parser, parse_many::{ separated_by_comma_until_right_brace, separated_by_comma_until_right_paren, without_separator, }, - Parser, }; -impl<'a> Parser<'a> { +impl Parser<'_> { pub(crate) fn parse_expression_or_error(&mut self) -> Expression { self.parse_expression_or_error_impl(true) // allow constructors } @@ -47,7 +47,10 @@ impl<'a> Parser<'a> { expr } else { self.push_expected_expression(); - Expression { kind: ExpressionKind::Error, span: self.span_at_previous_token_end() } + Expression { + kind: ExpressionKind::Error, + location: self.location_at_previous_token_end(), + } } } @@ -59,7 +62,7 @@ impl<'a> Parser<'a> { /// = UnaryOp Term /// | AtomOrUnaryRightExpression pub(super) fn parse_term(&mut self, allow_constructors: bool) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if let Some(operator) = self.parse_unary_op() { let Some(rhs) = self.parse_term(allow_constructors) else { @@ -67,8 +70,8 @@ impl<'a> Parser<'a> { return None; }; let kind = ExpressionKind::prefix(operator, rhs); - let span = self.span_since(start_span); - return Some(Expression { kind, span }); + let location = self.location_since(start_location); + return Some(Expression { kind, location }); } self.parse_atom_or_unary_right(allow_constructors) @@ -94,12 +97,12 @@ impl<'a> Parser<'a> { /// AtomOrUnaryRightExpression /// = Atom UnaryRightExpression* fn parse_atom_or_unary_right(&mut self, allow_constructors: bool) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let mut atom = self.parse_atom(allow_constructors)?; let mut parsed; loop { - (atom, parsed) = self.parse_unary_right(atom, start_span); + (atom, parsed) = self.parse_unary_right(atom, start_location); if parsed { continue; } else { @@ -115,37 +118,41 @@ impl<'a> Parser<'a> { /// | MemberAccessOrMethodCallExpression /// | CastExpression /// | IndexExpression - fn parse_unary_right(&mut self, mut atom: Expression, start_span: Span) -> (Expression, bool) { + fn parse_unary_right( + &mut self, + mut atom: Expression, + start_location: Location, + ) -> (Expression, bool) { let mut parsed; - (atom, parsed) = self.parse_call(atom, start_span); + (atom, parsed) = self.parse_call(atom, start_location); if parsed { return (atom, parsed); } - (atom, parsed) = self.parse_member_access_or_method_call(atom, start_span); + (atom, parsed) = self.parse_member_access_or_method_call(atom, start_location); if parsed { return (atom, parsed); } - (atom, parsed) = self.parse_cast(atom, start_span); + (atom, parsed) = self.parse_cast(atom, start_location); if parsed { return (atom, parsed); } - self.parse_index(atom, start_span) + self.parse_index(atom, start_location) } /// CallExpression = Atom CallArguments - fn parse_call(&mut self, atom: Expression, start_span: Span) -> (Expression, bool) { + fn parse_call(&mut self, atom: Expression, start_location: Location) -> (Expression, bool) { if let Some(call_arguments) = self.parse_call_arguments() { let kind = ExpressionKind::Call(Box::new(CallExpression { func: Box::new(atom), arguments: call_arguments.arguments, is_macro_call: call_arguments.is_macro_call, })); - let span = self.span_since(start_span); - let atom = Expression { kind, span }; + let location = self.location_since(start_location); + let atom = Expression { kind, location }; (atom, true) } else { (atom, false) @@ -162,7 +169,7 @@ impl<'a> Parser<'a> { fn parse_member_access_or_method_call( &mut self, atom: Expression, - start_span: Span, + start_location: Location, ) -> (Expression, bool) { if !self.eat_dot() { return (atom, false); @@ -187,8 +194,8 @@ impl<'a> Parser<'a> { })) }; - let span = self.span_since(start_span); - let atom = Expression { kind, span }; + let location = self.location_since(start_location); + let atom = Expression { kind, location }; (atom, true) } @@ -196,31 +203,31 @@ impl<'a> Parser<'a> { if let Some(ident) = self.eat_ident() { Some(ident) } else if let Some(int) = self.eat_int() { - Some(Ident::new(int.to_string(), self.previous_token_span)) + Some(Ident::new(int.to_string(), self.previous_token_location)) } else { self.push_error( ParserErrorReason::ExpectedFieldName(self.token.token().clone()), - self.current_token_span, + self.current_token_location, ); None } } /// CastExpression = Atom 'as' Type - fn parse_cast(&mut self, atom: Expression, start_span: Span) -> (Expression, bool) { + fn parse_cast(&mut self, atom: Expression, start_location: Location) -> (Expression, bool) { if !self.eat_keyword(Keyword::As) { return (atom, false); } let typ = self.parse_type_or_error(); let kind = ExpressionKind::Cast(Box::new(CastExpression { lhs: atom, r#type: typ })); - let span = self.span_since(start_span); - let atom = Expression { kind, span }; + let location = self.location_since(start_location); + let atom = Expression { kind, location }; (atom, true) } /// IndexExpression = Atom '[' Expression ']' - fn parse_index(&mut self, atom: Expression, start_span: Span) -> (Expression, bool) { + fn parse_index(&mut self, atom: Expression, start_location: Location) -> (Expression, bool) { if !self.eat_left_bracket() { return (atom, false); } @@ -228,8 +235,8 @@ impl<'a> Parser<'a> { let index = self.parse_expression_or_error(); self.eat_or_error(Token::RightBracket); let kind = ExpressionKind::Index(Box::new(IndexExpression { collection: atom, index })); - let span = self.span_since(start_span); - let atom = Expression { kind, span }; + let location = self.location_since(start_location); + let atom = Expression { kind, location }; (atom, true) } @@ -261,20 +268,14 @@ impl<'a> Parser<'a> { /// | InternedExpression /// | InternedStatementExpression fn parse_atom(&mut self, allow_constructors: bool) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let kind = self.parse_atom_kind(allow_constructors)?; - Some(Expression { kind, span: self.span_since(start_span) }) + Some(Expression { kind, location: self.location_since(start_location) }) } fn parse_atom_kind(&mut self, allow_constructors: bool) -> Option { - let span_before_doc_comments = self.current_token_span; - let doc_comments = self.parse_outer_doc_comments(); - if !doc_comments.is_empty() { - self.push_error( - ParserErrorReason::DocCommentDoesNotDocumentAnything, - span_before_doc_comments, - ); - } + // Like in Rust, we allow parsing doc comments on top of an expression but they always produce a warning. + self.warn_on_outer_doc_comments(); if let Some(kind) = self.parse_unsafe_expr() { return Some(kind); @@ -298,10 +299,10 @@ impl<'a> Parser<'a> { Token::InternedUnresolvedTypeData(..) | Token::QuotedType(..) ) && self.next_is(Token::LeftBrace) { - let span = self.current_token_span; + let location = self.current_token_location; let typ = self.parse_interned_type().or_else(|| self.parse_resolved_type()).unwrap(); self.eat_or_error(Token::LeftBrace); - let typ = UnresolvedType { typ, span }; + let typ = UnresolvedType { typ, location }; return Some(self.parse_constructor(typ)); } @@ -386,26 +387,32 @@ impl<'a> Parser<'a> { /// UnsafeExpression = 'unsafe' Block fn parse_unsafe_expr(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; + let comments_before_unsafe = self.current_token_comments.clone(); if !self.eat_keyword(Keyword::Unsafe) { return None; } - if self.current_token_comments.is_empty() { - if let Some(statement_comments) = &mut self.statement_comments { - if !statement_comments.trim().to_lowercase().starts_with("safety:") { - self.push_error(ParserErrorReason::MissingSafetyComment, start_span); - } + let comments: &str = if comments_before_unsafe.is_empty() { + if let Some(statement_comments) = &self.statement_comments { + statement_comments } else { - self.push_error(ParserErrorReason::MissingSafetyComment, start_span); + "" } - } else if !self.current_token_comments.trim().to_lowercase().starts_with("safety:") { - self.push_error(ParserErrorReason::MissingSafetyComment, start_span); + } else { + &comments_before_unsafe + }; + + if !comments.lines().any(|line| line.trim().to_lowercase().starts_with("safety:")) { + self.push_error(ParserErrorReason::MissingSafetyComment, start_location); } if let Some(block) = self.parse_block() { - Some(ExpressionKind::Unsafe(block, self.span_since(start_span))) + Some(ExpressionKind::Unsafe(UnsafeExpression { + block, + unsafe_keyword_location: start_location, + })) } else { Some(ExpressionKind::Error) } @@ -439,11 +446,7 @@ impl<'a> Parser<'a> { Self::parse_constructor_field, ); - ExpressionKind::Constructor(Box::new(ConstructorExpression { - typ, - fields, - struct_type: None, - })) + ExpressionKind::Constructor(Box::new(ConstructorExpression { typ, fields })) } fn parse_constructor_field(&mut self) -> Option<(Ident, Expression)> { @@ -471,26 +474,26 @@ impl<'a> Parser<'a> { let condition = self.parse_expression_except_constructor_or_error(); - let start_span = self.current_token_span; + let start_location = self.current_token_location; let Some(consequence) = self.parse_block() else { self.expected_token(Token::LeftBrace); - let span = self.span_at_previous_token_end(); + let location = self.location_at_previous_token_end(); return Some(ExpressionKind::If(Box::new(IfExpression { condition, - consequence: Expression { kind: ExpressionKind::Error, span }, + consequence: Expression { kind: ExpressionKind::Error, location }, alternative: None, }))); }; - let span = self.span_since(start_span); - let consequence = Expression { kind: ExpressionKind::Block(consequence), span }; + let location = self.location_since(start_location); + let consequence = Expression { kind: ExpressionKind::Block(consequence), location }; let alternative = if self.eat_keyword(Keyword::Else) { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if let Some(block) = self.parse_block() { - let span = self.span_since(start_span); - Some(Expression { kind: ExpressionKind::Block(block), span }) + let location = self.location_since(start_location); + Some(Expression { kind: ExpressionKind::Block(block), location }) } else if let Some(if_expr) = self.parse_if_expr() { - Some(Expression { kind: if_expr, span: self.span_since(start_span) }) + Some(Expression { kind: if_expr, location: self.location_since(start_location) }) } else { self.expected_token(Token::LeftBrace); None @@ -504,7 +507,6 @@ impl<'a> Parser<'a> { /// MatchExpression = 'match' ExpressionExceptConstructor '{' MatchRule* '}' pub(super) fn parse_match_expr(&mut self) -> Option { - let start_span = self.current_token_span; if !self.eat_keyword(Keyword::Match) { return None; } @@ -519,7 +521,6 @@ impl<'a> Parser<'a> { Self::parse_match_rule, ); - self.push_error(ParserErrorReason::ExperimentalFeature("Match expressions"), start_span); Some(ExpressionKind::Match(Box::new(MatchExpression { expression, rules }))) } @@ -528,11 +529,11 @@ impl<'a> Parser<'a> { let pattern = self.parse_expression()?; self.eat_or_error(Token::FatArrow); - let start_span = self.current_token_span; + let start_location = self.current_token_location; let branch = match self.parse_block() { Some(block) => { - let span = self.span_since(start_span); - let block = Expression::new(ExpressionKind::Block(block), span); + let location = self.location_since(start_location); + let block = Expression::new(ExpressionKind::Block(block), location); self.eat_comma(); // comma is optional if we have a block block } @@ -551,21 +552,21 @@ impl<'a> Parser<'a> { return None; } - let start_span = self.current_token_span; + let start_location = self.current_token_location; let Some(block) = self.parse_block() else { self.expected_token(Token::LeftBrace); return None; }; - Some(ExpressionKind::Comptime(block, self.span_since(start_span))) + Some(ExpressionKind::Comptime(block, self.location_since(start_location))) } /// UnquoteExpression /// = '$' identifier /// | '$' '(' Expression ')' fn parse_unquote_expr(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.eat(Token::DollarSign) { return None; @@ -574,25 +575,25 @@ impl<'a> Parser<'a> { if let Some(path) = self.parse_path() { let expr = Expression { kind: ExpressionKind::Variable(path), - span: self.span_since(start_span), + location: self.location_since(start_location), }; return Some(ExpressionKind::Unquote(Box::new(expr))); } - let span_at_left_paren = self.current_token_span; + let location_at_left_paren = self.current_token_location; if self.eat_left_paren() { let expr = self.parse_expression_or_error(); self.eat_or_error(Token::RightParen); let expr = Expression { kind: ExpressionKind::Parenthesized(Box::new(expr)), - span: self.span_since(span_at_left_paren), + location: self.location_since(location_at_left_paren), }; return Some(ExpressionKind::Unquote(Box::new(expr))); } self.push_error( ParserErrorReason::ExpectedIdentifierOrLeftParenAfterDollar, - self.current_token_span, + self.current_token_location, ); None @@ -600,9 +601,9 @@ impl<'a> Parser<'a> { /// TypePathExpression = PrimitiveType '::' identifier ( '::' GenericTypeArgs )? fn parse_type_path_expr(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let typ = self.parse_primitive_type()?; - let typ = UnresolvedType { typ, span: self.span_since(start_span) }; + let typ = UnresolvedType { typ, location: self.location_since(start_location) }; self.eat_or_error(Token::DoubleColon); @@ -610,7 +611,7 @@ impl<'a> Parser<'a> { ident } else { self.expected_identifier(); - Ident::new(String::new(), self.span_at_previous_token_end()) + Ident::new(String::new(), self.location_at_previous_token_end()) }; let turbofish = self.eat_double_colon().then(|| { @@ -718,7 +719,7 @@ impl<'a> Parser<'a> { } let comma_after_first_expr = self.eat_comma(); - let second_expr_span = self.current_token_span; + let second_expr_location = self.current_token_location; let mut exprs = self.parse_many( "expressions", @@ -727,7 +728,7 @@ impl<'a> Parser<'a> { ); if !exprs.is_empty() && !comma_after_first_expr { - self.expected_token_separating_items(Token::Comma, "expressions", second_expr_span); + self.expected_token_separating_items(Token::Comma, "expressions", second_expr_location); } exprs.insert(0, first_expr); @@ -791,7 +792,7 @@ impl<'a> Parser<'a> { /// | 'assert' Arguments /// | 'assert_eq' Arguments pub(super) fn parse_constrain_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let kind = self.parse_constrain_kind()?; Some(match kind { @@ -802,16 +803,23 @@ impl<'a> Parser<'a> { } let arguments = arguments.unwrap_or_default(); - ConstrainExpression { kind, arguments, span: self.span_since(start_span) } + ConstrainExpression { + kind, + arguments, + location: self.location_since(start_location), + } } ConstrainKind::Constrain => { - self.push_error(ParserErrorReason::ConstrainDeprecated, self.previous_token_span); + self.push_error( + ParserErrorReason::ConstrainDeprecated, + self.previous_token_location, + ); let expression = self.parse_expression_or_error(); ConstrainExpression { kind, arguments: vec![expression], - span: self.span_since(start_span), + location: self.location_since(start_location), } } }) @@ -846,7 +854,7 @@ impl<'a> Parser<'a> { Some(BlockExpression { statements }) } - fn parse_statement_in_block(&mut self) -> Option<(Statement, (Option, Span))> { + fn parse_statement_in_block(&mut self) -> Option<(Statement, (Option, Location))> { if let Some(statement) = self.parse_statement() { Some(statement) } else { @@ -857,13 +865,13 @@ impl<'a> Parser<'a> { fn check_statements_require_semicolon( &mut self, - statements: Vec<(Statement, (Option, Span))>, + statements: Vec<(Statement, (Option, Location))>, ) -> Vec { let last = statements.len().saturating_sub(1); let iter = statements.into_iter().enumerate(); - vecmap(iter, |(i, (statement, (semicolon, span)))| { + vecmap(iter, |(i, (statement, (semicolon, location)))| { statement - .add_semicolon(semicolon, span, i == last, &mut |error| self.errors.push(error)) + .add_semicolon(semicolon, location, i == last, &mut |error| self.errors.push(error)) }) } @@ -882,19 +890,20 @@ mod tests { StatementKind, UnaryOp, UnresolvedTypeData, }, parser::{ + Parser, ParserErrorReason, parser::tests::{ expect_no_errors, get_single_error, get_single_error_reason, get_source_with_error_span, }, - Parser, ParserErrorReason, }, + signed_field::SignedField, token::Token, }; fn parse_expression_no_errors(src: &str) -> Expression { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let expr = parser.parse_expression_or_error(); - assert_eq!(expr.span.end() as usize, src.len()); + assert_eq!(expr.location.span.end() as usize, src.len()); expect_no_errors(&parser.errors); expr } @@ -914,22 +923,20 @@ mod tests { fn parses_integer_literal() { let src = "42"; let expr = parse_expression_no_errors(src); - let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + let ExpressionKind::Literal(Literal::Integer(value)) = expr.kind else { panic!("Expected integer literal"); }; - assert_eq!(field, 42_u128.into()); - assert!(!negative); + assert_eq!(value, SignedField::positive(42_u128)); } #[test] fn parses_negative_integer_literal() { let src = "-42"; let expr = parse_expression_no_errors(src); - let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + let ExpressionKind::Literal(Literal::Integer(value)) = expr.kind else { panic!("Expected integer literal"); }; - assert_eq!(field, 42_u128.into()); - assert!(negative); + assert_eq!(value, SignedField::negative(42_u128)); } #[test] @@ -939,11 +946,10 @@ mod tests { let ExpressionKind::Parenthesized(expr) = expr.kind else { panic!("Expected parenthesized expression"); }; - let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + let ExpressionKind::Literal(Literal::Integer(value)) = expr.kind else { panic!("Expected integer literal"); }; - assert_eq!(field, 42_u128.into()); - assert!(!negative); + assert_eq!(value, SignedField::positive(42_u128)); } #[test] @@ -995,18 +1001,16 @@ mod tests { assert_eq!(exprs.len(), 2); let expr = exprs.remove(0); - let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + let ExpressionKind::Literal(Literal::Integer(value)) = expr.kind else { panic!("Expected integer literal"); }; - assert_eq!(field, 1_u128.into()); - assert!(!negative); + assert_eq!(value, SignedField::positive(1_u128)); let expr = exprs.remove(0); - let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + let ExpressionKind::Literal(Literal::Integer(value)) = expr.kind else { panic!("Expected integer literal"); }; - assert_eq!(field, 2_u128.into()); - assert!(!negative); + assert_eq!(value, SignedField::positive(2_u128)); } #[test] @@ -1023,11 +1027,10 @@ mod tests { panic!("Expected expression statement"); }; - let ExpressionKind::Literal(Literal::Integer(field, negative)) = expr.kind else { + let ExpressionKind::Literal(Literal::Integer(value)) = expr.kind else { panic!("Expected integer literal"); }; - assert_eq!(field, 1_u128.into()); - assert!(!negative); + assert_eq!(value, SignedField::positive(1_u128)); } #[test] @@ -1056,9 +1059,9 @@ mod tests { 2 3 }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let expr = parser.parse_expression_or_error(); - assert_eq!(expr.span.end() as usize, src.len()); + assert_eq!(expr.location.span.end() as usize, src.len()); assert_eq!(parser.errors.len(), 2); assert!(matches!( parser.errors[0].reason(), @@ -1079,11 +1082,13 @@ mod tests { let src = " // Safety: test unsafe { 1 }"; - let expr = parse_expression_no_errors(src); - let ExpressionKind::Unsafe(block, _) = expr.kind else { + let mut parser = Parser::for_str_with_dummy_file(src); + let expr = parser.parse_expression_or_error(); + assert!(parser.errors.is_empty()); + let ExpressionKind::Unsafe(unsafe_expression) = expr.kind else { panic!("Expected unsafe expression"); }; - assert_eq!(block.statements.len(), 1); + assert_eq!(unsafe_expression.block.statements.len(), 1); } #[test] @@ -1092,12 +1097,12 @@ mod tests { /// Safety: test unsafe { 1 }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let expr = parser.parse_expression().unwrap(); - let ExpressionKind::Unsafe(block, _) = expr.kind else { + let ExpressionKind::Unsafe(unsafe_expression) = expr.kind else { panic!("Expected unsafe expression"); }; - assert_eq!(block.statements.len(), 1); + assert_eq!(unsafe_expression.block.statements.len(), 1); } #[test] @@ -1107,7 +1112,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); parser.parse_expression(); let error = get_single_error(&parser.errors, span); assert_eq!(error.to_string(), "Expected an expression but found end of input"); @@ -1120,7 +1125,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); parser.parse_expression(); let reason = get_single_error_reason(&parser.errors, span); let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { @@ -1173,9 +1178,9 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let expr = parser.parse_expression_or_error(); - assert_eq!(expr.span.end() as usize, src.len()); + assert_eq!(expr.location.span.end() as usize, src.len()); let reason = get_single_error_reason(&parser.errors, span); let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { @@ -1334,9 +1339,9 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let expr = parser.parse_expression_or_error(); - assert_eq!(expr.span.end() as usize, src.len()); + assert_eq!(expr.location.span.end() as usize, src.len()); let reason = get_single_error_reason(&parser.errors, span); let ParserErrorReason::ExpectedTokenSeparatingTwoItems { token, items } = reason else { panic!("Expected a different error"); @@ -1355,7 +1360,7 @@ mod tests { #[test] fn parses_call_with_wrong_expression() { let src = "foo(]) "; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); parser.parse_expression_or_error(); assert!(!parser.errors.is_empty()); } @@ -1478,7 +1483,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let expr = parser.parse_expression_or_error(); let error = get_single_error(&parser.errors, span); @@ -1506,7 +1511,7 @@ mod tests { ^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let expr = parser.parse_expression_or_error(); let error = get_single_error(&parser.errors, span); @@ -1595,7 +1600,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); parser.parse_expression(); let error = get_single_error(&parser.errors, span); assert_eq!(error.to_string(), "Expected a type but found end of input"); @@ -1616,9 +1621,9 @@ mod tests { fn parses_operators() { for operator in BinaryOpKind::iter() { let src = format!("1 {operator} 2"); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let expr = parser.parse_expression_or_error(); - assert_eq!(expr.span.end() as usize, src.len()); + assert_eq!(expr.location.span.end() as usize, src.len()); assert!(parser.errors.is_empty(), "Expected no errors for {operator}"); let ExpressionKind::Infix(infix_expr) = expr.kind else { panic!("Expected infix for {operator}"); @@ -1637,14 +1642,22 @@ mod tests { let multiply_or_divide_or_modulo = "1 * 2 / 3 % 4"; let expected_multiply_or_divide_or_modulo = "(((1 * 2) / 3) % 4)"; - let add_or_subtract = format!("{multiply_or_divide_or_modulo} + {multiply_or_divide_or_modulo} - {multiply_or_divide_or_modulo}"); - let expected_add_or_subtract = format!("(({expected_multiply_or_divide_or_modulo} + {expected_multiply_or_divide_or_modulo}) - {expected_multiply_or_divide_or_modulo})"); + let add_or_subtract = format!( + "{multiply_or_divide_or_modulo} + {multiply_or_divide_or_modulo} - {multiply_or_divide_or_modulo}" + ); + let expected_add_or_subtract = format!( + "(({expected_multiply_or_divide_or_modulo} + {expected_multiply_or_divide_or_modulo}) - {expected_multiply_or_divide_or_modulo})" + ); let shift = format!("{add_or_subtract} << {add_or_subtract} >> {add_or_subtract}"); - let expected_shift = format!("(({expected_add_or_subtract} << {expected_add_or_subtract}) >> {expected_add_or_subtract})"); + let expected_shift = format!( + "(({expected_add_or_subtract} << {expected_add_or_subtract}) >> {expected_add_or_subtract})" + ); let less_or_greater = format!("{shift} < {shift} > {shift} <= {shift} >= {shift}"); - let expected_less_or_greater = format!("(((({expected_shift} < {expected_shift}) > {expected_shift}) <= {expected_shift}) >= {expected_shift})"); + let expected_less_or_greater = format!( + "(((({expected_shift} < {expected_shift}) > {expected_shift}) <= {expected_shift}) >= {expected_shift})" + ); let xor = format!("{less_or_greater} ^ {less_or_greater}"); let expected_xor = format!("({expected_less_or_greater} ^ {expected_less_or_greater})"); @@ -1810,7 +1823,7 @@ mod tests { ^^^^^^^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let expression = parser.parse_expression_or_error(); let ExpressionKind::Constrain(constrain) = expression.kind else { panic!("Expected constrain expression"); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs index 29e864200f3e..f10b790e63f6 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs @@ -13,28 +13,28 @@ use crate::{ }; use acvm::AcirField; -use noirc_errors::Span; +use noirc_errors::{Location, Span}; use super::parse_many::separated_by_comma_until_right_paren; use super::pattern::SelfPattern; -use super::{pattern::PatternOrSelf, Parser}; +use super::{Parser, pattern::PatternOrSelf}; pub(crate) struct FunctionDefinitionWithOptionalBody { pub(crate) name: Ident, pub(crate) generics: UnresolvedGenerics, pub(crate) parameters: Vec, pub(crate) body: Option, - pub(crate) span: Span, + pub(crate) location: Location, pub(crate) where_clause: Vec, pub(crate) return_type: FunctionReturnType, pub(crate) return_visibility: Visibility, } -impl<'a> Parser<'a> { +impl Parser<'_> { /// Function = 'fn' identifier Generics FunctionParameters ( '->' Visibility Type )? WhereClause ( Block | ';' ) pub(crate) fn parse_function( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, visibility: ItemVisibility, is_comptime: bool, is_unconstrained: bool, @@ -52,7 +52,7 @@ impl<'a> Parser<'a> { pub(crate) fn parse_function_definition( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, visibility: ItemVisibility, is_comptime: bool, is_unconstrained: bool, @@ -74,7 +74,7 @@ impl<'a> Parser<'a> { generics: func.generics, parameters: func.parameters, body: func.body.unwrap_or_else(empty_body), - span: func.span, + location: func.location, where_clause: func.where_clause, return_type: func.return_type, return_visibility: func.return_visibility, @@ -88,7 +88,7 @@ impl<'a> Parser<'a> { ) -> FunctionDefinitionWithOptionalBody { let Some(name) = self.eat_ident() else { self.expected_identifier(); - return empty_function(self.previous_token_span); + return empty_function(self.previous_token_location); }; let generics = self.parse_generics(); @@ -99,7 +99,7 @@ impl<'a> Parser<'a> { None => { self.push_error( ParserErrorReason::MissingParametersForFunctionDefinition, - name.span(), + name.location(), ); Vec::new() } @@ -109,15 +109,32 @@ impl<'a> Parser<'a> { let visibility = self.parse_visibility(); (FunctionReturnType::Ty(self.parse_type_or_error()), visibility) } else { - (FunctionReturnType::Default(self.span_at_previous_token_end()), Visibility::Private) + // This will return the span between `)` and `{` + // + // fn foo() { } + // ^^^ + let mut location = self.previous_token_location.merge(self.current_token_location); + + // Here we change it to this (if there's space) + // + // fn foo() { } + // ^ + if location.span.end() - location.span.start() >= 3 { + location = Location::new( + Span::from(location.span.start() + 1..location.span.end() - 1), + location.file, + ); + } + + (FunctionReturnType::Default(location), Visibility::Private) }; let where_clause = self.parse_where_clause(); - let body_start_span = self.current_token_span; + let body_start_location = self.current_token_location; let body = if self.eat_semicolons() { if !allow_optional_body { - self.push_error(ParserErrorReason::ExpectedFunctionBody, body_start_span); + self.push_error(ParserErrorReason::ExpectedFunctionBody, body_start_location); } None @@ -130,7 +147,7 @@ impl<'a> Parser<'a> { generics, parameters, body, - span: self.span_since(body_start_span), + location: self.location_since(body_start_location), where_clause, return_type, return_visibility, @@ -154,7 +171,7 @@ impl<'a> Parser<'a> { fn parse_function_parameter(&mut self, allow_self: bool) -> Option { loop { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let pattern_or_self = if allow_self { self.parse_pattern_or_self() @@ -175,49 +192,54 @@ impl<'a> Parser<'a> { }; return Some(match pattern_or_self { - PatternOrSelf::Pattern(pattern) => self.pattern_param(pattern, start_span), + PatternOrSelf::Pattern(pattern) => self.pattern_param(pattern, start_location), PatternOrSelf::SelfPattern(self_pattern) => self.self_pattern_param(self_pattern), }); } } - fn pattern_param(&mut self, pattern: Pattern, start_span: Span) -> Param { + fn pattern_param(&mut self, pattern: Pattern, start_location: Location) -> Param { let (visibility, typ) = if !self.eat_colon() { self.push_error( ParserErrorReason::MissingTypeForFunctionParameter, - Span::from(pattern.span().start()..self.current_token_span.end()), + pattern.location().merge(self.current_token_location), ); let visibility = Visibility::Private; - let typ = UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }; + let typ = + UnresolvedType { typ: UnresolvedTypeData::Error, location: Location::dummy() }; (visibility, typ) } else { - (self.parse_visibility(), self.parse_type_or_error()) + ( + self.parse_visibility(), + self.parse_type_or_error_with_recovery(&[Token::Comma, Token::RightParen]), + ) }; - Param { visibility, pattern, typ, span: self.span_since(start_span) } + Param { visibility, pattern, typ, location: self.location_since(start_location) } } fn self_pattern_param(&mut self, self_pattern: SelfPattern) -> Param { - let ident_span = self.previous_token_span; - let ident = Ident::new("self".to_string(), ident_span); - let path = Path::from_single("Self".to_owned(), ident_span); + let ident_location = self.previous_token_location; + let ident = Ident::new("self".to_string(), ident_location); + let path = Path::from_single("Self".to_owned(), ident_location); let no_args = GenericTypeArgs::default(); - let mut self_type = UnresolvedTypeData::Named(path, no_args, true).with_span(ident_span); + let mut self_type = + UnresolvedTypeData::Named(path, no_args, true).with_location(ident_location); let mut pattern = Pattern::Identifier(ident); if self_pattern.reference { - self_type = - UnresolvedTypeData::MutableReference(Box::new(self_type)).with_span(ident_span); + self_type = UnresolvedTypeData::MutableReference(Box::new(self_type)) + .with_location(ident_location); } else if self_pattern.mutable { - pattern = Pattern::Mutable(Box::new(pattern), ident_span, true); + pattern = Pattern::Mutable(Box::new(pattern), ident_location, true); } Param { visibility: Visibility::Private, pattern, typ: self_type, - span: self.span_since(ident_span), + location: self.location_since(ident_location), } } @@ -256,17 +278,20 @@ impl<'a> Parser<'a> { Visibility::Private } - fn validate_attributes(&mut self, attributes: Vec<(Attribute, Span)>) -> Attributes { + fn validate_attributes(&mut self, attributes: Vec<(Attribute, Location)>) -> Attributes { let mut function = None; let mut secondary = Vec::new(); - for (index, (attribute, span)) in attributes.into_iter().enumerate() { + for (index, (attribute, location)) in attributes.into_iter().enumerate() { match attribute { Attribute::Function(attr) => { if function.is_none() { function = Some((attr, index)); } else { - self.push_error(ParserErrorReason::MultipleFunctionAttributesFound, span); + self.push_error( + ParserErrorReason::MultipleFunctionAttributesFound, + location, + ); } } Attribute::Secondary(attr) => secondary.push(attr), @@ -277,15 +302,16 @@ impl<'a> Parser<'a> { } } -fn empty_function(span: Span) -> FunctionDefinitionWithOptionalBody { +fn empty_function(location: Location) -> FunctionDefinitionWithOptionalBody { + let span = Span::from(location.span.end()..location.span.end()); FunctionDefinitionWithOptionalBody { name: Ident::default(), generics: Vec::new(), parameters: Vec::new(), body: None, - span: Span::from(span.end()..span.end()), + location: Location::new(span, location.file), where_clause: Vec::new(), - return_type: FunctionReturnType::Default(Span::default()), + return_type: FunctionReturnType::Default(Location::dummy()), return_visibility: Visibility::Private, } } @@ -297,21 +323,22 @@ fn empty_body() -> BlockExpression { #[cfg(test)] mod tests { use crate::{ - ast::{ItemVisibility, NoirFunction, UnresolvedTypeData, Visibility}, + ast::{ + IntegerBitSize, ItemVisibility, NoirFunction, Signedness, UnresolvedTypeData, + Visibility, + }, + parse_program_with_dummy_file, parser::{ - parser::{ - parse_program, - tests::{ - expect_no_errors, get_single_error, get_single_error_reason, - get_source_with_error_span, - }, - }, ItemKind, ParserErrorReason, + parser::tests::{ + expect_no_errors, get_single_error, get_single_error_reason, + get_source_with_error_span, + }, }, }; fn parse_function_no_error(src: &str) -> NoirFunction { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -405,7 +432,7 @@ mod tests { #[test] fn parse_function_unclosed_parentheses() { let src = "fn foo(x: i32,"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -422,7 +449,7 @@ mod tests { ^^^^^^^^^^^^^^ "; let (src, span) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::MultipleFunctionAttributesFound)); } @@ -434,7 +461,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::ExpectedFunctionBody)); } @@ -446,7 +473,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let ItemKind::Function(noir_function) = &module.items[0].kind else { panic!("Expected function"); @@ -464,7 +491,7 @@ mod tests { ^^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let ItemKind::Function(noir_function) = &module.items[0].kind else { panic!("Expected function"); @@ -482,7 +509,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let ItemKind::Function(noir_function) = &module.items[0].kind else { panic!("Expected function"); @@ -509,8 +536,38 @@ mod tests { ^^^ "; let (src, span) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::MissingParametersForFunctionDefinition)); } + + #[test] + fn parse_function_with_keyword_before_type() { + let src = " + fn foo(x: mut i32, y: i64) {} + ^^^ + "; + let (src, span) = get_source_with_error_span(src); + let (mut module, errors) = parse_program_with_dummy_file(&src); + let error = get_single_error(&errors, span); + assert_eq!(error.to_string(), "Expected a type but found 'mut'"); + + assert_eq!(module.items.len(), 1); + let item = module.items.remove(0); + let ItemKind::Function(noir_function) = item.kind else { + panic!("Expected function"); + }; + + let params = noir_function.parameters(); + assert_eq!(params.len(), 2); + + assert_eq!( + params[0].typ.typ, + UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::ThirtyTwo) + ); + assert_eq!( + params[1].typ.typ, + UnresolvedTypeData::Integer(Signedness::Signed, IntegerBitSize::SixtyFour) + ); + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/generics.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/generics.rs index f577a237615a..0bac5d5f34ba 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/generics.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/generics.rs @@ -3,13 +3,13 @@ use crate::{ GenericTypeArg, GenericTypeArgs, IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, }, - parser::{labels::ParsingRuleLabel, ParserErrorReason}, + parser::{ParserErrorReason, labels::ParsingRuleLabel}, token::{Keyword, Token, TokenKind}, }; -use super::{parse_many::separated_by_comma, Parser}; +use super::{Parser, parse_many::separated_by_comma}; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Generics = ( '<' GenericsList? '>' )? /// /// GenericsList = Generic ( ',' Generic )* ','? @@ -71,11 +71,11 @@ impl<'a> Parser<'a> { // If we didn't get a type after the colon, error and assume it's u32 self.push_error( ParserErrorReason::MissingTypeForNumericGeneric, - self.current_token_span, + self.current_token_location, ); let typ = UnresolvedType { typ: UnresolvedTypeData::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo), - span: self.span_at_previous_token_end(), + location: self.location_at_previous_token_end(), }; return Some(UnresolvedGeneric::Numeric { ident, typ }); } @@ -85,7 +85,7 @@ impl<'a> Parser<'a> { if matches!(signedness, Signedness::Signed) || matches!(bit_size, IntegerBitSize::SixtyFour) { - self.push_error(ParserErrorReason::ForbiddenNumericGenericType, typ.span); + self.push_error(ParserErrorReason::ForbiddenNumericGenericType, typ.location); } } @@ -97,7 +97,7 @@ impl<'a> Parser<'a> { let token = self.eat_kind(TokenKind::QuotedType)?; match token.into_token() { Token::QuotedType(id) => { - Some(UnresolvedGeneric::Resolved(id, self.previous_token_span)) + Some(UnresolvedGeneric::Resolved(id, self.previous_token_location)) } _ => unreachable!(), } @@ -167,22 +167,22 @@ mod tests { use crate::{ ast::{GenericTypeArgs, IntegerBitSize, Signedness, UnresolvedGeneric, UnresolvedTypeData}, parser::{ + Parser, ParserErrorReason, parser::tests::{ expect_no_errors, get_single_error_reason, get_source_with_error_span, }, - Parser, ParserErrorReason, }, }; fn parse_generics_no_errors(src: &str) -> Vec { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let generics = parser.parse_generics(); expect_no_errors(&parser.errors); generics } fn parse_generic_type_args_no_errors(src: &str) -> GenericTypeArgs { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let generics = parser.parse_generic_type_args(); expect_no_errors(&parser.errors); generics @@ -263,7 +263,7 @@ mod tests { ^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); parser.parse_generics(); let reason = get_single_error_reason(&parser.errors, span); assert!(matches!(reason, ParserErrorReason::ForbiddenNumericGenericType)); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/global.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/global.rs index 2cd8343fe31d..2edb3eeaa187 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/global.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/global.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{ @@ -11,11 +11,11 @@ use crate::{ use super::Parser; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Global = 'global' identifier OptionalTypeAnnotation '=' Expression ';' pub(crate) fn parse_global( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, comptime: bool, mutable: bool, ) -> LetStatement { @@ -31,9 +31,9 @@ impl<'a> Parser<'a> { pattern: ident_to_pattern(Ident::default(), mutable), r#type: UnresolvedType { typ: UnresolvedTypeData::Unspecified, - span: Span::default(), + location: Location::dummy(), }, - expression: Expression { kind: ExpressionKind::Error, span: Span::default() }, + expression: Expression { kind: ExpressionKind::Error, location: Location::dummy() }, attributes, comptime, is_global_let, @@ -47,8 +47,8 @@ impl<'a> Parser<'a> { let expression = if self.eat_assign() { self.parse_expression_or_error() } else { - self.push_error(ParserErrorReason::GlobalWithoutValue, pattern.span()); - Expression { kind: ExpressionKind::Error, span: Span::default() } + self.push_error(ParserErrorReason::GlobalWithoutValue, pattern.location()); + Expression { kind: ExpressionKind::Error, location: Location::dummy() } }; if !self.eat_semicolons() { @@ -61,8 +61,8 @@ impl<'a> Parser<'a> { fn ident_to_pattern(ident: Ident, mutable: bool) -> Pattern { if mutable { - let span = ident.span(); - Pattern::Mutable(Box::new(Pattern::Identifier(ident)), span, false) + let location = ident.location(); + Pattern::Mutable(Box::new(Pattern::Identifier(ident)), location, false) } else { Pattern::Identifier(ident) } @@ -77,20 +77,18 @@ mod tests { ExpressionKind, IntegerBitSize, ItemVisibility, LetStatement, Literal, Pattern, Signedness, UnresolvedTypeData, }, + parse_program_with_dummy_file, parser::{ - parser::{ - parse_program, - tests::{ - expect_no_errors, get_single_error, get_single_error_reason, - get_source_with_error_span, - }, - }, ItemKind, ParserErrorReason, + parser::tests::{ + expect_no_errors, get_single_error, get_single_error_reason, + get_source_with_error_span, + }, }, }; fn parse_global_no_errors(src: &str) -> (LetStatement, ItemVisibility) { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -158,7 +156,7 @@ mod tests { ^^^ "; let (src, span) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::GlobalWithoutValue)); } @@ -170,7 +168,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let error = get_single_error(&errors, span); assert_eq!(error.to_string(), "Expected a ';' but found end of input"); } @@ -188,13 +186,11 @@ mod tests { assert_eq!(let_statement.pattern.span().start(), 16); assert_eq!(let_statement.pattern.span().end(), 19); - let ExpressionKind::Literal(Literal::Integer(abs_value, is_negative)) = - let_statement.expression.kind - else { + let ExpressionKind::Literal(Literal::Integer(value)) = let_statement.expression.kind else { panic!("Expected integer literal expression, got {:?}", let_statement.expression.kind); }; - assert!(is_negative); - assert_eq!(abs_value, FieldElement::from(17u128)); + assert!(value.is_negative); + assert_eq!(value.field, FieldElement::from(17u128)); } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/impls.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/impls.rs index 4b5054984d4f..3fdfbc7d28ad 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/impls.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/impls.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{ @@ -6,32 +6,32 @@ use crate::{ TraitImplItem, TraitImplItemKind, TypeImpl, UnresolvedGeneric, UnresolvedType, UnresolvedTypeData, }, - parser::{labels::ParsingRuleLabel, ParserErrorReason}, + parser::{ParserErrorReason, labels::ParsingRuleLabel}, token::{Keyword, Token}, }; -use super::{parse_many::without_separator, Parser}; +use super::{Parser, parse_many::without_separator}; pub(crate) enum Impl { Impl(TypeImpl), TraitImpl(NoirTraitImpl), } -impl<'a> Parser<'a> { +impl Parser<'_> { /// Impl /// = TypeImpl /// | TraitImpl pub(crate) fn parse_impl(&mut self) -> Impl { let generics = self.parse_generics(); - let type_span_start = self.current_token_span; + let type_location_start = self.current_token_location; let object_type = self.parse_type_or_error(); - let type_span = self.span_since(type_span_start); + let type_location = self.location_since(type_location_start); if self.eat_keyword(Keyword::For) { Impl::TraitImpl(self.parse_trait_impl(generics, object_type)) } else { - Impl::Impl(self.parse_type_impl(object_type, type_span, generics)) + Impl::Impl(self.parse_type_impl(object_type, type_location, generics)) } } @@ -39,18 +39,18 @@ impl<'a> Parser<'a> { fn parse_type_impl( &mut self, object_type: UnresolvedType, - type_span: Span, + type_location: Location, generics: Vec, ) -> TypeImpl { let where_clause = self.parse_where_clause(); let methods = self.parse_type_impl_body(); - TypeImpl { object_type, type_span, generics, where_clause, methods } + TypeImpl { object_type, type_location, generics, where_clause, methods } } /// TypeImplBody = '{' TypeImplItem* '}' /// /// TypeImplItem = OuterDocComments Attributes Modifiers Function - fn parse_type_impl_body(&mut self) -> Vec<(Documented, Span)> { + fn parse_type_impl_body(&mut self) -> Vec<(Documented, Location)> { if !self.eat_left_brace() { self.expected_token(Token::LeftBrace); return Vec::new(); @@ -63,10 +63,10 @@ impl<'a> Parser<'a> { ) } - fn parse_type_impl_method(&mut self) -> Option<(Documented, Span)> { + fn parse_type_impl_method(&mut self) -> Option<(Documented, Location)> { self.parse_item_in_list(ParsingRuleLabel::Function, |parser| { let doc_comments = parser.parse_outer_doc_comments(); - let start_span = parser.current_token_span; + let start_location = parser.current_token_location; let attributes = parser.parse_attributes(); let modifiers = parser.parse_modifiers( false, // allow mutable @@ -80,7 +80,7 @@ impl<'a> Parser<'a> { modifiers.unconstrained.is_some(), true, // allow_self ); - Some((Documented::new(method, doc_comments), parser.span_since(start_span))) + Some((Documented::new(method, doc_comments), parser.location_since(start_location))) } else { parser.modifiers_not_followed_by_an_item(modifiers); None @@ -118,11 +118,11 @@ impl<'a> Parser<'a> { fn parse_trait_impl_item(&mut self) -> Option> { self.parse_item_in_list(ParsingRuleLabel::TraitImplItem, |parser| { - let start_span = parser.current_token_span; + let start_location = parser.current_token_location; let doc_comments = parser.parse_outer_doc_comments(); if let Some(kind) = parser.parse_trait_impl_item_kind() { - let item = TraitImplItem { kind, span: parser.span_since(start_span) }; + let item = TraitImplItem { kind, location: parser.location_since(start_location) }; Some(Documented::new(item, doc_comments)) } else { None @@ -157,14 +157,17 @@ impl<'a> Parser<'a> { self.eat_semicolons(); return Some(TraitImplItemKind::Type { name: Ident::default(), - alias: UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }, + alias: UnresolvedType { + typ: UnresolvedTypeData::Error, + location: Location::dummy(), + }, }); }; let alias = if self.eat_assign() { self.parse_type_or_error() } else { - UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() } + UnresolvedType { typ: UnresolvedTypeData::Error, location: Location::dummy() } }; self.eat_semicolons(); @@ -192,7 +195,7 @@ impl<'a> Parser<'a> { self.parse_expression_or_error() } else { self.expected_token(Token::Assign); - Expression { kind: ExpressionKind::Error, span: Span::default() } + Expression { kind: ExpressionKind::Error, location: Location::dummy() } }; self.eat_semicolons(); @@ -210,7 +213,7 @@ impl<'a> Parser<'a> { if modifiers.visibility != ItemVisibility::Private { self.push_error( ParserErrorReason::TraitImplVisibilityIgnored, - modifiers.visibility_span, + modifiers.visibility_location, ); } @@ -236,17 +239,15 @@ mod tests { ast::{ ItemVisibility, NoirTraitImpl, Pattern, TraitImplItemKind, TypeImpl, UnresolvedTypeData, }, + parse_program_with_dummy_file, parser::{ - parser::{ - parse_program, - tests::{expect_no_errors, get_single_error, get_source_with_error_span}, - }, ItemKind, + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, }, }; fn parse_type_impl_no_errors(src: &str) -> TypeImpl { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -257,7 +258,7 @@ mod tests { } fn parse_trait_impl_no_errors(src: &str) -> NoirTraitImpl { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -402,7 +403,7 @@ mod tests { #[test] fn parse_empty_impl_missing_right_brace() { let src = "impl Foo {"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -415,7 +416,7 @@ mod tests { #[test] fn parse_empty_impl_incorrect_body() { let src = "impl Foo { hello fn foo() {} }"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -538,7 +539,7 @@ mod tests { ^^^^^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -558,7 +559,7 @@ mod tests { ^^^^^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let item = &module.items[0]; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/infix.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/infix.rs index f006923b8a25..c900228cc866 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/infix.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/infix.rs @@ -1,4 +1,4 @@ -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Located, Location}; use crate::{ ast::{BinaryOpKind, Expression, ExpressionKind, InfixExpression}, @@ -157,22 +157,22 @@ impl<'a> Parser<'a> { Next: FnMut(&mut Parser<'a>, bool) -> Option, Op: FnMut(&mut Parser<'a>) -> Option, { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let mut lhs = next(self, allow_constructors)?; loop { - let operator_start_span = self.current_token_span; + let operator_start_location = self.current_token_location; let Some(operator) = op(self) else { break; }; - let operator = Spanned::from(operator_start_span, operator); + let operator = Located::from(operator_start_location, operator); let Some(rhs) = next(self, allow_constructors) else { self.push_expected_expression(); break; }; - lhs = self.new_infix_expression(lhs, operator, rhs, start_span); + lhs = self.new_infix_expression(lhs, operator, rhs, start_location); } Some(lhs) @@ -181,13 +181,13 @@ impl<'a> Parser<'a> { fn new_infix_expression( &self, lhs: Expression, - operator: Spanned, + operator: Located, rhs: Expression, - start_span: Span, + start_location: Location, ) -> Expression { let infix_expr = InfixExpression { lhs, operator, rhs }; let kind = ExpressionKind::Infix(Box::new(infix_expr)); - let span = self.span_since(start_span); - Expression { kind, span } + let location = self.location_since(start_location); + Expression { kind, location } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item.rs index 6fa67ff08533..121a1e749f2a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item.rs @@ -1,11 +1,11 @@ use iter_extended::vecmap; use crate::{ - parser::{labels::ParsingRuleLabel, Item, ItemKind, ParserErrorReason}, + parser::{Item, ItemKind, ParserErrorReason, labels::ParsingRuleLabel}, token::{Keyword, Token}, }; -use super::{impls::Impl, parse_many::without_separator, Parser}; +use super::{Parser, impls::Impl, parse_many::without_separator}; impl<'a> Parser<'a> { pub(crate) fn parse_top_level_items(&mut self) -> Vec { @@ -89,16 +89,16 @@ impl<'a> Parser<'a> { /// Item = OuterDocComments ItemKind fn parse_item(&mut self) -> Vec { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let doc_comments = self.parse_outer_doc_comments(); let kinds = self.parse_item_kind(); - let span = self.span_since(start_span); + let location = self.location_since(start_location); if kinds.is_empty() && !doc_comments.is_empty() { - self.push_error(ParserErrorReason::DocCommentDoesNotDocumentAnything, start_span); + self.push_error(ParserErrorReason::DocCommentDoesNotDocumentAnything, start_location); } - vecmap(kinds, |kind| Item { kind, span, doc_comments: doc_comments.clone() }) + vecmap(kinds, |kind| Item { kind, location, doc_comments: doc_comments.clone() }) } /// This method returns one 'ItemKind' in the majority of cases. @@ -123,7 +123,7 @@ impl<'a> Parser<'a> { return vec![ItemKind::InnerAttribute(kind)]; } - let start_span = self.current_token_span; + let start_location = self.current_token_location; let attributes = self.parse_attributes(); let modifiers = self.parse_modifiers( @@ -149,7 +149,7 @@ impl<'a> Parser<'a> { return vec![ItemKind::Struct(self.parse_struct( attributes, modifiers.visibility, - start_span, + start_location, ))]; } @@ -159,7 +159,7 @@ impl<'a> Parser<'a> { return vec![ItemKind::Enum(self.parse_enum( attributes, modifiers.visibility, - start_span, + start_location, ))]; } @@ -176,7 +176,7 @@ impl<'a> Parser<'a> { self.comptime_mutable_and_unconstrained_not_applicable(modifiers); let (noir_trait, noir_impl) = - self.parse_trait(attributes, modifiers.visibility, start_span); + self.parse_trait(attributes, modifiers.visibility, start_location); let mut output = vec![ItemKind::Trait(noir_trait)]; if let Some(noir_impl) = noir_impl { output.push(ItemKind::TraitImpl(noir_impl)); @@ -202,7 +202,7 @@ impl<'a> Parser<'a> { self.comptime_mutable_and_unconstrained_not_applicable(modifiers); return vec![ItemKind::TypeAlias( - self.parse_type_alias(modifiers.visibility, start_span), + self.parse_type_alias(modifiers.visibility, start_location), )]; } @@ -235,7 +235,7 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use crate::{ - parse_program, + parse_program_with_dummy_file, parser::parser::tests::{get_single_error, get_source_with_error_span}, }; @@ -246,7 +246,7 @@ mod tests { ^^^^^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 2); let error = get_single_error(&errors, span); assert_eq!(error.to_string(), "Expected an item but found 'hello'"); @@ -259,7 +259,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let error = get_single_error(&errors, span); assert_eq!(error.to_string(), "Expected a '}' but found end of input"); @@ -273,7 +273,7 @@ mod tests { ^^^^^^^^^^^^^^^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let error = get_single_error(&errors, span); assert!(error.to_string().contains("This doc comment doesn't document anything")); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item_visibility.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item_visibility.rs index 5aea5f6a45f8..91ace6a62ce3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item_visibility.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/item_visibility.rs @@ -5,7 +5,7 @@ use crate::{ use super::Parser; -impl<'a> Parser<'a> { +impl Parser<'_> { /// ItemVisibility /// = 'pub' // ItemVisibility::Public /// | 'pub' '(' 'crate' ')' // ItemVisibility::PublicCrate @@ -39,15 +39,15 @@ mod tests { use crate::{ ast::ItemVisibility, parser::{ - parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, Parser, + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, }, }; #[test] fn parses_private_visibility() { let src = "("; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let visibility = parser.parse_item_visibility(); expect_no_errors(&parser.errors); assert_eq!(visibility, ItemVisibility::Private); @@ -56,7 +56,7 @@ mod tests { #[test] fn parses_public_visibility() { let src = "pub"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let visibility = parser.parse_item_visibility(); expect_no_errors(&parser.errors); assert_eq!(visibility, ItemVisibility::Public); @@ -69,7 +69,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let visibility = parser.parse_item_visibility(); assert_eq!(visibility, ItemVisibility::Public); let error = get_single_error(&parser.errors, span); @@ -83,7 +83,7 @@ mod tests { ^^^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let visibility = parser.parse_item_visibility(); assert_eq!(visibility, ItemVisibility::Public); let error = get_single_error(&parser.errors, span); @@ -96,7 +96,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let visibility = parser.parse_item_visibility(); assert_eq!(visibility, ItemVisibility::PublicCrate); let error = get_single_error(&parser.errors, span); @@ -106,7 +106,7 @@ mod tests { #[test] fn parses_public_crate_visibility() { let src = "pub(crate)"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let visibility = parser.parse_item_visibility(); expect_no_errors(&parser.errors); assert_eq!(visibility, ItemVisibility::PublicCrate); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/lambda.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/lambda.rs index a6eeb4286218..25f803c8e1de 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/lambda.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/lambda.rs @@ -4,9 +4,9 @@ use crate::{ token::Token, }; -use super::{parse_many::separated_by_comma, Parser}; +use super::{Parser, parse_many::separated_by_comma}; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Lambda = '|' LambdaParameters? '|' ( '->' Type )? Expression /// /// LambdaParameters = LambdaParameter ( ',' LambdaParameter )? ','? diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/modifiers.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/modifiers.rs index a668d3bae6a7..896a27c04169 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/modifiers.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/modifiers.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ast::ItemVisibility, token::Keyword}; @@ -7,31 +7,31 @@ use super::Parser; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub(crate) struct Modifiers { pub(crate) visibility: ItemVisibility, - pub(crate) visibility_span: Span, - pub(crate) unconstrained: Option, - pub(crate) comptime: Option, - pub(crate) mutable: Option, + pub(crate) visibility_location: Location, + pub(crate) unconstrained: Option, + pub(crate) comptime: Option, + pub(crate) mutable: Option, } -impl<'a> Parser<'a> { +impl Parser<'_> { /// Modifiers = ItemVisibility 'unconstrained'? 'comptime'? 'mut'? /// /// NOTE: we also allow `unconstrained` before the visibility for backwards compatibility. /// The formatter will put it after the visibility. pub(crate) fn parse_modifiers(&mut self, allow_mutable: bool) -> Modifiers { let unconstrained = if self.eat_keyword(Keyword::Unconstrained) { - Some(self.previous_token_span) + Some(self.previous_token_location) } else { None }; - let start_span = self.current_token_span; + let start_location = self.current_token_location; let visibility = self.parse_item_visibility(); - let visibility_span = self.span_since(start_span); + let visibility_location = self.location_since(start_location); let unconstrained = if unconstrained.is_none() { if self.eat_keyword(Keyword::Unconstrained) { - Some(self.previous_token_span) + Some(self.previous_token_location) } else { None } @@ -39,14 +39,17 @@ impl<'a> Parser<'a> { unconstrained }; - let comptime = - if self.eat_keyword(Keyword::Comptime) { Some(self.previous_token_span) } else { None }; + let comptime = if self.eat_keyword(Keyword::Comptime) { + Some(self.previous_token_location) + } else { + None + }; let mutable = if allow_mutable && self.eat_keyword(Keyword::Mut) { - Some(self.previous_token_span) + Some(self.previous_token_location) } else { None }; - Modifiers { visibility, visibility_span, unconstrained, comptime, mutable } + Modifiers { visibility, visibility_location, unconstrained, comptime, mutable } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/module.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/module.rs index 1bc3d7b5bebb..cae0a328d756 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/module.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/module.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{Ident, ItemVisibility, ModuleDeclaration}, @@ -8,12 +8,12 @@ use crate::{ use super::Parser; -impl<'a> Parser<'a> { +impl Parser<'_> { /// ModOrContract /// = ( 'mod' | 'contract' ) identifier ( '{' Module '}' | ';' ) pub(super) fn parse_mod_or_contract( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, is_contract: bool, visibility: ItemVisibility, ) -> ItemKind { @@ -58,16 +58,16 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { - use crate::parser::{ - parser::{parse_program, tests::expect_no_errors}, - ItemKind, + use crate::{ + parse_program_with_dummy_file, + parser::{ItemKind, parser::tests::expect_no_errors}, }; #[test] fn parse_module_declaration() { // TODO: `contract foo;` is parsed correctly but we don't it's considered a module let src = "mod foo;"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -80,7 +80,7 @@ mod tests { #[test] fn parse_submodule() { let src = "mod foo { mod bar; }"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -95,7 +95,7 @@ mod tests { #[test] fn parse_contract() { let src = "contract foo {}"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/parse_many.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/parse_many.rs index be156eb16188..1c77aac7f180 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/parse_many.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/parse_many.rs @@ -42,11 +42,7 @@ impl<'a> Parser<'a> { F: FnMut(&mut Parser<'a>) -> Option, { let f = |x: &mut Parser<'a>| { - if let Some(result) = f(x) { - vec![result] - } else { - vec![] - } + if let Some(result) = f(x) { vec![result] } else { vec![] } }; self.parse_many_to_many_return_trailing_separator_if_any(items, separated_by, f) } @@ -70,7 +66,7 @@ impl<'a> Parser<'a> { } } - let start_span = self.current_token_span; + let start_location = self.current_token_location; let mut new_elements = f(self); if new_elements.is_empty() { if let Some(end) = &separated_by.until { @@ -81,7 +77,7 @@ impl<'a> Parser<'a> { if let Some(separator) = &separated_by.token { if !trailing_separator && !elements.is_empty() { - self.expected_token_separating_items(separator.clone(), items, start_span); + self.expected_token_separating_items(separator.clone(), items, start_location); } } 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 99aedc6df897..a58bd9e1bb18 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 @@ -3,13 +3,13 @@ use crate::parser::ParserErrorReason; use crate::token::{Keyword, Token}; -use noirc_errors::Span; +use noirc_errors::Location; use crate::{parser::labels::ParsingRuleLabel, token::TokenKind}; use super::Parser; -impl<'a> Parser<'a> { +impl Parser<'_> { #[cfg(test)] pub(crate) fn parse_path_or_error(&mut self) -> Path { if let Some(path) = self.parse_path() { @@ -17,11 +17,7 @@ impl<'a> Parser<'a> { } else { self.expected_label(ParsingRuleLabel::Path); - Path { - segments: Vec::new(), - kind: PathKind::Plain, - span: self.span_at_previous_token_end(), - } + Path::plain(Vec::new(), self.location_at_previous_token_end()) } } @@ -44,11 +40,7 @@ impl<'a> Parser<'a> { } else { self.expected_label(ParsingRuleLabel::Path); - Path { - segments: Vec::new(), - kind: PathKind::Plain, - span: self.span_at_previous_token_end(), - } + Path::plain(Vec::new(), self.location_at_previous_token_end()) } } @@ -65,7 +57,7 @@ impl<'a> Parser<'a> { allow_turbofish: bool, allow_trailing_double_colon: bool, ) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let kind = self.parse_path_kind(); @@ -73,7 +65,7 @@ impl<'a> Parser<'a> { kind, allow_turbofish, allow_trailing_double_colon, - start_span, + start_location, )?; if path.segments.is_empty() { if path.kind != PathKind::Plain { @@ -90,20 +82,16 @@ impl<'a> Parser<'a> { kind: PathKind, allow_turbofish: bool, allow_trailing_double_colon: bool, - start_span: Span, + start_location: Location, ) -> Option { let path = self.parse_path_after_kind( kind, allow_turbofish, allow_trailing_double_colon, - start_span, + start_location, ); - if path.segments.is_empty() && path.kind == PathKind::Plain { - None - } else { - Some(path) - } + if path.segments.is_empty() && path.kind == PathKind::Plain { None } else { Some(path) } } /// Parses a path assuming the path's kind (plain, `crate::`, `super::`, etc.) @@ -114,14 +102,14 @@ impl<'a> Parser<'a> { kind: PathKind, allow_turbofish: bool, allow_trailing_double_colon: bool, - start_span: Span, + start_location: Location, ) -> Path { let mut segments = Vec::new(); if self.token.kind() == TokenKind::Ident { loop { let ident = self.eat_ident().unwrap(); - let span = ident.span(); + let location = ident.location(); let generics = if allow_turbofish && self.at(Token::DoubleColon) @@ -133,7 +121,11 @@ impl<'a> Parser<'a> { None }; - segments.push(PathSegment { ident, generics, span }); + segments.push(PathSegment { + ident, + generics, + location: self.location_since(location), + }); if self.at(Token::DoubleColon) && matches!(self.next_token.token(), Token::Ident(..)) @@ -151,7 +143,8 @@ impl<'a> Parser<'a> { } } - Path { segments, kind, span: self.span_since(start_span) } + let location = self.location_since(start_location); + Path { segments, kind, kind_location: start_location, location } } /// PathGenerics = GenericTypeArgs @@ -165,7 +158,7 @@ impl<'a> Parser<'a> { let generics = self.parse_generic_type_args(); for (name, _typ) in &generics.named_args { - self.push_error(on_named_arg_error.clone(), name.span()); + self.push_error(on_named_arg_error.clone(), name.location()); } Some(generics.ordered_args) @@ -208,7 +201,7 @@ impl<'a> Parser<'a> { ident } else { self.expected_identifier(); - Ident::new(String::new(), self.span_at_previous_token_end()) + Ident::new(String::new(), self.location_at_previous_token_end()) }; Some(AsTraitPath { typ, trait_path, trait_generics, impl_item }) @@ -221,13 +214,13 @@ mod tests { use crate::{ ast::{Path, PathKind}, parser::{ - parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, Parser, + parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, }, }; fn parse_path_no_errors(src: &str) -> Path { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let path = parser.parse_path_or_error(); expect_no_errors(&parser.errors); path @@ -294,9 +287,9 @@ mod tests { #[test] fn parses_plain_one_segment_with_trailing_colons() { let src = "foo::"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let path = parser.parse_path_or_error(); - assert_eq!(path.span.end() as usize, src.len()); + assert_eq!(path.location.span.end() as usize, src.len()); assert_eq!(parser.errors.len(), 1); assert_eq!(path.kind, PathKind::Plain); assert_eq!(path.segments.len(), 1); @@ -322,9 +315,9 @@ mod tests { #[test] fn parses_path_stops_before_trailing_double_colon() { let src = "foo::bar::"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let path = parser.parse_path_or_error(); - assert_eq!(path.span.end() as usize, src.len()); + assert_eq!(path.location.span.end() as usize, src.len()); assert_eq!(parser.errors.len(), 1); assert_eq!(path.to_string(), "foo::bar"); } @@ -332,9 +325,9 @@ mod tests { #[test] fn parses_path_with_turbofish_stops_before_trailing_double_colon() { let src = "foo::bar::<1>::"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let path = parser.parse_path_or_error(); - assert_eq!(path.span.end() as usize, src.len()); + assert_eq!(path.location.span.end() as usize, src.len()); assert_eq!(parser.errors.len(), 1); assert_eq!(path.to_string(), "foo::bar::<1>"); } @@ -346,7 +339,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let path = parser.parse_path(); assert!(path.is_none()); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/pattern.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/pattern.rs index 50779e9ccffa..61fb1572c172 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/pattern.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/pattern.rs @@ -1,14 +1,14 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{Ident, Path, Pattern}, - parser::{labels::ParsingRuleLabel, ParserErrorReason}, + parser::{ParserErrorReason, labels::ParsingRuleLabel}, token::{Keyword, Token, TokenKind}, }; use super::{ - parse_many::{separated_by_comma_until_right_brace, separated_by_comma_until_right_paren}, Parser, + parse_many::{separated_by_comma_until_right_brace, separated_by_comma_until_right_paren}, }; pub(crate) enum PatternOrSelf { @@ -22,29 +22,29 @@ pub(crate) struct SelfPattern { pub(crate) mutable: bool, } -impl<'a> Parser<'a> { +impl Parser<'_> { pub(crate) fn parse_pattern_or_error(&mut self) -> Pattern { if let Some(pattern) = self.parse_pattern() { return pattern; } self.expected_label(ParsingRuleLabel::Pattern); - Pattern::Identifier(Ident::new(String::new(), self.span_at_previous_token_end())) + Pattern::Identifier(Ident::new(String::new(), self.location_at_previous_token_end())) } /// Pattern /// = 'mut' PatternNoMut pub(crate) fn parse_pattern(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let mutable = self.eat_keyword(Keyword::Mut); - self.parse_pattern_after_modifiers(mutable, start_span) + self.parse_pattern_after_modifiers(mutable, start_location) } /// PatternOrSelf /// = Pattern /// | SelfPattern pub(crate) fn parse_pattern_or_self(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.next_is_colon() && self.eat_self() { return Some(PatternOrSelf::SelfPattern(SelfPattern { @@ -61,7 +61,7 @@ impl<'a> Parser<'a> { })); } else { return Some(PatternOrSelf::Pattern( - self.parse_pattern_after_modifiers(true, start_span)?, + self.parse_pattern_after_modifiers(true, start_location)?, )); } } @@ -77,15 +77,15 @@ impl<'a> Parser<'a> { } else { self.push_error( ParserErrorReason::RefMutCanOnlyBeUsedWithSelf, - self.current_token_span, + self.current_token_location, ); return Some(PatternOrSelf::Pattern( - self.parse_pattern_after_modifiers(true, start_span)?, + self.parse_pattern_after_modifiers(true, start_location)?, )); } } - Some(PatternOrSelf::Pattern(self.parse_pattern_after_modifiers(false, start_span)?)) + Some(PatternOrSelf::Pattern(self.parse_pattern_after_modifiers(false, start_location)?)) } fn next_is_colon(&self) -> bool { @@ -95,13 +95,13 @@ impl<'a> Parser<'a> { pub(crate) fn parse_pattern_after_modifiers( &mut self, mutable: bool, - start_span: Span, + start_location: Location, ) -> Option { let pattern = self.parse_pattern_no_mut()?; Some(if mutable { Pattern::Mutable( Box::new(pattern), - self.span_since(start_span), + self.location_since(start_location), false, // is synthesized ) } else { @@ -117,7 +117,7 @@ impl<'a> Parser<'a> { /// /// IdentifierPattern = identifier fn parse_pattern_no_mut(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if let Some(pattern) = self.parse_interned_pattern() { return Some(pattern); @@ -131,18 +131,18 @@ impl<'a> Parser<'a> { if self.at_built_in_type() { self.push_error( ParserErrorReason::ExpectedPatternButFoundType(self.token.token().clone()), - self.current_token_span, + self.current_token_location, ); } return None; }; if self.eat_left_brace() { - return Some(self.parse_struct_pattern(path, start_span)); + return Some(self.parse_struct_pattern(path, start_location)); } if !path.is_ident() { - self.push_error(ParserErrorReason::InvalidPattern, path.span); + self.push_error(ParserErrorReason::InvalidPattern, path.location); let ident = path.segments.pop().unwrap().ident; return Some(Pattern::Identifier(ident)); @@ -158,7 +158,7 @@ impl<'a> Parser<'a> { match token.into_token() { Token::InternedPattern(pattern) => { - Some(Pattern::Interned(pattern, self.previous_token_span)) + Some(Pattern::Interned(pattern, self.previous_token_location)) } _ => unreachable!(), } @@ -168,7 +168,7 @@ impl<'a> Parser<'a> { /// /// PatternList = Pattern ( ',' Pattern )* ','? fn parse_tuple_pattern(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.eat_left_paren() { return None; @@ -180,7 +180,7 @@ impl<'a> Parser<'a> { Self::parse_tuple_pattern_element, ); - Some(Pattern::Tuple(patterns, self.span_since(start_span))) + Some(Pattern::Tuple(patterns, self.location_since(start_location))) } fn parse_tuple_pattern_element(&mut self) -> Option { @@ -197,14 +197,14 @@ impl<'a> Parser<'a> { /// StructPatternFields = StructPatternField ( ',' StructPatternField )? ','? /// /// StructPatternField = identifier ( ':' Pattern )? - fn parse_struct_pattern(&mut self, path: Path, start_span: Span) -> Pattern { + fn parse_struct_pattern(&mut self, path: Path, start_location: Location) -> Pattern { let fields = self.parse_many( "struct fields", separated_by_comma_until_right_brace(), Self::parse_struct_pattern_field, ); - Pattern::Struct(path, fields, self.span_since(start_span)) + Pattern::Struct(path, fields, self.location_since(start_location)) } fn parse_struct_pattern_field(&mut self) -> Option<(Ident, Pattern)> { @@ -254,17 +254,17 @@ mod tests { use crate::{ ast::Pattern, parser::{ + Parser, ParserErrorReason, parser::tests::{ expect_no_errors, get_single_error, get_single_error_reason, get_source_with_error_span, }, - Parser, ParserErrorReason, }, token::{Keyword, Token}, }; fn parse_pattern_no_errors(src: &str) -> Pattern { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let pattern = parser.parse_pattern_or_error(); expect_no_errors(&parser.errors); pattern @@ -307,7 +307,7 @@ mod tests { #[test] fn parses_unclosed_tuple_pattern() { let src = "(foo,"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let pattern = parser.parse_pattern_or_error(); assert_eq!(parser.errors.len(), 1); let Pattern::Tuple(patterns, _) = pattern else { panic!("Expected a tuple pattern") }; @@ -317,7 +317,7 @@ mod tests { #[test] fn parses_struct_pattern_no_fields() { let src = "foo::Bar {}"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let pattern = parser.parse_pattern_or_error(); expect_no_errors(&parser.errors); let Pattern::Struct(path, patterns, _) = pattern else { @@ -353,7 +353,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let pattern = parser.parse_pattern_or_error(); let error = get_single_error(&parser.errors, span); @@ -377,7 +377,7 @@ mod tests { #[test] fn parses_unclosed_struct_pattern() { let src = "foo::Bar { x"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let pattern = parser.parse_pattern_or_error(); assert_eq!(parser.errors.len(), 1); let Pattern::Struct(path, _, _) = pattern else { panic!("Expected a struct pattern") }; @@ -391,7 +391,7 @@ mod tests { ^^^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let pattern = parser.parse_pattern(); assert!(pattern.is_none()); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement.rs index d8679da6ba88..600ddec43c96 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -1,4 +1,4 @@ -use noirc_errors::{Span, Spanned}; +use noirc_errors::{Located, Location}; use crate::{ ast::{ @@ -6,33 +6,30 @@ use crate::{ ForLoopStatement, ForRange, Ident, InfixExpression, LValue, LetStatement, Statement, StatementKind, WhileStatement, }, - parser::{labels::ParsingRuleLabel, ParserErrorReason}, + parser::{ParserErrorReason, labels::ParsingRuleLabel}, token::{Attribute, Keyword, Token, TokenKind}, }; use super::Parser; -impl<'a> Parser<'a> { +impl Parser<'_> { pub(crate) fn parse_statement_or_error(&mut self) -> Statement { if let Some((statement, (_token, _span))) = self.parse_statement() { statement } else { self.expected_label(ParsingRuleLabel::Statement); - Statement { kind: StatementKind::Error, span: self.span_at_previous_token_end() } + Statement { + kind: StatementKind::Error, + location: self.location_at_previous_token_end(), + } } } /// Statement = Attributes StatementKind ';'? - pub(crate) fn parse_statement(&mut self) -> Option<(Statement, (Option, Span))> { + pub(crate) fn parse_statement(&mut self) -> Option<(Statement, (Option, Location))> { loop { - let span_before_doc_comments = self.current_token_span; - let doc_comments = self.parse_outer_doc_comments(); - if !doc_comments.is_empty() { - self.push_error( - ParserErrorReason::DocCommentDoesNotDocumentAnything, - span_before_doc_comments, - ); - } + // Like in Rust, we allow parsing doc comments on top of a statement but they always produce a warning. + self.warn_on_outer_doc_comments(); if !self.current_token_comments.is_empty() { self.statement_comments = Some(std::mem::take(&mut self.current_token_comments)); @@ -41,26 +38,25 @@ impl<'a> Parser<'a> { } let attributes = self.parse_attributes(); - let start_span = self.current_token_span; + let start_location = self.current_token_location; let kind = self.parse_statement_kind(attributes); - self.statement_comments = None; - let (semicolon_token, semicolon_span) = if self.at(Token::Semicolon) { + let (semicolon_token, semicolon_location) = if self.at(Token::Semicolon) { let token = self.token.clone(); self.bump(); - let span = token.to_span(); + let location = token.location(); - (Some(token.into_token()), span) + (Some(token.into_token()), location) } else { - (None, self.previous_token_span) + (None, self.previous_token_location) }; - let span = self.span_since(start_span); + let location = self.location_since(start_location); if let Some(kind) = kind { - let statement = Statement { kind, span }; - return Some((statement, (semicolon_token, semicolon_span))); + let statement = Statement { kind, location }; + return Some((statement, (semicolon_token, semicolon_location))); } self.expected_label(ParsingRuleLabel::Statement); @@ -102,14 +98,14 @@ impl<'a> Parser<'a> { /// ExpressionStatement = Expression fn parse_statement_kind( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, ) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if let Some(token) = self.eat_kind(TokenKind::InternedStatement) { match token.into_token() { Token::InternedStatement(statement) => { - return Some(StatementKind::Interned(statement)) + return Some(StatementKind::Interned(statement)); } _ => unreachable!(), } @@ -125,7 +121,7 @@ impl<'a> Parser<'a> { if self.eat_keyword(Keyword::Return) { self.parse_expression(); - self.push_error(ParserErrorReason::EarlyReturn, self.span_since(start_span)); + self.push_error(ParserErrorReason::EarlyReturn, self.location_since(start_location)); return Some(StatementKind::Error); } @@ -151,26 +147,26 @@ impl<'a> Parser<'a> { } if let Some(kind) = self.parse_if_expr() { - let span = self.span_since(start_span); - return Some(StatementKind::Expression(Expression { kind, span })); + let location = self.location_since(start_location); + return Some(StatementKind::Expression(Expression { kind, location })); } if let Some(kind) = self.parse_match_expr() { - let span = self.span_since(start_span); - return Some(StatementKind::Expression(Expression { kind, span })); + let location = self.location_since(start_location); + return Some(StatementKind::Expression(Expression { kind, location })); } if let Some(block) = self.parse_block() { return Some(StatementKind::Expression(Expression { kind: ExpressionKind::Block(block), - span: self.span_since(start_span), + location: self.location_since(start_location), })); } if let Some(token) = self.eat_kind(TokenKind::InternedLValue) { match token.into_token() { Token::InternedLValue(lvalue) => { - let lvalue = LValue::Interned(lvalue, self.span_since(start_span)); + let lvalue = LValue::Interned(lvalue, self.location_since(start_location)); self.eat_or_error(Token::Assign); let expression = self.parse_expression_or_error(); return Some(StatementKind::Assign(AssignStatement { lvalue, expression })); @@ -188,7 +184,7 @@ impl<'a> Parser<'a> { } else { self.push_error( ParserErrorReason::InvalidLeftHandSideOfAssignment, - expression.span, + expression.location, ); } } @@ -204,13 +200,13 @@ impl<'a> Parser<'a> { }; let expression = Expression::new( ExpressionKind::Infix(Box::new(infix)), - self.span_since(start_span), + self.location_since(start_location), ); return Some(StatementKind::Assign(AssignStatement { lvalue, expression })); } else { self.push_error( ParserErrorReason::InvalidLeftHandSideOfAssignment, - expression.span, + expression.location, ); } } @@ -219,7 +215,7 @@ impl<'a> Parser<'a> { } fn next_is_op_assign(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let operator = if self.next_is(Token::Assign) { match self.token.token() { Token::Plus => Some(BinaryOpKind::Add), @@ -243,7 +239,7 @@ impl<'a> Parser<'a> { if let Some(operator) = operator { self.bump(); self.bump(); - Some(Spanned::from(self.span_since(start_span), operator)) + Some(Located::from(self.location_since(start_location), operator)) } else { None } @@ -251,7 +247,7 @@ impl<'a> Parser<'a> { /// ForStatement = 'for' identifier 'in' ForRange Block fn parse_for(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.eat_keyword(Keyword::For) { return None; @@ -260,76 +256,86 @@ impl<'a> Parser<'a> { let Some(identifier) = self.eat_ident() else { self.expected_identifier(); let identifier = Ident::default(); - return Some(self.empty_for_loop(identifier, start_span)); + return Some(self.empty_for_loop(identifier, start_location)); }; if !self.eat_keyword(Keyword::In) { self.expected_token(Token::Keyword(Keyword::In)); - return Some(self.empty_for_loop(identifier, start_span)); + return Some(self.empty_for_loop(identifier, start_location)); } let range = self.parse_for_range(); - let block_start_span = self.current_token_span; + let block_start_location = self.current_token_location; let block = if let Some(block) = self.parse_block() { Expression { kind: ExpressionKind::Block(block), - span: self.span_since(block_start_span), + location: self.location_since(block_start_location), } } else { self.expected_token(Token::LeftBrace); - Expression { kind: ExpressionKind::Error, span: self.span_since(block_start_span) } + Expression { + kind: ExpressionKind::Error, + location: self.location_since(block_start_location), + } }; - Some(ForLoopStatement { identifier, range, block, span: self.span_since(start_span) }) + Some(ForLoopStatement { + identifier, + range, + block, + location: self.location_since(start_location), + }) } /// LoopStatement = 'loop' Block - fn parse_loop(&mut self) -> Option<(Expression, Span)> { - let start_span = self.current_token_span; + fn parse_loop(&mut self) -> Option<(Expression, Location)> { + let start_location = self.current_token_location; if !self.eat_keyword(Keyword::Loop) { return None; } - self.push_error(ParserErrorReason::ExperimentalFeature("loops"), start_span); - - let block_start_span = self.current_token_span; + let block_start_location = self.current_token_location; let block = if let Some(block) = self.parse_block() { Expression { kind: ExpressionKind::Block(block), - span: self.span_since(block_start_span), + location: self.location_since(block_start_location), } } else { self.expected_token(Token::LeftBrace); - Expression { kind: ExpressionKind::Error, span: self.span_since(block_start_span) } + Expression { + kind: ExpressionKind::Error, + location: self.location_since(block_start_location), + } }; - Some((block, start_span)) + Some((block, start_location)) } /// WhileStatement = 'while' ExpressionExceptConstructor Block fn parse_while(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.eat_keyword(Keyword::While) { return None; } - self.push_error(ParserErrorReason::ExperimentalFeature("while loops"), start_span); - let condition = self.parse_expression_except_constructor_or_error(); - let block_start_span = self.current_token_span; + let block_start_location = self.current_token_location; let block = if let Some(block) = self.parse_block() { Expression { kind: ExpressionKind::Block(block), - span: self.span_since(block_start_span), + location: self.location_since(block_start_location), } } else { self.expected_token(Token::LeftBrace); - Expression { kind: ExpressionKind::Error, span: self.span_since(block_start_span) } + Expression { + kind: ExpressionKind::Error, + location: self.location_since(block_start_location), + } }; - Some(WhileStatement { condition, body: block, while_keyword_span: start_span }) + Some(WhileStatement { condition, body: block, while_keyword_location: start_location }) } /// ForRange @@ -349,15 +355,15 @@ impl<'a> Parser<'a> { } } - fn empty_for_loop(&mut self, identifier: Ident, start_span: Span) -> ForLoopStatement { + fn empty_for_loop(&mut self, identifier: Ident, start_location: Location) -> ForLoopStatement { ForLoopStatement { identifier, range: ForRange::Array(Expression { kind: ExpressionKind::Error, - span: Span::default(), + location: Location::dummy(), }), - block: Expression { kind: ExpressionKind::Error, span: Span::default() }, - span: self.span_since(start_span), + block: Expression { kind: ExpressionKind::Error, location: Location::dummy() }, + location: self.location_since(start_location), } } @@ -373,9 +379,9 @@ impl<'a> Parser<'a> { /// ComptimeFor = 'comptime' ForStatement fn parse_comptime_statement( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, ) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.eat_keyword(Keyword::Comptime) { return None; @@ -384,7 +390,7 @@ impl<'a> Parser<'a> { if let Some(kind) = self.parse_comptime_statement_kind(attributes) { return Some(StatementKind::Comptime(Box::new(Statement { kind, - span: self.span_since(start_span), + location: self.location_since(start_location), }))); } @@ -399,14 +405,14 @@ impl<'a> Parser<'a> { fn parse_comptime_statement_kind( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, ) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if let Some(block) = self.parse_block() { return Some(StatementKind::Expression(Expression { kind: ExpressionKind::Block(block), - span: self.span_since(start_span), + location: self.location_since(start_location), })); } @@ -422,7 +428,10 @@ impl<'a> Parser<'a> { } /// LetStatement = 'let' pattern OptionalTypeAnnotation '=' Expression - fn parse_let_statement(&mut self, attributes: Vec<(Attribute, Span)>) -> Option { + fn parse_let_statement( + &mut self, + attributes: Vec<(Attribute, Location)>, + ) -> Option { if !self.eat_keyword(Keyword::Let) { return None; } @@ -434,7 +443,7 @@ impl<'a> Parser<'a> { self.parse_expression_or_error() } else { self.expected_token(Token::Assign); - Expression { kind: ExpressionKind::Error, span: self.current_token_span } + Expression { kind: ExpressionKind::Error, location: self.current_token_location } }; Some(LetStatement { @@ -453,16 +462,16 @@ mod tests { use crate::{ ast::{ExpressionKind, ForRange, LValue, Statement, StatementKind, UnresolvedTypeData}, parser::{ + Parser, ParserErrorReason, parser::tests::{ expect_no_errors, get_single_error, get_single_error_reason, get_source_with_error_span, }, - Parser, ParserErrorReason, }, }; fn parse_statement_no_errors(src: &str) -> Statement { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement_or_error(); expect_no_errors(&parser.errors); statement @@ -512,7 +521,9 @@ mod tests { fn parses_let_statement_with_unsafe() { let src = "// Safety: comment let x = unsafe { 1 };"; - let statement = parse_statement_no_errors(src); + let mut parser = Parser::for_str_with_dummy_file(src); + let statement = parser.parse_statement_or_error(); + assert!(parser.errors.is_empty()); let StatementKind::Let(let_statement) = statement.kind else { panic!("Expected let statement"); }; @@ -523,7 +534,7 @@ mod tests { fn parses_let_statement_with_unsafe_doc_comment() { let src = "/// Safety: doc comment let x = unsafe { 1 };"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let (statement, _) = parser.parse_statement().unwrap(); let StatementKind::Let(let_statement) = statement.kind else { panic!("Expected let statement"); @@ -531,6 +542,20 @@ mod tests { assert_eq!(let_statement.pattern.to_string(), "x"); } + #[test] + fn parses_let_statement_with_unsafe_after_some_other_comment() { + let src = "// Top comment + // Safety: comment + let x = unsafe { 1 };"; + let mut parser = Parser::for_str_with_dummy_file(src); + let statement = parser.parse_statement_or_error(); + assert!(parser.errors.is_empty()); + let StatementKind::Let(let_statement) = statement.kind else { + panic!("Expected let statement"); + }; + assert_eq!(let_statement.pattern.to_string(), "x"); + } + #[test] fn parses_comptime_block() { let src = "comptime { 1 }"; @@ -644,7 +669,7 @@ mod tests { #[test] fn parses_assignment_with_unsafe() { - let src = "// Safety: test + let src = "// Safety: test x = unsafe { 1 }"; let statement = parse_statement_no_errors(src); let StatementKind::Assign(assign) = statement.kind else { @@ -722,7 +747,7 @@ mod tests { ^^^^^^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let statement = parser.parse_statement_or_error(); assert!(matches!(statement.kind, StatementKind::Error)); let reason = get_single_error_reason(&parser.errors, span); @@ -736,7 +761,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let statement = parser.parse_statement_or_error(); assert!(matches!(statement.kind, StatementKind::Let(..))); let error = get_single_error(&parser.errors, span); @@ -746,7 +771,7 @@ mod tests { #[test] fn recovers_on_unknown_statement_followed_by_semicolon() { let src = " ] ;"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement(); assert!(statement.is_none()); assert_eq!(parser.errors.len(), 2); @@ -755,7 +780,7 @@ mod tests { #[test] fn recovers_on_unknown_statement_followed_by_right_brace() { let src = " ] }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement(); assert!(statement.is_none()); assert_eq!(parser.errors.len(), 2); @@ -764,23 +789,23 @@ mod tests { #[test] fn parses_empty_loop() { let src = "loop { }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement_or_error(); - let StatementKind::Loop(block, span) = statement.kind else { + let StatementKind::Loop(block, location) = statement.kind else { panic!("Expected loop"); }; let ExpressionKind::Block(block) = block.kind else { panic!("Expected block"); }; assert!(block.statements.is_empty()); - assert_eq!(span.start(), 0); - assert_eq!(span.end(), 4); + assert_eq!(location.span.start(), 0); + assert_eq!(location.span.end(), 4); } #[test] fn parses_loop_with_statements() { let src = "loop { 1; 2 }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement_or_error(); let StatementKind::Loop(block, _) = statement.kind else { panic!("Expected loop"); @@ -794,7 +819,7 @@ mod tests { #[test] fn parses_let_with_assert() { let src = "let _ = assert(true);"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement_or_error(); let StatementKind::Let(let_statement) = statement.kind else { panic!("Expected let"); @@ -805,7 +830,7 @@ mod tests { #[test] fn parses_empty_while() { let src = "while true { }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement_or_error(); let StatementKind::While(while_) = statement.kind else { panic!("Expected while"); @@ -814,8 +839,8 @@ mod tests { panic!("Expected block"); }; assert!(block.statements.is_empty()); - assert_eq!(while_.while_keyword_span.start(), 0); - assert_eq!(while_.while_keyword_span.end(), 5); + assert_eq!(while_.while_keyword_location.span.start(), 0); + assert_eq!(while_.while_keyword_location.span.end(), 5); assert_eq!(while_.condition.to_string(), "true"); } @@ -823,7 +848,7 @@ mod tests { #[test] fn parses_while_with_statements() { let src = "while true { 1; 2 }"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let statement = parser.parse_statement_or_error(); let StatementKind::While(while_) = statement.kind else { panic!("Expected while"); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement_or_expression_or_lvalue.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement_or_expression_or_lvalue.rs index fdc187f3fb2a..0ac1b1972c4f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement_or_expression_or_lvalue.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/statement_or_expression_or_lvalue.rs @@ -12,7 +12,7 @@ pub enum StatementOrExpressionOrLValue { LValue(LValue), } -impl<'a> Parser<'a> { +impl Parser<'_> { /// Parses either a statement, an expression or an LValue. Returns `StatementKind::Error` /// if none can be parsed, recording an error if so. /// @@ -20,13 +20,13 @@ impl<'a> Parser<'a> { pub(crate) fn parse_statement_or_expression_or_lvalue( &mut self, ) -> StatementOrExpressionOrLValue { - let start_span = self.current_token_span; + let start_location = self.current_token_location; // First check if it's an interned LValue if let Some(token) = self.eat_kind(TokenKind::InternedLValue) { match token.into_token() { Token::InternedLValue(lvalue) => { - let lvalue = LValue::Interned(lvalue, self.span_since(start_span)); + let lvalue = LValue::Interned(lvalue, self.location_since(start_location)); // If it is, it could be something like `lvalue = expr`: check that. if self.eat(Token::Assign) { @@ -34,7 +34,7 @@ impl<'a> Parser<'a> { let kind = StatementKind::Assign(AssignStatement { lvalue, expression }); return StatementOrExpressionOrLValue::Statement(Statement { kind, - span: self.span_since(start_span), + location: self.location_since(start_location), }); } else { return StatementOrExpressionOrLValue::LValue(lvalue); 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 b066565e6801..62f49035f722 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,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{Documented, Ident, ItemVisibility, NoirStruct, StructField, UnresolvedGenerics}, @@ -6,17 +6,17 @@ use crate::{ token::{Attribute, SecondaryAttribute, Token}, }; -use super::{parse_many::separated_by_comma_until_right_brace, Parser}; +use super::{Parser, parse_many::separated_by_comma_until_right_brace}; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Struct = 'struct' identifier Generics '{' StructField* '}' /// /// StructField = OuterDocComments identifier ':' Type pub(crate) fn parse_struct( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, visibility: ItemVisibility, - start_span: Span, + start_location: Location, ) -> NoirStruct { let attributes = self.validate_secondary_attributes(attributes); @@ -27,19 +27,19 @@ impl<'a> Parser<'a> { attributes, visibility, Vec::new(), - start_span, + start_location, ); }; let generics = self.parse_generics(); if self.eat_semicolons() { - return self.empty_struct(name, attributes, visibility, generics, start_span); + return self.empty_struct(name, attributes, visibility, generics, start_location); } if !self.eat_left_brace() { self.expected_token(Token::LeftBrace); - return self.empty_struct(name, attributes, visibility, generics, start_span); + return self.empty_struct(name, attributes, visibility, generics, start_location); } let fields = self.parse_many( @@ -54,7 +54,7 @@ impl<'a> Parser<'a> { visibility, generics, fields, - span: self.span_since(start_span), + location: self.location_since(start_location), } } @@ -65,7 +65,7 @@ impl<'a> Parser<'a> { // Loop until we find an identifier, skipping anything that's not one loop { - let doc_comments_start_span = self.current_token_span; + let doc_comments_start_location = self.current_token_location; doc_comments = self.parse_outer_doc_comments(); visibility = self.parse_item_visibility(); @@ -82,7 +82,7 @@ impl<'a> Parser<'a> { if !doc_comments.is_empty() { self.push_error( ParserErrorReason::DocCommentDoesNotDocumentAnything, - self.span_since(doc_comments_start_span), + self.location_since(doc_comments_start_location), ); } @@ -113,7 +113,7 @@ impl<'a> Parser<'a> { attributes: Vec, visibility: ItemVisibility, generics: UnresolvedGenerics, - start_span: Span, + start_location: Location, ) -> NoirStruct { NoirStruct { name, @@ -121,7 +121,7 @@ impl<'a> Parser<'a> { visibility, generics, fields: Vec::new(), - span: self.span_since(start_span), + location: self.location_since(start_location), } } } @@ -130,20 +130,18 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{IntegerBitSize, NoirStruct, Signedness, UnresolvedGeneric, UnresolvedTypeData}, + parse_program_with_dummy_file, parser::{ - parser::{ - parse_program, - tests::{ - expect_no_errors, get_single_error, get_single_error_reason, - get_source_with_error_span, - }, - }, ItemKind, ParserErrorReason, + parser::tests::{ + expect_no_errors, get_single_error, get_single_error_reason, + get_source_with_error_span, + }, }, }; fn parse_struct_no_errors(src: &str) -> NoirStruct { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -218,7 +216,7 @@ mod tests { #[test] fn parse_empty_struct_with_doc_comments() { let src = "/// Hello\nstruct Foo {}"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -232,7 +230,7 @@ mod tests { #[test] fn parse_unclosed_struct() { let src = "struct Foo {"; - let (module, errors) = parse_program(src); + let (module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 1); assert_eq!(module.items.len(), 1); let item = &module.items[0]; @@ -249,7 +247,7 @@ mod tests { ^^^^^^^ "; let (src, span) = get_source_with_error_span(src); - let (_, errors) = parse_program(&src); + let (_, errors) = parse_program_with_dummy_file(&src); let reason = get_single_error_reason(&errors, span); assert!(matches!(reason, ParserErrorReason::NoFunctionAttributesAllowedOnType)); } @@ -261,7 +259,7 @@ mod tests { ^^ "; let (src, span) = get_source_with_error_span(src); - let (module, errors) = parse_program(&src); + let (module, errors) = parse_program_with_dummy_file(&src); assert_eq!(module.items.len(), 1); let item = &module.items[0]; 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 53df5cd00b1b..c37163ebc36c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -1,6 +1,6 @@ use iter_extended::vecmap; -use noirc_errors::Span; +use noirc_errors::{Located, Location}; use crate::ast::{ Documented, GenericTypeArg, GenericTypeArgs, ItemVisibility, NoirTrait, Path, Pattern, @@ -8,27 +8,28 @@ use crate::ast::{ }; use crate::{ ast::{Ident, UnresolvedTypeData}, - parser::{labels::ParsingRuleLabel, NoirTraitImpl, ParserErrorReason}, + parser::{NoirTraitImpl, ParserErrorReason, labels::ParsingRuleLabel}, token::{Attribute, Keyword, SecondaryAttribute, Token}, }; -use super::parse_many::without_separator; use super::Parser; +use super::parse_many::without_separator; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Trait = 'trait' identifier Generics ( ':' TraitBounds )? WhereClause TraitBody /// | 'trait' identifier Generics '=' TraitBounds WhereClause ';' pub(crate) fn parse_trait( &mut self, - attributes: Vec<(Attribute, Span)>, + attributes: Vec<(Attribute, Location)>, visibility: ItemVisibility, - start_span: Span, + start_location: Location, ) -> (NoirTrait, Option) { let attributes = self.validate_secondary_attributes(attributes); let Some(name) = self.eat_ident() else { self.expected_identifier(); - let noir_trait = empty_trait(attributes, visibility, self.span_since(start_span)); + let noir_trait = + empty_trait(attributes, visibility, self.location_since(start_location)); let no_implicit_impl = None; return (noir_trait, no_implicit_impl); }; @@ -41,7 +42,7 @@ impl<'a> Parser<'a> { let bounds = self.parse_trait_bounds(); if bounds.is_empty() { - self.push_error(ParserErrorReason::EmptyTraitAlias, self.previous_token_span); + self.push_error(ParserErrorReason::EmptyTraitAlias, self.previous_token_location); } let where_clause = self.parse_where_clause(); @@ -60,17 +61,17 @@ impl<'a> Parser<'a> { (bounds, where_clause, items, is_alias) }; - let span = self.span_since(start_span); + let location = self.location_since(start_location); let noir_impl = is_alias.then(|| { - let object_type_ident = Ident::new("#T".to_string(), span); + let object_type_ident = Ident::from(Located::from(location, "#T".to_string())); let object_type_path = Path::from_ident(object_type_ident.clone()); let object_type_generic = UnresolvedGeneric::Variable(object_type_ident); let is_synthesized = true; let object_type = UnresolvedType { typ: UnresolvedTypeData::Named(object_type_path, vec![].into(), is_synthesized), - span, + location, }; let mut impl_generics = generics.clone(); @@ -85,7 +86,7 @@ impl<'a> Parser<'a> { vec![].into(), is_synthesized, ), - span, + location, }; GenericTypeArg::Ordered(generic_type) @@ -94,7 +95,7 @@ impl<'a> Parser<'a> { let r#trait = UnresolvedType { typ: UnresolvedTypeData::Named(trait_name, trait_generics, false), - span, + location, }; // bounds from trait @@ -117,7 +118,7 @@ impl<'a> Parser<'a> { generics, bounds, where_clause, - span, + location, items, attributes, visibility, @@ -205,7 +206,7 @@ impl<'a> Parser<'a> { self.parse_type_or_error() } else { self.expected_token(Token::Colon); - UnresolvedType { typ: UnresolvedTypeData::Unspecified, span: Span::default() } + UnresolvedType { typ: UnresolvedTypeData::Unspecified, location: Location::dummy() } }; let default_value = @@ -223,7 +224,10 @@ impl<'a> Parser<'a> { ); if modifiers.visibility != ItemVisibility::Private { - self.push_error(ParserErrorReason::TraitVisibilityIgnored, modifiers.visibility_span); + self.push_error( + ParserErrorReason::TraitVisibilityIgnored, + modifiers.visibility_location, + ); } if !self.eat_keyword(Keyword::Fn) { @@ -243,7 +247,7 @@ impl<'a> Parser<'a> { if let Pattern::Identifier(ident) = param.pattern { Some((ident, param.typ)) } else { - self.push_error(ParserErrorReason::InvalidPattern, param.pattern.span()); + self.push_error(ParserErrorReason::InvalidPattern, param.pattern.location()); None } }) @@ -266,14 +270,14 @@ impl<'a> Parser<'a> { fn empty_trait( attributes: Vec, visibility: ItemVisibility, - span: Span, + location: Location, ) -> NoirTrait { NoirTrait { name: Ident::default(), generics: Vec::new(), bounds: Vec::new(), where_clause: Vec::new(), - span, + location, items: Vec::new(), attributes, visibility, @@ -285,18 +289,18 @@ fn empty_trait( mod tests { use crate::{ ast::{NoirTrait, NoirTraitImpl, TraitItem, UnresolvedTypeData}, + parse_program_with_dummy_file, parser::{ + ItemKind, parser::{ - parse_program, - tests::{expect_no_errors, get_single_error, get_source_with_error_span}, ParserErrorReason, + tests::{expect_no_errors, get_single_error, get_source_with_error_span}, }, - ItemKind, }, }; fn parse_trait_opt_impl_no_errors(src: &str) -> (NoirTrait, Option) { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); let (item, impl_item) = if module.items.len() == 2 { let item = module.items.remove(0); @@ -342,7 +346,7 @@ mod tests { #[test] fn parse_empty_trait_alias() { let src = "trait Foo = ;"; - let (_module, errors) = parse_program(src); + let (_module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 2); assert_eq!(errors[1].reason(), Some(ParserErrorReason::EmptyTraitAlias).as_ref()); } @@ -401,7 +405,7 @@ mod tests { #[test] fn parse_empty_trait_alias_with_generics() { let src = "trait Foo = ;"; - let (_module, errors) = parse_program(src); + let (_module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 2); assert_eq!(errors[1].reason(), Some(ParserErrorReason::EmptyTraitAlias).as_ref()); } @@ -464,7 +468,7 @@ mod tests { #[test] fn parse_empty_trait_alias_with_where_clause() { let src = "trait Foo = where A: Z;"; - let (_module, errors) = parse_program(src); + let (_module, errors) = parse_program_with_dummy_file(src); assert_eq!(errors.len(), 2); assert_eq!(errors[1].reason(), Some(ParserErrorReason::EmptyTraitAlias).as_ref()); } @@ -534,7 +538,7 @@ mod tests { ^^^ "; let (src, span) = get_source_with_error_span(src); - let (_module, errors) = parse_program(&src); + let (_module, errors) = parse_program_with_dummy_file(&src); let error = get_single_error(&errors, span); assert!(error.to_string().contains("Visibility is ignored on a trait method")); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_alias.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_alias.rs index 52815dc37831..464e3e897c50 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_alias.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_alias.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{Ident, ItemVisibility, NoirTypeAlias, UnresolvedType, UnresolvedTypeData}, @@ -7,12 +7,12 @@ use crate::{ use super::Parser; -impl<'a> Parser<'a> { +impl Parser<'_> { /// TypeAlias = 'type' identifier Generics '=' Type ';' pub(crate) fn parse_type_alias( &mut self, visibility: ItemVisibility, - start_span: Span, + start_location: Location, ) -> NoirTypeAlias { let Some(name) = self.eat_ident() else { self.expected_identifier(); @@ -20,8 +20,8 @@ impl<'a> Parser<'a> { visibility, name: Ident::default(), generics: Vec::new(), - typ: UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }, - span: start_span, + typ: UnresolvedType { typ: UnresolvedTypeData::Error, location: Location::dummy() }, + location: start_location, }; }; @@ -30,25 +30,25 @@ impl<'a> Parser<'a> { if !self.eat_assign() { self.expected_token(Token::Assign); - let span = self.span_since(start_span); + let location = self.location_since(start_location); self.eat_semicolons(); return NoirTypeAlias { visibility, name, generics, - typ: UnresolvedType { typ: UnresolvedTypeData::Error, span: Span::default() }, - span, + typ: UnresolvedType { typ: UnresolvedTypeData::Error, location: Location::dummy() }, + location, }; } let typ = self.parse_type_or_error(); - let span = self.span_since(start_span); + let location = self.location_since(start_location); if !self.eat_semicolons() { self.expected_token(Token::Semicolon); } - NoirTypeAlias { visibility, name, generics, typ, span } + NoirTypeAlias { visibility, name, generics, typ, location } } } @@ -56,14 +56,12 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{NoirTypeAlias, UnresolvedTypeData}, - parser::{ - parser::{parse_program, tests::expect_no_errors}, - ItemKind, - }, + parse_program_with_dummy_file, + parser::{ItemKind, parser::tests::expect_no_errors}, }; fn parse_type_alias_no_errors(src: &str) -> NoirTypeAlias { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_expression.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_expression.rs index 83b04bb157a2..f6d6dbd8a25d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/type_expression.rs @@ -1,16 +1,16 @@ use crate::{ + BinaryTypeOperator, ast::{GenericTypeArgs, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, - parser::{labels::ParsingRuleLabel, ParserError}, + parser::{ParserError, labels::ParsingRuleLabel}, token::Token, - BinaryTypeOperator, }; use acvm::acir::{AcirField, FieldElement}; -use noirc_errors::Span; +use noirc_errors::Location; -use super::{parse_many::separated_by_comma_until_right_paren, Parser}; +use super::{Parser, parse_many::separated_by_comma_until_right_paren}; -impl<'a> Parser<'a> { +impl Parser<'_> { /// TypeExpression= AddOrSubtractTypeExpression pub(crate) fn parse_type_expression( &mut self, @@ -24,15 +24,15 @@ impl<'a> Parser<'a> { /// AddOrSubtractTypeExpression /// = MultiplyOrDivideOrModuloTypeExpression ( ( '+' | '-' ) MultiplyOrDivideOrModuloTypeExpression )* fn parse_add_or_subtract_type_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let lhs = self.parse_multiply_or_divide_or_modulo_type_expression()?; - Some(self.parse_add_or_subtract_type_expression_after_lhs(lhs, start_span)) + Some(self.parse_add_or_subtract_type_expression_after_lhs(lhs, start_location)) } fn parse_add_or_subtract_type_expression_after_lhs( &mut self, mut lhs: UnresolvedTypeExpression, - start_span: Span, + start_location: Location, ) -> UnresolvedTypeExpression { loop { let operator = if self.eat(Token::Plus) { @@ -45,12 +45,12 @@ impl<'a> Parser<'a> { match self.parse_multiply_or_divide_or_modulo_type_expression() { Some(rhs) => { - let span = self.span_since(start_span); + let location = self.location_since(start_location); lhs = UnresolvedTypeExpression::BinaryOperation( Box::new(lhs), operator, Box::new(rhs), - span, + location, ); } None => { @@ -67,15 +67,15 @@ impl<'a> Parser<'a> { fn parse_multiply_or_divide_or_modulo_type_expression( &mut self, ) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let lhs = self.parse_term_type_expression()?; - Some(self.parse_multiply_or_divide_or_modulo_type_expression_after_lhs(lhs, start_span)) + Some(self.parse_multiply_or_divide_or_modulo_type_expression_after_lhs(lhs, start_location)) } fn parse_multiply_or_divide_or_modulo_type_expression_after_lhs( &mut self, mut lhs: UnresolvedTypeExpression, - start_span: Span, + start_location: Location, ) -> UnresolvedTypeExpression { loop { let operator = if self.eat(Token::Star) { @@ -90,12 +90,12 @@ impl<'a> Parser<'a> { match self.parse_term_type_expression() { Some(rhs) => { - let span = self.span_since(start_span); + let location = self.location_since(start_location); lhs = UnresolvedTypeExpression::BinaryOperation( Box::new(lhs), operator, Box::new(rhs), - span, + location, ); } None => { @@ -112,18 +112,19 @@ impl<'a> Parser<'a> { /// = '- TermTypeExpression /// | AtomTypeExpression fn parse_term_type_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if self.eat(Token::Minus) { return match self.parse_term_type_expression() { Some(rhs) => { - let lhs = UnresolvedTypeExpression::Constant(FieldElement::zero(), start_span); + let lhs = + UnresolvedTypeExpression::Constant(FieldElement::zero(), start_location); let op = BinaryTypeOperator::Subtraction; - let span = self.span_since(start_span); + let location = self.location_since(start_location); Some(UnresolvedTypeExpression::BinaryOperation( Box::new(lhs), op, Box::new(rhs), - span, + location, )) } None => { @@ -159,8 +160,7 @@ impl<'a> Parser<'a> { /// ConstantTypeExpression = int fn parse_constant_type_expression(&mut self) -> Option { let int = self.eat_int()?; - - Some(UnresolvedTypeExpression::Constant(int, self.previous_token_span)) + Some(UnresolvedTypeExpression::Constant(int, self.previous_token_location)) } /// VariableTypeExpression = Path @@ -193,7 +193,7 @@ impl<'a> Parser<'a> { /// TypeOrTypeExpression = Type | TypeExpression pub(crate) fn parse_type_or_type_expression(&mut self) -> Option { let typ = self.parse_add_or_subtract_type_or_type_expression()?; - let span = typ.span; + let span = typ.location; // If we end up with a Variable type expression, make it a Named type (they are equivalent), // but for testing purposes and simplicity we default to types instead of type expressions. @@ -203,7 +203,7 @@ impl<'a> Parser<'a> { { UnresolvedType { typ: UnresolvedTypeData::Named(path, GenericTypeArgs::default(), false), - span, + location: span, } } else { typ @@ -212,7 +212,7 @@ impl<'a> Parser<'a> { } fn parse_add_or_subtract_type_or_type_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let lhs = self.parse_multiply_or_divide_or_modulo_type_or_type_expression()?; // If lhs is a type then no operator can follow, so we stop right away @@ -221,14 +221,14 @@ impl<'a> Parser<'a> { } let lhs = type_to_type_expr(lhs).unwrap(); - let lhs = self.parse_add_or_subtract_type_expression_after_lhs(lhs, start_span); - Some(type_expr_to_type(lhs, self.span_since(start_span))) + let lhs = self.parse_add_or_subtract_type_expression_after_lhs(lhs, start_location); + Some(type_expr_to_type(lhs, self.location_since(start_location))) } fn parse_multiply_or_divide_or_modulo_type_or_type_expression( &mut self, ) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let lhs = self.parse_term_type_or_type_expression()?; // If lhs is a type then no operator can follow, so we stop right away @@ -238,27 +238,28 @@ impl<'a> Parser<'a> { let lhs = type_to_type_expr(lhs).unwrap(); let lhs = - self.parse_multiply_or_divide_or_modulo_type_expression_after_lhs(lhs, start_span); - Some(type_expr_to_type(lhs, self.span_since(start_span))) + self.parse_multiply_or_divide_or_modulo_type_expression_after_lhs(lhs, start_location); + Some(type_expr_to_type(lhs, self.location_since(start_location))) } fn parse_term_type_or_type_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if self.eat(Token::Minus) { // If we ate '-' what follows must be a type expression, never a type return match self.parse_term_type_expression() { Some(rhs) => { - let lhs = UnresolvedTypeExpression::Constant(FieldElement::zero(), start_span); + let lhs = + UnresolvedTypeExpression::Constant(FieldElement::zero(), start_location); let op = BinaryTypeOperator::Subtraction; - let span = self.span_since(start_span); + let location = self.location_since(start_location); let type_expr = UnresolvedTypeExpression::BinaryOperation( Box::new(lhs), op, Box::new(rhs), - span, + location, ); let typ = UnresolvedTypeData::Expression(type_expr); - Some(UnresolvedType { typ, span }) + Some(UnresolvedType { typ, location }) } None => { self.push_expected_expression(); @@ -271,19 +272,19 @@ impl<'a> Parser<'a> { } fn parse_atom_type_or_type_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if let Some(path) = self.parse_path() { let generics = self.parse_generic_type_args(); let typ = UnresolvedTypeData::Named(path, generics, false); - let span = self.span_since(start_span); - return Some(UnresolvedType { typ, span }); + let location = self.location_since(start_location); + return Some(UnresolvedType { typ, location }); } if let Some(type_expr) = self.parse_constant_type_expression() { let typ = UnresolvedTypeData::Expression(type_expr); - let span = self.span_since(start_span); - return Some(UnresolvedType { typ, span }); + let location = self.location_since(start_location); + return Some(UnresolvedType { typ, location }); } if let Some(typ) = self.parse_parenthesized_type_or_type_expression() { @@ -294,7 +295,7 @@ impl<'a> Parser<'a> { } fn parse_parenthesized_type_or_type_expression(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; if !self.eat_left_paren() { return None; @@ -303,7 +304,7 @@ impl<'a> Parser<'a> { if self.eat_right_paren() { return Some(UnresolvedType { typ: UnresolvedTypeData::Unit, - span: self.span_since(start_span), + location: self.location_since(start_location), }); } @@ -318,19 +319,19 @@ impl<'a> Parser<'a> { self.eat_or_error(Token::RightParen); return Some(UnresolvedType { typ: UnresolvedTypeData::Expression(type_expr), - span: typ.span, + location: typ.location, }); } if self.eat_right_paren() { return Some(UnresolvedType { typ: UnresolvedTypeData::Parenthesized(Box::new(typ)), - span: self.span_since(start_span), + location: self.location_since(start_location), }); } let comma_after_first_type = self.eat_comma(); - let second_type_span = self.current_token_span; + let second_type_location = self.current_token_location; let mut types = self.parse_many( "tuple items", @@ -339,14 +340,14 @@ impl<'a> Parser<'a> { ); if !types.is_empty() && !comma_after_first_type { - self.expected_token_separating_items(Token::Comma, "tuple items", second_type_span); + self.expected_token_separating_items(Token::Comma, "tuple items", second_type_location); } types.insert(0, typ); Some(UnresolvedType { typ: UnresolvedTypeData::Tuple(types), - span: self.span_since(start_span), + location: self.location_since(start_location), }) } @@ -356,7 +357,7 @@ impl<'a> Parser<'a> { Err(ParserError::expected_label( ParsingRuleLabel::TypeExpression, self.token.token().clone(), - self.current_token_span, + self.current_token_location, )) } } @@ -383,9 +384,9 @@ fn type_is_type_expr(typ: &UnresolvedType) -> bool { } } -fn type_expr_to_type(lhs: UnresolvedTypeExpression, span: Span) -> UnresolvedType { +fn type_expr_to_type(lhs: UnresolvedTypeExpression, location: Location) -> UnresolvedType { let lhs = UnresolvedTypeData::Expression(lhs); - UnresolvedType { typ: lhs, span } + UnresolvedType { typ: lhs, location } } #[cfg(test)] @@ -393,26 +394,26 @@ mod tests { use core::panic; use crate::{ + BinaryTypeOperator, ast::{UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, parser::{ + Parser, ParserErrorReason, parser::tests::{ expect_no_errors, get_single_error_reason, get_source_with_error_span, }, - Parser, ParserErrorReason, }, token::Token, - BinaryTypeOperator, }; fn parse_type_expression_no_errors(src: &str) -> UnresolvedTypeExpression { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let expr = parser.parse_type_expression().unwrap(); expect_no_errors(&parser.errors); expr } fn parse_type_or_type_expression_no_errors(src: &str) -> UnresolvedType { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let typ = parser.parse_type_or_type_expression().unwrap(); expect_no_errors(&parser.errors); typ @@ -569,7 +570,7 @@ mod tests { ^^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let typ = parser.parse_type_or_type_expression().unwrap(); @@ -594,7 +595,7 @@ mod tests { #[test] fn parses_type_or_type_expression_tuple_type_single_element() { let src = "(Field,)"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let typ = parser.parse_type_or_type_expression().unwrap(); expect_no_errors(&parser.errors); let UnresolvedTypeData::Tuple(types) = typ.typ else { 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 6b3575e19eef..bcbf57d863d6 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,29 +1,49 @@ use acvm::{AcirField, FieldElement}; use crate::{ + QuotedType, ast::{UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}, - parser::{labels::ParsingRuleLabel, ParserErrorReason}, + parser::{ParserErrorReason, labels::ParsingRuleLabel}, token::{Keyword, Token, TokenKind}, - QuotedType, }; -use super::{parse_many::separated_by_comma_until_right_paren, Parser}; +use super::{Parser, parse_many::separated_by_comma_until_right_paren}; -impl<'a> Parser<'a> { +impl Parser<'_> { pub(crate) fn parse_type_or_error(&mut self) -> UnresolvedType { if let Some(typ) = self.parse_type() { typ } else { self.expected_label(ParsingRuleLabel::Type); - UnresolvedTypeData::Error.with_span(self.span_at_previous_token_end()) + UnresolvedTypeData::Error.with_location(self.location_at_previous_token_end()) + } + } + + /// Tries to parse a type. If the current token doesn't denote a type and it's not + /// one of `stop_tokens`, try to parse a type starting from the next token (and so on). + pub(crate) fn parse_type_or_error_with_recovery( + &mut self, + stop_tokens: &[Token], + ) -> UnresolvedType { + loop { + let typ = self.parse_type_or_error(); + if typ.typ != UnresolvedTypeData::Error { + return typ; + } + + if self.at_eof() || stop_tokens.contains(self.token.token()) { + return typ; + } + + self.bump(); } } pub(crate) fn parse_type(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let typ = self.parse_unresolved_type_data()?; - let span = self.span_since(start_span); - Some(UnresolvedType { typ, span }) + let location = self.location_since(start_location); + Some(UnresolvedType { typ, location }) } fn parse_unresolved_type_data(&mut self) -> Option { @@ -122,7 +142,7 @@ impl<'a> Parser<'a> { Err(err) => { self.push_error( ParserErrorReason::InvalidBitSize(err.0), - self.previous_token_span, + self.previous_token_location, ); UnresolvedTypeData::Error } @@ -139,8 +159,10 @@ impl<'a> Parser<'a> { if !self.eat_less() { self.expected_token(Token::Less); - let expr = - UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span); + let expr = UnresolvedTypeExpression::Constant( + FieldElement::zero(), + self.current_token_location, + ); return Some(UnresolvedTypeData::String(expr)); } @@ -148,7 +170,10 @@ impl<'a> Parser<'a> { Ok(expr) => expr, Err(error) => { self.errors.push(error); - UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span) + UnresolvedTypeExpression::Constant( + FieldElement::zero(), + self.current_token_location, + ) } }; @@ -164,9 +189,12 @@ impl<'a> Parser<'a> { if !self.eat_less() { self.expected_token(Token::Less); - let expr = - UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span); - let typ = UnresolvedTypeData::Error.with_span(self.span_at_previous_token_end()); + let expr = UnresolvedTypeExpression::Constant( + FieldElement::zero(), + self.current_token_location, + ); + let typ = + UnresolvedTypeData::Error.with_location(self.location_at_previous_token_end()); return Some(UnresolvedTypeData::FormatString(expr, Box::new(typ))); } @@ -174,7 +202,10 @@ impl<'a> Parser<'a> { Ok(expr) => expr, Err(error) => { self.errors.push(error); - UnresolvedTypeExpression::Constant(FieldElement::zero(), self.current_token_span) + UnresolvedTypeExpression::Constant( + FieldElement::zero(), + self.current_token_location, + ) } }; @@ -257,7 +288,7 @@ impl<'a> Parser<'a> { self.eat_or_error(Token::RightBracket); typ } else { - UnresolvedTypeData::Unit.with_span(self.span_at_previous_token_end()) + UnresolvedTypeData::Unit.with_location(self.location_at_previous_token_end()) }; if !self.eat_left_paren() { @@ -281,7 +312,7 @@ impl<'a> Parser<'a> { self.parse_type_or_error() } else { self.expected_token(Token::Arrow); - UnresolvedTypeData::Unit.with_span(self.span_at_previous_token_end()) + UnresolvedTypeData::Unit.with_location(self.location_at_previous_token_end()) }; Some(UnresolvedTypeData::Function(args, Box::new(ret), Box::new(env), unconstrained)) @@ -289,11 +320,7 @@ impl<'a> Parser<'a> { fn parse_parameter(&mut self) -> Option { let typ = self.parse_type_or_error(); - if let UnresolvedTypeData::Error = typ.typ { - None - } else { - Some(typ) - } + if let UnresolvedTypeData::Error = typ.typ { None } else { Some(typ) } } fn parse_trait_as_type(&mut self) -> Option { @@ -422,7 +449,7 @@ impl<'a> Parser<'a> { } pub(super) fn unspecified_type_at_previous_token_end(&self) -> UnresolvedType { - UnresolvedTypeData::Unspecified.with_span(self.span_at_previous_token_end()) + UnresolvedTypeData::Unspecified.with_location(self.location_at_previous_token_end()) } } @@ -431,16 +458,16 @@ mod tests { use strum::IntoEnumIterator; use crate::{ + QuotedType, ast::{IntegerBitSize, Signedness, UnresolvedType, UnresolvedTypeData}, parser::{ + Parser, ParserErrorReason, parser::tests::{expect_no_errors, get_single_error, get_source_with_error_span}, - Parser, }, - QuotedType, }; fn parse_type_no_errors(src: &str) -> UnresolvedType { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let typ = parser.parse_type_or_error(); expect_no_errors(&parser.errors); typ @@ -470,6 +497,28 @@ mod tests { )); } + #[test] + fn errors_on_invalid_bit_size() { + let src = "u31"; + let mut parser = Parser::for_str_with_dummy_file(src); + let typ = parser.parse_type_or_error(); + assert_eq!(typ.typ, UnresolvedTypeData::Error); + assert_eq!(parser.errors.len(), 1); + let error = &parser.errors[0]; + assert!(matches!(error.reason(), Some(ParserErrorReason::InvalidBitSize(..)))); + } + + #[test] + fn errors_on_i128() { + let src = "i128"; + let mut parser = Parser::for_str_with_dummy_file(src); + let typ = parser.parse_type_or_error(); + assert_eq!(typ.typ, UnresolvedTypeData::Error); + assert_eq!(parser.errors.len(), 1); + let error = &parser.errors[0]; + assert!(matches!(error.reason(), Some(ParserErrorReason::InvalidBitSize(..)))); + } + #[test] fn parses_field_type() { let src = "Field"; @@ -546,7 +595,7 @@ mod tests { #[test] fn parses_unclosed_parentheses_type() { let src = "(Field"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let typ = parser.parse_type_or_error(); assert_eq!(parser.errors.len(), 1); let UnresolvedTypeData::Parenthesized(typ) = typ.typ else { @@ -591,7 +640,7 @@ mod tests { ^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); parser.parse_type(); let error = get_single_error(&parser.errors, span); assert_eq!(error.to_string(), "Expected a ']' but found end of input"); @@ -666,7 +715,7 @@ mod tests { #[test] fn parses_function_type_with_colon_in_parameter() { let src = "fn(value: T) -> Field"; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let _ = parser.parse_type_or_error(); assert!(!parser.errors.is_empty()); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/use_tree.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/use_tree.rs index 08834383f99c..ebe058632113 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/use_tree.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/use_tree.rs @@ -1,4 +1,4 @@ -use noirc_errors::Span; +use noirc_errors::Location; use crate::{ ast::{Ident, Path, PathKind, UseTree, UseTreeKind}, @@ -6,21 +6,23 @@ use crate::{ token::{Keyword, Token}, }; -use super::{parse_many::separated_by_comma_until_right_brace, Parser}; +use super::{Parser, parse_many::separated_by_comma_until_right_brace}; -impl<'a> Parser<'a> { +impl Parser<'_> { /// Use = 'use' PathKind PathNoTurbofish UseTree /// /// UseTree = PathNoTurbofish ( '::' '{' UseTreeList? '}' )? /// /// UseTreeList = UseTree (',' UseTree)* ','? pub(super) fn parse_use_tree(&mut self) -> UseTree { - let start_span = self.current_token_span; + let start_location = self.current_token_location; let kind = self.parse_path_kind(); let use_tree = self.parse_use_tree_without_kind( - start_span, kind, false, // nested + start_location, + kind, + false, // nested ); if !self.eat_semicolons() { self.expected_token(Token::Semicolon); @@ -30,14 +32,15 @@ impl<'a> Parser<'a> { pub(super) fn parse_use_tree_without_kind( &mut self, - start_span: Span, + start_location: Location, kind: PathKind, nested: bool, ) -> UseTree { let prefix = self.parse_path_after_kind( - kind, false, // allow turbofish + kind, + false, // allow turbofish false, // allow trailing double colon - start_span, + start_location, ); let trailing_double_colon = if prefix.segments.is_empty() && kind != PathKind::Plain { true @@ -56,37 +59,37 @@ impl<'a> Parser<'a> { UseTree { prefix, kind: UseTreeKind::List(use_trees), - span: self.span_since(start_span), + location: self.location_since(start_location), } } else { self.expected_token(Token::LeftBrace); - self.parse_path_use_tree_end(prefix, nested, start_span) + self.parse_path_use_tree_end(prefix, nested, start_location) } } else { - self.parse_path_use_tree_end(prefix, nested, start_span) + self.parse_path_use_tree_end(prefix, nested, start_location) } } fn parse_use_tree_in_list(&mut self) -> Option { - let start_span = self.current_token_span; + let start_location = self.current_token_location; // Special case: "self" cannot be followed by anything else if self.eat_self() { return Some(UseTree { - prefix: Path { segments: Vec::new(), kind: PathKind::Plain, span: start_span }, - kind: UseTreeKind::Path(Ident::new("self".to_string(), start_span), None), - span: start_span, + prefix: Path::plain(Vec::new(), start_location), + kind: UseTreeKind::Path(Ident::new("self".to_string(), start_location), None), + location: start_location, }); } let use_tree = self.parse_use_tree_without_kind( - start_span, + start_location, PathKind::Plain, true, // nested ); // If we didn't advance at all, we are done - if start_span == self.current_token_span { + if start_location.span == self.current_token_location.span { self.expected_label(ParsingRuleLabel::UseSegment); None } else { @@ -98,7 +101,7 @@ impl<'a> Parser<'a> { &mut self, mut prefix: Path, nested: bool, - start_span: Span, + start_location: Location, ) -> UseTree { if prefix.segments.is_empty() { if nested { @@ -109,7 +112,7 @@ impl<'a> Parser<'a> { UseTree { prefix, kind: UseTreeKind::Path(Ident::default(), None), - span: self.span_since(start_span), + location: self.location_since(start_location), } } else { let ident = prefix.segments.pop().unwrap().ident; @@ -118,21 +121,21 @@ impl<'a> Parser<'a> { UseTree { prefix, kind: UseTreeKind::Path(ident, Some(alias)), - span: self.span_since(start_span), + location: self.location_since(start_location), } } else { self.expected_identifier(); UseTree { prefix, kind: UseTreeKind::Path(ident, None), - span: self.span_since(start_span), + location: self.location_since(start_location), } } } else { UseTree { prefix, kind: UseTreeKind::Path(ident, None), - span: self.span_since(start_span), + location: self.location_since(start_location), } } } @@ -143,14 +146,12 @@ impl<'a> Parser<'a> { mod tests { use crate::{ ast::{ItemVisibility, PathKind, UseTree, UseTreeKind}, - parser::{ - parser::{parse_program, tests::expect_no_errors}, - ItemKind, - }, + parse_program_with_dummy_file, + parser::{ItemKind, parser::tests::expect_no_errors}, }; fn parse_use_tree_no_errors(src: &str) -> (UseTree, ItemVisibility) { - let (mut module, errors) = parse_program(src); + let (mut module, errors) = parse_program_with_dummy_file(src); expect_no_errors(&errors); assert_eq!(module.items.len(), 1); let item = module.items.remove(0); @@ -281,14 +282,14 @@ mod tests { #[test] fn errors_on_crate_in_subtree() { let src = "use foo::{crate::bar}"; - let (_, errors) = parse_program(src); + let (_, errors) = parse_program_with_dummy_file(src); assert!(!errors.is_empty()); } #[test] fn errors_on_double_colon_after_self() { let src = "use foo::{self::bar};"; - let (_, errors) = parse_program(src); + let (_, errors) = parse_program_with_dummy_file(src); assert!(!errors.is_empty()); } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/where_clause.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/where_clause.rs index 8945e6f29f5f..08adea0a20a2 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/where_clause.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/where_clause.rs @@ -1,15 +1,15 @@ use crate::{ - ast::{GenericTypeArgs, Path, PathKind, TraitBound, UnresolvedTraitConstraint, UnresolvedType}, + ast::{GenericTypeArgs, Path, TraitBound, UnresolvedTraitConstraint, UnresolvedType}, parser::labels::ParsingRuleLabel, token::{Keyword, Token}, }; use super::{ - parse_many::{separated_by, separated_by_comma}, Parser, + parse_many::{separated_by, separated_by_comma}, }; -impl<'a> Parser<'a> { +impl Parser<'_> { /// WhereClause = 'where' WhereClauseItems? /// /// WhereClauseItems = WhereClauseItem ( ',' WhereClauseItem )* ','? @@ -70,11 +70,7 @@ impl<'a> Parser<'a> { self.expected_label(ParsingRuleLabel::TraitBound); TraitBound { - trait_path: Path { - kind: PathKind::Plain, - segments: Vec::new(), - span: self.span_at_previous_token_end(), - }, + trait_path: Path::plain(Vec::new(), self.location_at_previous_token_end()), trait_id: None, trait_generics: GenericTypeArgs::default(), } @@ -93,16 +89,16 @@ mod tests { use crate::{ ast::UnresolvedTraitConstraint, parser::{ + Parser, ParserErrorReason, parser::tests::{ expect_no_errors, get_single_error_reason, get_source_with_error_span, }, - Parser, ParserErrorReason, }, token::Token, }; fn parse_where_clause_no_errors(src: &str) -> Vec { - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); let constraints = parser.parse_where_clause(); expect_no_errors(&parser.errors); constraints @@ -154,7 +150,7 @@ mod tests { ^^^ "; let (src, span) = get_source_with_error_span(src); - let mut parser = Parser::for_str(&src); + let mut parser = Parser::for_str_with_dummy_file(&src); let mut constraints = parser.parse_where_clause(); let reason = get_single_error_reason(&parser.errors, span); @@ -179,7 +175,7 @@ mod tests { #[test] fn parses_where_clause_missing_trait_bound() { let src = "where Foo: "; - let mut parser = Parser::for_str(src); + let mut parser = Parser::for_str_with_dummy_file(src); parser.parse_where_clause(); assert!(!parser.errors.is_empty()); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/resolve_locations.rs b/noir/noir-repo/compiler/noirc_frontend/src/resolve_locations.rs index 4daf088a2f11..652ce4aa557f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/resolve_locations.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/resolve_locations.rs @@ -32,9 +32,24 @@ impl NodeInterner { } /// Returns the Type of the expression that exists at the given location. - pub fn type_at_location(&self, location: Location) -> Option { - let index = self.find_location_index(location)?; - Some(self.id_type(index)) + pub fn type_at_location(&self, location: Location) -> Option<&Type> { + // This is similar to `find_location_index` except that we skip indexes for which there is no type + let mut location_candidate: Option<(&Index, &Location, &Type)> = None; + + for (index, interned_location) in self.id_to_location.iter() { + if interned_location.contains(&location) { + if let Some(typ) = self.try_id_type(*index) { + if let Some(current_location) = location_candidate { + if interned_location.span.is_smaller(¤t_location.1.span) { + location_candidate = Some((index, interned_location, typ)); + } + } else { + location_candidate = Some((index, interned_location, typ)); + } + } + } + } + location_candidate.map(|(_index, _location, typ)| typ) } /// Returns the [Location] of the definition of the given Ident found at [Span] of the given [FileId]. diff --git a/noir/noir-repo/compiler/noirc_frontend/src/signed_field.rs b/noir/noir-repo/compiler/noirc_frontend/src/signed_field.rs new file mode 100644 index 000000000000..925b128ea245 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/signed_field.rs @@ -0,0 +1,207 @@ +use acvm::{AcirField, FieldElement}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct SignedField { + pub field: FieldElement, + pub is_negative: bool, +} + +impl SignedField { + pub fn new(field: FieldElement, is_negative: bool) -> Self { + Self { field, is_negative } + } + + pub fn positive(field: impl Into) -> Self { + Self { field: field.into(), is_negative: false } + } + + pub fn negative(field: impl Into) -> Self { + Self { field: field.into(), is_negative: true } + } + + pub fn zero() -> SignedField { + Self { field: FieldElement::zero(), is_negative: false } + } + + pub fn one() -> SignedField { + Self { field: FieldElement::one(), is_negative: false } + } + + /// Convert a signed integer to a SignedField, carefully handling + /// INT_MIN in the process. Note that to convert an unsigned integer + /// you can call `SignedField::positive`. + #[inline] + pub fn from_signed(value: T) -> Self + where + T: num_traits::Signed + AbsU128, + { + let negative = value.is_negative(); + let value = value.abs_u128(); + SignedField::new(value.into(), negative) + } + + /// Convert a SignedField into an unsigned integer type (up to u128), + /// returning None if the value does not fit (e.g. if it is negative). + #[inline] + pub fn try_to_unsigned>(self) -> Option { + if self.is_negative { + return None; + } + + assert!(std::mem::size_of::() <= std::mem::size_of::()); + let u128_value = self.field.try_into_u128()?; + u128_value.try_into().ok() + } + + /// Convert a SignedField into a signed integer type (up to i128), + /// returning None if the value does not fit. This function is more complex + /// for handling negative values, specifically INT_MIN which we can't cast from + /// a u128 to i128 without wrapping it. + #[inline] + pub fn try_to_signed(self) -> Option + where + T: TryFrom + TryFrom + num_traits::Signed + num_traits::Bounded + AbsU128, + u128: TryFrom, + { + let u128_value = self.field.try_into_u128()?; + + if self.is_negative { + // The positive version of the minimum value of this type. + // E.g. 128 for i8. + let positive_min = T::min_value().abs_u128(); + + // If it is the min value, we can't negate it without overflowing + // so test for it and return it directly + if u128_value == positive_min { + Some(T::min_value()) + } else { + let i128_value = -(u128_value as i128); + T::try_from(i128_value).ok() + } + } else { + T::try_from(u128_value).ok() + } + } +} + +impl std::ops::Neg for SignedField { + type Output = Self; + + fn neg(mut self) -> Self::Output { + self.is_negative = !self.is_negative; + self + } +} + +impl Ord for SignedField { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + if self.is_negative != other.is_negative { + if self.is_negative { std::cmp::Ordering::Less } else { std::cmp::Ordering::Greater } + } else if self.is_negative { + // Negative comparisons should be reversed so that -2 < -1 + other.field.cmp(&self.field) + } else { + self.field.cmp(&other.field) + } + } +} + +impl PartialOrd for SignedField { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl From for SignedField { + fn from(value: FieldElement) -> Self { + Self::new(value, false) + } +} + +impl From for FieldElement { + fn from(value: SignedField) -> Self { + if value.is_negative { -value.field } else { value.field } + } +} + +impl std::fmt::Display for SignedField { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.is_negative { + write!(f, "-")?; + } + write!(f, "{}", self.field) + } +} + +impl rangemap::StepLite for SignedField { + fn add_one(&self) -> Self { + if self.is_negative { + if self.field.is_one() { + Self::new(FieldElement::zero(), false) + } else { + Self::new(self.field - FieldElement::one(), self.is_negative) + } + } else { + Self::new(self.field + FieldElement::one(), self.is_negative) + } + } + + fn sub_one(&self) -> Self { + if self.is_negative { + Self::new(self.field + FieldElement::one(), self.is_negative) + } else if self.field.is_zero() { + Self::new(FieldElement::one(), true) + } else { + Self::new(self.field - FieldElement::one(), self.is_negative) + } + } +} + +pub trait AbsU128 { + /// Necessary to handle casting to unsigned generically without overflowing on INT_MIN. + fn abs_u128(self) -> u128; +} + +macro_rules! impl_unsigned_abs_for { + ($typ:ty) => { + impl AbsU128 for $typ { + fn abs_u128(self) -> u128 { + self.unsigned_abs() as u128 + } + } + }; +} + +impl_unsigned_abs_for!(i8); +impl_unsigned_abs_for!(i16); +impl_unsigned_abs_for!(i32); +impl_unsigned_abs_for!(i64); +impl_unsigned_abs_for!(i128); + +#[cfg(test)] +mod tests { + use super::SignedField; + + #[test] + fn int_min_to_signed_field_roundtrip() { + let x = i128::MIN; + let field = SignedField::from_signed(x); + assert_eq!(field.try_to_signed(), Some(x)); + } + + #[test] + fn comparisons() { + let neg_two = SignedField::negative(2u32); + let neg_one = SignedField::negative(1u32); + let zero = SignedField::positive(0u32); + let one = SignedField::positive(1u32); + let two = SignedField::positive(2u32); + + assert!(one < two); + assert!(zero < one); + assert!(neg_one < zero); + assert!(neg_two < neg_one); + + assert!(two > neg_two); + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs index 6ff3b204f2e0..e53aa392fa96 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs @@ -3,6 +3,7 @@ mod aliases; mod arithmetic_generics; mod bound_checks; +mod enums; mod imports; mod metaprogramming; mod name_shadowing; @@ -15,22 +16,17 @@ mod visibility; // XXX: These tests repeat a lot of code // what we should do is have test cases which are passed to a test harness // A test harness will allow for more expressive and readable tests -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; +use crate::elaborator::{FrontendOptions, UnstableFeature}; use fm::FileId; use iter_extended::vecmap; -use noirc_errors::Location; +use noirc_errors::{CustomDiagnostic, Location, Span}; -use crate::ast::IntegerBitSize; -use crate::hir::comptime::InterpreterError; +use crate::hir::Context; use crate::hir::def_collector::dc_crate::CompilationError; -use crate::hir::def_collector::errors::{DefCollectorErrorKind, DuplicateType}; use crate::hir::def_map::ModuleData; -use crate::hir::resolution::errors::ResolverError; -use crate::hir::resolution::import::PathResolutionError; -use crate::hir::type_check::TypeCheckError; -use crate::hir::Context; use crate::node_interner::{NodeInterner, StmtId}; use crate::hir::def_collector::dc_crate::DefCollector; @@ -42,16 +38,16 @@ use crate::monomorphization::errors::MonomorphizationError; use crate::monomorphization::monomorphize; use crate::parser::{ItemKind, ParserErrorReason}; use crate::token::SecondaryAttribute; -use crate::{parse_program, ParsedModule}; +use crate::{ParsedModule, parse_program}; use fm::FileManager; use noirc_arena::Arena; -pub(crate) fn has_parser_error(errors: &[(CompilationError, FileId)]) -> bool { - errors.iter().any(|(e, _f)| matches!(e, CompilationError::ParseError(_))) +pub(crate) fn has_parser_error(errors: &[CompilationError]) -> bool { + errors.iter().any(|e| matches!(e, CompilationError::ParseError(_))) } -pub(crate) fn remove_experimental_warnings(errors: &mut Vec<(CompilationError, FileId)>) { - errors.retain(|(error, _)| match error { +pub(crate) fn remove_experimental_warnings(errors: &mut Vec) { + errors.retain(|error| match error { CompilationError::ParseError(error) => { !matches!(error.reason(), Some(ParserErrorReason::ExperimentalFeature(..))) } @@ -59,19 +55,29 @@ pub(crate) fn remove_experimental_warnings(errors: &mut Vec<(CompilationError, F }); } -pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec<(CompilationError, FileId)>) { - get_program_with_maybe_parser_errors( - src, false, // allow parser errors - ) +pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec) { + let allow_parser_errors = false; + get_program_with_options(src, allow_parser_errors, FrontendOptions::test_default()) +} + +pub(crate) fn get_program_using_features( + src: &str, + features: &[UnstableFeature], +) -> (ParsedModule, Context<'static, 'static>, Vec) { + let allow_parser_errors = false; + let mut options = FrontendOptions::test_default(); + options.enabled_unstable_features = features; + get_program_with_options(src, allow_parser_errors, options) } /// Compile a program. /// /// The stdlib is not available for these snippets. -pub(crate) fn get_program_with_maybe_parser_errors( +pub(crate) fn get_program_with_options( src: &str, allow_parser_errors: bool, -) -> (ParsedModule, Context, Vec<(CompilationError, FileId)>) { + options: FrontendOptions, +) -> (ParsedModule, Context<'static, 'static>, Vec) { let root = std::path::Path::new("/"); let fm = FileManager::new(root); @@ -80,8 +86,8 @@ pub(crate) fn get_program_with_maybe_parser_errors( let root_file_id = FileId::dummy(); let root_crate_id = context.crate_graph.add_crate_root(root_file_id); - let (program, parser_errors) = parse_program(src); - let mut errors = vecmap(parser_errors, |e| (e.into(), root_file_id)); + let (program, parser_errors) = parse_program(src, root_file_id); + let mut errors = vecmap(parser_errors, |e| e.into()); remove_experimental_warnings(&mut errors); if allow_parser_errors || !has_parser_error(&errors) { @@ -116,23 +122,19 @@ pub(crate) fn get_program_with_maybe_parser_errors( extern_prelude: BTreeMap::new(), }; - let debug_comptime_in_file = None; - let pedantic_solving = true; - // Now we want to populate the CrateDefMap using the DefCollector errors.extend(DefCollector::collect_crate_and_dependencies( def_map, &mut context, program.clone().into_sorted(), root_file_id, - debug_comptime_in_file, - pedantic_solving, + options, )); } (program, context, errors) } -pub(crate) fn get_program_errors(src: &str) -> Vec<(CompilationError, FileId)> { +pub(crate) fn get_program_errors(src: &str) -> Vec { get_program(src).2 } @@ -143,6 +145,161 @@ fn assert_no_errors(src: &str) { } } +/// Given a source file with annotated errors, like this +/// +/// fn main() -> pub i32 { +/// ^^^ expected i32 because of return type +/// true +/// ~~~~ bool returned here +/// } +/// +/// where: +/// - lines with "^^^" are primary errors +/// - lines with "~~~" are secondary errors +/// +/// this method will check that compiling the program without those error markers +/// will produce errors at those locations and with/ those messages. +fn check_errors(src: &str) { + let allow_parser_errors = false; + check_errors_with_options(src, allow_parser_errors, FrontendOptions::test_default()); +} + +fn check_errors_using_features(src: &str, features: &[UnstableFeature]) { + let allow_parser_errors = false; + let mut options = FrontendOptions::test_default(); + options.enabled_unstable_features = features; + check_errors_with_options(src, allow_parser_errors, options); +} + +fn check_errors_with_options(src: &str, allow_parser_errors: bool, options: FrontendOptions) { + let lines = src.lines().collect::>(); + + // Here we'll hold just the lines that are code + let mut code_lines = Vec::new(); + // Here we'll capture lines that are primary error spans, like: + // + // ^^^ error message + let mut primary_spans_with_errors: Vec<(Span, String)> = Vec::new(); + // Here we'll capture lines that are secondary error spans, like: + // + // ~~~ error message + let mut secondary_spans_with_errors: Vec<(Span, String)> = Vec::new(); + // The byte at the start of this line + let mut byte = 0; + // The length of the last line, needed to go back to the byte at the beginning of the last line + let mut last_line_length = 0; + for line in lines { + if let Some((span, message)) = + get_error_line_span_and_message(line, '^', byte, last_line_length) + { + primary_spans_with_errors.push((span, message)); + continue; + } + + if let Some((span, message)) = + get_error_line_span_and_message(line, '~', byte, last_line_length) + { + secondary_spans_with_errors.push((span, message)); + continue; + } + + code_lines.push(line); + + byte += line.len() + 1; // For '\n' + last_line_length = line.len(); + } + + let mut primary_spans_with_errors: HashMap = + primary_spans_with_errors.into_iter().collect(); + + let mut secondary_spans_with_errors: HashMap = + secondary_spans_with_errors.into_iter().collect(); + + let src = code_lines.join("\n"); + let (_, _, errors) = get_program_with_options(&src, allow_parser_errors, options); + if errors.is_empty() && !primary_spans_with_errors.is_empty() { + panic!("Expected some errors but got none"); + } + + let errors = errors.iter().map(CustomDiagnostic::from).collect::>(); + for error in &errors { + let secondary = error + .secondaries + .first() + .unwrap_or_else(|| panic!("Expected {:?} to have a secondary label", error)); + let span = secondary.location.span; + let message = &error.message; + + let Some(expected_message) = primary_spans_with_errors.remove(&span) else { + if let Some(message) = secondary_spans_with_errors.get(&span) { + panic!( + "Error at {span:?} with message {message:?} is annotated as secondary but should be primary" + ); + } else { + panic!( + "Couldn't find primary error at {span:?} with message {message:?}.\nAll errors: {errors:?}" + ); + } + }; + + assert_eq!(message, &expected_message, "Primary error at {span:?} has unexpected message"); + + for secondary in &error.secondaries { + let message = &secondary.message; + if message.is_empty() { + continue; + } + + let span = secondary.location.span; + let Some(expected_message) = secondary_spans_with_errors.remove(&span) else { + if let Some(message) = primary_spans_with_errors.get(&span) { + panic!( + "Error at {span:?} with message {message:?} is annotated as primary but should be secondary" + ); + } else { + panic!( + "Couldn't find secondary error at {span:?} with message {message:?}.\nAll errors: {errors:?}" + ); + }; + }; + + assert_eq!( + message, &expected_message, + "Secondary error at {span:?} has unexpected message" + ); + } + } + + if !primary_spans_with_errors.is_empty() { + panic!("These primary errors didn't happen: {primary_spans_with_errors:?}"); + } + + if !secondary_spans_with_errors.is_empty() { + panic!("These secondary errors didn't happen: {secondary_spans_with_errors:?}"); + } +} + +/// Helper function for `check_errors` that returns the span that +/// `^^^^` or `~~~~` occupy, together with the message that follows it. +fn get_error_line_span_and_message( + line: &str, + char: char, + byte: usize, + last_line_length: usize, +) -> Option<(Span, String)> { + if !line.trim().starts_with(char) { + return None; + } + + let chars = line.chars().collect::>(); + let first_caret = chars.iter().position(|c| *c == char).unwrap(); + let last_caret = chars.iter().rposition(|c| *c == char).unwrap(); + let start = byte - last_line_length; + let span = Span::from((start + first_caret - 1) as u32..(start + last_caret) as u32); + let error = line.trim().trim_start_matches(char).trim().to_string(); + Some((span, error)) +} + #[test] fn check_trait_implemented_for_all_t() { let src = " @@ -205,10 +362,13 @@ fn check_trait_implementation_duplicate_method() { impl Default for Foo { // Duplicate trait methods should not compile fn default(x: Field, y: Field) -> Field { + ~~~~~~~ First trait associated function found here y + 2 * x } // Duplicate trait methods should not compile fn default(x: Field, y: Field) -> Field { + ^^^^^^^ Duplicate definitions of trait associated function with name default found + ~~~~~~~ Second trait associated function found here x + 2 * y } } @@ -216,27 +376,7 @@ fn check_trait_implementation_duplicate_method() { fn main() { let _ = Foo { bar: 1, array: [2, 3] }; // silence Foo never constructed warning }"; - - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { - typ, - first_def, - second_def, - }) => { - assert_eq!(typ, &DuplicateType::TraitAssociatedFunction); - assert_eq!(first_def, "default"); - assert_eq!(second_def, "default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -251,6 +391,7 @@ fn check_trait_wrong_method_return_type() { impl Default for Foo { fn default() -> Field { + ^^^^^ Expected type Foo, found type Field 0 } } @@ -259,25 +400,7 @@ fn check_trait_wrong_method_return_type() { let _ = Foo {}; // silence Foo never constructed warning } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) => { - assert_eq!(expected_typ, "Foo"); - assert_eq!(expr_typ, "Field"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -294,6 +417,7 @@ fn check_trait_wrong_method_return_type2() { impl Default for Foo { fn default(x: Field, _y: Field) -> Field { + ^^^^^ Expected type Foo, found type Field x } } @@ -301,25 +425,32 @@ fn check_trait_wrong_method_return_type2() { fn main() { let _ = Foo { bar: 1, array: [2, 3] }; // silence Foo never constructed warning }"; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) => { - assert_eq!(expected_typ, "Foo"); - assert_eq!(expr_typ, "Field"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; + check_errors(src); +} + +#[test] +fn check_trait_wrong_method_return_type3() { + let src = " + trait Default { + fn default(x: Field, y: Field) -> Self; + } + + struct Foo { + bar: Field, + array: [Field; 2], + } + + impl Default for Foo { + fn default(_x: Field, _y: Field) { + ^ Expected type Foo, found type () + } + } + + fn main() { + let _ = Foo { bar: 1, array: [2, 3] }; // silence Foo never constructed warning } + "; + check_errors(src); } #[test] @@ -338,6 +469,8 @@ fn check_trait_missing_implementation() { } impl Default for Foo { + ^^^ Method `method2` from trait `Default` is not implemented + ~~~ Please implement method2 here fn default(x: Field, y: Field) -> Self { Self { bar: x, array: [x,y] } } @@ -346,25 +479,7 @@ fn check_trait_missing_implementation() { fn main() { } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::TraitMissingMethod { - trait_name, - method_name, - trait_impl_span: _, - }) => { - assert_eq!(trait_name, "Default"); - assert_eq!(method_name, "method2"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -375,8 +490,8 @@ fn check_trait_not_in_scope() { array: [Field; 2], } - // Default trait does not exist impl Default for Foo { + ^^^^^^^ Trait Default not found fn default(x: Field, y: Field) -> Self { Self { bar: x, array: [x,y] } } @@ -384,23 +499,8 @@ fn check_trait_not_in_scope() { fn main() { } - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::TraitNotFound { - trait_path, - }) => { - assert_eq!(trait_path.as_string(), "Default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -414,9 +514,9 @@ fn check_trait_wrong_method_name() { array: [Field; 2], } - // wrong trait name method should not compile impl Default for Foo { fn does_not_exist(x: Field, y: Field) -> Self { + ^^^^^^^^^^^^^^ Method with name `does_not_exist` is not part of trait `Default`, therefore it can't be implemented Self { bar: x, array: [x,y] } } } @@ -424,28 +524,7 @@ fn check_trait_wrong_method_name() { fn main() { let _ = Foo { bar: 1, array: [2, 3] }; // silence Foo never constructed warning }"; - let compilation_errors = get_program_errors(src); - assert!(!has_parser_error(&compilation_errors)); - assert!( - compilation_errors.len() == 1, - "Expected 1 compilation error, got: {:?}", - compilation_errors - ); - - for (err, _file_id) in compilation_errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::MethodNotInTrait { - trait_name, - impl_method, - }) => { - assert_eq!(trait_name, "Default"); - assert_eq!(impl_method, "does_not_exist"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -461,6 +540,7 @@ fn check_trait_wrong_parameter() { impl Default for Foo { fn default(x: u32) -> Self { + ^^^ Parameter #1 of method `default` must be of type Field, not u32 Foo {bar: x} } } @@ -468,27 +548,7 @@ fn check_trait_wrong_parameter() { fn main() { } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { - method_name, - expected_typ, - actual_typ, - .. - }) => { - assert_eq!(method_name, "default"); - assert_eq!(expected_typ, "Field"); - assert_eq!(actual_typ, "u32"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -505,34 +565,15 @@ fn check_trait_wrong_parameter2() { impl Default for Foo { fn default(x: Field, y: Foo) -> Self { + ^^^ Parameter #2 of method `default` must be of type Field, not Foo Self { bar: x, array: [x, y.bar] } } } fn main() { - }"; - - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { - method_name, - expected_typ, - actual_typ, - .. - }) => { - assert_eq!(method_name, "default"); - assert_eq!(expected_typ, "Field"); - assert_eq!(actual_typ, "Foo"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; } + "; + check_errors(src); } #[test] @@ -540,30 +581,14 @@ fn check_trait_wrong_parameter_type() { let src = " pub trait Default { fn default(x: Field, y: NotAType) -> Field; + ^^^^^^^^ Could not resolve 'NotAType' in path } fn main(x: Field, y: Field) { assert(y == x); - }"; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - - // This is a duplicate error in the name resolver & type checker. - // In the elaborator there is no duplicate and only 1 error is issued - assert!(errors.len() <= 2, "Expected 1 or 2 errors, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Unresolved(ident), - )) => { - assert_eq!(ident, "NotAType"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; } + "; + check_errors(src); } #[test] @@ -580,6 +605,7 @@ fn check_trait_wrong_parameters_count() { impl Default for Foo { fn default(x: Field) -> Self { + ^^^^^^^ `Default::default` expects 2 parameters, but this method has 1 Self { bar: x, array: [x, x] } } } @@ -587,28 +613,7 @@ fn check_trait_wrong_parameters_count() { fn main() { } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::MismatchTraitImplNumParameters { - actual_num_parameters, - expected_num_parameters, - trait_name, - method_name, - .. - }) => { - assert_eq!(actual_num_parameters, &1_usize); - assert_eq!(expected_num_parameters, &2_usize); - assert_eq!(method_name, "default"); - assert_eq!(trait_name, "Default"); - } - _ => { - panic!("No other errors are expected in this test case! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -619,6 +624,7 @@ fn check_trait_impl_for_non_type() { } impl Default for main { + ^^^^ expected type got function fn default(x: Field, y: Field) -> Field { x + y } @@ -626,20 +632,7 @@ fn check_trait_impl_for_non_type() { fn main() {} "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::ResolverError(ResolverError::Expected { expected, got, .. }) => { - assert_eq!(*expected, "type"); - assert_eq!(*got, "function"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -655,8 +648,8 @@ fn check_impl_struct_not_trait() { z: Field, } - // Default is a struct not a trait impl Default for Foo { + ^^^^^^^ Default is not a trait, therefore it can't be implemented fn default(x: Field, y: Field) -> Self { Self { bar: x, array: [x,y] } } @@ -666,27 +659,14 @@ fn check_impl_struct_not_trait() { let _ = Default { x: 1, z: 1 }; // silence Default never constructed warning } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::NotATrait { - not_a_trait_name, - }) => { - assert_eq!(not_a_trait_name.to_string(), "Default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] fn check_trait_duplicate_declaration() { let src = " trait Default { + ~~~~~~~ First trait definition found here fn default(x: Field, y: Field) -> Self; } @@ -701,32 +681,16 @@ fn check_trait_duplicate_declaration() { } } - trait Default { + ^^^^^^^ Duplicate definitions of trait definition with name Default found + ~~~~~~~ Second trait definition found here fn default(x: Field) -> Self; } fn main() { - }"; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { - typ, - first_def, - second_def, - }) => { - assert_eq!(typ, &DuplicateType::Trait); - assert_eq!(first_def, "Default"); - assert_eq!(second_def, "Default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; } + "; + check_errors(src); } #[test] @@ -739,29 +703,17 @@ fn check_trait_duplicate_implementation() { } impl Default for Foo { + ~~~~~~~ Previous impl defined here } impl Default for Foo { + ^^^ Impl for type `Foo` overlaps with existing impl + ~~~ Overlapping impl } fn main() { let _ = Foo { bar: 1 }; // silence Foo never constructed warning } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { - .. - }) => (), - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { - .. - }) => (), - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -776,31 +728,19 @@ fn check_trait_duplicate_implementation_with_alias() { type MyType = MyStruct; impl Default for MyStruct { + ~~~~~~~ Previous impl defined here } impl Default for MyType { + ^^^^^^ Impl for type `MyType` overlaps with existing impl + ~~~~~~ Overlapping impl } fn main() { let _ = MyStruct {}; // silence MyStruct never constructed warning } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { - .. - }) => (), - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { - .. - }) => (), - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -818,7 +758,8 @@ fn test_impl_self_within_default_def() { fn ok(self) -> Self { self } - }"; + } + "; assert_no_errors(src); } @@ -945,6 +886,7 @@ fn resolve_empty_function() { "; assert_no_errors(src); } + #[test] fn resolve_basic_function() { let src = r#" @@ -955,24 +897,18 @@ fn resolve_basic_function() { "#; assert_no_errors(src); } + #[test] fn resolve_unused_var() { let src = r#" fn main(x : Field) { let y = x + x; + ^ unused variable y + ~ unused variable assert(x == x); } "#; - - let errors = get_program_errors(src); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - // It should be regarding the unused variable - match &errors[0].0 { - CompilationError::ResolverError(ResolverError::UnusedVariable { ident }) => { - assert_eq!(&ident.0.contents, "y"); - } - _ => unreachable!("we should only have an unused var error"), - } + check_errors(src); } #[test] @@ -981,17 +917,11 @@ fn resolve_unresolved_var() { fn main(x : Field) { let y = x + x; assert(y == z); + ^ cannot find `z` in this scope + ~ not found in this scope } "#; - let errors = get_program_errors(src); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - // It should be regarding the unresolved var `z` (Maybe change to undeclared and special case) - match &errors[0].0 { - CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, span: _ }) => { - assert_eq!(name, "z"); - } - _ => unimplemented!("we should only have an unresolved variable"), - } + check_errors(src); } #[test] @@ -999,23 +929,10 @@ fn unresolved_path() { let src = " fn main(x : Field) { let _z = some::path::to::a::func(x); + ^^^^ Could not resolve 'some' in path } "; - let errors = get_program_errors(src); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (compilation_error, _file_id) in errors { - match compilation_error { - CompilationError::ResolverError(err) => { - match err { - ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { - assert_eq!(name.to_string(), "some"); - } - _ => unimplemented!("we should only have an unresolved function"), - }; - } - _ => unimplemented!(), - } - } + check_errors(src); } #[test] @@ -1034,36 +951,16 @@ fn multiple_resolution_errors() { let src = r#" fn main(x : Field) { let y = foo::bar(x); + ^^^ Could not resolve 'foo' in path let z = y + a; + ^ cannot find `a` in this scope + ~ not found in this scope + ^ unused variable z + ~ unused variable + } "#; - - let errors = get_program_errors(src); - assert!(errors.len() == 3, "Expected 3 errors, got: {:?}", errors); - - // Errors are: - // `a` is undeclared - // `z` is unused - // `foo::bar` does not exist - for (compilation_error, _file_id) in errors { - match compilation_error { - CompilationError::ResolverError(err) => { - match err { - ResolverError::UnusedVariable { ident } => { - assert_eq!(&ident.0.contents, "z"); - } - ResolverError::VariableNotDeclared { name, .. } => { - assert_eq!(name, "a"); - } - ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { - assert_eq!(name.to_string(), "foo"); - } - _ => unimplemented!(), - }; - } - _ => unimplemented!(), - } - } + check_errors(src); } #[test] @@ -1145,8 +1042,8 @@ fn resolve_basic_closure() { #[test] fn resolve_simplified_closure() { // based on bug https://github.com/noir-lang/noir/issues/1088 - - let src = r#"fn do_closure(x: Field) -> Field { + let src = r#" + fn do_closure(x: Field) -> Field { let y = x; let ret_capture = || { y @@ -1211,38 +1108,20 @@ fn resolve_fmt_strings() { let src = r#" fn main() { let string = f"this is i: {i}"; + ^ cannot find `i` in this scope + ~ not found in this scope println(string); + ^^^^^^^^^^^^^^^ Unused expression result of type fmtstr<14, ()> let new_val = 10; println(f"random_string{new_val}{new_val}"); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unused expression result of type fmtstr<31, (Field, Field)> } fn println(x : T) -> T { x } "#; - - let errors = get_program_errors(src); - assert!(errors.len() == 3, "Expected 5 errors, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::ResolverError(ResolverError::VariableNotDeclared { - name, .. - }) => { - assert_eq!(name, "i"); - } - CompilationError::TypeError(TypeCheckError::UnusedResultError { - expr_type: _, - expr_span, - }) => { - let a = src.get(expr_span.start() as usize..expr_span.end() as usize).unwrap(); - assert!( - a == "println(string)" || a == "println(f\"random_string{new_val}{new_val}\")" - ); - } - _ => unimplemented!(), - }; - } + check_errors(src); } fn monomorphize_program(src: &str) -> Result { @@ -1291,24 +1170,20 @@ fn lambda$f1(mut env$l1: (Field)) -> Field { check_rewrite(src, expected_rewrite); } -// TODO(https://github.com/noir-lang/noir/issues/6780): currently failing -// with a stack overflow #[test] -#[ignore] fn deny_cyclic_globals() { let src = r#" global A: u32 = B; + ^ This global recursively depends on itself + ^ Dependency cycle found + ~ 'A' recursively depends on itself: A -> B -> A global B: u32 = A; + ^ Variable not in scope + ~ Could not find variable fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::DependencyCycle { .. }) - )); + check_errors(src); } #[test] @@ -1316,9 +1191,11 @@ fn deny_cyclic_type_aliases() { let src = r#" type A = B; type B = A; + ^^^^^^^^^^ Dependency cycle found + ~~~~~~~~~~ 'B' recursively depends on itself: B -> A -> B fn main() {} "#; - assert_eq!(get_program_errors(src).len(), 1); + check_errors(src); } #[test] @@ -1328,9 +1205,10 @@ fn ensure_nested_type_aliases_type_check() { type B = u8; fn main() { let _a: A = 0 as u16; + ^^^^^^^^ Expected type A, found type u16 } "#; - assert_eq!(get_program_errors(src).len(), 1); + check_errors(src); } #[test] @@ -1339,7 +1217,7 @@ fn type_aliases_in_entry_point() { type Foo = u8; fn main(_x: Foo) {} "#; - assert_eq!(get_program_errors(src).len(), 0); + assert_no_errors(src); } #[test] @@ -1351,7 +1229,7 @@ fn operators_in_global_used_in_type() { let _array: [Field; COUNT] = [1, 2, 3]; } "#; - assert_eq!(get_program_errors(src).len(), 0); + assert_no_errors(src); } #[test] @@ -1361,14 +1239,18 @@ fn break_and_continue_in_constrained_fn() { for i in 0 .. 10 { if i == 2 { continue; + ^^^^^^^^^ continue is only allowed in unconstrained functions + ~~~~~~~~~ Constrained code must always have a known number of loop iterations } if i == 5 { break; + ^^^^^^ break is only allowed in unconstrained functions + ~~~~~~ Constrained code must always have a known number of loop iterations } } } "#; - assert_eq!(get_program_errors(src).len(), 2); + check_errors(src); } #[test] @@ -1376,10 +1258,12 @@ fn break_and_continue_outside_loop() { let src = r#" unconstrained fn main() { continue; + ^^^^^^^^^ continue is only allowed within loops break; + ^^^^^^ break is only allowed within loops } "#; - assert_eq!(get_program_errors(src).len(), 2); + check_errors(src); } // Regression for #2540 @@ -1395,8 +1279,7 @@ fn for_loop_over_array() { hello(array); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } // Regression for #4545 @@ -1406,51 +1289,47 @@ fn type_aliases_in_main() { type Outer = [u8; N]; fn main(_arg: Outer<1>) {} "#; - assert_eq!(get_program_errors(src).len(), 0); + assert_no_errors(src); } #[test] fn ban_mutable_globals() { - // Mutable globals are only allowed in a comptime context let src = r#" mut global FOO: Field = 0; + ^^^ Only `comptime` globals may be mutable fn main() { let _ = FOO; // silence FOO never used warning } "#; - assert_eq!(get_program_errors(src).len(), 1); + check_errors(src); } #[test] fn deny_inline_attribute_on_unconstrained() { + // TODO: improve the error location let src = r#" #[no_predicates] unconstrained pub fn foo(x: Field, y: Field) { + ^^^ misplaced #[no_predicates] attribute on unconstrained function foo. Only allowed on constrained functions + ~~~ misplaced #[no_predicates] attribute assert(x != y); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::NoPredicatesAttributeOnUnconstrained { .. }) - )); + check_errors(src); } #[test] fn deny_fold_attribute_on_unconstrained() { + // TODO: improve the error location let src = r#" #[fold] unconstrained pub fn foo(x: Field, y: Field) { + ^^^ misplaced #[fold] attribute on unconstrained function foo. Only allowed on constrained functions + ~~~ misplaced #[fold] attribute assert(x != y); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::FoldAttributeOnUnconstrained { .. }) - )); + check_errors(src); } #[test] @@ -1480,8 +1359,7 @@ fn specify_function_types_with_turbofish() { let _ = generic_func::(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -1514,8 +1392,7 @@ fn specify_method_types_with_turbofish() { let _ = foo.generic_method::(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -1543,14 +1420,10 @@ fn incorrect_turbofish_count_function_call() { fn main() { let _ = generic_func::(); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Expected 2 generics from this function, but 3 were provided } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), - )); + check_errors(src); } #[test] @@ -1581,14 +1454,10 @@ fn incorrect_turbofish_count_method_call() { fn main() { let foo: Foo = Foo { inner: 1 }; let _ = foo.generic_method::(); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Expected 1 generic from this function, but 2 were provided } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), - )); + check_errors(src); } #[test] @@ -1599,15 +1468,12 @@ fn struct_numeric_generic_in_function() { } pub fn bar() { + ^ N has a type of Foo. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`. + ~ Unsupported numeric generic type let _ = Foo { inner: 1 }; // silence Foo never constructed warning } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); + check_errors(src); } #[test] @@ -1618,19 +1484,18 @@ fn struct_numeric_generic_in_struct() { } pub struct Bar { } + ^ N has a type of Foo. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`. + ~ Unsupported numeric generic type "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType(_)), - )); + check_errors(src); } #[test] fn bool_numeric_generic() { let src = r#" pub fn read() -> Field { + ^ N has a type of bool. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`. + ~ Unsupported numeric generic type if N { 0 } else { @@ -1638,12 +1503,7 @@ fn bool_numeric_generic() { } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); + check_errors(src); } #[test] @@ -1652,49 +1512,43 @@ fn numeric_generic_binary_operation_type_mismatch() { pub fn foo() -> bool { let mut check: bool = true; check = N; + ^ Cannot assign an expression of type Field to a value of type bool check } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }), - )); + check_errors(src); } #[test] fn bool_generic_as_loop_bound() { let src = r#" - pub fn read() { // error here - let mut fields = [0; N]; // error here - for i in 0..N { // error here + pub fn read() { + ^ N has a type of bool. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`. + ~ Unsupported numeric generic type + let mut fields = [0; N]; + ^ The numeric generic is not of type `u32` + ~ expected `u32`, found `bool` + for i in 0..N { + ^ Expected type Field, found type bool fields[i] = i + 1; } assert(fields[0] == 1); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); - - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, expr_typ, .. - }) = &errors[2].0 - else { - panic!("Got an error other than a type mismatch"); - }; + check_errors(src); +} - assert_eq!(expected_typ, "Field"); - assert_eq!(expr_typ, "bool"); +#[test] +fn wrong_type_in_for_range() { + let src = r#" + pub fn foo() { + for _ in true..false { + ^^^^ The type bool cannot be used in a for loop + + } + } + "#; + check_errors(src); } #[test] @@ -1711,91 +1565,68 @@ fn numeric_generic_as_struct_field_type_fails() { pub struct Foo { a: Field, b: N, + ^ Expected type, found numeric generic + ~ not a type } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] fn normal_generic_as_array_length() { + // TODO: improve error location, should be just on N let src = r#" pub struct Foo { a: Field, b: [Field; N], + ^^^^^^^^^^ Type provided when a numeric generic was expected + ~~~~~~~~~~ the numeric generic is not of type `u32` } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] fn numeric_generic_as_param_type() { let src = r#" pub fn foo(x: I) -> I { + ^ Expected type, found numeric generic + ~ not a type + ^ Expected type, found numeric generic + ~ not a type + + let _q: I = 5; + ^ Expected type, found numeric generic + ~ not a type x } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); - - // Error from the parameter type - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // Error from the let statement annotated type - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // Error from the return type - assert!(matches!( - errors[2].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] fn numeric_generic_as_unused_param_type() { let src = r#" pub fn foo(_x: I) { } + ^ Expected type, found numeric generic + ~ not a type "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] fn numeric_generic_as_unused_trait_fn_param_type() { let src = r#" trait Foo { + ^^^ unused trait Foo + ~~~ unused trait fn foo(_x: I) { } + ^ Expected type, found numeric generic + ~ not a type } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // Foo is unused - assert!(matches!( - errors[1].0, - CompilationError::ResolverError(ResolverError::UnusedItem { .. }), - )); + check_errors(src); } #[test] @@ -1807,24 +1638,16 @@ fn numeric_generic_as_return_type() { } fn foo(x: T) -> I where T: Zeroed { + ^ Expected type, found numeric generic + ~ not a type + ^^^ unused function foo + ~~~ unused function x.zeroed() } fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - // Error from the return type - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // foo is unused - assert!(matches!( - errors[1].0, - CompilationError::ResolverError(ResolverError::UnusedItem { .. }), - )); + check_errors(src); } #[test] @@ -1836,14 +1659,11 @@ fn numeric_generic_used_in_nested_type_fails() { } pub struct Bar { inner: N + ^ Expected type, found numeric generic + ~ not a type } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] @@ -1852,17 +1672,14 @@ fn normal_generic_used_in_nested_array_length_fail() { pub struct Foo { a: Field, b: Bar, + ^ Type provided when a numeric generic was expected + ~ the numeric generic is not of type `u32` } pub struct Bar { inner: [Field; N] } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] @@ -1977,6 +1794,7 @@ fn numeric_generic_used_in_turbofish() { // allow u16 to be used as an array size #[test] fn numeric_generic_u16_array_size() { + // TODO: improve the error location let src = r#" fn len(_arr: [Field; N]) -> u32 { N @@ -1984,19 +1802,14 @@ fn numeric_generic_u16_array_size() { pub fn foo() -> u32 { let fields: [Field; N] = [0; N]; + ^ The numeric generic is not of type `u32` + ~ expected `u32`, found `u16` + ^^^^^^^^^^ The numeric generic is not of type `u32` + ~~~~~~~~~~ expected `u32`, found `u16` len(fields) } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] @@ -2010,8 +1823,7 @@ fn numeric_generic_field_larger_than_u32() { let _ = foo::(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -2034,8 +1846,7 @@ fn numeric_generic_field_arithmetic_larger_than_u32() { let _ = size(foo::()); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -2043,14 +1854,11 @@ fn cast_256_to_u8_size_checks() { let src = r#" fn main() { assert(256 as u8 == 0); + ^^^^^^^^^ Casting value of type Field to a smaller type (u8) + ~~~~~~~~~ casting untyped value (256) to a type with a maximum size (255) that's smaller than it } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::DownsizingCast { .. }), - )); + check_errors(src); } // TODO(https://github.com/noir-lang/noir/issues/6247): @@ -2062,8 +1870,7 @@ fn cast_negative_one_to_u8_size_checks() { assert((-1) as u8 != 0); } "#; - let errors = get_program_errors(src); - assert!(errors.is_empty()); + assert_no_errors(src); } #[test] @@ -2098,48 +1905,36 @@ fn normal_generic_used_when_numeric_expected_in_where_clause() { } pub fn read() -> T where T: Deserialize { + ^ Type provided when a numeric generic was expected + ~ the numeric generic is not of type `u32` T::deserialize([0, 1]) } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); + // TODO: improve the error location for the array (should be on N) let src = r#" trait Deserialize { fn deserialize(fields: [Field; N]) -> Self; } pub fn read() -> T where T: Deserialize { + ^ Type provided when a numeric generic was expected + ~ the numeric generic is not of type `u32` let mut fields: [Field; N] = [0; N]; + ^ Type provided when a numeric generic was expected + ~ the numeric generic is not of type `u32` + ^^^^^^^^^^ Type provided when a numeric generic was expected + ~~~~~~~~~~ the numeric generic is not of type `u32` for i in 0..N { + ^ cannot find `N` in this scope + ~ not found in this scope fields[i] = i as Field + 1; } T::deserialize(fields) } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 4); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[2].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // N - assert!(matches!( - errors[3].0, - CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), - )); + check_errors(src); } #[test] @@ -2153,6 +1948,8 @@ fn numeric_generics_type_kind_mismatch() { fn bar() -> u16 { foo::() + ^ The numeric generic is not of type `u32` + ~ expected `u32`, found `u16` } global M: u16 = 3; @@ -2161,12 +1958,7 @@ fn numeric_generics_type_kind_mismatch() { let _ = bar::(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] @@ -2188,6 +1980,7 @@ fn numeric_generics_value_kind_mismatch_u32_u64() { pub fn push(&mut self, elem: T) { assert(self.len < MaxLen, "push out of bounds"); + ^^^^^^^^^^^^^^^^^ Integers must have the same bit width LHS is 64, RHS is 32 self.storage[self.len] = elem; self.len += 1; } @@ -2197,20 +1990,12 @@ fn numeric_generics_value_kind_mismatch_u32_u64() { let _ = BoundedVec { storage: [1], len: 1 }; // silence never constructed warning } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::IntegerBitWidth { - bit_width_x: IntegerBitSize::SixtyFour, - bit_width_y: IntegerBitSize::ThirtyTwo, - .. - }), - )); + check_errors(src); } #[test] fn quote_code_fragments() { + // TODO: have the error also point to `contact!` as a secondary // This test ensures we can quote (and unquote/splice) code fragments // which by themselves are not valid code. They only need to be valid // by the time they are unquoted into the macro's call site. @@ -2218,6 +2003,7 @@ fn quote_code_fragments() { fn main() { comptime { concat!(quote { assert( }, quote { false); }); + ^^^^^ Assertion failed } } @@ -2225,11 +2011,7 @@ fn quote_code_fragments() { quote { $a $b } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use InterpreterError::FailingConstraint; - assert!(matches!(&errors[0].0, CompilationError::InterpreterError(FailingConstraint { .. }))); + check_errors(src); } #[test] @@ -2241,6 +2023,7 @@ fn impl_stricter_than_trait_no_trait_method_constraints() { // We want to make sure we trigger the error when override a trait method // which itself has no trait constraints. fn serialize(self) -> [Field; N]; + ~~~~~~~~~ definition of `serialize` from trait } trait ToField { @@ -2262,6 +2045,8 @@ fn impl_stricter_than_trait_no_trait_method_constraints() { impl Serialize<2> for MyType { fn serialize(self) -> [Field; 2] where T: ToField { + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `T: ToField` [ self.a.to_field(), self.b.to_field() ] } } @@ -2276,13 +2061,7 @@ fn impl_stricter_than_trait_no_trait_method_constraints() { let _ = MyType { a: 1, b: 1 }; // silence MyType never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { .. }) - )); + check_errors(src); } #[test] @@ -2295,26 +2074,18 @@ fn impl_stricter_than_trait_different_generics() { fn foo_good() where T: Default; fn foo_bad() where T: Default; + ~~~~~~~ definition of `foo_bad` from trait } impl Foo for () { fn foo_good() where A: Default {} fn foo_bad() where B: Default {} + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `B: Default` } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "B")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } + check_errors(src); } #[test] @@ -2336,14 +2107,17 @@ fn impl_stricter_than_trait_different_object_generics() { fn bar_good() where Option: MyTrait, OtherOption>: OtherTrait; fn bar_bad() where Option: MyTrait, OtherOption>: OtherTrait; + ~~~~~~~ definition of `bar_bad` from trait fn array_good() where [T; 8]: MyTrait; fn array_bad() where [T; 8]: MyTrait; + ~~~~~~~~~ definition of `array_bad` from trait fn tuple_good() where (Option, Option): MyTrait; fn tuple_bad() where (Option, Option): MyTrait; + ~~~~~~~~~ definition of `tuple_bad` from trait } impl Bar for () { @@ -2356,58 +2130,27 @@ fn impl_stricter_than_trait_different_object_generics() { where OtherOption>: OtherTrait, Option: MyTrait { } + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `Option: MyTrait` fn array_good() where [A; 8]: MyTrait { } fn array_bad() where [B; 8]: MyTrait { } + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `[B; 8]: MyTrait` fn tuple_good() where (Option, Option): MyTrait { } fn tuple_bad() where (Option, Option): MyTrait { } + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `(Option, Option): MyTrait` } fn main() { let _ = OtherOption { inner: Option { inner: 1 } }; // silence unused warnings } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "Option")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } - - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[1].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "[B; 8]")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } - - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[2].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "(Option, Option)")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } + check_errors(src); } #[test] @@ -2423,32 +2166,22 @@ fn impl_stricter_than_trait_different_trait() { trait Bar { fn bar() where Option: Default; + ~~~ definition of `bar` from trait } impl Bar for () { // Trait constraint differs due to the trait even though the constraint // types are the same. fn bar() where Option: OtherDefault {} + ^^^^^^^^^^^^ impl has stricter requirements than trait + ~~~~~~~~~~~~ impl has extra requirement `Option: OtherDefault` } fn main() { let _ = Option { inner: 1 }; // silence Option never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "Option")); - assert!(matches!(constraint_name.as_str(), "OtherDefault")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } + check_errors(src); } #[test] @@ -2458,6 +2191,7 @@ fn trait_impl_where_clause_stricter_pass() { fn good_foo() where H: OtherTrait; fn bad_foo() where H: OtherTrait; + ~~~~~~~ definition of `bad_foo` from trait } trait OtherTrait {} @@ -2470,58 +2204,35 @@ fn trait_impl_where_clause_stricter_pass() { fn good_foo() where B: OtherTrait { } fn bad_foo() where A: OtherTrait { } + ^^^^^^^^^^ impl has stricter requirements than trait + ~~~~~~~~~~ impl has extra requirement `A: OtherTrait` } fn main() { let _ = Option { inner: 1 }; // silence Option never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "A")); - assert!(matches!(constraint_name.as_str(), "OtherTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } -} + check_errors(src); +} #[test] fn impl_stricter_than_trait_different_trait_generics() { let src = r#" trait Foo { fn foo() where T: T2; + ~~~ definition of `foo` from trait } impl Foo for () { // Should be A: T2 fn foo() where A: T2 {} + ^^ impl has stricter requirements than trait + ~~ impl has extra requirement `A: T2` } trait T2 {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - constraint_generics, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "A")); - assert!(matches!(constraint_name.as_str(), "T2")); - assert!(matches!(constraint_generics.ordered[0].to_string().as_str(), "B")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } + check_errors(src); } #[test] @@ -2558,6 +2269,8 @@ fn impl_not_found_for_inner_impl() { impl MyType { fn do_thing_with_serialization_with_extra_steps(self) -> Field { process_array(serialize_thing(self)) + ^^^^^^^^^^^^^^^ No matching impl found for `T: ToField` + ~~~~~~~~~~~~~~~ No impl for `T: ToField` } } @@ -2565,13 +2278,7 @@ fn impl_not_found_for_inner_impl() { let _ = MyType { a: 1, b: 1 }; // silence MyType never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::TypeError(TypeCheckError::NoMatchingImplFound { .. }) - )); + check_errors(src); } #[test] @@ -2579,16 +2286,12 @@ fn cannot_call_unconstrained_function_outside_of_unsafe() { let src = r#" fn main() { foo(); + ^^^^^ Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block } unconstrained fn foo() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); - }; + check_errors(src); } #[test] @@ -2596,26 +2299,19 @@ fn cannot_call_unconstrained_first_class_function_outside_of_unsafe() { let src = r#" fn main() { let func = foo; - // Warning should trigger here func(); + ^^^^^^ Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block inner(func); } fn inner(x: unconstrained fn() -> ()) { - // Warning should trigger here x(); + ^^^ Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block } unconstrained fn foo() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - for error in &errors { - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &error.0 else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); - }; - } + check_errors(src); } #[test] @@ -2649,15 +2345,11 @@ fn missing_unsafe_block_when_needing_type_annotations() { impl BigNumTrait for BigNum { fn __is_zero(self) -> bool { self.__is_zero_impl() + ^^^^^^^^^^^^^^^^^^^ Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); - }; + check_errors(src); } #[test] @@ -2666,6 +2358,7 @@ fn cannot_pass_unconstrained_function_to_regular_function() { fn main() { let func = foo; expect_regular(func); + ^^^^ Converting an unconstrained fn to a non-unconstrained fn is unsafe } unconstrained fn foo() {} @@ -2673,12 +2366,7 @@ fn cannot_pass_unconstrained_function_to_regular_function() { fn expect_regular(_func: fn() -> ()) { } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { - panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); - }; + check_errors(src); } #[test] @@ -2686,24 +2374,13 @@ fn cannot_assign_unconstrained_and_regular_fn_to_variable() { let src = r#" fn main() { let _func = if true { foo } else { bar }; + ^^^ Expected type fn() -> (), found type unconstrained fn() -> () } fn foo() {} unconstrained fn bar() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0].0 else { - panic!("Expected a context error, got {:?}", errors[0].0); - }; - - if let TypeCheckError::TypeMismatch { expected_typ, expr_typ, .. } = err.as_ref() { - assert_eq!(expected_typ, "fn() -> ()"); - assert_eq!(expr_typ, "unconstrained fn() -> ()"); - } else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; + check_errors(src); } #[test] @@ -2727,18 +2404,14 @@ fn cannot_pass_unconstrained_function_to_constrained_function() { fn main() { let func = foo; expect_regular(func); + ^^^^ Converting an unconstrained fn to a non-unconstrained fn is unsafe } unconstrained fn foo() {} fn expect_regular(_func: fn() -> ()) {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { - panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); - }; + check_errors(src); } #[test] @@ -2775,24 +2448,11 @@ fn trait_impl_generics_count_mismatch() { trait Foo {} impl Foo<()> for Field {} + ^^^ Foo expects 0 generics but 1 was given - fn main() {}"#; - 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, "Foo"); - assert_eq!(*expected, 0); - assert_eq!(*found, 1); + fn main() {} + "#; + check_errors(src); } #[test] @@ -2810,28 +2470,15 @@ fn duplicate_struct_field() { let src = r#" pub struct Foo { x: i32, + ~ First struct field found here x: i32, + ^ Duplicate definitions of struct field with name x found + ~ Second struct field found here } fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { - typ: _, - first_def, - second_def, - }) = &errors[0].0 - else { - panic!("Expected a 'duplicate' 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(), 30); - assert_eq!(second_def.span().start(), 46); + check_errors(src); } #[test] @@ -2869,23 +2516,12 @@ fn incorrect_generic_count_on_struct_impl() { let src = r#" struct Foo {} impl Foo {} + ^^^ Foo expects 0 generics but 1 was given fn main() { let _ = Foo {}; // silence Foo never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { - found, expected, .. - }) = errors[0].0 - else { - panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(found, 1); - assert_eq!(expected, 0); + check_errors(src); } #[test] @@ -2893,23 +2529,12 @@ fn incorrect_generic_count_on_type_alias() { let src = r#" pub struct Foo {} pub type Bar = Foo; + ^^^ Foo expects 0 generics but 1 was given fn main() { let _ = Foo {}; // silence Foo never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { - found, expected, .. - }) = errors[0].0 - else { - panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(found, 1); - assert_eq!(expected, 0); + check_errors(src); } #[test] @@ -2966,14 +2591,18 @@ fn uses_self_type_in_trait_where_clause() { } pub trait Foo where Self: Trait { + ~~~~~ required by this bound in `Foo` fn foo(self) -> bool { self.trait_func() + ^^^^^^^^^^^^^^^^^ No method named 'trait_func' found for type 'Bar' } } struct Bar {} impl Foo for Bar { + ^^^ The trait bound `_: Trait` is not satisfied + ~~~ The trait `Trait` is not implemented for `_` } @@ -2981,22 +2610,7 @@ fn uses_self_type_in_trait_where_clause() { let _ = Bar {}; // silence Bar never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - let CompilationError::ResolverError(ResolverError::TraitNotImplemented { .. }) = &errors[0].0 - else { - panic!("Expected a trait not implemented error, got {:?}", errors[0].0); - }; - - let CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { method_name, .. }) = - &errors[1].0 - else { - panic!("Expected an unresolved method call error, got {:?}", errors[1].0); - }; - - assert_eq!(method_name, "trait_func"); + check_errors(src); } #[test] @@ -3024,16 +2638,10 @@ fn error_on_cast_over_type_variable() { fn main() { let x = "a"; let _: Field = foo(|x| x as Field, x); + ^ Expected type Field, found type str<1> } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) - )); + check_errors(src); } #[test] @@ -3116,15 +2724,9 @@ fn impl_missing_associated_type() { } impl Foo for () {} + ^^^ `Foo` is missing the associated type `Assoc` "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - &errors[0].0, - CompilationError::TypeError(TypeCheckError::MissingNamedTypeArg { .. }) - )); + check_errors(src); } #[test] @@ -3144,22 +2746,12 @@ fn as_trait_path_syntax_resolves_outside_impl() { // AsTraitPath syntax is a bit silly when associated types // are explicitly specified let _: i64 = 1 as >::Assoc; + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Expected type i64, found type i32 let _ = Bar {}; // silence Bar never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use CompilationError::TypeError; - use TypeCheckError::TypeMismatch; - let TypeError(TypeMismatch { expected_typ, expr_typ, .. }) = errors[0].0.clone() else { - panic!("Expected TypeMismatch error, found {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "i64".to_string()); - assert_eq!(expr_typ, "i32".to_string()); + check_errors(src); } #[test] @@ -3177,70 +2769,71 @@ fn as_trait_path_syntax_no_impl() { fn main() { let _: i64 = 1 as >::Assoc; + ^^^ No matching impl found for `Bar: Foo` + ~~~ No impl for `Bar: Foo` let _ = Bar {}; // silence Bar never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use CompilationError::TypeError; - assert!(matches!(&errors[0].0, TypeError(TypeCheckError::NoMatchingImplFound { .. }))); + check_errors(src); } #[test] -fn dont_infer_globals_to_u32_from_type_use() { +fn do_not_infer_globals_to_u32_from_type_use() { let src = r#" global ARRAY_LEN = 3; + ^^^^^^^^^ Globals must have a specified type + ~ Inferred type is `Field` global STR_LEN: _ = 2; + ^^^^^^^ Globals must have a specified type + ~ Inferred type is `Field` global FMT_STR_LEN = 2; + ^^^^^^^^^^^ Globals must have a specified type + ~ Inferred type is `Field` fn main() { let _a: [u32; ARRAY_LEN] = [1, 2, 3]; + ^^^^^^^^^^^^^^^^ The numeric generic is not of type `u32` + ~~~~~~~~~~~~~~~~ expected `u32`, found `Field` let _b: str = "hi"; + ^^^^^^^^^^^^ The numeric generic is not of type `u32` + ~~~~~~~~~~~~ expected `u32`, found `Field` let _c: fmtstr = f"hi"; + ^^^^^^^^^^^^^^^^^^^^^^ The numeric generic is not of type `u32` + ~~~~~~~~~~~~~~~~~~~~~~ expected `u32`, found `Field` } "#; - - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 6); - for (error, _file_id) in errors.drain(0..3) { - assert!(matches!( - error, - CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) - )); - } - for (error, _file_id) in errors { - assert!(matches!( - error, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) - )); - } + check_errors(src); } #[test] -fn dont_infer_partial_global_types() { +fn do_not_infer_partial_global_types() { let src = r#" pub global ARRAY: [Field; _] = [0; 3]; + ^^^^^ Globals must have a specified type + ~~~~~~ Inferred type is `[Field; 3]` pub global NESTED_ARRAY: [[Field; _]; 3] = [[]; 3]; + ^^^^^^^^^^^^ Globals must have a specified type + ~~~~~~~ Inferred type is `[[Field; 0]; 3]` pub global STR: str<_> = "hi"; + ^^^ Globals must have a specified type + ~~~~ Inferred type is `str<2>` + pub global NESTED_STR: [str<_>] = &["hi"]; + ^^^^^^^^^^ Globals must have a specified type + ~~~~~~~ Inferred type is `[str<2>]` pub global FORMATTED_VALUE: str<5> = "there"; pub global FMT_STR: fmtstr<_, _> = f"hi {FORMATTED_VALUE}"; - pub global TUPLE_WITH_MULTIPLE: ([str<_>], [[Field; _]; 3]) = (&["hi"], [[]; 3]); + ^^^^^^^ Globals must have a specified type + ~~~~~~~~~~~~~~~~~~~~~~~ Inferred type is `fmtstr<20, (str<5>)>` + pub global TUPLE_WITH_MULTIPLE: ([str<_>], [[Field; _]; 3]) = + ^^^^^^^^^^^^^^^^^^^ Globals must have a specified type + (&["hi"], [[]; 3]); + ~~~~~~~~~~~~~~~~~~ Inferred type is `([str<2>], [[Field; 0]; 3])` fn main() { } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 6); - for (error, _file_id) in errors { - assert!(matches!( - error, - CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) - )); - } + check_errors(src); } #[test] @@ -3256,9 +2849,7 @@ fn u32_globals_as_sizes_in_types() { let _c: fmtstr = f"hi"; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -3270,6 +2861,8 @@ fn struct_array_len() { impl Array { pub fn len(self) -> u32 { + ^^^^ unused variable self + ~~~~ unused variable N as u32 } } @@ -3281,17 +2874,11 @@ fn struct_array_len() { assert(ys.len() == 2); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnusedVariable { .. }) - )); + check_errors(src); } // TODO(https://github.com/noir-lang/noir/issues/6245): -// support u16 as an array size +// support u8 as an array size #[test] fn non_u32_as_array_length() { let src = r#" @@ -3299,15 +2886,11 @@ fn non_u32_as_array_length() { fn main() { let _a: [u32; ARRAY_LEN] = [1, 2, 3]; + ^^^^^^^^^^^^^^^^ The numeric generic is not of type `u32` + ~~~~~~~~~~~~~~~~ expected `u32`, found `u8` } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) - )); + check_errors(src); } #[test] @@ -3319,9 +2902,7 @@ fn use_non_u32_generic_in_struct() { let _: S<3> = S {}; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -3344,10 +2925,7 @@ fn use_numeric_generic_in_trait_method() { let _ = Bar{}.foo(bytes); } "#; - - let errors = get_program_errors(src); - println!("{errors:?}"); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -3373,26 +2951,17 @@ fn trait_unconstrained_methods_typechecked_correctly() { assert_eq(2.foo(), 2.identity() as Field); } "#; - - let errors = get_program_errors(src); - println!("{errors:?}"); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] fn error_if_attribute_not_in_scope() { let src = r#" #[not_in_scope] + ^^^^^^^^^^^^^^^ Attribute function `not_in_scope` is not in scope fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::AttributeFunctionNotInScope { .. }) - )); + check_errors(src); } #[test] @@ -3405,9 +2974,7 @@ fn arithmetic_generics_rounding_pass() { fn round(_x: [Field; N / M * M]) {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -3417,17 +2984,12 @@ fn arithmetic_generics_rounding_fail() { // Do not simplify N/M*M to just N // This should be 3/2*2 = 2, not 3 round::<3, 2>([1, 2, 3]); + ^^^^^^^^^ Expected type [Field; 2], found type [Field; 3] } fn round(_x: [Field; N / M * M]) {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) - )); + check_errors(src); } #[test] @@ -3445,15 +3007,10 @@ fn arithmetic_generics_rounding_fail_on_struct() { // Do not simplify N/M*M to just N // This should be 3/2*2 = 2, not 3 let _: W<3> = foo(w_3, w_2); + ^^^^^^^^^^^^^ Expected type W<3>, found type W<2> } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) - )); + check_errors(src); } #[test] @@ -3466,42 +3023,58 @@ fn unconditional_recursion_fail() { let srcs = vec![ r#" fn main() { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing main() } "#, r#" fn main() -> pub bool { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing if main() { true } else { false } } "#, r#" fn main() -> pub bool { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing if true { main() } else { main() } } "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing main() + main() } "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing 1 + main() } "#, r#" fn main() -> pub bool { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing let _ = main(); true } "#, r#" fn main(a: u64, b: u64) -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing main(a + b, main(a, b)) } "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing foo(1, main()) } fn foo(a: u64, b: u64) -> u64 { @@ -3510,12 +3083,16 @@ fn unconditional_recursion_fail() { "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing let (a, b) = (main(), main()); a + b } "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing let mut sum = 0; for i in 0 .. main() { sum += i; @@ -3526,19 +3103,7 @@ fn unconditional_recursion_fail() { ]; for src in srcs { - let errors = get_program_errors(src); - assert!( - !errors.is_empty(), - "expected 'unconditional recursion' error, got nothing; src = {src}" - ); - - for (error, _) in errors { - let CompilationError::ResolverError(ResolverError::UnconditionalRecursion { .. }) = - error - else { - panic!("Expected an 'unconditional recursion' error, got {:?}; src = {src}", error); - }; - } + check_errors(src); } } @@ -3738,117 +3303,89 @@ fn errors_with_better_message_when_trying_to_invoke_struct_field_that_is_a_funct impl Foo { fn call(self) -> bool { self.wrapped(1) + ^^^^^^^^^^^^^^^ Cannot invoke function field 'wrapped' on type 'Foo' as a method + ~~~~~~~~~~~~~~~ to call the function stored in 'wrapped', surround the field access with parentheses: '(', ')' } } fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::CannotInvokeStructFieldFunctionType { - method_name, - .. - }) = &errors[0].0 - else { - panic!("Expected a 'CannotInvokeStructFieldFunctionType' error, got {:?}", errors[0].0); - }; - - assert_eq!(method_name, "wrapped"); -} - -fn test_disallows_attribute_on_impl_method( - attr: &str, - check_error: impl FnOnce(&CompilationError), -) { - let src = format!( - " - pub struct Foo {{ }} - - impl Foo {{ - #[{attr}] - fn foo() {{ }} - }} - - fn main() {{ }} - " - ); - let errors = get_program_errors(&src); - assert_eq!(errors.len(), 1); - check_error(&errors[0].0); -} - -fn test_disallows_attribute_on_trait_impl_method( - attr: &str, - check_error: impl FnOnce(&CompilationError), -) { - let src = format!( - " - pub trait Trait {{ - fn foo() {{ }} - }} - - pub struct Foo {{ }} - - impl Trait for Foo {{ - #[{attr}] - fn foo() {{ }} - }} - - fn main() {{ }} - " - ); - let errors = get_program_errors(&src); - assert_eq!(errors.len(), 1); - check_error(&errors[0].0); + check_errors(src); } #[test] fn disallows_test_attribute_on_impl_method() { - test_disallows_attribute_on_impl_method("test", |error| { - assert!(matches!( - error, - CompilationError::DefinitionError( - DefCollectorErrorKind::TestOnAssociatedFunction { .. } - ) - )); - }); + // TODO: improve the error location + let src = " + pub struct Foo { } + + impl Foo { + #[test] + fn foo() { } + ^^^ The `#[test]` attribute is disallowed on `impl` methods + } + + fn main() { } + "; + check_errors(src); } #[test] fn disallows_test_attribute_on_trait_impl_method() { - test_disallows_attribute_on_trait_impl_method("test", |error| { - assert!(matches!( - error, - CompilationError::DefinitionError( - DefCollectorErrorKind::TestOnAssociatedFunction { .. } - ) - )); - }); + let src = " + pub trait Trait { + fn foo() { } + } + + pub struct Foo { } + + impl Trait for Foo { + #[test] + fn foo() { } + ^^^ The `#[test]` attribute is disallowed on `impl` methods + } + + fn main() { } + "; + check_errors(src); } #[test] fn disallows_export_attribute_on_impl_method() { - test_disallows_attribute_on_impl_method("export", |error| { - assert!(matches!( - error, - CompilationError::DefinitionError( - DefCollectorErrorKind::ExportOnAssociatedFunction { .. } - ) - )); - }); + // TODO: improve the error location + let src = " + pub struct Foo { } + + impl Foo { + #[export] + fn foo() { } + ^^^ The `#[export]` attribute is disallowed on `impl` methods + } + + fn main() { } + "; + check_errors(src); } #[test] fn disallows_export_attribute_on_trait_impl_method() { - test_disallows_attribute_on_trait_impl_method("export", |error| { - assert!(matches!( - error, - CompilationError::DefinitionError( - DefCollectorErrorKind::ExportOnAssociatedFunction { .. } - ) - )); - }); + // TODO: improve the error location + let src = " + pub trait Trait { + fn foo() { } + } + + pub struct Foo { } + + impl Trait for Foo { + #[export] + fn foo() { } + ^^^ The `#[export]` attribute is disallowed on `impl` methods + } + + fn main() { } + "; + check_errors(src); } #[test] @@ -3867,38 +3404,27 @@ fn disallows_underscore_on_right_hand_side() { fn main() { let _ = 1; let _x = _; + ^ in expressions, `_` can only be used on the left-hand side of an assignment + ~ `_` not allowed here } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, .. }) = - &errors[0].0 - else { - panic!("Expected a VariableNotDeclared error, got {:?}", errors[0].0); - }; - - assert_eq!(name, "_"); + check_errors(src); } #[test] fn errors_on_cyclic_globals() { let src = r#" pub comptime global A: u32 = B; + ^ This global recursively depends on itself + ^ Dependency cycle found + ~ 'A' recursively depends on itself: A -> B -> A pub comptime global B: u32 = A; + ^ Variable not in scope + ~ Could not find variable fn main() { } "#; - let errors = get_program_errors(src); - - assert!(errors.iter().any(|(error, _)| matches!( - error, - CompilationError::InterpreterError(InterpreterError::GlobalsDependencyCycle { .. }) - ))); - assert!(errors.iter().any(|(error, _)| matches!( - error, - CompilationError::ResolverError(ResolverError::DependencyCycle { .. }) - ))); + check_errors(src); } #[test] @@ -3907,18 +3433,14 @@ fn warns_on_unneeded_unsafe() { fn main() { // Safety: test unsafe { + ^^^^^^ Unnecessary `unsafe` block foo() } } fn foo() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::TypeError(TypeCheckError::UnnecessaryUnsafeBlock { .. }) - )); + check_errors(src); } #[test] @@ -3929,6 +3451,8 @@ fn warns_on_nested_unsafe() { unsafe { // Safety: test unsafe { + ^^^^^^ Unnecessary `unsafe` block + ~~~~~~ Because it's nested inside another `unsafe` block foo() } } @@ -3936,12 +3460,7 @@ fn warns_on_nested_unsafe() { unconstrained fn foo() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::TypeError(TypeCheckError::NestedUnsafeBlock { .. }) - )); + check_errors(src); } #[test] @@ -4228,28 +3747,6 @@ fn regression_7088() { assert_no_errors(src); } -#[test] -fn error_with_duplicate_enum_variant() { - let src = r#" - enum Foo { - Bar(i32), - Bar(u8), - } - - fn main() {} - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - assert!(matches!( - &errors[0].0, - CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { .. }) - )); - assert!(matches!( - &errors[1].0, - CompilationError::ResolverError(ResolverError::UnusedItem { .. }) - )); -} - #[test] fn errors_on_empty_loop_no_break() { let src = r#" @@ -4262,14 +3759,11 @@ fn errors_on_empty_loop_no_break() { unconstrained fn foo() { loop {} + ^^^^ `loop` must have at least one `break` in it + ~~~~ Infinite loops are disallowed } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::ResolverError(ResolverError::LoopWithoutBreak { .. }) - )); + check_errors(src); } #[test] @@ -4285,6 +3779,8 @@ fn errors_on_loop_without_break() { unconstrained fn foo() { let mut x = 1; loop { + ^^^^ `loop` must have at least one `break` in it + ~~~~ Infinite loops are disallowed x += 1; bar(x); } @@ -4292,12 +3788,7 @@ fn errors_on_loop_without_break() { fn bar(_: Field) {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::ResolverError(ResolverError::LoopWithoutBreak { .. }) - )); + check_errors(src); } #[test] @@ -4313,6 +3804,8 @@ fn errors_on_loop_without_break_with_nested_loop() { unconstrained fn foo() { let mut x = 1; loop { + ^^^^ `loop` must have at least one `break` in it + ~~~~ Infinite loops are disallowed x += 1; bar(x); loop { @@ -4324,12 +3817,7 @@ fn errors_on_loop_without_break_with_nested_loop() { fn bar(_: Field) {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::ResolverError(ResolverError::LoopWithoutBreak { .. }) - )); + check_errors(src); } #[test] @@ -4354,16 +3842,11 @@ fn errors_on_if_without_else_type_mismatch() { fn main() { if true { 1 + ^ Expected type Field, found type () } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0].0 else { - panic!("Expected a Context error"); - }; - assert!(matches!(**err, TypeCheckError::TypeMismatch { .. })); + check_errors(src); } #[test] @@ -4378,15 +3861,11 @@ fn errors_if_for_body_type_is_not_unit() { fn main() { for _ in 0..1 { 1 + ^ Expected type (), found type Field } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) = &errors[0].0 else { - panic!("Expected a TypeMismatch error"); - }; + check_errors(src); } #[test] @@ -4397,15 +3876,11 @@ fn errors_if_loop_body_type_is_not_unit() { if false { break; } 1 + ^ Expected type (), found type Field } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) = &errors[0].0 else { - panic!("Expected a TypeMismatch error"); - }; + check_errors(src); } #[test] @@ -4414,13 +3889,228 @@ fn errors_if_while_body_type_is_not_unit() { unconstrained fn main() { while 1 == 1 { 1 + ^ Expected type (), found type Field } } "#; + check_errors(src); +} + +#[test] +fn check_impl_duplicate_method_without_self() { + let src = " + pub struct Foo {} + + impl Foo { + fn foo() {} + ~~~ first definition found here + fn foo() {} + ^^^ duplicate definitions of foo found + ~~~ second definition found here + } + + fn main() {} + "; + check_errors(src); +} + +#[test] +fn int_min_global() { + let src = r#" + global MIN: i8 = -128; + fn main() { + let _x = MIN; + } + "#; + let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); + assert_eq!(errors.len(), 0); +} - let CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) = &errors[0].0 else { - panic!("Expected a TypeMismatch error"); - }; +#[test] +fn subtract_to_int_min() { + // This would cause an integer underflow panic before + let src = r#" + fn main() { + let _x: i8 = comptime { + let y: i8 = -127; + let z = y - 1; + z + }; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + +#[test] +fn mutate_with_reference_in_lambda() { + let src = r#" + fn main() { + let x = &mut 3; + let f = || { + *x += 2; + }; + f(); + assert(*x == 5); + } + "#; + + assert_no_errors(src); +} + +#[test] +fn mutate_with_reference_marked_mutable_in_lambda() { + let src = r#" + fn main() { + let mut x = &mut 3; + let f = || { + *x += 2; + }; + f(); + assert(*x == 5); + } + "#; + assert_no_errors(src); +} + +#[test] +fn deny_capturing_mut_variable_without_reference_in_lambda() { + let src = r#" + fn main() { + let mut x = 3; + let f = || { + x += 2; + ^ Mutable variable x captured in lambda must be a mutable reference + ~ Use '&mut' instead of 'mut' to capture a mutable variable. + }; + f(); + assert(x == 5); + } + "#; + check_errors(src); +} + +#[test] +fn deny_capturing_mut_variable_without_reference_in_nested_lambda() { + let src = r#" + fn main() { + let mut x = 3; + let f = || { + let inner = || { + x += 2; + ^ Mutable variable x captured in lambda must be a mutable reference + ~ Use '&mut' instead of 'mut' to capture a mutable variable. + }; + inner(); + }; + f(); + assert(x == 5); + } + "#; + check_errors(src); +} + +#[test] +fn allow_capturing_mut_variable_only_used_immutably() { + let src = r#" + fn main() { + let mut x = 3; + let f = || x; + let _x2 = f(); + assert(x == 3); + } + "#; + assert_no_errors(src); +} + +#[test] +fn deny_capturing_mut_var_as_param_to_function() { + let src = r#" + fn main() { + let mut x = 3; + let f = || mutate(&mut x); + ^ Mutable variable x captured in lambda must be a mutable reference + ~ Use '&mut' instead of 'mut' to capture a mutable variable. + f(); + assert(x == 3); + } + + fn mutate(x: &mut Field) { + *x = 5; + } + "#; + check_errors(src); +} + +#[test] +fn deny_capturing_mut_var_as_param_to_function_in_nested_lambda() { + let src = r#" + fn main() { + let mut x = 3; + let f = || { + let inner = || mutate(&mut x); + ^ Mutable variable x captured in lambda must be a mutable reference + ~ Use '&mut' instead of 'mut' to capture a mutable variable. + inner(); + }; + f(); + assert(x == 3); + } + + fn mutate(x: &mut Field) { + *x = 5; + } + "#; + check_errors(src); +} + +#[test] +fn deny_capturing_mut_var_as_param_to_impl_method() { + let src = r#" + struct Foo { + value: Field, + } + + impl Foo { + fn mutate(&mut self) { + self.value = 2; + } + } + + fn main() { + let mut foo = Foo { value: 1 }; + let f = || foo.mutate(); + ^^^ Mutable variable foo captured in lambda must be a mutable reference + ~~~ Use '&mut' instead of 'mut' to capture a mutable variable. + f(); + assert(foo.value == 2); + } + "#; + check_errors(src); +} + +#[test] +fn deny_attaching_mut_ref_to_immutable_object() { + let src = r#" + struct Foo { + value: Field, + } + + impl Foo { + fn mutate(&mut self) { + self.value = 2; + } + } + + fn main() { + let foo = Foo { value: 1 }; + let f = || foo.mutate(); + ^^^ Cannot mutate immutable variable `foo` + f(); + assert(foo.value == 2); + } + "#; + check_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/arithmetic_generics.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/arithmetic_generics.rs index 83de9c077ab3..bcbdbdd6211e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/arithmetic_generics.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/arithmetic_generics.rs @@ -2,11 +2,10 @@ use acvm::{AcirField, FieldElement}; -use super::get_program_errors; use crate::hir::type_check::TypeCheckError; use crate::hir_def::types::{BinaryTypeOperator, Type}; use crate::monomorphization::errors::MonomorphizationError; -use crate::tests::get_monomorphization_error; +use crate::tests::{assert_no_errors, get_monomorphization_error}; #[test] fn arithmetic_generics_canonicalization_deduplication_regression() { @@ -23,8 +22,7 @@ fn arithmetic_generics_canonicalization_deduplication_regression() { }; } "#; - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); } #[test] @@ -52,9 +50,7 @@ fn checked_casts_do_not_prevent_canonicalization() { } } "#; - let errors = get_program_errors(source); - println!("{:?}", errors); - assert_eq!(errors.len(), 0); + assert_no_errors(source); } #[test] @@ -76,9 +72,7 @@ fn arithmetic_generics_checked_cast_zeros() { bar(w) } "#; - - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); let monomorphization_error = get_monomorphization_error(source); assert!(monomorphization_error.is_some()); @@ -123,9 +117,7 @@ fn arithmetic_generics_checked_cast_indirect_zeros() { let _ = bar(w); } "#; - - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); let monomorphization_error = get_monomorphization_error(source); assert!(monomorphization_error.is_some()); @@ -166,8 +158,7 @@ fn global_numeric_generic_larger_than_u32() { let _ = foo::(); } "#; - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); } #[test] @@ -196,6 +187,5 @@ fn global_arithmetic_generic_larger_than_u32() { let _ = foo::().size(); } "#; - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/bound_checks.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/bound_checks.rs index 05669bda4111..6a084e68c7e3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/bound_checks.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/bound_checks.rs @@ -1,24 +1,14 @@ -use crate::hir::def_collector::dc_crate::CompilationError; - -use super::get_program_errors; +use crate::tests::check_errors; #[test] fn overflowing_u8() { let src = r#" fn main() { let _: u8 = 256; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `256` cannot fit into `u8` which has range `0..=255`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } + ^^^ The value `256` cannot fit into `u8` which has range `0..=255` + } + "#; + check_errors(src); } #[test] @@ -26,18 +16,10 @@ fn underflowing_u8() { let src = r#" fn main() { let _: u8 = -1; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `-1` cannot fit into `u8` which has range `0..=255`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } + ^^ The value `-1` cannot fit into `u8` which has range `0..=255` + } + "#; + check_errors(src); } #[test] @@ -45,18 +27,10 @@ fn overflowing_i8() { let src = r#" fn main() { let _: i8 = 128; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `128` cannot fit into `i8` which has range `-128..=127`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } + ^^^ The value `128` cannot fit into `i8` which has range `-128..=127` + } + "#; + check_errors(src); } #[test] @@ -64,16 +38,8 @@ fn underflowing_i8() { let src = r#" fn main() { let _: i8 = -129; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `-129` cannot fit into `i8` which has range `-128..=127`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } + ^^^^ The value `-129` cannot fit into `i8` which has range `-128..=127` + } + "#; + check_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/enums.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/enums.rs new file mode 100644 index 000000000000..d9da717dc568 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/enums.rs @@ -0,0 +1,368 @@ +use crate::{ + hir::def_collector::dc_crate::CompilationError, + parser::ParserErrorReason, + tests::{assert_no_errors, get_program_using_features}, +}; + +use super::{check_errors, check_errors_using_features}; + +#[test] +fn error_with_duplicate_enum_variant() { + let src = r#" + pub enum Foo { + Bar(i32), + ~~~ First enum variant found here + Bar(u8), + ^^^ Duplicate definitions of enum variant with name Bar found + ~~~ Second enum variant found here + } + + fn main() {} + "#; + check_errors(src); +} + +#[test] +fn errors_on_unspecified_unstable_enum() { + // Enums are experimental - this will need to be updated when they are stabilized + let src = r#" + enum Foo { Bar } + ^^^ This requires the unstable feature 'enums' which is not enabled + ~~~ Pass -Zenums to nargo to enable this feature at your own risk. + + fn main() { + let _x = Foo::Bar; + } + "#; + let no_features = &[]; + check_errors_using_features(src, no_features); +} + +#[test] +fn errors_on_unspecified_unstable_match() { + // TODO: update this test. Right now it's hard to test because the span happens in the entire + // `match` node but ideally it would be nice if it only happened in the `match` keyword. + // Enums are experimental - this will need to be updated when they are stabilized + let src = r#" + fn main() { + match 3 { + _ => (), + } + } + "#; + + let no_features = &[]; + let errors = get_program_using_features(src, no_features).2; + assert_eq!(errors.len(), 1); + + let CompilationError::ParseError(error) = &errors[0] else { + panic!("Expected a ParseError experimental feature error"); + }; + + assert!(matches!(error.reason(), Some(ParserErrorReason::ExperimentalFeature(_)))); +} + +#[test] +fn errors_on_repeated_match_variables_in_pattern() { + let src = r#" + fn main() { + match (1, 2) { + (_x, _x) => (), + ^^ Variable `_x` was already defined in the same match pattern + ~~ `_x` redefined here + ~~ `_x` was previously defined here + } + } + "#; + check_errors(src); +} + +#[test] +fn duplicate_field_in_match_struct_pattern() { + let src = r#" + fn main() { + let foo = Foo { x: 10, y: 20 }; + match foo { + Foo { x: _, x: _, y: _ } => {} + ^ duplicate field x + } + } + + struct Foo { + x: i32, + y: Field, + } + "#; + check_errors(src); +} + +#[test] +fn missing_field_in_match_struct_pattern() { + let src = r#" + fn main() { + let foo = Foo { x: 10, y: 20 }; + match foo { + Foo { x: _ } => {} + ^^^ missing field y in struct Foo + } + } + + struct Foo { + x: i32, + y: Field, + } + "#; + check_errors(src); +} + +#[test] +fn no_such_field_in_match_struct_pattern() { + let src = r#" + fn main() { + let foo = Foo { x: 10, y: 20 }; + match foo { + Foo { x: _, y: _, z: _ } => {} + ^ no such field z defined in struct Foo + } + } + + struct Foo { + x: i32, + y: Field, + } + "#; + check_errors(src); +} + +#[test] +fn match_integer_type_mismatch_in_pattern() { + let src = r#" + fn main() { + match 2 { + Foo::One(_) => (), + ^^^^^^^^ Expected type Field, found type Foo + } + } + + enum Foo { + One(i32), + } + "#; + check_errors(src); +} + +#[test] +fn match_shadow_global() { + let src = r#" + fn main() { + match 2 { + foo => assert_eq(foo, 2), + } + } + + fn foo() {} + "#; + assert_no_errors(src); +} + +#[test] +fn match_no_shadow_global() { + let src = r#" + fn main() { + match 2 { + crate::foo => (), + ^^^^^^^^^^ Expected a struct, enum, or literal pattern, but found a function + } + } + + fn foo() {} + "#; + check_errors(src); +} + +#[test] +fn constructor_arg_arity_mismatch_in_pattern() { + let src = r#" + fn main() { + match Foo::One(1) { + Foo::One(_, _) => (), + ^^^^^^^^ Expected 1 argument, but found 2 + Foo::Two(_) => (), + ^^^^^^^^ Expected 2 arguments, but found 1 + } + } + + enum Foo { + One(i32), + Two(i32, i32), + } + "#; + check_errors(src); +} + +#[test] +fn unreachable_match_case() { + check_errors( + r#" + fn main() { + match Opt::Some(Opt::Some(3)) { + Opt::Some(_) => (), + Opt::None => (), + Opt::Some(Opt::Some(_)) => (), + ^^^^^^^^^^^^^^^^^^^^^^^ Unreachable match case + ~~~~~~~~~~~~~~~~~~~~~~~ This pattern is redundant with one or more prior patterns + } + } + + enum Opt { + None, + Some(T), + } + "#, + ); +} + +#[test] +fn match_reachability_errors_ignored_when_there_is_a_type_error() { + // No comment on the second `None` case. + // Type errors in general mess up reachability errors in match cases. + // If we naively change to catch this case (which is easy) we also end up + // erroring that the `3 => ()` case is unreachable as well, which is true + // but we don't want to annoy users with an extra obvious error. This + // behavior matches Rust as well. + check_errors( + " + fn main() { + match Opt::Some(3) { + Opt::None => (), + Opt::Some(_) => {}, + Opt::None => (), + 3 => (), + ^ Expected type Opt, found type Field + } + } + + enum Opt { + None, + Some(T), + } + ", + ); +} + +#[test] +fn missing_single_case() { + check_errors( + " + fn main() { + match Opt::Some(3) { + ^^^^^^^^^^^^ Missing case: `Some(_)` + Opt::None => (), + } + } + + enum Opt { + None, + Some(T), + } + ", + ); +} + +#[test] +fn missing_many_cases() { + check_errors( + " + fn main() { + match Abc::A { + ^^^^^^ Missing cases: `C`, `D`, `E`, and 21 more not shown + Abc::A => (), + Abc::B => (), + } + } + + enum Abc { + A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z + } + ", + ); +} + +#[test] +fn missing_int_ranges() { + check_errors( + " + fn main() { + let x: i8 = 3; + match Opt::Some(x) { + ^^^^^^^^^^^^ Missing cases: `None`, `Some(-128..=3)`, `Some(5)`, and 1 more not shown + Opt::Some(4) => (), + Opt::Some(6) => (), + } + } + + enum Opt { + None, + Some(T), + } + ", + ); +} + +#[test] +fn missing_int_ranges_with_negatives() { + check_errors( + " + fn main() { + let x: i32 = -4; + match x { + ^ Missing cases: `-2147483648..=-6`, `-4..=-1`, `1..=2`, and 1 more not shown + -5 => (), + 0 => (), + 3 => (), + } + } + ", + ); +} + +#[test] +fn missing_cases_with_empty_match() { + check_errors( + " + fn main() { + match Abc::A {} + ^^^^^^ Missing cases: `A`, `B`, `C`, and 23 more not shown + } + + enum Abc { + A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z + } + ", + ); +} + +#[test] +fn missing_integer_cases_with_empty_match() { + check_errors( + " + fn main() { + let x: i8 = 3; + match x {} + ^ Missing cases: `i8` is non-empty + ~ Try adding a match-all pattern: `_` + } + ", + ); +} + +#[test] +fn match_on_empty_enum() { + check_errors( + " + pub fn foo(v: Void) { + match v {} + } + pub enum Void {}", + ); +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/imports.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/imports.rs index 8598d8296e59..ba2dad285d6a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/imports.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/imports.rs @@ -1,9 +1,6 @@ -use crate::hir::{ - def_collector::{dc_crate::CompilationError, errors::DefCollectorErrorKind}, - resolution::{errors::ResolverError, import::PathResolutionError}, -}; +use crate::tests::check_errors; -use super::{assert_no_errors, get_program_errors}; +use super::assert_no_errors; #[test] fn use_super() { @@ -23,19 +20,11 @@ fn use_super() { #[test] fn no_super() { - let src = "use super::some_func;"; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( - PathResolutionError::NoSuper(span), - )) = &errors[0].0 - else { - panic!("Expected a 'no super' error, got {:?}", errors[0].0); - }; - - assert_eq!(span.start(), 4); - assert_eq!(span.end(), 9); + let src = " + use super::some_func; + ^^^^^ There is no super module + "; + check_errors(src); } #[test] @@ -69,18 +58,11 @@ fn warns_on_use_of_private_exported_item() { fn main() { foo::baz(); + ^^^ baz is private and not visible from the current module + ~~~ baz is private } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - &errors[0].0, - CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(..), - )) - )); + check_errors(src); } #[test] @@ -110,22 +92,15 @@ fn warns_on_re_export_of_item_with_less_visibility() { } pub use bar::baz; + ^^^ cannot re-export baz because it has less visibility than this use statement + ~~~ consider marking baz as pub } fn main() { foo::baz(); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - &errors[0].0, - CompilationError::DefinitionError( - DefCollectorErrorKind::CannotReexportItemWithLessVisibility { .. } - ) - )); + check_errors(src); } #[test] @@ -136,21 +111,10 @@ fn errors_if_using_alias_in_import() { } use foo::bar::baz; + ^^^ bar is a type alias, not a module fn main() { } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( - PathResolutionError::NotAModule { ident, kind }, - )) = &errors[0].0 - else { - panic!("Expected a 'not a module' error, got {:?}", errors[0].0); - }; - - assert_eq!(ident.to_string(), "bar"); - assert_eq!(*kind, "type alias"); + check_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/metaprogramming.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/metaprogramming.rs index b42342fa47d3..a19ef17d8353 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/metaprogramming.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/metaprogramming.rs @@ -1,17 +1,15 @@ -use noirc_errors::Spanned; +use noirc_errors::Located; use crate::{ ast::Ident, hir::{ - comptime::InterpreterError, + comptime::ComptimeError, def_collector::{ dc_crate::CompilationError, errors::{DefCollectorErrorKind, DuplicateType}, }, - resolution::errors::ResolverError, - type_check::TypeCheckError, }, - parser::ParserErrorReason, + tests::check_errors, }; use super::{assert_no_errors, get_program_errors}; @@ -23,39 +21,30 @@ fn comptime_let() { comptime let my_var = 2; assert_eq(my_var, 2); }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] fn comptime_code_rejects_dynamic_variable() { - let src = r#"fn main(x: Field) { + let src = r#" + fn main(x: Field) { comptime let my_var = (x - x) + 2; + ^ Non-comptime variable `x` referenced in comptime code + ~ Non-comptime variables can't be used in comptime code assert_eq(my_var, 2); - }"#; - let errors = get_program_errors(src); - - assert_eq!(errors.len(), 1); - match &errors[0].0 { - CompilationError::InterpreterError(InterpreterError::NonComptimeVarReferenced { - name, - .. - }) => { - assert_eq!(name, "x"); - } - _ => panic!("expected an InterpreterError"), } + "#; + check_errors(src); } #[test] fn comptime_type_in_runtime_code() { - let source = "pub fn foo(_f: FunctionDefinition) {}"; - let errors = get_program_errors(source); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::ComptimeTypeInRuntimeCode { .. }) - )); + let source = " + pub fn foo(_f: FunctionDefinition) {} + ^^^^^^^^^^^^^^^^^^ Comptime-only type `FunctionDefinition` cannot be used in runtime code + ~~~~~~~~~~~~~~~~~~ Comptime-only type used here + "; + check_errors(source); } #[test] @@ -64,6 +53,7 @@ fn macro_result_type_mismatch() { fn main() { comptime { let x = unquote!(quote { "test" }); + ^^^^^^^^^^^^^^^^^^^^^^^^^^ Expected type Field, found type str<4> let _: Field = x; } } @@ -72,13 +62,7 @@ fn macro_result_type_mismatch() { q } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) - )); + check_errors(src); } #[test] @@ -130,6 +114,8 @@ fn allows_references_to_structs_generated_by_macros() { #[test] fn errors_if_macros_inject_functions_with_name_collisions() { + // This can't be tested using `check_errors` right now because the two secondary + // errors land on the same span. let src = r#" comptime fn make_colliding_functions(_s: StructDefinition) -> Quoted { quote { @@ -150,14 +136,21 @@ fn errors_if_macros_inject_functions_with_name_collisions() { } "#; - let errors = get_program_errors(src); + let mut errors = get_program_errors(src); assert_eq!(errors.len(), 1); + + let CompilationError::ComptimeError(ComptimeError::ErrorRunningAttribute { error, .. }) = + errors.remove(0) + else { + panic!("Expected a ComptimeError, got {:?}", errors[0]); + }; + assert!(matches!( - &errors[0].0, + *error, CompilationError::DefinitionError( DefCollectorErrorKind::Duplicate { typ: DuplicateType::Function, - first_def: Ident(Spanned { contents, .. }), + first_def: Ident(Located { contents, .. }), .. }, ) if contents == "foo" @@ -194,6 +187,8 @@ fn does_not_fail_to_parse_macro_on_parser_warning() { quote { pub fn bar() { unsafe { + ^^^^^^ Unsafe block must have a safety comment above it + ~~~~~~ The comment must start with the "Safety: " word foo(); } } @@ -204,12 +199,5 @@ fn does_not_fail_to_parse_macro_on_parser_warning() { bar() } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ParseError(parser_error) = &errors[0].0 else { - panic!("Expected a ParseError, got {:?}", errors[0].0); - }; - - assert!(matches!(parser_error.reason(), Some(ParserErrorReason::MissingSafetyComment))); + check_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/references.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/references.rs index 5dbc395eb590..a11cd0eea538 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/references.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/references.rs @@ -1,9 +1,4 @@ -use crate::hir::{ - def_collector::dc_crate::CompilationError, resolution::errors::ResolverError, - type_check::TypeCheckError, -}; - -use super::get_program_errors; +use crate::tests::check_errors; #[test] fn cannot_mutate_immutable_variable() { @@ -11,21 +6,12 @@ fn cannot_mutate_immutable_variable() { fn main() { let array = [1]; mutate(&mut array); + ^^^^^ Cannot mutate immutable variable `array` } fn mutate(_: &mut [Field; 1]) {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = - &errors[0].0 - else { - panic!("Expected a CannotMutateImmutableVariable error"); - }; - - assert_eq!(name, "array"); + check_errors(src); } #[test] @@ -38,23 +24,14 @@ fn cannot_mutate_immutable_variable_on_member_access() { fn main() { let foo = Foo { x: 0 }; mutate(&mut foo.x); + ^^^^^ Cannot mutate immutable variable `foo` } fn mutate(foo: &mut Field) { *foo = 1; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = - &errors[0].0 - else { - panic!("Expected a CannotMutateImmutableVariable error"); - }; - - assert_eq!(name, "foo"); + check_errors(src); } #[test] @@ -62,23 +39,15 @@ fn does_not_crash_when_passing_mutable_undefined_variable() { let src = r#" fn main() { mutate(&mut undefined); + ^^^^^^^^^ cannot find `undefined` in this scope + ~~~~~~~~~ not found in this scope } fn mutate(foo: &mut Field) { *foo = 1; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, .. }) = - &errors[0].0 - else { - panic!("Expected a VariableNotDeclared error"); - }; - - assert_eq!(name, "undefined"); + check_errors(src); } #[test] @@ -90,6 +59,7 @@ fn constrained_reference_to_unconstrained() { // Safety: test context unsafe { mut_ref_input(x_ref, y); + ^^^^^ Cannot pass a mutable reference from a constrained runtime to an unconstrained runtime } } @@ -100,13 +70,5 @@ fn constrained_reference_to_unconstrained() { *x = y; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::ConstrainedReferenceToUnconstrained { .. }) = - &errors[0].0 - else { - panic!("Expected an error about passing a constrained reference to unconstrained"); - }; + check_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/traits.rs index 7f252b556c2c..d2f9d9a96724 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/traits.rs @@ -1,9 +1,6 @@ -use crate::hir::def_collector::dc_crate::CompilationError; -use crate::hir::def_collector::errors::DefCollectorErrorKind; -use crate::hir::resolution::errors::ResolverError; -use crate::hir::resolution::import::PathResolutionError; -use crate::hir::type_check::TypeCheckError; -use crate::tests::{get_program_errors, get_program_with_maybe_parser_errors}; +use crate::elaborator::FrontendOptions; + +use crate::tests::{check_errors, get_program_with_options}; use super::assert_no_errors; @@ -107,16 +104,12 @@ fn trait_inheritance_with_generics_4() { fn trait_inheritance_dependency_cycle() { let src = r#" trait Foo: Bar {} + ^^^ Dependency cycle found + ~~~ 'Foo' recursively depends on itself: Foo -> Bar -> Foo trait Bar: Foo {} fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::DependencyCycle { .. }) - )); + check_errors(src); } #[test] @@ -125,43 +118,31 @@ fn trait_inheritance_missing_parent_implementation() { pub trait Foo {} pub trait Bar: Foo {} + ~~~ required by this bound in `Bar` pub struct Struct {} impl Bar for Struct {} + ^^^^^^ The trait bound `Struct: Foo` is not satisfied + ~~~~~~ The trait `Foo` is not implemented for `Struct` fn main() { let _ = Struct {}; // silence Struct never constructed warning } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::TraitNotImplemented { - impl_trait, - missing_trait: the_trait, - type_missing_trait: typ, - .. - }) = &errors[0].0 - else { - panic!("Expected a TraitNotImplemented error, got {:?}", &errors[0].0); - }; - - assert_eq!(the_trait, "Foo"); - assert_eq!(typ, "Struct"); - assert_eq!(impl_trait, "Bar"); + check_errors(src); } #[test] fn errors_on_unknown_type_in_trait_where_clause() { let src = r#" pub trait Foo where T: Unknown {} + ^^^^^^^ Could not resolve 'Unknown' in path fn main() { } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); + check_errors(src); } #[test] @@ -220,6 +201,7 @@ fn does_not_error_if_impl_trait_constraint_is_satisfied_for_type_variable() { "#; assert_no_errors(src); } + #[test] fn errors_if_impl_trait_constraint_is_not_satisfied() { let src = r#" @@ -230,6 +212,7 @@ fn errors_if_impl_trait_constraint_is_not_satisfied() { pub trait Foo where T: Greeter, + ~~~~~~~ required by this bound in `Foo` { fn greet(object: U) where @@ -244,25 +227,12 @@ fn errors_if_impl_trait_constraint_is_not_satisfied() { pub struct Bar; impl Foo for Bar {} + ^^^ The trait bound `SomeGreeter: Greeter` is not satisfied + ~~~ The trait `Greeter` is not implemented for `SomeGreeter` fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::TraitNotImplemented { - impl_trait, - missing_trait: the_trait, - type_missing_trait: typ, - .. - }) = &errors[0].0 - else { - panic!("Expected a TraitNotImplemented error, got {:?}", &errors[0].0); - }; - - assert_eq!(the_trait, "Greeter"); - assert_eq!(typ, "SomeGreeter"); - assert_eq!(impl_trait, "Foo"); + check_errors(src); } #[test] @@ -441,6 +411,7 @@ fn trait_alias_polymorphic_where_clause() { fn qux(x: T) -> bool where T: Qux { x.foo().bar().baz() + ^^^^^^^^^^^^^^^^^^^ No method named 'baz' found for type 'U' } impl Foo for Field { @@ -463,35 +434,14 @@ fn trait_alias_polymorphic_where_clause() { fn main() { assert(0.foo().bar().baz() == qux(0)); + ^^^ No matching impl found for `T: Baz` + ~~~ No impl for `T: Baz` } "#; // TODO(https://github.com/noir-lang/noir/issues/6467) // assert_no_errors(src); - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - match &errors[0].0 { - CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { - method_name, .. - }) => { - assert_eq!(method_name, "baz"); - } - other => { - panic!("expected UnresolvedMethodCall, but found {:?}", other); - } - } - - match &errors[1].0 { - CompilationError::TypeError(TypeCheckError::NoMatchingImplFound(err)) => { - assert_eq!(err.constraints.len(), 2); - assert_eq!(err.constraints[0].1, "Baz"); - assert_eq!(err.constraints[1].1, "Qux<_>"); - } - other => { - panic!("expected NoMatchingImplFound, but found {:?}", other); - } - } + check_errors(src); } // TODO(https://github.com/noir-lang/noir/issues/6467): currently failing, so @@ -517,10 +467,12 @@ fn trait_alias_with_where_clause_has_equivalent_errors() { pub fn qux(x: T, _: U) -> bool where U: Qux { x.baz() + ^^^^^^^ No method named 'baz' found for type 'T' } fn main() {} "#; + check_errors(src); let alias_src = r#" trait Bar { @@ -535,37 +487,12 @@ fn trait_alias_with_where_clause_has_equivalent_errors() { pub fn qux(x: T, _: U) -> bool where U: Qux { x.baz() + ^^^^^^^ No method named 'baz' found for type 'T' } fn main() {} "#; - - let errors = get_program_errors(src); - let alias_errors = get_program_errors(alias_src); - - assert_eq!(errors.len(), 1); - assert_eq!(alias_errors.len(), 1); - - match (&errors[0].0, &alias_errors[0].0) { - ( - CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { - method_name, - object_type, - .. - }), - CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { - method_name: alias_method_name, - object_type: alias_object_type, - .. - }), - ) => { - assert_eq!(method_name, alias_method_name); - assert_eq!(object_type, alias_object_type); - } - other => { - panic!("expected UnresolvedMethodCall, but found {:?}", other); - } - } + check_errors(alias_src); } #[test] @@ -650,9 +577,9 @@ fn does_not_crash_on_as_trait_path_with_empty_path() { fn main() {} "#; - let (_, _, errors) = get_program_with_maybe_parser_errors( - src, true, // allow parser errors - ); + let allow_parser_errors = true; + let options = FrontendOptions::test_default(); + let (_, _, errors) = get_program_with_options(src, allow_parser_errors, options); assert!(!errors.is_empty()); } @@ -661,6 +588,7 @@ fn warns_if_trait_is_not_in_scope_for_function_call_and_there_is_only_one_trait_ let src = r#" fn main() { let _ = Bar::foo(); + ^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } pub struct Bar { @@ -678,17 +606,7 @@ fn warns_if_trait_is_not_in_scope_for_function_call_and_there_is_only_one_trait_ } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0].0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -790,6 +708,8 @@ fn errors_if_trait_is_not_in_scope_for_function_call_and_there_are_multiple_cand let src = r#" fn main() { let _ = Bar::foo(); + ^^^ Could not resolve 'foo' in path + ~~~ The following traits which provide `foo` are implemented but not in scope: `private_mod::Foo2`, `private_mod::Foo` } pub struct Bar { @@ -817,18 +737,7 @@ fn errors_if_trait_is_not_in_scope_for_function_call_and_there_are_multiple_cand } } "#; - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::UnresolvedWithPossibleTraitsToImport { ident, mut traits }, - )) = errors.remove(0).0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - traits.sort(); - assert_eq!(traits, vec!["private_mod::Foo", "private_mod::Foo2"]); + check_errors(src); } #[test] @@ -839,6 +748,8 @@ fn errors_if_multiple_trait_methods_are_in_scope_for_function_call() { fn main() { let _ = Bar::foo(); + ^^^ Multiple applicable items in scope + ~~~ All these trait which provide `foo` are implemented and in scope: `private_mod::Foo2`, `private_mod::Foo` } pub struct Bar { @@ -866,18 +777,7 @@ fn errors_if_multiple_trait_methods_are_in_scope_for_function_call() { } } "#; - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::MultipleTraitsInScope { ident, mut traits }, - )) = errors.remove(0).0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - traits.sort(); - assert_eq!(traits, vec!["private_mod::Foo", "private_mod::Foo2"]); + check_errors(src); } #[test] @@ -886,6 +786,7 @@ fn warns_if_trait_is_not_in_scope_for_method_call_and_there_is_only_one_trait_me fn main() { let bar = Bar { x: 42 }; let _ = bar.foo(); + ^^^^^^^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } pub struct Bar { @@ -904,17 +805,7 @@ fn warns_if_trait_is_not_in_scope_for_method_call_and_there_is_only_one_trait_me } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0].0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -952,6 +843,8 @@ fn errors_if_trait_is_not_in_scope_for_method_call_and_there_are_multiple_candid fn main() { let bar = Bar { x: 42 }; let _ = bar.foo(); + ^^^^^^^^^ Could not resolve 'foo' in path + ~~~~~~~~~ The following traits which provide `foo` are implemented but not in scope: `private_mod::Foo2`, `private_mod::Foo` } pub struct Bar { @@ -980,18 +873,7 @@ fn errors_if_trait_is_not_in_scope_for_method_call_and_there_are_multiple_candid } } "#; - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::UnresolvedWithPossibleTraitsToImport { ident, mut traits }, - )) = errors.remove(0).0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - traits.sort(); - assert_eq!(traits, vec!["private_mod::Foo", "private_mod::Foo2"]); + check_errors(src); } #[test] @@ -1003,6 +885,8 @@ fn errors_if_multiple_trait_methods_are_in_scope_for_method_call() { fn main() { let bar = Bar { x : 42 }; let _ = bar.foo(); + ^^^^^^^^^ Multiple applicable items in scope + ~~~~~~~~~ All these trait which provide `foo` are implemented and in scope: `private_mod::Foo2`, `private_mod::Foo` } pub struct Bar { @@ -1031,18 +915,7 @@ fn errors_if_multiple_trait_methods_are_in_scope_for_method_call() { } } "#; - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::MultipleTraitsInScope { ident, mut traits }, - )) = errors.remove(0).0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - traits.sort(); - assert_eq!(traits, vec!["private_mod::Foo", "private_mod::Foo2"]); + check_errors(src); } #[test] @@ -1082,28 +955,17 @@ fn type_checks_trait_default_method_and_errors() { let src = r#" pub trait Foo { fn foo(self) -> i32 { + ^^^ expected type i32, found type bool + ~~~ expected i32 because of return type let _ = self; true + ~~~~ bool returned here } } fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { - expected, - actual, - .. - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected.to_string(), "i32"); - assert_eq!(actual.to_string(), "bool"); + check_errors(src); } #[test] @@ -1145,6 +1007,7 @@ fn warns_if_trait_is_not_in_scope_for_primitive_function_call_and_there_is_only_ let src = r#" fn main() { let _ = Field::foo(); + ^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } mod private_mod { @@ -1159,17 +1022,7 @@ fn warns_if_trait_is_not_in_scope_for_primitive_function_call_and_there_is_only_ } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0].0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -1178,6 +1031,7 @@ fn warns_if_trait_is_not_in_scope_for_primitive_method_call_and_there_is_only_on fn main() { let x: Field = 1; let _ = x.foo(); + ^^^^^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } mod private_mod { @@ -1192,17 +1046,7 @@ fn warns_if_trait_is_not_in_scope_for_primitive_method_call_and_there_is_only_on } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0].0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -1211,6 +1055,7 @@ fn warns_if_trait_is_not_in_scope_for_generic_function_call_and_there_is_only_on fn main() { let x: i32 = 1; let _ = x.foo(); + ^^^^^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } mod private_mod { @@ -1225,17 +1070,7 @@ fn warns_if_trait_is_not_in_scope_for_generic_function_call_and_there_is_only_on } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0].0 - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -1246,25 +1081,19 @@ fn error_on_duplicate_impl_with_associated_type() { } impl Foo for i32 { + ~~~ Previous impl defined here type Bar = u32; } impl Foo for i32 { + ^^^ Impl for type `i32` overlaps with existing impl + ~~~ Overlapping impl type Bar = u8; } fn main() {} "#; - - // Expect "Impl for type `i32` overlaps with existing impl" - // and "Previous impl defined here" - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - use CompilationError::DefinitionError; - use DefCollectorErrorKind::*; - assert!(matches!(&errors[0].0, DefinitionError(OverlappingImpl { .. }))); - assert!(matches!(&errors[1].0, DefinitionError(OverlappingImplNote { .. }))); + check_errors(src); } #[test] @@ -1275,25 +1104,19 @@ fn error_on_duplicate_impl_with_associated_constant() { } impl Foo for i32 { + ~~~ Previous impl defined here let Bar = 5; } impl Foo for i32 { + ^^^ Impl for type `i32` overlaps with existing impl + ~~~ Overlapping impl let Bar = 6; } fn main() {} "#; - - // Expect "Impl for type `i32` overlaps with existing impl" - // and "Previous impl defined here" - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - use CompilationError::DefinitionError; - use DefCollectorErrorKind::*; - assert!(matches!(&errors[0].0, DefinitionError(OverlappingImpl { .. }))); - assert!(matches!(&errors[1].0, DefinitionError(OverlappingImplNote { .. }))); + check_errors(src); } // See https://github.com/noir-lang/noir/issues/6530 @@ -1337,8 +1160,7 @@ fn regression_6530() { let _: Field = foo.into(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -1385,12 +1207,41 @@ fn calls_trait_method_using_struct_name_when_multiple_impls_exist_and_errors_tur } fn main() { let _ = U60Repr::::from2([1, 2, 3]); + ^^^^^^^^^ Expected type Field, found type [Field; 3] } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) - )); + check_errors(src); +} + +#[test] +fn as_trait_path_in_expression() { + let src = r#" + fn main() { + cursed::(); + } + + fn cursed() + where T: Foo + Foo2 + { + ::bar(1); + ::bar(()); + + // Use each function with different generic arguments + ::bar(()); + } + + trait Foo { fn bar(x: U); } + trait Foo2 { fn bar(x: U); } + + pub struct S {} + + impl Foo for S { + fn bar(_x: Z) {} + } + + impl Foo2 for S { + fn bar(_x: Z) {} + } + "#; + assert_no_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/turbofish.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/turbofish.rs index 3e34ea9521b5..d1bf9002ab74 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/turbofish.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/turbofish.rs @@ -1,10 +1,6 @@ -use crate::hir::{ - def_collector::dc_crate::CompilationError, - resolution::{errors::ResolverError, import::PathResolutionError}, - type_check::TypeCheckError, -}; +use crate::tests::check_errors; -use super::{assert_no_errors, get_program_errors}; +use super::assert_no_errors; #[test] fn turbofish_numeric_generic_nested_call() { @@ -66,15 +62,10 @@ fn turbofish_in_constructor_generics_mismatch() { fn main() { let _ = Foo:: { x: 1 }; + ^^^^^^^^^^^^ struct Foo expects 1 generic but 2 were given } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::GenericCountMismatch { .. }), - )); + check_errors(src); } #[test] @@ -87,21 +78,10 @@ fn turbofish_in_constructor() { fn main() { let x: Field = 0; let _ = Foo:: { x: x }; + ^ Expected type i32, found type Field } "#; - - 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"); + check_errors(src); } #[test] @@ -122,6 +102,7 @@ fn turbofish_in_struct_pattern() { #[test] fn turbofish_in_struct_pattern_errors_if_type_mismatch() { + // TODO: maybe the error should be on the expression let src = r#" struct Foo { x: T @@ -130,17 +111,11 @@ fn turbofish_in_struct_pattern_errors_if_type_mismatch() { fn main() { let value: Field = 0; let Foo:: { x } = Foo { x: value }; + ^^^^^^^^^^^^^^^^ Cannot assign an expression of type Foo to a value of type Foo 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); - }; + check_errors(src); } #[test] @@ -153,26 +128,11 @@ fn turbofish_in_struct_pattern_generic_count_mismatch() { fn main() { let value = 0; let Foo:: { x } = Foo { x: value }; + ^^^^^^^^^^^^ struct Foo expects 1 generic but 2 were given 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); + check_errors(src); } #[test] @@ -202,19 +162,10 @@ fn errors_if_turbofish_after_module() { fn main() { moo::::foo(); + ^^^^^^^ turbofish (`::<_>`) not allowed on module `moo` } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TurbofishNotAllowedOnItem { item, .. }, - )) = &errors[0].0 - else { - panic!("Expected a turbofish not allowed on item error, got {:?}", errors[0].0); - }; - assert_eq!(item, "module `moo`"); + check_errors(src); } #[test] @@ -252,22 +203,10 @@ fn turbofish_in_type_before_call_errors() { fn main() { let _ = Foo::::new(true); + ^^^^ Expected type i32, found type bool } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "i32"); - assert_eq!(expr_typ, "bool"); + check_errors(src); } #[test] @@ -312,22 +251,10 @@ fn use_generic_type_alias_with_turbofish_in_method_call_errors() { fn main() { let _ = Bar::::new(true); + ^^^^ Expected type i32, found type bool } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "i32"); - assert_eq!(expr_typ, "bool"); + check_errors(src); } #[test] @@ -371,22 +298,10 @@ fn use_generic_type_alias_with_partial_generics_with_turbofish_in_method_call_er fn main() { let _ = Bar::::new(1, 1); + ^ Expected type bool, found type Field } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "bool"); - assert_eq!(expr_typ, "Field"); + check_errors(src); } #[test] @@ -407,22 +322,10 @@ fn use_generic_type_alias_with_partial_generics_with_turbofish_in_method_call_er fn main() { let _ = Bar::::new(true, true); + ^^^^ Expected type i32, found type bool } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "i32"); - assert_eq!(expr_typ, "bool"); + check_errors(src); } #[test] @@ -440,20 +343,8 @@ fn trait_function_with_turbofish_on_trait_gives_error() { fn main() { let _: i32 = Foo::::foo(1); + ^ Expected type bool, found type Field } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "bool"); - assert_eq!(expr_typ, "Field"); + check_errors(src); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs index c38e604f2c3b..a3c501142444 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/unused_items.rs @@ -1,9 +1,4 @@ -use crate::{ - hir::{def_collector::dc_crate::CompilationError, resolution::errors::ResolverError}, - tests::assert_no_errors, -}; - -use super::get_program_errors; +use crate::tests::{assert_no_errors, check_errors}; #[test] fn errors_on_unused_private_import() { @@ -17,6 +12,8 @@ fn errors_on_unused_private_import() { } use foo::bar; + ^^^ unused import bar + ~~~ unused import use foo::baz; use foo::Foo; @@ -27,17 +24,7 @@ fn errors_on_unused_private_import() { baz(); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "bar"); - assert_eq!(item.item_type(), "import"); + check_errors(src); } #[test] @@ -52,6 +39,8 @@ fn errors_on_unused_pub_crate_import() { } pub(crate) use foo::bar; + ^^^ unused import bar + ~~~ unused import use foo::baz; use foo::Foo; @@ -62,17 +51,7 @@ fn errors_on_unused_pub_crate_import() { baz(); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "bar"); - assert_eq!(item.item_type(), "import"); + check_errors(src); } #[test] @@ -88,51 +67,37 @@ fn errors_on_unused_function() { fn foo() { + ^^^ unused function foo + ~~~ unused function bar(); } fn bar() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "foo"); - assert_eq!(item.item_type(), "function"); + check_errors(src); } #[test] fn errors_on_unused_struct() { let src = r#" struct Foo {} + ^^^ struct `Foo` is never constructed + ~~~ struct is never constructed struct Bar {} fn main() { let _ = Bar {}; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "Foo"); - assert_eq!(item.item_type(), "struct"); + check_errors(src); } #[test] fn errors_on_unused_trait() { let src = r#" trait Foo {} + ^^^ unused trait Foo + ~~~ unused trait trait Bar {} pub struct Baz { @@ -143,17 +108,7 @@ fn errors_on_unused_trait() { fn main() { } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "Foo"); - assert_eq!(item.item_type(), "trait"); + check_errors(src); } #[test] @@ -171,44 +126,28 @@ fn silences_unused_variable_warning() { fn errors_on_unused_type_alias() { let src = r#" type Foo = Field; + ^^^ unused type alias Foo + ~~~ unused type alias type Bar = Field; pub fn bar(_: Bar) {} fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "Foo"); - assert_eq!(item.item_type(), "type alias"); + check_errors(src); } #[test] fn warns_on_unused_global() { let src = r#" global foo: u32 = 1; + ^^^ unused global foo + ~~~ unused global global bar: Field = 1; fn main() { let _ = bar; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0 - else { - panic!("Expected an unused item warning"); - }; - - assert_eq!(ident.to_string(), "foo"); - assert_eq!(item.item_type(), "global"); + check_errors(src); } #[test] @@ -262,9 +201,7 @@ fn no_warning_on_inner_struct_when_parent_is_used() { assert_eq(foos[0].a, 10); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs index 917394316cf9..ee53dcbc84ff 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests/visibility.rs @@ -1,10 +1,4 @@ -use crate::{ - hir::{ - def_collector::{dc_crate::CompilationError, errors::DefCollectorErrorKind}, - resolution::{errors::ResolverError, import::PathResolutionError}, - }, - tests::{assert_no_errors, get_program_errors}, -}; +use crate::tests::{assert_no_errors, check_errors}; #[test] fn errors_once_on_unused_import_that_is_not_accessible() { @@ -14,39 +8,13 @@ fn errors_once_on_unused_import_that_is_not_accessible() { struct Foo {} } use moo::Foo; + ^^^ Foo is private and not visible from the current module + ~~~ Foo is private fn main() { let _ = Foo {}; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( - PathResolutionError::Private { .. } - )) - )); -} - -fn assert_type_is_more_private_than_item_error(src: &str, private_typ: &str, public_item: &str) { - let errors = get_program_errors(src); - - assert!(!errors.is_empty(), "expected visibility error, got nothing"); - for (error, _) in &errors { - let CompilationError::ResolverError(ResolverError::TypeIsMorePrivateThenItem { - typ, - item, - .. - }) = error - else { - panic!("Expected a type vs item visibility error, got {}", error); - }; - - assert_eq!(typ, private_typ); - assert_eq!(item, public_item); - } - assert_eq!(errors.len(), 1, "only expected one error"); + check_errors(src); } #[test] @@ -54,12 +22,13 @@ fn errors_if_type_alias_aliases_more_private_type() { let src = r#" struct Foo {} pub type Bar = Foo; + ^^^ Type `Foo` is more private than item `Bar` pub fn no_unused_warnings() { let _: Bar = Foo {}; } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Foo", "Bar"); + check_errors(src); } #[test] @@ -68,13 +37,14 @@ fn errors_if_type_alias_aliases_more_private_type_in_generic() { pub struct Generic { value: T } struct Foo {} pub type Bar = Generic; + ^^^^^^^^^^^^ Type `Foo` is more private than item `Bar` pub fn no_unused_warnings() { let _ = Foo {}; let _: Bar = Generic { value: Foo {} }; } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Foo", "Bar"); + check_errors(src); } #[test] @@ -84,6 +54,7 @@ fn errors_if_pub_type_alias_leaks_private_type_in_generic() { struct Bar {} pub struct Foo { pub value: T } pub type FooBar = Foo; + ^^^^^^^^ Type `Bar` is more private than item `FooBar` pub fn no_unused_warnings() { let _: FooBar = Foo { value: Bar {} }; @@ -91,7 +62,7 @@ fn errors_if_pub_type_alias_leaks_private_type_in_generic() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "FooBar"); + check_errors(src); } #[test] @@ -101,6 +72,7 @@ fn errors_if_pub_struct_field_leaks_private_type_in_generic() { struct Bar {} pub struct Foo { pub value: T } pub struct FooBar { pub value: Foo } + ^^^^^ Type `Bar` is more private than item `FooBar::value` pub fn no_unused_warnings() { let _ = FooBar { value: Foo { value: Bar {} } }; @@ -108,7 +80,7 @@ fn errors_if_pub_struct_field_leaks_private_type_in_generic() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "FooBar::value"); + check_errors(src); } #[test] @@ -118,12 +90,13 @@ fn errors_if_pub_function_leaks_private_type_in_return() { struct Bar {} pub fn bar() -> Bar { + ^^^ Type `Bar` is more private than item `bar` Bar {} } } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "bar"); + check_errors(src); } #[test] @@ -132,6 +105,7 @@ fn errors_if_pub_function_leaks_private_type_in_arg() { pub mod moo { struct Bar {} pub fn bar(_bar: Bar) {} + ^^^ Type `Bar` is more private than item `bar` pub fn no_unused_warnings() { let _ = Bar {}; @@ -139,7 +113,7 @@ fn errors_if_pub_function_leaks_private_type_in_arg() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "bar"); + check_errors(src); } #[test] @@ -172,6 +146,7 @@ fn errors_if_pub_function_on_pub_struct_returns_private() { impl Foo { pub fn bar() -> Bar { + ^^^ Type `Bar` is more private than item `bar` Bar {} } } @@ -182,7 +157,7 @@ fn errors_if_pub_function_on_pub_struct_returns_private() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "bar"); + check_errors(src); } #[test] @@ -218,6 +193,7 @@ fn errors_if_pub_trait_returns_private_struct() { pub trait Foo { fn foo() -> Bar; + ^^^ Type `Bar` is more private than item `foo` } pub fn no_unused_warnings() { @@ -226,7 +202,7 @@ fn errors_if_pub_trait_returns_private_struct() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "foo"); + check_errors(src); } #[test] @@ -262,20 +238,11 @@ fn errors_if_trying_to_access_public_function_inside_private_module() { } fn main() { foo::bar::baz() + ^^^ bar is private and not visible from the current module + ~~~ bar is private } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "bar"); + check_errors(src); } #[test] @@ -293,22 +260,13 @@ fn warns_if_calling_private_struct_method() { pub fn method(foo: moo::Foo) { foo.bar() + ^^^ bar is private and not visible from the current module + ~~~ bar is private } fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "bar"); + check_errors(src); } #[test] @@ -385,22 +343,13 @@ fn error_when_accessing_private_struct_field() { fn foo(foo: moo::Foo) -> Field { foo.x + ^ x is private and not visible from the current module + ~ x is private } fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "x"); + check_errors(src); } #[test] @@ -454,20 +403,11 @@ fn error_when_using_private_struct_field_in_constructor() { fn main() { let _ = moo::Foo { x: 1 }; + ^ x is private and not visible from the current module + ~ x is private } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "x"); + check_errors(src); } #[test] @@ -481,24 +421,15 @@ fn error_when_using_private_struct_field_in_struct_pattern() { fn foo(foo: moo::Foo) -> Field { let moo::Foo { x } = foo; + ^ x is private and not visible from the current module + ~ x is private x } fn main() { } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "x"); + check_errors(src); } #[test] @@ -563,21 +494,12 @@ fn errors_if_accessing_private_struct_member_inside_comptime_context() { comptime { let foo = Foo::new(5); let _ = foo.inner; + ^^^^^ inner is private and not visible from the current module + ~~~~~ inner is private }; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "inner"); + check_errors(src); } #[test] @@ -592,6 +514,7 @@ fn errors_if_accessing_private_struct_member_inside_function_generated_at_compti use foo::Foo; #[generate_inner_accessor] + ~~~~~~~~~~~~~~~~~~~~~~~~~~ While running this function attribute struct Bar { bar_inner: Foo, } @@ -600,6 +523,8 @@ fn errors_if_accessing_private_struct_member_inside_function_generated_at_compti quote { fn bar_get_foo_inner(x: Bar) -> Field { x.bar_inner.foo_inner + ^^^^^^^^^ foo_inner is private and not visible from the current module + ~~~~~~~~~ foo_inner is private } } } @@ -608,16 +533,5 @@ fn errors_if_accessing_private_struct_member_inside_function_generated_at_compti let _ = bar_get_foo_inner(x); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0].0 - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "foo_inner"); + check_errors(src); } diff --git a/noir/noir-repo/compiler/wasm/package.json b/noir/noir-repo/compiler/wasm/package.json index e4a0795f0fe2..bb965244a3ed 100644 --- a/noir/noir-repo/compiler/wasm/package.json +++ b/noir/noir-repo/compiler/wasm/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "1.0.0-beta.2", + "version": "1.0.0-beta.3", "license": "(MIT OR Apache-2.0)", "main": "dist/main.js", "types": "./dist/types/src/index.d.cts", diff --git a/noir/noir-repo/compiler/wasm/src/compile.rs b/noir/noir-repo/compiler/wasm/src/compile.rs index e823f90add58..8c0359bbced0 100644 --- a/noir/noir-repo/compiler/wasm/src/compile.rs +++ b/noir/noir-repo/compiler/wasm/src/compile.rs @@ -5,7 +5,7 @@ use js_sys::{JsString, Object}; use nargo::parse_all; use noirc_artifacts::{contract::ContractArtifact, program::ProgramArtifact}; use noirc_driver::{ - add_dep, file_manager_with_stdlib, prepare_crate, prepare_dependency, CompileOptions, + CompileOptions, add_dep, file_manager_with_stdlib, prepare_crate, prepare_dependency, }; use noirc_evaluator::errors::SsaReport; use noirc_frontend::{ @@ -13,7 +13,7 @@ use noirc_frontend::{ hir::Context, }; use serde::Deserialize; -use std::{collections::HashMap, path::Path}; +use std::{collections::BTreeMap, path::Path}; use wasm_bindgen::prelude::*; use crate::errors::{CompileError, JsCompileError}; @@ -128,16 +128,21 @@ impl JsCompileContractResult { #[derive(Deserialize, Default)] pub(crate) struct DependencyGraph { pub(crate) root_dependencies: Vec, - pub(crate) library_dependencies: HashMap>, + pub(crate) library_dependencies: BTreeMap>, } +/// This map contains the paths of all of the files in the entry-point crate and +/// the transitive dependencies of the entry-point crate. +/// +/// This is for all intents and purposes the file system that the compiler will use to resolve/compile +/// files in the crate being compiled and its dependencies. +/// +/// Using a `BTreeMap` to add files to the [FileManager] in a deterministic order, +/// which affects the `FileId` in the `Location`s in the AST on which the `hash` is based. +/// Note that we cannot expect to match the IDs assigned by the `FileManager` used by `nargo`, +/// because there the order is determined by the dependency graph as well as the file name. #[wasm_bindgen] -// This is a map containing the paths of all of the files in the entry-point crate and -// the transitive dependencies of the entry-point crate. -// -// This is for all intents and purposes the file system that the compiler will use to resolve/compile -// files in the crate being compiled and its dependencies. #[derive(Deserialize, Default)] -pub struct PathToFileSourceMap(pub(crate) HashMap); +pub struct PathToFileSourceMap(pub(crate) BTreeMap); #[wasm_bindgen] impl PathToFileSourceMap { @@ -171,7 +176,7 @@ pub fn compile_program( let compiled_program = noirc_driver::compile_main(&mut context, crate_id, &compile_options, None) .map_err(|errs| { - CompileError::with_file_diagnostics( + CompileError::with_custom_diagnostics( "Failed to compile program", errs, &context.file_manager, @@ -181,7 +186,7 @@ pub fn compile_program( let optimized_program = nargo::ops::transform_program(compiled_program, expression_width); nargo::ops::check_program(&optimized_program).map_err(|errs| { - CompileError::with_file_diagnostics( + CompileError::with_custom_diagnostics( "Compiled program is not solvable", errs, &context.file_manager, @@ -207,8 +212,8 @@ pub fn compile_contract( let compiled_contract = noirc_driver::compile_contract(&mut context, crate_id, &compile_options) - .map_err(|errs| { - CompileError::with_file_diagnostics( + .map_err(|errs: Vec| { + CompileError::with_custom_diagnostics( "Failed to compile contract", errs, &context.file_manager, @@ -231,7 +236,7 @@ fn prepare_context( ::into_serde(&JsValue::from(dependency_graph)) .map_err(|err| err.to_string())? } else { - DependencyGraph { root_dependencies: vec![], library_dependencies: HashMap::new() } + DependencyGraph { root_dependencies: vec![], library_dependencies: BTreeMap::new() } }; let fm = file_manager_with_source_map(file_source_map); @@ -272,7 +277,7 @@ pub(crate) fn file_manager_with_source_map(source_map: PathToFileSourceMap) -> F // upon some library `lib1`. Then the packages that `lib1` depend upon will be placed in the // `library_dependencies` list and the `lib1` will be placed in the `root_dependencies` list. fn process_dependency_graph(context: &mut Context, dependency_graph: DependencyGraph) { - let mut crate_names: HashMap<&CrateName, CrateId> = HashMap::new(); + let mut crate_names: BTreeMap<&CrateName, CrateId> = BTreeMap::new(); for lib in &dependency_graph.root_dependencies { let crate_id = add_noir_lib(context, lib); @@ -311,8 +316,8 @@ mod test { use crate::compile::PathToFileSourceMap; - use super::{file_manager_with_source_map, process_dependency_graph, DependencyGraph}; - use std::{collections::HashMap, path::Path}; + use super::{DependencyGraph, file_manager_with_source_map, process_dependency_graph}; + use std::{collections::BTreeMap, path::Path}; fn setup_test_context(source_map: PathToFileSourceMap) -> Context<'static, 'static> { let mut fm = file_manager_with_source_map(source_map); @@ -333,7 +338,7 @@ mod test { #[test] fn test_works_with_empty_dependency_graph() { let dependency_graph = - DependencyGraph { root_dependencies: vec![], library_dependencies: HashMap::new() }; + DependencyGraph { root_dependencies: vec![], library_dependencies: BTreeMap::new() }; let source_map = PathToFileSourceMap::default(); let mut context = setup_test_context(source_map); @@ -348,7 +353,7 @@ mod test { fn test_works_with_root_dependencies() { let dependency_graph = DependencyGraph { root_dependencies: vec![crate_name("lib1")], - library_dependencies: HashMap::new(), + library_dependencies: BTreeMap::new(), }; let source_map = PathToFileSourceMap( @@ -368,7 +373,7 @@ mod test { fn test_works_with_duplicate_root_dependencies() { let dependency_graph = DependencyGraph { root_dependencies: vec![crate_name("lib1"), crate_name("lib1")], - library_dependencies: HashMap::new(), + library_dependencies: BTreeMap::new(), }; let source_map = PathToFileSourceMap( @@ -387,7 +392,7 @@ mod test { fn test_works_with_transitive_dependencies() { let dependency_graph = DependencyGraph { root_dependencies: vec![crate_name("lib1")], - library_dependencies: HashMap::from([ + library_dependencies: BTreeMap::from([ (crate_name("lib1"), vec![crate_name("lib2")]), (crate_name("lib2"), vec![crate_name("lib3")]), ]), @@ -413,7 +418,7 @@ mod test { fn test_works_with_missing_dependencies() { let dependency_graph = DependencyGraph { root_dependencies: vec![crate_name("lib1")], - library_dependencies: HashMap::from([(crate_name("lib2"), vec![crate_name("lib3")])]), + library_dependencies: BTreeMap::from([(crate_name("lib2"), vec![crate_name("lib3")])]), }; let source_map = PathToFileSourceMap( diff --git a/noir/noir-repo/compiler/wasm/src/compile_new.rs b/noir/noir-repo/compiler/wasm/src/compile_new.rs index ac2f79147b3e..37065c8f8255 100644 --- a/noir/noir-repo/compiler/wasm/src/compile_new.rs +++ b/noir/noir-repo/compiler/wasm/src/compile_new.rs @@ -1,12 +1,12 @@ use crate::compile::{ - file_manager_with_source_map, JsCompileContractResult, JsCompileProgramResult, - PathToFileSourceMap, + JsCompileContractResult, JsCompileProgramResult, PathToFileSourceMap, + file_manager_with_source_map, }; use crate::errors::{CompileError, JsCompileError}; use acvm::acir::circuit::ExpressionWidth; use nargo::parse_all; use noirc_driver::{ - add_dep, compile_contract, compile_main, prepare_crate, prepare_dependency, CompileOptions, + CompileOptions, add_dep, compile_contract, compile_main, prepare_crate, prepare_dependency, }; use noirc_frontend::{ graph::{CrateId, CrateName}, @@ -109,7 +109,7 @@ impl CompilerContext { let compiled_program = compile_main(&mut self.context, root_crate_id, &compile_options, None) .map_err(|errs| { - CompileError::with_file_diagnostics( + CompileError::with_custom_diagnostics( "Failed to compile program", errs, &self.context.file_manager, @@ -119,7 +119,7 @@ impl CompilerContext { let optimized_program = nargo::ops::transform_program(compiled_program, expression_width); nargo::ops::check_program(&optimized_program).map_err(|errs| { - CompileError::with_file_diagnostics( + CompileError::with_custom_diagnostics( "Compiled program is not solvable", errs, &self.context.file_manager, @@ -148,7 +148,7 @@ impl CompilerContext { let compiled_contract = compile_contract(&mut self.context, root_crate_id, &compile_options) .map_err(|errs| { - CompileError::with_file_diagnostics( + CompileError::with_custom_diagnostics( "Failed to compile contract", errs, &self.context.file_manager, @@ -280,7 +280,7 @@ mod test { use noirc_driver::prepare_crate; use noirc_frontend::hir::Context; - use crate::compile::{file_manager_with_source_map, PathToFileSourceMap}; + use crate::compile::{PathToFileSourceMap, file_manager_with_source_map}; use std::path::Path; diff --git a/noir/noir-repo/compiler/wasm/src/errors.rs b/noir/noir-repo/compiler/wasm/src/errors.rs index ef56dcfc911c..47927df10562 100644 --- a/noir/noir-repo/compiler/wasm/src/errors.rs +++ b/noir/noir-repo/compiler/wasm/src/errors.rs @@ -4,7 +4,7 @@ use serde::Serialize; use wasm_bindgen::prelude::*; use fm::FileManager; -use noirc_errors::FileDiagnostic; +use noirc_errors::CustomDiagnostic; #[wasm_bindgen(typescript_custom_section)] const DIAGNOSTICS: &'static str = r#" @@ -87,8 +87,7 @@ pub struct Diagnostic { } impl Diagnostic { - fn new(file_diagnostic: &FileDiagnostic, file: String) -> Diagnostic { - let diagnostic = &file_diagnostic.diagnostic; + fn new(diagnostic: &CustomDiagnostic, file: String) -> Diagnostic { let message = diagnostic.message.clone(); let secondaries = diagnostic @@ -96,8 +95,8 @@ impl Diagnostic { .iter() .map(|label| DiagnosticLabel { message: label.message.clone(), - start: label.span.start(), - end: label.span.end(), + start: label.location.span.start(), + end: label.location.span.end(), }) .collect(); @@ -116,16 +115,16 @@ impl CompileError { CompileError { message: message.to_string(), diagnostics: vec![] } } - pub fn with_file_diagnostics( + pub fn with_custom_diagnostics( message: &str, - file_diagnostics: Vec, + custom_diagnostics: Vec, file_manager: &FileManager, ) -> CompileError { - let diagnostics: Vec<_> = file_diagnostics + let diagnostics: Vec<_> = custom_diagnostics .iter() .map(|err| { let file_path = file_manager - .path(err.file_id) + .path(err.file) .expect("File must exist to have caused diagnostics"); Diagnostic::new(err, file_path.to_str().unwrap().to_string()) }) diff --git a/noir/noir-repo/compiler/wasm/src/lib.rs b/noir/noir-repo/compiler/wasm/src/lib.rs index 6753faf20096..d24b6f4ed01a 100644 --- a/noir/noir-repo/compiler/wasm/src/lib.rs +++ b/noir/noir-repo/compiler/wasm/src/lib.rs @@ -10,8 +10,8 @@ use gloo_utils::format::JsValueSerdeExt; use noirc_driver::{GIT_COMMIT, GIT_DIRTY, NOIRC_VERSION}; use serde::{Deserialize, Serialize}; -use tracing_subscriber::prelude::*; use tracing_subscriber::EnvFilter; +use tracing_subscriber::prelude::*; use tracing_web::MakeWebConsoleWriter; mod compile; @@ -21,8 +21,8 @@ mod errors; pub use compile::{compile_contract, compile_program}; // Expose the new Context-Centric API -pub use compile_new::{compile_contract_, compile_program_, CompilerContext, CrateIDWrapper}; -use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; +pub use compile_new::{CompilerContext, CrateIDWrapper, compile_contract_, compile_program_}; +use wasm_bindgen::{JsValue, prelude::wasm_bindgen}; #[derive(Serialize, Deserialize)] pub struct BuildInfo { diff --git a/noir/noir-repo/compiler/wasm/src/types/noir_artifact.ts b/noir/noir-repo/compiler/wasm/src/types/noir_artifact.ts index b7b42186c1a1..2abdea63c516 100644 --- a/noir/noir-repo/compiler/wasm/src/types/noir_artifact.ts +++ b/noir/noir-repo/compiler/wasm/src/types/noir_artifact.ts @@ -58,6 +58,8 @@ export interface ABIVariable { export interface NoirFunctionEntry { /** The name of the function. */ name: string; + /** The hash of the circuit. */ + hash?: string; /** Whether the function is unconstrained. */ is_unconstrained: boolean; /** The custom attributes applied to the function. */ @@ -96,7 +98,7 @@ export interface ProgramArtifact { /** Version of noir used for the build. */ noir_version: string; /** The hash of the circuit. */ - hash?: number; + hash?: string; /** * The ABI of the function. */ abi: Abi; /** The bytecode of the circuit in base64. */ diff --git a/noir/noir-repo/compiler/wasm/test/compiler/shared/compile.test.ts b/noir/noir-repo/compiler/wasm/test/compiler/shared/compile.test.ts index f9e37530cbcd..8c9af58fa0ae 100644 --- a/noir/noir-repo/compiler/wasm/test/compiler/shared/compile.test.ts +++ b/noir/noir-repo/compiler/wasm/test/compiler/shared/compile.test.ts @@ -27,10 +27,11 @@ export function shouldCompileProgramIdentically( // Prepare noir-wasm artifact const noirWasmProgram = noirWasmArtifact.program; expect(noirWasmProgram).not.to.be.undefined; - const [_noirWasmDebugInfos, norWasmFileMap] = deleteProgramDebugMetadata(noirWasmProgram); + const [_noirWasmDebugInfos, noirWasmFileMap] = deleteProgramDebugMetadata(noirWasmProgram); normalizeVersion(noirWasmProgram); - // We first compare both contracts without considering debug info + // We first compare both contracts without considering debug info. + // We can't expect hashes to match `nargo` because of the different order in which dependencies are visited. delete (noirWasmProgram as Partial).hash; delete (nargoArtifact as Partial).hash; expect(nargoArtifact).to.deep.eq(noirWasmProgram); @@ -38,7 +39,7 @@ export function shouldCompileProgramIdentically( // Compare the file maps, ignoring keys, since those depend in the order in which files are visited, // which may change depending on the file manager implementation. Also ignores paths, since the base // path is reported differently between nargo and noir-wasm. - expect(getSources(nargoFileMap)).to.have.members(getSources(norWasmFileMap)); + expect(getSources(nargoFileMap)).to.have.members(getSources(noirWasmFileMap)); // Compare the debug symbol information, ignoring the actual ids used for file identifiers. // Debug symbol info looks like the following, what we need is to ignore the 'file' identifiers @@ -63,16 +64,23 @@ export function shouldCompileContractIdentically( // Prepare noir-wasm artifact const noirWasmContract = noirWasmArtifact.contract; expect(noirWasmContract).not.to.be.undefined; - const [noirWasmDebugInfos, norWasmFileMap] = deleteContractDebugMetadata(noirWasmContract); + const [noirWasmDebugInfos, noirWasmFileMap] = deleteContractDebugMetadata(noirWasmContract); normalizeVersion(noirWasmContract); // We first compare both contracts without considering debug info + // We can't expect hashes to match `nargo` because of the different order in which dependencies are visited. + nargoArtifact.functions.forEach(function (f) { + delete (f as Partial).hash; + }); + noirWasmContract.functions.forEach(function (f) { + delete (f as Partial).hash; + }); expect(nargoArtifact).to.deep.eq(noirWasmContract); // Compare the file maps, ignoring keys, since those depend in the order in which files are visited, // which may change depending on the file manager implementation. Also ignores paths, since the base // path is reported differently between nargo and noir-wasm. - expect(getSources(nargoFileMap)).to.have.members(getSources(norWasmFileMap)); + expect(getSources(nargoFileMap)).to.have.members(getSources(noirWasmFileMap)); // Compare the debug symbol information, ignoring the actual ids used for file identifiers. // Debug symbol info looks like the following, what we need is to ignore the 'file' identifiers diff --git a/noir/noir-repo/cspell.json b/noir/noir-repo/cspell.json index c7ad22afa96c..3bbbede78ccc 100644 --- a/noir/noir-repo/cspell.json +++ b/noir/noir-repo/cspell.json @@ -261,7 +261,10 @@ "whitespaces", "YOLO", "zkhash", - "zshell" + "zshell", + "flamegraph", + "flamegraphs", + "lookback" ], "ignorePaths": [ "./**/node_modules/**", diff --git a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.mdx b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.mdx index 5f6a7b08ec17..c8c7894ff911 100644 --- a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.mdx +++ b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.mdx @@ -66,7 +66,7 @@ This will compile your source code into a Noir build artifact to be stored in th ```sh bb write_vk_ultra_keccak_honk -b ./target/.json -bb contract --scheme ultra_honk +bb contract_ultra_honk ``` @@ -271,11 +271,13 @@ It would be incorrect to say that a Noir proof verification costs any gas at all ::: -## A Note on EVM chains +## Compatibility with different EVM chains -Noir proof verification requires the ecMul, ecAdd and ecPairing precompiles. Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. You can find an incomplete list of which EVM chains support these precompiles [here](https://www.evmdiff.com/features?feature=precompiles). +Barretenberg proof verification requires the `ecMul`, `ecAdd`, `ecPairing`, and `modexp` EVM precompiles. You can deploy and use the verifier contract on all EVM chains that support the precompiles. -For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: +EVM Diff provides a great table of which EVM chains support which precompiles: https://www.evmdiff.com/features?feature=precompiles + +Some EVM chains manually tested to work with the Barretenberg verifier include: - Optimism - Arbitrum @@ -289,7 +291,12 @@ For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently suppo - Linea - Moonbeam -If you test any other chains, please open a PR on this page to update the list. +Meanwhile, some EVM chains chains manually tested that failed to work with the Barretenberg verifier include: + +- zkSync ERA +- Polygon zkEVM + +Pull requests to update this section is welcome and appreciated if you have compatibility updates on existing / new chains to contribute: https://github.com/noir-lang/noir ## What's next diff --git a/noir/noir-repo/docs/docs/noir/concepts/data_types/integers.md b/noir/noir-repo/docs/docs/noir/concepts/data_types/integers.md index b8a5d4980296..ff3fafa1f905 100644 --- a/noir/noir-repo/docs/docs/noir/concepts/data_types/integers.md +++ b/noir/noir-repo/docs/docs/noir/concepts/data_types/integers.md @@ -58,54 +58,6 @@ fn main(x: i16, y: i16) { Modulo operation is defined for negative integers thanks to integer division, so that the equality `x = (x/y)*y + (x%y)` holds. -## 128 bits Unsigned Integers - -The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind: -- You cannot cast between a native integer and `U128` -- There is a higher performance cost when using `U128`, compared to a native type. - -Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. - -```rust -fn main() { - let x = U128::from_integer(23); - let y = U128::from_hex("0x7"); - let z = x + y; - assert(z.to_integer() == 30); -} -``` - -`U128` is implemented with two 64 bits limbs, representing the low and high bits, which explains the performance cost. You should expect `U128` to be twice more costly for addition and four times more costly for multiplication. -You can construct a U128 from its limbs: -```rust -fn main(x: u64, y: u64) { - let z = U128::from_u64s_be(x,y); - assert(z.hi == x as Field); - assert(z.lo == y as Field); -} -``` - -Note that the limbs are stored as Field elements in order to avoid unnecessary conversions. -Apart from this, most operations will work as usual: - -```rust -fn main(x: U128, y: U128) { - // multiplication - let c = x * y; - // addition and subtraction - let c = c - x + y; - // division - let c = x / y; - // bit operation; - let c = x & y | y; - // bit shift - let c = x << y; - // comparisons; - let c = x < y; - let c = x == y; -} -``` - ## Overflows Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: diff --git a/noir/noir-repo/docs/docs/noir/concepts/traits.md b/noir/noir-repo/docs/docs/noir/concepts/traits.md index 17cc04a97518..af5b396bfb82 100644 --- a/noir/noir-repo/docs/docs/noir/concepts/traits.md +++ b/noir/noir-repo/docs/docs/noir/concepts/traits.md @@ -153,6 +153,37 @@ fn main() { } ``` +## As Trait Syntax + +Rarely to call a method it may not be sufficient to use the general method call syntax of `obj.method(args)`. +One case where this may happen is if there are two traits in scope which both define a method with the same name. +For example: + +```rust +trait Foo { fn bar(); } +trait Foo2 { fn bar(); } + +fn example() + where T: Foo + Foo2 +{ + // How to call Foo::bar and Foo2::bar? +} +``` + +In the above example we have both `Foo` and `Foo2` which define a `bar` method. The normal way to resolve +this would be to use the static method syntax `Foo::bar(object)` but there is no object in this case and +`Self` does not appear in the type signature of `bar` at all so we would not know which impl to choose. +For these situations there is the "as trait" syntax: `::method(object, args...)` + +```rust +fn example() + where T: Foo + Foo2 +{ + ::bar(); + ::bar(); +} +``` + ## Generic Implementations You can add generics to a trait implementation by adding the generic list after the `impl` keyword: diff --git a/noir/noir-repo/docs/docs/noir/modules_packages_crates/dependencies.md b/noir/noir-repo/docs/docs/noir/modules_packages_crates/dependencies.md index 22186b225988..cb8765323929 100644 --- a/noir/noir-repo/docs/docs/noir/modules_packages_crates/dependencies.md +++ b/noir/noir-repo/docs/docs/noir/modules_packages_crates/dependencies.md @@ -77,14 +77,14 @@ use lib_a; You can also import only the specific parts of dependency that you want to use, like so: ```rust -use std::hash::sha256; +use std::hash::blake3; use std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, You can import multiple items in the same line by enclosing them in curly braces: ```rust -use std::hash::{keccak256, sha256}; +use std::hash::{blake2s, blake3}; ``` We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. diff --git a/noir/noir-repo/docs/docs/noir/standard_library/black_box_fns.md b/noir/noir-repo/docs/docs/noir/standard_library/black_box_fns.md index e9392b20a92f..ed905ecb5c29 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/black_box_fns.md +++ b/noir/noir-repo/docs/docs/noir/standard_library/black_box_fns.md @@ -23,7 +23,7 @@ Here is a list of the current black box functions: - AND - XOR - RANGE -- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) +- [Keccakf1600](./cryptographic_primitives/hashes.mdx#keccakf1600) - [Recursive proof verification](./recursion.mdx) 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. diff --git a/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx b/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx index b7518fa95c10..334873e68635 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx +++ b/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -1,8 +1,7 @@ --- title: Hash methods description: - Learn about the cryptographic primitives ready to use for any Noir project, including sha256, - blake2s and pedersen + Learn about the cryptographic primitives ready to use for any Noir project keywords: [cryptographic primitives, Noir project, sha256, blake2s, pedersen, hash] sidebar_position: 0 @@ -10,23 +9,11 @@ sidebar_position: 0 import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; -## sha256 +## sha256 compression -Given an array of bytes, returns the resulting sha256 hash. -Specify a message_size to hash only the first `message_size` bytes of the input. - -#include_code sha256 noir_stdlib/src/hash/sha256.nr rust - -example: -#include_code sha256_var test_programs/execution_success/sha256/src/main.nr rust - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::sha256::sha256_var(x, 4); -} -``` +Performs a sha256 compression on an input and initial state, returning the resulting state. +#include_code sha256_compression noir_stdlib/src/hash/mod.nr rust @@ -88,17 +75,11 @@ example: -## keccak256 +## keccakf1600 -Given an array of bytes (`u8`), returns the resulting keccak hash as an array of -32 bytes (`[u8; 32]`). Specify a message_size to hash only the first -`message_size` bytes of the input. - -#include_code keccak256 noir_stdlib/src/hash/mod.nr rust - -example: +Given an initial `[u64; 25]` state, returns the state resulting from applying a keccakf1600 permutation (`[u64; 25]`). -#include_code keccak256 test_programs/execution_success/keccak256/src/main.nr rust +#include_code keccakf1600 noir_stdlib/src/hash/mod.nr rust diff --git a/noir/noir-repo/docs/docs/noir/standard_library/traits.md b/noir/noir-repo/docs/docs/noir/standard_library/traits.md index e6f6f80ff032..ed923c0707a1 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/traits.md +++ b/noir/noir-repo/docs/docs/noir/standard_library/traits.md @@ -71,7 +71,7 @@ As a general rule of thumb, `From` may be implemented in the [situations where i - The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. - The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. - The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. -- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. +- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `u128: From<[u8; 16]>`, the methods `u128::from_le_bytes` and `u128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `u128` from the same byte array. One additional recommendation specific to Noir is: - The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. diff --git a/noir/noir-repo/docs/docs/tooling/profiler.md b/noir/noir-repo/docs/docs/tooling/profiler.md new file mode 100644 index 000000000000..c31271facbb3 --- /dev/null +++ b/noir/noir-repo/docs/docs/tooling/profiler.md @@ -0,0 +1,135 @@ +--- +title: Noir Profiler +description: Learn about the Noir Profiler, how to generate execution flamegraphs, identify bottlenecks, and visualize optimizations. +keywords: [profiling, profiler, flamegraph] +sidebar_position: 0 +--- + +`noir-profiler` is a sampling profiler designed to analyze and visualize Noir programs. It assists developers to identify bottlenecks by mapping execution data back to the original source code. + +### Installation + +`noir-profiler` comes out of the box with [noirup](../getting_started/noir_installation.md). Test that you have the profiler installed by running `noir-profiler --version`. + +### Usage + +Let's start by creating a simple Noir program. All this program aims to do is zero out an array past some dynamic index. + +```rust +fn main(ptr: pub u32, mut array: [u32; 32]) -> pub [u32; 32] { + for i in 0..32 { + if i > ptr { + array[i] = 0; + } + } + array +} +``` +You can use these values for the `Prover.toml`: +```toml +ptr = 1 +array = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] +``` + +Running `nargo info` we can get some information about the opcodes produced by this program, but it doesn't give us a lot of info on its own. Compile and execute this program normally using `nargo compile` and `nargo execute`. + +### Generating an ACIR opcode flamegraph + +The program on its own is quite high-level. Let's get a more granular look at what is happening by using `noir-profiler`. + +After compiling the program, run the following: +```sh +noir-profiler opcodes --artifact-path ./target/program.json --output ./target/ +``` +Below you can see an example flamegraph with a total 387 opcodes (using `nargo` version 1.0.0-beta.2): +![ACIR Flamegraph Unoptimized](@site/static/img/tooling/profiler/acir-flamegraph-unoptimized.png) + +You should now have a flamegraph that maps ACIR opcodes to their corresponding locations in the source code. We strongly recommend generating these graphs yourself as you follow this guide. Opening the flamegraph in a browser provides a more interactive experience, allowing you to click into and examine different regions of the graph. Simply viewing the image file won't offer the same level of insight. + +We can see that the majority of opcodes come from the write to `array[i]`. Now that we have some more information about our program's bottlenecks, let's optimize it. + +#### Transform conditional writes into reads + +We can improve our circuit's efficiency using [unconstrained functions](../noir/concepts/unconstrained.md). + +Let's replace expensive array writes with array gets with the new code below: +```rust +fn main(ptr: pub u32, array: [u32; 32]) -> pub [u32; 32] { + // Safety: Sets all elements after `ptr` in `array` to zero. + let zeroed_array = unsafe { zero_out_array(ptr, array) }; + for i in 0..32 { + if i > ptr { + assert_eq(zeroed_array[i], 0); + } else { + assert_eq(zeroed_array[i], array[i]); + } + } + zeroed_array +} + +unconstrained fn zero_out_array(ptr: u32, mut array: [u32; 32]) -> [u32; 32] { + for i in 0..32 { + if i > ptr { + array[i] = 0; + } + } + array +} +``` +We chose to instead write our array inside of the unconstrained function. Then inside of our circuit we assert on every value in the array returned from the unconstrained function. + +This new program produces the following ACIR opcodes flamegraph with a total of 284 opcodes: +![ACIR Flamegraph Optimized](@site/static/img/tooling/profiler/acir-flamegraph-optimized.png) + +In the above image we searched for the ACIR opcodes due to `i > ptr` in the source code. Trigger a search by clicking on "Search" in the top right corner of the flamegraph. In the bottom right corner of the image above, you will note that the flamegraph displays the percentage of all opcodes associated with that search. Searching for `memory::op` in the optimized flamegraph will result in no matches. This is due to no longer using a dynamic array in our circuit. By dynamic array, we are referring to using a dynamic index (values reliant upon witness inputs) when working with arrays. Most of the memory operations, have now been replaced with arithmetic operations as we are reading two arrays from known constant indices. + +### Generate a backend gates flamegraph + +Unfortunately, ACIR opcodes do not give us a full picture of where the cost of this program lies. +The `gates` command also accepts a backend binary. In the [quick start guide](../getting_started/quick_start.md#proving-backend) you can see how to get started with the [Barretenberg proving backend](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg). + +Run the following command: +```sh +noir-profiler gates --artifact-path ./target/program.json --backend-path bb --output ./target +``` +`--backend-path` accepts a path to the backend binary. In the above command we assume that you have the backend binary path saved in your PATH. If you do not, you will have to pass the binary's absolute path. + +This produces the following flamegraph with 3,737 total backend gates (using `bb` version 0.76.4): +![Gates Flamegraph Unoptimized](@site/static/img/tooling/profiler/gates-flamegraph-unoptimized.png) + +Searching for ACIR `memory::op` opcodes, they look to cause about 18.2% of the backend gates. + +You will notice that the majority of the backend gates come from the ACIR range opcodes. This is due to the way UltraHonk handles range constraints, which is the backend used in this example. UltraHonk uses lookup tables internally for its range gates. These can take up the majority of the gates for a small circuit, but whose impact becomes more meaningful in larger circuits. If our array was much larger, range gates would become a much smaller percentage of our total circuit. +Here is an example backend gates flamegraph for the same program in this guide but with an array of size 2048: +![Gates Flamegraph Unoptimized 2048](@site/static/img/tooling/profiler/gates-flamegraph-unoptimized-2048.png) +Every backend implements ACIR opcodes differently, so it is important to profile both the ACIR and the backend gates to get a full picture. + +Now let's generate a graph for our optimized circuit with an array of size 32. We get the following flamegraph that produces 3,062 total backend gates: +![Gates Flamegraph Optimized](@site/static/img/tooling/profiler/gates-flamegraph-optimized.png) + +In the optimized flamegraph, we searched for the backend gates due to `i > ptr` in the source code. The backend gates associated with this call stack were only 3.8% of the total backend gates. If we look back to the ACIR flamegraph, that same code was the cause of 43.3% ACIR opcodes. This discrepancy reiterates the earlier point about profiling both the ACIR opcodes and backend gates. + +For posterity, here is the flamegraph for the same program with a size 2048 array: +![Gates Flamegraph Optimized 2048](@site/static/img/tooling/profiler/gates-flamegraph-optimized-2048.png) + +### Generate an unconstrained execution trace flamegraph + +The profiler also enables developers to generate a flamegraph of the unconstrained execution trace. For unconstrained functions Noir compiles down to Brillig bytecode, thus we will be seeing a flamegraph of Brillig opcodes, rather than ACIR opcodes. + +Let's take our initial program and simply add an `unconstrained` modifier before main (e.g. `unconstrained fn main`). Then run the following command: +```sh +noir-profiler execution-opcodes --artifact-name ./target/program.json --prover_toml_path Prover.toml --output ./target +``` +This matches the `opcodes` command, except that now we need to accept a `Prover.toml` file to profile execution with a specific set of inputs. + +We will get the following flamegraph with 1,582 opcodes executed: +![Brillig Trace Initial Program](@site/static/img/tooling/profiler/brillig-trace-initial-32.png) + +Circuit programming (ACIR) is an entirely different execution paradigm compared to regular programming. To demonstrate this point further, let's generate an execution trace for our optimized ACIR program once we have modified `main` to be `unconstrained`. + +We then get the following flamegraph with 2,125 opcodes executed: +![Brillig Trace "Optimized"](@site/static/img/tooling/profiler/brillig-trace-opt-32.png) + +In the above graph we are searching for `new_array`, which shows up zero matches in the initial program. In the unconstrained environment, the updated program essentially just adds extra unnecessary checks. Thus, we see a longer execution trace. + +`execution-opcodes` is useful for when you are searching for bottlenecks in unconstrained code. This can be especially meaningful for optimizing witness generation. Even though unconstrained execution helps us skip proving steps, we still need to compute the relevant inputs/outputs outside of the circuit before proving. diff --git a/noir/noir-repo/docs/docs/tooling/security.md b/noir/noir-repo/docs/docs/tooling/security.md index e14481efc317..8a09d231a7da 100644 --- a/noir/noir-repo/docs/docs/tooling/security.md +++ b/noir/noir-repo/docs/docs/tooling/security.md @@ -39,7 +39,7 @@ Here, the results of `factor` are two elements of the returned array. The value This pass checks if the constraint coverage of Brillig calls is sufficient in these terms. -The check is at the moment disabled by default due to performance concerns and can be enabled by passing the `--enable-brillig-constraints-check` option to `nargo`. +The check is enabled by default and can be disabled by passing the `--skip-brillig-constraints-check` option to `nargo`. #### Lookback option diff --git a/noir/noir-repo/docs/static/img/tooling/profiler/acir-flamegraph-optimized.png b/noir/noir-repo/docs/static/img/tooling/profiler/acir-flamegraph-optimized.png new file mode 100644 index 0000000000000000000000000000000000000000..7237d7868fcce6b18de88d652b9d006ea2cd71a0 GIT binary patch literal 90387 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 z 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 z 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 R{SSaId1)o7A_+sk{|~h518)ET literal 0 HcmV?d00001 diff --git a/noir/noir-repo/docs/static/img/tooling/profiler/acir-flamegraph-unoptimized.png b/noir/noir-repo/docs/static/img/tooling/profiler/acir-flamegraph-unoptimized.png new file mode 100644 index 0000000000000000000000000000000000000000..d807539dcc56cf5bf6b9f894e2e47e5e2a443749 GIT binary patch literal 90830 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

4eg{j`CvW~lsetVfBLgO^ zM~T%NQblC)SP&f7gm!>+!*c$og#QI2@t2?{{v&Qw=e9Q}ygAM0>Zer)DC&CA}9tQ^oQB0Cw9rc* z;Nn2%Y`-Sp^M9_Pfc?+ENy+CA0w2ACz^Pibwaq*3&*~o2Ww6({CQ_uZ*F>Us5^fL< zz^5YfV_H)z3PWAIPhd7+|L?!J$Y1v{>IWRo7<6w$mVovI%1^#Wnez@upgca@ zYm3a4Lta3Ao$mjBVZUw@C;kRPqqvIjllmz9-T@QTv^Q^=JdkAUxzNDiYrg)GxPON) zj^{Oj@MD%cDgT_-zG)6MGx&ME>{95smT;?TR%yA$@l8C@^Jm-`)d15aD`+1?); z^%BH&p!^xoF4%G9vY`f1MgF*hYr(Ba&GyBI^{$9EoLfM3*wumpuD)`yDziAma$`Qt z^ZoH#Ov>uXSQc9)EuU`hgZSi$6R~q8dd)nr(fub$)pZN5&n;5mYPq1`u2bf#^z6t~wiX8Li3QLUEs}=>k*i0uQ{=_!m*#_EacrY^VYq3ha#0yjF zj6ah>BN+ez29J>s1xkG

5jXK#i4Cf|Q>?VY$rbkhokqzp`uJ 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 KT)B+#*Z&8&^A!vL literal 0 HcmV?d00001 diff --git a/noir/noir-repo/docs/static/img/tooling/profiler/gates-flamegraph-optimized.png b/noir/noir-repo/docs/static/img/tooling/profiler/gates-flamegraph-optimized.png new file mode 100644 index 0000000000000000000000000000000000000000..ea445d1950fcb1a60ffb3b60d6e02ba4ca9c501f GIT binary patch literal 34504 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 zⅆ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 ixbrm%;T8WPftb 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 "Open with Codespaces". It should take a few seconds to load, and you're ready to go. :::info -If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. +If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.2/noir/concepts/traits.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.2/noir/concepts/traits.md index 13818ecffac7..17cc04a97518 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.2/noir/concepts/traits.md +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.2/noir/concepts/traits.md @@ -111,6 +111,48 @@ fn foo(elements: [T], thing: U) where } ``` +## Invoking trait methods + +As seen in the previous section, the `area` method was invoked on a type `T` that had a where clause `T: Area`. + +To invoke `area` on a type that directly implements the trait `Area`, the trait must be in scope (imported): + +```rust +use geometry::Rectangle; + +fn main() { + let rectangle = Rectangle { width: 1, height: 2}; + let area = rectangle.area(); // Error: the compiler doesn't know which `area` method this is +} +``` + +The above program errors because there might be multiple traits with an `area` method, all implemented +by `Rectangle`, and it's not clear which one should be used. + +To make the above program compile, the trait must be imported: + +```rust +use geometry::Rectangle; +use geometry::Area; // Bring the Area trait into scope + +fn main() { + let rectangle = Rectangle { width: 1, height: 2}; + let area = rectangle.area(); // OK: will use `area` from `geometry::Area` +} +``` + +An error will also be produced if multiple traits with an `area` method are in scope. If both traits +are needed in a file you can use the fully-qualified path to the trait: + +```rust +use geometry::Rectangle; + +fn main() { + let rectangle = Rectangle { width: 1, height: 2}; + let area = geometry::Area::area(rectangle); +} +``` + ## Generic Implementations You can add generics to a trait implementation by adding the generic list after the `impl` keyword: @@ -325,20 +367,6 @@ let x: Field = Default::default(); let result = x + Default::default(); ``` -:::warning - -```rust -let _ = Default::default(); -``` - -If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be -arbitrarily selected. This occurs most often when the result of a trait function call with no parameters -is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type, -always refer to it via the implementation type's namespace - e.g. `MyType::default()`. -This is set to change to an error in future Noir versions. - -::: - ## Default Method Implementations A trait can also have default implementations of its methods by giving a body to the desired functions. diff --git a/noir/noir-repo/docs/docs/tooling/cspell.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/cspell.json similarity index 55% rename from noir/noir-repo/docs/docs/tooling/cspell.json rename to noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/cspell.json index e8126ba0874b..c60b0a597b10 100644 --- a/noir/noir-repo/docs/docs/tooling/cspell.json +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/cspell.json @@ -1,5 +1,5 @@ { "words": [ - "lookback" + "Cryptdoku" ] } diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-oracle.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-oracle.md new file mode 100644 index 000000000000..821e1f95c04e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-oracle.md @@ -0,0 +1,57 @@ +--- +title: Oracles +description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations. +keywords: + - Noir Programming + - Oracles + - JSON-RPC + - Foreign Call Handlers + - Constrained Functions + - Blockchain Programming +sidebar_position: 1 +--- + +If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs. + +![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg) + +A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source? + +Oracles are functions that provide this feature. + +## Use cases + +An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method. + +Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md). + +In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles. + +## Constraining oracles + +Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. + +To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have an oracle call like this: + +```rust +#[oracle(getNoun)] +unconstrained fn get_noun(address: Field) -> Field +``` + +This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract. + +In short, **Oracles don't prove anything. Your Noir program does.** + +:::danger + +If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained! + +::: + +## How to use Oracles + +On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. + +In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they match the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. + +If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-recursion.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-recursion.md new file mode 100644 index 000000000000..df8529ef4e02 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-recursion.md @@ -0,0 +1,176 @@ +--- +title: Recursive proofs +description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency. + +keywords: + [ + "Recursive Proofs", + "Zero-Knowledge Programming", + "Noir", + "EVM Blockchain", + "Smart Contracts", + "Recursion in Noir", + "Alice and Bob Guessing Game", + "Recursive Merkle Tree", + "Reusable Components", + "Optimizing Computational Resources", + "Improving Efficiency", + "Verification Key", + "Aggregation", + "Recursive zkSNARK schemes", + "PLONK", + "Proving and Verification Keys" + ] +sidebar_position: 1 +pagination_next: how_to/how-to-recursion +--- + +In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: + +```js +function factorial(n) { + if (n === 0 || n === 1) { + return 1; + } else { + return n * factorial(n - 1); + } +} +``` + +In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack: + +```md + Is `n` 1? <--------- + /\ / + / \ n = n -1 + / \ / + Yes No -------- +``` + +In Zero-Knowledge, recursion has some similarities. + +It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid. + +This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism. + +## Examples + +Let us look at some of these examples + +### Alice and Bob - Guessing game + +Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins. + +So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed. + +This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. + +As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". + +She can then generate a proof that she verified his proof, and so on. + +```md + Did you fail? <-------------------------- + / \ / + / \ n = n -1 + / \ / + Yes No / + | | / + | | / + | You win / + | / + | / +Generate proof of that / + + / + my own guess ---------------- +``` + +### Charlie - Recursive merkle tree + +Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count! + +If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such: + +```md + abcd + __________|______________ + | | + ab cd + _____|_____ ______|______ + | | | | + alice bob charlie daniel +``` + +Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes. + +### Daniel - Reusable components + +Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit. + +He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup. + +## What params do I need + +As you can see in the [recursion reference](noir/standard_library/recursion.mdx), a simple recursive proof requires: + +- The proof to verify +- The Verification Key of the circuit that generated the proof +- A hash of this verification key, as it's needed for some backends +- The public inputs for the proof + +:::info + +Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. + +So, taking the example of Alice and Bob and their guessing game: + +- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit +- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. + +We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. + +::: + +## Some architecture + +As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples: + +### Adding some logic to a proof verification + +This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections: + +- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify) +- A `guessing` section, which is basically the logic part where the actual guessing happens + +In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on. + +### Aggregating proofs + +In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere. + +To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits: + +- A `main`, non-recursive circuit with some logic +- A `recursive` circuit meant to verify two proofs in one proof + +The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day. + +### Recursively verifying different circuits + +Nothing prevents you from verifying different circuits in a recursive proof, for example: + +- A `circuit1` circuit +- A `circuit2` circuit +- A `recursive` circuit + +In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received) + +## How fast is it + +At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. + +Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. + +## How can I try it + +Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-writing-noir.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-writing-noir.md new file mode 100644 index 000000000000..b4265a14dbf4 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/explainers/explainer-writing-noir.md @@ -0,0 +1,208 @@ +--- +title: Thinking in Circuits +description: Considerations when writing Noir programs +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 proof size and proving time, so from a product point of view this should be kept as low as possible. + +Whilst writing efficient code for web apps and Solidity have some 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 + +Programs written in anything from pseudo code to C, can be translated into Noir. A Rust program written for execution can be readily ported to Noir thanks to the similarities in syntax. + +:::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: + - Conceptually 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, the `bb` binary has a `gates` option). +::: + +### Numerical types + +As mentioned earlier Noir has many familiar integer types (eg `i8`, `u64`). Ideally after bringing a program into Noir, proving/verifying of its execution just works where needed: client/server side, on an evm, or on the Aztec network. + +A program optimized for execution may leverage the binary representations of integers, reducing the number of clock cycles, and thus time of execution. +The cryptography in a proving backend makes use of a `Field` type, and leveraging this lower level type correctly can reduce gate count, and thus proof size and proving time. + +In some instances simply replacing the integer type with a `Field` could save on some range checks (and hence gates). +Note: when casting a `Field` to an integer type, the value is converted based on the integer binary representation. Eg a Field variable with a value of 260 `as u8` becomes 4 + +### `Field`s for efficiency + +`Field` types have their own underlying representation that is efficient for cryptography, which is different to binary representations efficient for CPUs. So, mathematically speaking, things like bitwise operations do not directly translate to fields. That said, the same outcome can be achieved if wanting to use the Field type as a number with lower overhead. + +For instance shift (`<<`) and or (`|`) work seamlessly with integer types (bit-packing `u8`'s into a `u16`): +``` + high as u16 << 8 | low as u16 +``` + +More efficiently with `Field` types, the equivalent is: +``` + low.assert_max_bit_size::<8>(); // ensure Field values could be represented as 8 bit numbers + high.assert_max_bit_size::<8>(); + (high * 2.pow_32(8) + low) +``` +(Note, the power of two can instead be a constant (256) or global evaluated at compile time) + +The first snippet is good for compatibility when using existing code, converting to the latter can help optimize frequently used functions. + +:::tip +Where possible, use the `Field` type for values. Writing code with smaller value types and bit-packing strategies will result in MORE gates +::: + +### 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. +::: + +### Reduce what is inside loops and conditional logic + +Putting less logic inside an `if` (`else`, etc) paths, or inside a loop, translates to less gates required to represent the program. The compiler should mostly take care of this. + +A loop duplicates the gates for each iteration of the loop, or put another way, "unrolls" the loop. Any calculations/calls that are unchanged in the loop should be calculated once before, and the result used in the loop. + +An `if` statement is "flattened" and gates created for each path even if execution uses only one path. Furthermore, there are additional operations required for each path. Sometimes this can have a multiplying effect on the operations in the `if` and `else` etc. + +:::tip +Only have essential computation inside conditional logic and loops, and calculate anything else once (before, or after, depending). +::: + +### 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-v1.0.0-beta.3/getting_started/noir_installation.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/noir_installation.md new file mode 100644 index 000000000000..05f036d4f6d6 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/noir_installation.md @@ -0,0 +1,106 @@ +--- +title: Standalone Noir Installation +description: There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains how to specify which version to install when using noirup, and using WSL for windows. +keywords: [ + Installation + Nargo + Noirup + Binaries + Compiling from Source + WSL for Windows + macOS + Linux + Nix + Direnv + Uninstalling Nargo + ] +sidebar_position: 2 +--- + +Noirup is the endorsed method for installing Nargo, streamlining the process of fetching binaries or compiling from source. It supports a range of options to cater to your specific needs, from nightly builds and specific versions to compiling from various sources. + +### Installing Noirup + +First, ensure you have `noirup` installed: + +```sh +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +### Fetching Binaries + +With `noirup`, you can easily switch between different Nargo versions, including nightly builds: + +- **Nightly Version**: Install the latest nightly build. + + ```sh + noirup --version nightly + ``` + +- **Specific Version**: Install a specific version of Nargo. + + ```sh + noirup --version + ``` + +### Compiling from Source + +`noirup` also enables compiling Nargo from various sources: + +- **From a Specific Branch**: Install from the latest commit on a branch. + + ```sh + noirup --branch + ``` + +- **From a Fork**: Install from the main branch of a fork. + + ```sh + noirup --repo + ``` + +- **From a Specific Branch in a Fork**: Install from a specific branch in a fork. + + ```sh + noirup --repo --branch + ``` + +- **From a Specific Pull Request**: Install from a specific PR. + + ```sh + noirup --pr + ``` + +- **From a Specific Commit**: Install from a specific commit. + + ```sh + noirup -C + ``` + +- **From Local Source**: Compile and install from a local directory. + + ```sh + noirup --path ./path/to/local/source + ``` + +## Installation on Windows + +The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. However, it is available by using Windows Subsystem for Linux (WSL). + +Step 1: Follow the instructions [here](https://learn.microsoft.com/en-us/windows/wsl/install) to install and run WSL. + +step 2: Follow the [Noirup instructions](#installing-noirup). + +## Setting up shell completions + +Once `nargo` is installed, you can [set up shell completions for it](setting_up_shell_completions.md). + +## Uninstalling Nargo + +If you installed Nargo with `noirup`, you can uninstall Nargo by removing the files in `~/.nargo`, `~/nargo`, and `~/noir_cache`. This ensures that all installed binaries, configurations, and cache related to Nargo are fully removed from your system. + +```bash +rm -r ~/.nargo +rm -r ~/nargo +rm -r ~/noir_cache +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/project_breakdown.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/project_breakdown.md new file mode 100644 index 000000000000..e442e377040f --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/project_breakdown.md @@ -0,0 +1,159 @@ +--- +title: Project Breakdown +description: + Learn about the anatomy of a Nargo project, including the purpose of the Prover TOML + file, and how to prove and verify your program. +keywords: + [Nargo, Nargo project, Prover.toml, proof verification, private asset transfer] +sidebar_position: 1 +--- + +This section breaks down our hello world program from the previous section. + +## Anatomy of a Nargo Project + +Upon creating a new project with `nargo new` and building the in/output files with `nargo check` +commands, you would get a minimal Nargo project of the following structure: + + - src + - Prover.toml + - Nargo.toml + +The source directory _src_ holds the source code for your Noir program. By default only a _main.nr_ +file will be generated within it. + +### Prover.toml + +_Prover.toml_ is used for specifying the input values for executing and proving the program. You can specify `toml` files with different names by using the `--prover-name` or `-p` flags, see the [Prover](#provertoml) section below. Optionally you may specify expected output values for prove-time checking as well. + +### Nargo.toml + +_Nargo.toml_ contains the environmental options of your project. It contains a "package" section and a "dependencies" section. + +Example Nargo.toml: + +```toml +[package] +name = "noir_starter" +type = "bin" +authors = ["Alice"] +compiler_version = "0.9.0" +description = "Getting started with Noir" +entry = "circuit/main.nr" +license = "MIT" + +[dependencies] +ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} +``` + +Nargo.toml for a [workspace](../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +#### Package section + +The package section defines a number of fields including: + +- `name` (**required**) - the name of the package +- `type` (**required**) - can be "bin", "lib", or "contract" to specify whether its a binary, library or Aztec contract +- `authors` (optional) - authors of the project +- `compiler_version` - specifies the version of the compiler to use. This is enforced by the compiler and follow's [Rust's versioning](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field), so a `compiler_version = 0.18.0` will enforce Nargo version 0.18.0, `compiler_version = ^0.18.0` will enforce anything above 0.18.0 but below 0.19.0, etc. For more information, see how [Rust handles these operators](https://docs.rs/semver/latest/semver/enum.Op.html) +- `description` (optional) +- `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 + +This is where you will specify any dependencies for your project. See the [Dependencies page](../noir/modules_packages_crates/dependencies.md) for more info. + +`./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or +verifier contract respectively. + +### main.nr + +The _main.nr_ file contains a `main` method, this method is the entry point into your Noir program. + +In our sample program, _main.nr_ looks like this: + +```rust +fn main(x : Field, y : Field) { + assert(x != y); +} +``` + +The parameters `x` and `y` can be seen as the API for the program and must be supplied by the prover. Since neither `x` nor `y` is marked as public, the verifier does not supply any inputs, when verifying the proof. + +The prover supplies the values for `x` and `y` in the _Prover.toml_ file. + +As for the program body, `assert` ensures that the condition to be satisfied (e.g. `x != y`) is constrained by the proof of the execution of said program (i.e. if the condition was not met, the verifier would reject the proof as an invalid proof). + +### Prover.toml + +The _Prover.toml_ file is a file which the prover uses to supply the inputs to the Noir program (both private and public). + +In our hello world program the _Prover.toml_ file looks like this: + +```toml +x = "1" +y = "2" +``` + +When the command `nargo execute` is executed, nargo will execute the Noir program using the inputs specified in `Prover.toml`, aborting if it finds that these do not satisfy the constraints defined by `main`. In this example, `x` and `y` must satisfy the inequality constraint `assert(x != y)`. + +If an output name is specified such as `nargo execute foo`, the witness generated by this execution will be written to `./target/foo.gz`. This can then be used to generate a proof of the execution. + +#### Arrays of Structs + +The following code shows how to pass an array of structs to a Noir program to generate a proof. + +```rust +// main.nr +struct Foo { + bar: Field, + baz: Field, +} + +fn main(foos: [Foo; 3]) -> pub Field { + foos[2].bar + foos[2].baz +} +``` + +Prover.toml: + +```toml +[[foos]] # foos[0] +bar = 0 +baz = 0 + +[[foos]] # foos[1] +bar = 0 +baz = 0 + +[[foos]] # foos[2] +bar = 1 +baz = 2 +``` + +#### Custom toml files + +You can specify a `toml` file with a different name to use for execution by using the `--prover-name` or `-p` flags. + +This command looks for proof inputs in the default **Prover.toml** and generates the witness and saves it at `./target/foo.gz`: + +```bash +nargo execute foo +``` + +This command looks for proof inputs in the custom **OtherProver.toml** and generates the witness and saves it at `./target/bar.gz`: + +```bash +nargo execute -p OtherProver bar +``` + +Now that you understand the concepts, you'll probably want some editor feedback while you are writing more complex code. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/quick_start.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/quick_start.md new file mode 100644 index 000000000000..7b4524f6b8e4 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/quick_start.md @@ -0,0 +1,127 @@ +--- +title: Quick Start +tags: [] +sidebar_position: 0 +--- + +## Installation + +### Noir + +The easiest way to develop with Noir is using Nargo the CLI tool. It provides you the ability to start new projects, compile, execute and test Noir programs from the terminal. + +You can use `noirup` the installation script to quickly install and update Nargo: + +```bash +curl -L https://raw.githubusercontent.com/noir-lang/noirup/refs/heads/main/install | bash +noirup +``` + +Once installed, you can [set up shell completions for the `nargo` command](setting_up_shell_completions). + +### Proving backend + +After installing Noir, we install a proving backend to work with our Noir programs. + +Proving backends provide you the abilities to generate proofs, verify proofs, generate smart contracts and more for your Noir programs. + +Different proving backends provide different tools for working with Noir programs, here we will use the [Barretenberg proving backend](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg) developed by Aztec Labs as an example. + +You can use the `bbup` installation script to quickly install and update BB, Barretenberg's CLI tool: + +You can find the full list of proving backends compatible with Noir in Awesome Noir. + +```bash +curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/refs/heads/master/barretenberg/bbup/install | bash +bbup +``` + +For the full list of proving backends compatible with Noir, visit [Awesome Noir](https://github.com/noir-lang/awesome-noir/?tab=readme-ov-file#proving-backends). + +## Nargo + +Nargo provides the ability to initiate and execute Noir projects. Let's initialize the traditional `hello_world`: + +```sh +nargo new hello_world +``` + +Two files will be created. + +- `src/main.nr` contains a simple boilerplate circuit +- `Nargo.toml` contains environmental options, such as name, author, dependencies, and others. + +Glancing at _main.nr_ , we can see that inputs in Noir are private by default, but can be labeled public using the keyword `pub`. This means that we will _assert_ that we know a value `x` which is different from `y` without revealing `x`: + +```rust +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` + +To learn more about private and public values, check the [Data Types](../noir/concepts/data_types/index.md) section. + +### Compiling and executing + +We can now use `nargo` to generate a _Prover.toml_ file, where our input values will be specified: + +```sh +cd hello_world +nargo check +``` + +Let's feed some valid values into this file: + +```toml +x = "1" +y = "2" +``` + +We're now ready to compile and execute our Noir program. By default the `nargo execute` command will do both, and generate the `witness` that we need to feed to our proving backend: + +```sh +nargo execute +``` + +The witness corresponding to this execution will then be written to the file _./target/witness-name.gz_. + +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_. + +With circuit compiled and witness generated, we're ready to prove. + +## Proving backend + +Different proving backends may provide different tools and commands to work with Noir programs. Here Barretenberg's `bb` CLI tool is used as an example: + +```sh +bb prove -b ./target/hello_world.json -w ./target/hello_world.gz -o ./target/proof +``` + +:::tip + +Naming can be confusing, specially as you pass them to the `bb` commands. If unsure, it won't hurt to delete the target folder and start fresh to make sure you're using the most recent versions of the compiled circuit and witness. + +::: + +The proof is now generated in the `target` folder. To verify it we first need to compute the verification key from the compiled circuit, and use it to verify: + +```sh +bb write_vk -b ./target/hello_world.json -o ./target/vk +bb verify -k ./target/vk -p ./target/proof +``` + +:::info + +Notice that in order to verify a proof, the verifier knows nothing but the circuit, which is compiled and used to generate the verification key. This is obviously quite important: private inputs remain private. + +As for the public inputs, you may have noticed they haven't been specified. This behavior varies with each particular backend, but barretenberg typically attaches them to the proof. You can see them by parsing and splitting it. For example if your public inputs are 32 bytes: + +```bash +head -c 32 ./target/proof | od -An -v -t x1 | tr -d $' \n' +``` + +::: + +Congratulations, you have now created and verified a proof for your very first Noir program! + +In the [next section](./project_breakdown.md), we will go into more detail on each step performed. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/setting_up_shell_completions.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/setting_up_shell_completions.md new file mode 100644 index 000000000000..0447321cbab0 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/getting_started/setting_up_shell_completions.md @@ -0,0 +1,87 @@ +--- +title: Setting up shell completions +tags: [] +sidebar_position: 3 +--- + +The `nargo` binary provides a command to generate shell completions: + +```bash +nargo generate-completion-script [shell] +``` + +where `shell` must be one of `bash`, `elvish`, `fish`, `powershell`, and `zsh`. + +Below we explain how to install them in some popular shells. + +## Installing Zsh Completions + +If you have `oh-my-zsh` installed, you might already have a directory of automatically loading completion scripts — `.oh-my-zsh/completions`. +If not, first create it: + +```bash +mkdir -p ~/.oh-my-zsh/completions` +``` + +Then copy the completion script to that directory: + +```bash +nargo generate-completion-script zsh > ~/.oh-my-zsh/completions/_nargo +``` + +Without `oh-my-zsh`, you’ll need to add a path for completion scripts to your function path, and turn on completion script auto-loading. +First, add these lines to `~/.zshrc`: + +```bash +fpath=(~/.zsh/completions $fpath) +autoload -U compinit +compinit +``` + +Next, create a directory at `~/.zsh/completions`: + +```bash +mkdir -p ~/.zsh/completions +``` + +Then copy the completion script to that directory: + +```bash +nargo generate-completion-script zsh > ~/.zsh/completions/_nargo +``` + +## Installing Bash Completions + +If you have [bash-completion](https://github.com/scop/bash-completion) installed, you can just copy the completion script to the `/usr/local/etc/bash_completion.d` directory: + +```bash +nargo generate-completion-script bash > /usr/local/etc/bash_completion.d/nargo +``` + +Without `bash-completion`, you’ll need to source the completion script directly. +First create a directory such as `~/.bash_completions/`: + +```bash +mkdir ~/.bash_completions/ +``` + +Copy the completion script to that directory: + +```bash +nargo generate-completion-script bash > ~/.bash_completions/nargo.bash +``` + +Then add the following line to `~/.bash_profile` or `~/.bashrc`: + + +```bash +source ~/.bash_completions/nargo.bash +``` + +## Installing Fish Completions + +Copy the completion script to any path listed in the environment variable `$fish_completion_path`. For example, a typical location is `~/.config/fish/completions/nargo.fish`: + +```bash +nargo generate-completion-script fish > ~/.config/fish/completions/nargo.fish +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/_category_.json new file mode 100644 index 000000000000..23b560f610b8 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/_category_.json new file mode 100644 index 000000000000..cc2cbb1c2533 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Debugging", + "position": 5, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_the_repl.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_the_repl.md new file mode 100644 index 000000000000..1d64dae3f37a --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_the_repl.md @@ -0,0 +1,164 @@ +--- +title: Using the REPL Debugger +description: + Step-by-step guide on how to debug your Noir circuits with the REPL Debugger. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + REPL, + ] +sidebar_position: 1 +--- + +#### Pre-requisites + +In order to use the REPL debugger, first you need to install recent enough versions of Nargo and vscode-noir. + +## Debugging a simple circuit + +Let's debug a simple circuit: + +```rust +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` + +To start the REPL debugger, using a terminal, go to a Noir circuit's home directory. Then: + +`$ nargo debug` + +You should be seeing this in your terminal: + +``` +[main] Starting debugger +At ~/noir-examples/recursion/circuits/main/src/main.nr:1:9 + 1 -> fn main(x : Field, y : pub Field) { + 2 assert(x != y); + 3 } +> +``` + +The debugger displays the current Noir code location, and it is now waiting for us to drive it. + +Let's first take a look at the available commands. For that we'll use the `help` command. + +``` +> help +Available commands: + + opcodes display ACIR opcodes + into step into to the next opcode + next step until a new source location is reached + out step until a new source location is reached + and the current stack frame is finished + break LOCATION:OpcodeLocation add a breakpoint at an opcode location + over step until a new source location is reached + without diving into function calls + restart restart the debugging session + delete LOCATION:OpcodeLocation delete breakpoint at an opcode location + witness show witness map + witness index:u32 display a single witness from the witness map + witness index:u32 value:String update a witness with the given value + memset index:usize value:String update a memory cell with the given + value + continue continue execution until the end of the + program + vars show variable values available at this point + in execution + stacktrace display the current stack trace + memory show memory (valid when executing unconstrained code) + step step to the next ACIR opcode + +Other commands: + + help Show this help message + quit Quit repl + +``` + +Some commands operate only for unconstrained functions, such as `memory` and `memset`. If you try to use them while execution is paused at an ACIR opcode, the debugger will simply inform you that you are not executing unconstrained code: + +``` +> memory +Unconstrained VM memory not available +> +``` + +Before continuing, we can take a look at the initial witness map: + +``` +> witness +_0 = 1 +_1 = 2 +> +``` + +Cool, since `x==1`, `y==2`, and we want to check that `x != y`, our circuit should succeed. At this point we could intervene and use the witness setter command to change one of the witnesses. Let's set `y=3`, then back to 2, so we don't affect the expected result: + +``` +> witness +_0 = 1 +_1 = 2 +> witness 1 3 +_1 = 3 +> witness +_0 = 1 +_1 = 3 +> witness 1 2 +_1 = 2 +> witness +_0 = 1 +_1 = 2 +> +``` + +Now we can inspect the current state of local variables. For that we use the `vars` command. + +``` +> vars +> +``` + +We currently have no vars in context, since we are at the entry point of the program. Let's use `next` to execute until the next point in the program. + +``` +> vars +> next +At ~/noir-examples/recursion/circuits/main/src/main.nr:1:20 + 1 -> fn main(x : Field, y : pub Field) { + 2 assert(x != y); + 3 } +> vars +x:Field = 0x01 +``` + +As a result of stepping, the variable `x`, whose initial value comes from the witness map, is now in context and returned by `vars`. + +``` +> next + 1 fn main(x : Field, y : pub Field) { + 2 -> assert(x != y); + 3 } +> vars +y:Field = 0x02 +x:Field = 0x01 +``` + +Stepping again we can finally see both variables and their values. And now we can see that the next assertion should succeed. + +Let's continue to the end: + +``` +> continue +(Continuing execution...) +Finished execution +> q +[main] Circuit witness successfully solved +``` + +Upon quitting the debugger after a solved circuit, the resulting circuit witness gets saved, equivalent to what would happen if we had run the same circuit with `nargo execute`. + +We just went through the basics of debugging using Noir REPL debugger. For a comprehensive reference, check out [the reference page](../../reference/debugger/debugger_repl.md). diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_vs_code.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_vs_code.md new file mode 100644 index 000000000000..ecd64fc26536 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/debugger/debugging_with_vs_code.md @@ -0,0 +1,68 @@ +--- +title: Using the VS Code Debugger +description: + Step-by-step guide on how to debug your Noir circuits with the VS Code Debugger configuration and features. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + VS Code, + IDE, + ] +sidebar_position: 0 +--- + +This guide will show you how to use VS Code with the vscode-noir extension to debug a Noir project. + +#### Pre-requisites + +- Nargo +- vscode-noir +- A Noir project with a `Nargo.toml`, `Prover.toml` and at least one Noir (`.nr`) containing an entry point function (typically `main`). + +## Running the debugger + +The easiest way to start debugging is to open the file you want to debug, and press `F5`. This will cause the debugger to launch, using your `Prover.toml` file as input. + +You should see something like this: + +![Debugger launched](@site/static/img/debugger/1-started.png) + +Let's inspect the state of the program. For that, we open VS Code's _Debug pane_. Look for this icon: + +![Debug pane icon](@site/static/img/debugger/2-icon.png) + +You will now see two categories of variables: Locals and Witness Map. + +![Debug pane expanded](@site/static/img/debugger/3-debug-pane.png) + +1. **Locals**: variables of your program. At this point in execution this section is empty, but as we step through the code it will get populated by `x`, `result`, `digest`, etc. + +2. **Witness map**: these are initially populated from your project's `Prover.toml` file. In this example, they will be used to populate `x` and `result` at the beginning of the `main` function. + +Most of the time you will probably be focusing mostly on locals, as they represent the high level state of your program. + +You might be interested in inspecting the witness map in case you are trying to solve a really low level issue in the compiler or runtime itself, so this concerns mostly advanced or niche users. + +Let's step through the program, by using the debugger buttons or their corresponding keyboard shortcuts. + +![Debugger buttons](@site/static/img/debugger/4-debugger-buttons.png) + +Now we can see in the variables pane that there's values for `digest`, `result` and `x`. + +![Inspecting locals](@site/static/img/debugger/5-assert.png) + +We can also inspect the values of variables by directly hovering on them on the code. + +![Hover locals](@site/static/img/debugger/6-hover.png) + +Let's set a break point at the `keccak256` function, so we can continue execution up to the point when it's first invoked without having to go one step at a time. + +We just need to click to the right of the line number 18. Once the breakpoint appears, we can click the `continue` button or use its corresponding keyboard shortcut (`F5` by default). + +![Breakpoint](@site/static/img/debugger/7-break.png) + +Now we are debugging the `keccak256` function, notice the _Call Stack pane_ at the lower right. This lets us inspect the current call stack of our process. + +That covers most of the current debugger functionalities. Check out [the reference](../../reference/debugger/debugger_vscode.md) for more details on how to configure the debugger. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-oracles.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-oracles.md new file mode 100644 index 000000000000..845c24c98291 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-oracles.md @@ -0,0 +1,275 @@ +--- +title: How to use Oracles +description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS. +keywords: + - Noir Programming + - Oracles + - Nargo + - NoirJS + - JSON RPC Server + - Foreign Call Handlers +sidebar_position: 1 +--- + +This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that: + +- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept. +- You have a Noir program to add oracles to. You can create one using one of the starters in [awesome-noir](https://github.com/noir-lang/awesome-noir?tab=readme-ov-file#boilerplates). +- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. +- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). + +## Rundown + +This guide has 3 major steps: + +1. How to modify our Noir program to make use of oracle calls as unconstrained functions +2. How to write a JSON RPC Server to resolve these oracle calls with Nargo +3. How to use them in Nargo and how to provide a custom resolver in NoirJS + +## Step 1 - Modify your Noir program + +An oracle is defined in a Noir program by defining two methods: + +- An unconstrained method - This tells the compiler that it is executing an [unconstrained function](../noir/concepts//unconstrained.md). +- A decorated oracle method - This tells the compiler that this method is an RPC call. + +An example of an oracle that returns a `Field` would be: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(number: Field) -> Field { } + +unconstrained fn get_sqrt(number: Field) -> Field { + sqrt(number) +} +``` + +In this example, we're wrapping our oracle function in an unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); +} +``` + +In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use. + +:::danger + +As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); + assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle! +} +``` + +::: + +:::info + +Currently, oracles only work with single params or array params. For example: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt([Field; 2]) -> [Field; 2] { } +``` + +::: + +## Step 2 - Write an RPC server + +Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript. + +Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { } + +unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] { + sqrt(input) +} + +fn main(input: [Field; 2]) { + let sqrt = get_sqrt(input); + assert(sqrt[0].pow_32(2) as u64 == input[0] as u64); + assert(sqrt[1].pow_32(2) as u64 == input[1] as u64); +} + +#[test] +fn test() { + let input = [4, 16]; + main(input); +} +``` + +:::info + +Why square root? + +In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values. + +::: + +Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example): + +```js +import { JSONRPCServer } from "json-rpc-2.0"; +import express from "express"; +import bodyParser from "body-parser"; + +const app = express(); +app.use(bodyParser.json()); + +const server = new JSONRPCServer(); +app.post("/", (req, res) => { + const jsonRPCRequest = req.body; + server.receive(jsonRPCRequest).then((jsonRPCResponse) => { + if (jsonRPCResponse) { + res.json(jsonRPCResponse); + } else { + res.sendStatus(204); + } + }); +}); + +app.listen(5555); +``` + +Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: + +```js +server.addMethod("resolve_foreign_call", async (params) => { + if (params[0].function !== "getSqrt") { + throw Error("Unexpected foreign call") + }; + const values = params[0].inputs[0].map((field) => { + return `${Math.sqrt(parseInt(field, 16))}`; + }); + return { values: [values] }; +}); +``` + +If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: + +```js +export type ForeignCallSingle = string; + +export type ForeignCallArray = string[]; + +export type ForeignCallResult = { + values: (ForeignCallSingle | ForeignCallArray)[]; +}; +``` + +:::info Multidimensional Arrays + +If the Oracle function is returning an array containing other arrays, such as `[['1','2],['3','4']]`, you need to provide the values in JSON as flattened values. In the previous example, it would be `['1', '2', '3', '4']`. In the Noir program, the Oracle signature can use a nested type, the flattened values will be automatically converted to the nested type. + +::: + +## Step 3 - Usage with Nargo + +Using the [`nargo` CLI tool](../reference/nargo_commands.md), you can use oracles in the `nargo test` and `nargo execute` commands by passing a value to `--oracle-resolver`. For example: + +```bash +nargo test --oracle-resolver http://localhost:5555 +``` + +This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator. + +## Step 4 - Usage with NoirJS + +In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything. + +For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app: + +```js +const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc + +await noir.execute(inputs, foreignCallHandler) +``` + +As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. + +:::tip + +Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)? + +You don't technically have to, but then how would you run `nargo test`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server. + +::: + +In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client. + +For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code): + +```js +import { JSONRPCClient } from "json-rpc-2.0"; + +// declaring the JSONRPCClient +const client = new JSONRPCClient((jsonRPCRequest) => { +// hitting the same JSON RPC Server we coded above + return fetch("http://localhost:5555", { + method: "POST", + headers: { + "content-type": "application/json", + }, + body: JSON.stringify(jsonRPCRequest), + }).then((response) => { + if (response.status === 200) { + return response + .json() + .then((jsonRPCResponse) => client.receive(jsonRPCResponse)); + } else if (jsonRPCRequest.id !== undefined) { + return Promise.reject(new Error(response.statusText)); + } + }); +}); + +// declaring a function that takes the name of the foreign call (getSqrt) and the inputs +const foreignCallHandler = async (name, input) => { + const inputs = input[0].map((i) => i.toString("hex")) + // notice that the "inputs" parameter contains *all* the inputs + // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] + const oracleReturn = await client.request("resolve_foreign_call", [ + { + function: name, + inputs: [inputs] + }, + ]); + return [oracleReturn.values[0]]; +}; + +// the rest of your NoirJS code +const input = { input: [4, 16] }; +const { witness } = await noir.execute(input, foreignCallHandler); +``` + +:::tip + +If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem: + +```bash +yarn add cors +``` + +and use it as a middleware: + +```js +import cors from "cors"; + +const app = express(); +app.use(cors()) +``` + +::: + +## Conclusion + +Hopefully by the end of this guide, you should be able to: + +- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands. +- Provide custom foreign call handlers for NoirJS. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-recursion.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-recursion.md new file mode 100644 index 000000000000..399e4d4b38a0 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-recursion.md @@ -0,0 +1,172 @@ +--- +title: How to use recursion on NoirJS +description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `bb.js`. +keywords: + [ + "NoirJS", + "EVM blockchain", + "smart contracts", + "recursion", + "solidity verifiers", + "Barretenberg backend", + "noir_js", + "intermediate proofs", + "final proofs", + "nargo compile", + "json import", + "recursive circuit", + "recursive app" + ] +sidebar_position: 1 +--- + +This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that: + +- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). +- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) +- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.mdx), and understand how it works. + +It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. + +:::info + +As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. + +::: + +In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume the following: + +- `main`: a circuit of type `assert(x != y)`, which we want to embed in another circuit recursively. For example when proving with the `bb` tool, we can use the `--recursive` CLI option to tell the backend that it should generate proofs that are friendly for verification within another circuit. +- `recursive`: a circuit that verifies `main` + +For a full example of how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. + +## Step 1: Setup + +In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface. + +For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object. + +It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency: + +```js +const backend = new UltraPlonkBackend(circuit, { threads: 8 }, { recursive: true }) +``` + +:::tip +You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores +::: + +## Step 2: Generating the witness and the proof for `main` + +After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. + +```js +const noir = new Noir(circuit) +const { witness } = noir.execute(input) +``` + +With this witness, you are now able to generate the intermediate proof for the main circuit: + +```js +const { proof, publicInputs } = await backend.generateProof(witness) +``` + +:::warning + +Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why! + +In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof. + +With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain. + +::: + +## Step 3 - Verification and proof artifacts + +Optionally, you are able to verify the intermediate proof: + +```js +const verified = await backend.verifyProof({ proof, publicInputs }) +``` + +This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate recursive proof artifacts that will be passed to the circuit that is verifying the proof we just generated. Instead of passing the proof and verification key as a byte array, we pass them as fields which makes it cheaper to verify in a circuit: + +```js +const { proofAsFields, vkAsFields, vkHash } = await backend.generateRecursiveProofArtifacts( { publicInputs, proof }, publicInputsCount) +``` + +This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away. + +:::info + +The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. + +::: + +:::warning + +One common mistake is to forget *who* makes this call. + +In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything! + +Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit. + +::: + +## Step 4 - Recursive proof generation + +With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it: + +```js +const recursiveInputs = { + verification_key: vkAsFields, // array of length 114 + proof: proofAsFields, // array of length 93 + size of public inputs + publicInputs: [mainInput.y], // using the example above, where `y` is the only public input + key_hash: vkHash, +} + +const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! +const { proof, publicInputs } = backend.generateProof(witness) +const verified = backend.verifyProof({ proof, publicInputs }) +``` + +You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! + +:::tip + +Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example: + +```js +const circuits = { + main: mainJSON, + recursive: recursiveJSON +} +const backends = { + main: new BarretenbergBackend(circuits.main), + recursive: new BarretenbergBackend(circuits.recursive) +} +const noir_programs = { + main: new Noir(circuits.main), + recursive: new Noir(circuits.recursive) +} +``` + +This allows you to neatly call exactly the method you want without conflicting names: + +```js +// Alice runs this 👇 +const { witness: mainWitness } = await noir_programs.main.execute(input) +const proof = await backends.main.generateProof(mainWitness) + +// Bob runs this 👇 +const verified = await backends.main.verifyProof(proof) +const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateRecursiveProofArtifacts( + proof, + numPublicInputs, +); +const { witness: recursiveWitness } = await noir_programs.recursive.execute(recursiveInputs) +const recursiveProof = await backends.recursive.generateProof(recursiveWitness); +``` + +::: diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-solidity-verifier.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-solidity-verifier.mdx new file mode 100644 index 000000000000..c8c7894ff911 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/how-to-solidity-verifier.mdx @@ -0,0 +1,305 @@ +--- +title: Generate a Solidity Verifier +description: + Learn how to run the verifier as a smart contract on the blockchain. Compile a Solidity verifier + contract for your Noir program and deploy it on any EVM blockchain acting as a verifier smart + contract. Read more to find out +keywords: + [ + solidity verifier, + smart contract, + blockchain, + compiler, + plonk_vk.sol, + EVM blockchain, + verifying Noir programs, + proving backend, + Barretenberg, + ] +sidebar_position: 0 +pagination_next: tutorials/noirjs_app +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Noir is universal. The witness and the compiled program can be fed into a proving backend such as Aztec's [Barretenberg](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg), which can then generate a verifier contract for deployment on blockchains. + +This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. + +Although not strictly in the domain of Noir itself, this guide shows how to generate a Solidity Verifier with Barretenberg and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: + +- You will be using Barretenberg as your proving backend +- You will be using an EVM blockchain to verify your proof +- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network +- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/quick_start.md) with Nargo and the example Hello Noir circuit +- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. + +## Rundown + +Generating a Solidity Verifier with Barretenberg contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: + +1. How to generate a solidity smart contract +2. How to compile the smart contract in the RemixIDE +3. How to deploy it to a testnet + +:::info[Which proving system to use?] + +Barretenberg currently provides two provers: `UltraPlonk` and `UltraHonk`. In a nutshell, `UltraHonk` is faster and uses less RAM, but its verifier contract is much more expensive. `UltraPlonk` is optimized for on-chain verification, but proving is more expensive. + +In any case, we provide instructions for both. Choose your poison ☠️ + +::: + +## Step 1 - Generate a contract + +This is by far the most straightforward step. Just run: + +```sh +nargo compile +``` + +This will compile your source code into a Noir build artifact to be stored in the `./target` directory. From here on, it's Barretenberg's work. You can generate the smart contract using the commands: + + + + +```sh +bb write_vk_ultra_keccak_honk -b ./target/.json +bb contract_ultra_honk +``` + + + + +```sh +bb write_vk -b ./target/.json +bb contract +``` + + + + +replacing `` with the name of your Noir project. A `Verifier.sol` contract is now in the target folder and can be deployed to any EVM blockchain acting as a verifier smart contract. + +You can find more information about `bb` and the default Noir proving backend on [this page](../getting_started/quick_start.md#proving-backend). + + +## Step 2 - Compiling + +We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open +
Remix and create a blank workspace. + +![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) + +We will create a new file to contain the contract Nargo generated, and copy-paste its content. + +:::warning + +You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. + +::: + +To compile our the verifier, we can navigate to the compilation tab: + +![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) + +Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely tell you the contract is too big to deploy on mainnet, or complain about a stack too deep: + +![Contract code too big](@site/static/img/how-tos/solidity_verifier_6.png) +![Stack too deep](@site/static/img/how-tos/solidity_verifier_8.png) + +To avoid this, you can just use some optimization. Open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. + +![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) + +## Step 3 - Deploying + +At this point we should have a compiled contract ready to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. + +Looking closely, we will notice that our "Solidity Verifier" is composed on multiple contracts working together. Remix will take care of the dependencies for us so we can simply deploy the Verifier contract by selecting it and hitting "deploy": + + + + +![Deploying HonkVerifier](@site/static/img/how-tos/solidity_verifier_7.png) + + + + +![Deploying PlonkVerifier](@site/static/img/how-tos/solidity_verifier_9.png) + + + + +A contract will show up in the "Deployed Contracts" section. + +## Step 4 - Verifying + +To verify a proof using the Solidity verifier contract, we call the `verify` function: + +```solidity +function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) +``` + +First generate a proof with `bb`. We need a `Prover.toml` file for our inputs. Run: + +```bash +nargo check +``` + +This will generate a `Prover.toml` you can fill with the values you want to prove. We can now execute the circuit with `nargo` and then use the proving backend to prove: + + + + +```bash +nargo execute +bb prove_ultra_keccak_honk -b ./target/.json -w ./target/ -o ./target/proof +``` + +:::tip[Public inputs] +Barretenberg attaches the public inputs to the proof, which in this case it's not very useful. If you're up for some JS, `bb.js` has [a method for it](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/ts/src/proof/index.ts), but in the CLI you can use this ugly snippet: + +```bash +cat ./target/proof | od -An -v -t x1 | tr -d $' \n' | sed 's/^.\{8\}//' | (read hex; echo "${hex:0:192}${hex:256}") +``` + +Beautiful. This assumes a circuit with one public input (32 bytes, for Barretenberg). For more inputs, you can just increment `hex:256` with 32 more bytes for each public input. + +::: + + + + +```bash +nargo execute +bb prove -b ./target/.json -w ./target/ -o ./target/proof +``` + + +:::tip[Public inputs] +Barretenberg attaches the public inputs to the proof, which in this case it's not very useful. If you're up for some JS, `bb.js` has [a method for it](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/ts/src/proof/index.ts), but in the CLI you can use this ugly snippet: + +```bash +tail -c +33 ./target/proof | od -An -v -t x1 | tr -d $' \n' +``` + +Beautiful. This assumes a circuit with one public input (32 bytes, for Barretenberg). For more inputs, you can just add 32 more bytes for each public input to the `tail` command. + +::: + + + + +Remix expects that the public inputs will be split into an array of `bytes32` values so `HEX_PUBLIC_INPUTS` needs to be split up into 32 byte chunks which are prefixed with `0x` accordingly. + +A programmatic example of how the `verify` function is called can be seen in the example zk voting application [here](https://github.com/noir-lang/noir-examples/blob/33e598c257e2402ea3a6b68dd4c5ad492bce1b0a/foundry-voting/src/zkVote.sol#L35): + +```solidity +function castVote(bytes calldata proof, uint proposalId, uint vote, bytes32 nullifierHash) public returns (bool) { + // ... + bytes32[] memory publicInputs = new bytes32[](4); + publicInputs[0] = merkleRoot; + publicInputs[1] = bytes32(proposalId); + publicInputs[2] = bytes32(vote); + publicInputs[3] = nullifierHash; + require(verifier.verify(proof, publicInputs), "Invalid proof"); +``` + +:::info[Return Values] + +A circuit doesn't have the concept of a return value. Return values are just syntactic sugar in Noir. + +Under the hood, the return value is passed as an input to the circuit and is checked at the end of the circuit program. + +For example, if you have Noir program like this: + +```rust +fn main( + // Public inputs + pubkey_x: pub Field, + pubkey_y: pub Field, + // Private inputs + priv_key: Field, +) -> pub Field +``` + +the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. + +Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. + +In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return]`. + +::: + +:::tip[Structs] + +You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. + +For example, consider the following program: + +```rust +struct Type1 { + val1: Field, + val2: Field, +} + +struct Nested { + t1: Type1, + is_true: bool, +} + +fn main(x: pub Field, nested: pub Nested, y: pub Field) { + //... +} +``` + +The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` + +::: + +The other function you can call is our entrypoint `verify` function, as defined above. + +:::tip + +It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. + +This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. + +It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). + +::: + +## Compatibility with different EVM chains + +Barretenberg proof verification requires the `ecMul`, `ecAdd`, `ecPairing`, and `modexp` EVM precompiles. You can deploy and use the verifier contract on all EVM chains that support the precompiles. + +EVM Diff provides a great table of which EVM chains support which precompiles: https://www.evmdiff.com/features?feature=precompiles + +Some EVM chains manually tested to work with the Barretenberg verifier include: + +- Optimism +- Arbitrum +- Polygon PoS +- Scroll +- Celo +- BSC +- Blast L2 +- Avalanche C-Chain +- Mode +- Linea +- Moonbeam + +Meanwhile, some EVM chains chains manually tested that failed to work with the Barretenberg verifier include: + +- zkSync ERA +- Polygon zkEVM + +Pull requests to update this section is welcome and appreciated if you have compatibility updates on existing / new chains to contribute: https://github.com/noir-lang/noir + +## What's next + +Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks. You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. + +You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/noir/noir-repo/docs/docs/how_to/merkle-proof.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/merkle-proof.mdx similarity index 100% rename from noir/noir-repo/docs/docs/how_to/merkle-proof.mdx rename to noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/merkle-proof.mdx diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/using-devcontainers.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/using-devcontainers.mdx new file mode 100644 index 000000000000..dcd30d898015 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/how_to/using-devcontainers.mdx @@ -0,0 +1,91 @@ +--- +title: Developer Containers and Codespaces +description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally." +keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"] +sidebar_position: 1 +--- + +Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser. + +## What's a devcontainer after all? + +A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box. + +There are many advantages to this: + +- It's platform and architecture agnostic +- You don't need to have an IDE installed, or Nargo, or use a terminal at all +- It's safer for using on a public machine or public network + +One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use. +Enter Codespaces. + +## Codespaces + +If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof? + +Nothing! Except perhaps the 30-40$ per hour it will cost you. + +The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick. + +Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool: + +- You can start coding Noir in less than a minute +- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be +- It makes it easy to share work with your frens +- It's fully reusable, you can stop and restart whenever you need to + +:::info + +Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine. + +::: + +## Tell me it's _actually_ easy + +It is! + +Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide. + + + +8 simple steps: + +#### 1. Create a new repository on GitHub. + +#### 2. Click "Start coding with Codespaces". This will use the default image. + +#### 3. Create a folder called `.devcontainer` in the root of your repository. + +#### 4. Create a file called `devcontainer.json` in the same folder, and paste the following code: + +```json +{ + "name": "Noir on Codespaces", + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "ghcr.io/noir-lang/features/noir:latest": { + "version": "1.0.0-beta.2" + } + } +} +``` +#### 6. Commit and push your changes + +This will pull the new image and build it, so it could take a minute or so + +#### 8. Done! +Just wait for the build to finish, and there's your easy Noir environment. Some examples of how to use it can be found in the [awesome-noir](https://github.com/noir-lang/awesome-noir?tab=readme-ov-file#boilerplates) repository. + +## How do I use it? + +Using the codespace is obviously much easier than setting it up. +Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go. + +:::info + +If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. +Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/index.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/index.mdx new file mode 100644 index 000000000000..5c116a73b3fe --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/index.mdx @@ -0,0 +1,76 @@ +--- +title: Noir Lang +hide_title: true +description: + Learn about the public alpha release of Noir, a domain specific language heavily influenced by Rust that compiles to + an intermediate language which can be compiled to an arithmetic circuit or a rank-1 constraint system. +keywords: + [Noir, + Domain Specific Language, + Rust, + Intermediate Language, + Arithmetic Circuit, + Rank-1 Constraint System, + Ethereum Developers, + Protocol Developers, + Blockchain Developers, + Proving System, + Smart Contract Language] +sidebar_position: 0 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import ThemedImage from '@theme/ThemedImage'; +import useBaseUrl from '@docusaurus/useBaseUrl'; + + + +Noir is an open-source Domain-Specific Language for safe and seamless construction of privacy-preserving Zero-Knowledge programs, requiring no previous knowledge on the underlying mathematics or cryptography. + +ZK programs are programs that can generate short proofs of statements without revealing all inputs to the statements. You can read more about Zero-Knowledge Proofs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). + +## What's new about Noir? + +Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. + +:::info + +Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. + +However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. + +::: + +## Who is Noir for? + +Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: + + + + Noir Logo + + Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. + + + Soliditry Verifier Example + Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) + + + Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. + + + + +## Libraries + +Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. +The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. +Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/migration_notes.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/migration_notes.md new file mode 100644 index 000000000000..6bd740024e5b --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/migration_notes.md @@ -0,0 +1,105 @@ +--- +title: Migration notes +description: Read about migration notes from previous versions, which could solve problems while updating +keywords: [Noir, notes, migration, updating, upgrading] +--- + +Noir is in full-speed development. Things break fast, wild, and often. This page attempts to leave some notes on errors you might encounter when upgrading and how to resolve them until proper patches are built. + +### `backend encountered an error: libc++.so.1` + +Depending on your OS, you may encounter the following error when running `nargo prove` for the first time: + +```text +The backend encountered an error: "/home/codespace/.nargo/backends/acvm-backend-barretenberg/backend_binary: error while loading shared libraries: libc++.so.1: cannot open shared object file: No such file or directory\n" +``` + +Install the `libc++-dev` library with: + +```bash +sudo apt install libc++-dev +``` + +## ≥0.19 + +### Enforcing `compiler_version` + +From this version on, the compiler will check for the `compiler_version` field in `Nargo.toml`, and will error if it doesn't match the current Nargo version in use. + +To update, please make sure this field in `Nargo.toml` matches the output of `nargo --version`. + +## ≥0.14 + +The index of the [for loops](noir/concepts/control_flow.md#loops) is now of type `u64` instead of `Field`. An example refactor would be: + +```rust +for i in 0..10 { + let i = i as Field; +} +``` + +## ≥v0.11.0 and Nargo backend + +From this version onwards, Nargo starts managing backends through the `nargo backend` command. Upgrading to the versions per usual steps might lead to: + +### `backend encountered an error` + +This is likely due to the existing locally installed version of proving backend (e.g. barretenberg) is incompatible with the version of Nargo in use. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo prove +``` + +with your Noir program. + +This will trigger the download and installation of the latest version of barretenberg compatible with your Nargo in use. + +### `backend encountered an error: illegal instruction` + +On certain Intel-based systems, an `illegal instruction` error may arise due to incompatibility of barretenberg with certain CPU instructions. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo backend install acvm-backend-barretenberg https://github.com/noir-lang/barretenberg-js-binary/raw/master/run-bb.tar.gz +``` + +This downloads and installs a specific bb.js based version of barretenberg binary from GitHub. + +The gzipped file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), where we need to gzip it as the Nargo currently expect the backend to be zipped up. + +Then run: + +``` +DESIRED_BINARY_VERSION=0.8.1 nargo info +``` + +This overrides the bb native binary with a bb.js node application instead, which should be compatible with most if not all hardware. This does come with the drawback of being generally slower than native binary. + +0.8.1 indicates bb.js version 0.8.1, so if you change that it will update to a different version or the default version in the script if none was supplied. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/_category_.json new file mode 100644 index 000000000000..7da08f8a8c5d --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Concepts", + "position": 0, + "collapsible": true, + "collapsed": true +} \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/assert.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/assert.md new file mode 100644 index 000000000000..c7bc42fa3228 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/assert.md @@ -0,0 +1,79 @@ +--- +title: Assert Function +description: + Learn about the `assert` and `static_assert` functions in Noir, which can be used to explicitly + constrain the predicate or comparison expression that follows to be true, and what happens if + the expression is false at runtime or compile-time, respectively. +keywords: [Noir programming language, assert statement, predicate expression, comparison expression] +sidebar_position: 4 +--- + +Noir includes a special `assert` function which will explicitly constrain the predicate/comparison +expression that follows to be true. If this expression is false at runtime, the program will fail to +be proven. As of v1.0.0-beta.2, assert statements are expressions and can be used in value contexts. + +Example: + +```rust +fn main(x : Field, y : Field) { + assert(x == y); +} +``` + +> Assertions only work for predicate operations, such as `==`. If there's any ambiguity on the operation, the program will fail to compile. For example, it is unclear if `assert(x + y)` would check for `x + y == 0` or simply would return `true`. + +You can optionally provide a message to be logged when the assertion fails: + +```rust +assert(x == y, "x and y are not equal"); +``` + +Aside string literals, the optional message can be a format string or any other type supported as input for Noir's [print](../standard_library/logging.md) functions. This feature lets you incorporate runtime variables into your failed assertion logs: + +```rust +assert(x == y, f"Expected x == y, but got {x} == {y}"); +``` + +Using a variable as an assertion message directly: + +```rust +struct myStruct { + myField: Field +} + +let s = myStruct { myField: y }; +assert(s.myField == x, s); +``` + +There is also a special `static_assert` function that behaves like `assert`, +but that runs at compile-time. + +```rust +fn main(xs: [Field; 3]) { + let x = 2 + 2; + let y = 4; + static_assert(x == y, "expected 2 + 2 to equal 4"); + + // This passes since the length of `xs` is known at compile-time + static_assert(xs.len() == 3, "expected the input to have 3 elements"); +} +``` + +This function fails when passed a dynamic (run-time) argument: + +```rust +fn main(x : Field, y : Field) { + // this fails because `x` is not known at compile-time + static_assert(x == 2, "expected x to be known at compile-time and equal to 2"); + + let mut example_slice = &[]; + if y == 4 { + example_slice = example_slice.push_back(0); + } + + // This fails because the length of `example_slice` is not known at + // compile-time + let error_message = "expected an empty slice, known at compile-time"; + static_assert(example_slice.len() == 0, error_message); +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comments.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comments.md new file mode 100644 index 000000000000..b51a85f5c949 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comments.md @@ -0,0 +1,33 @@ +--- +title: Comments +description: + Learn how to write comments in Noir programming language. A comment is a line of code that is + ignored by the compiler, but it can be read by programmers. Single-line and multi-line comments + are supported in Noir. +keywords: [Noir programming language, comments, single-line comments, multi-line comments] +sidebar_position: 10 +--- + +A comment is a line in your codebase which the compiler ignores, however it can be read by +programmers. + +Here is a single line comment: + +```rust +// This is a comment and is ignored +``` + +`//` is used to tell the compiler to ignore the rest of the line. + +Noir also supports multi-line block comments. Start a block comment with `/*` and end the block with `*/`. + +Noir does not natively support doc comments. You may be able to use [Rust doc comments](https://doc.rust-lang.org/reference/comments.html) in your code to leverage some Rust documentation build tools with Noir code. + +```rust +/* + This is a block comment describing a complex function. +*/ +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comptime.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comptime.md new file mode 100644 index 000000000000..4cdd2094a162 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/comptime.md @@ -0,0 +1,565 @@ +--- +title: Compile-time Code & Metaprogramming +description: Learn how to use metaprogramming in Noir to create macros or derive your own traits +keywords: [Noir, comptime, compile-time, metaprogramming, macros, quote, unquote] +sidebar_position: 15 +--- + +## Overview + +Metaprogramming in Noir is comprised of three parts: +1. `comptime` code +2. Quoting and unquoting +3. The metaprogramming API in `std::meta` + +Each of these are explained in more detail in the next sections but the wide picture is that +`comptime` allows us to write code which runs at compile-time. In this `comptime` code we +can quote and unquote snippets of the program, manipulate them, and insert them in other +parts of the program. Comptime functions which do this are said to be macros. Additionally, +there's a compile-time API of built-in types and functions provided by the compiler which allows +for greater analysis and modification of programs. + +--- + +## Comptime + +`comptime` is a new keyword in Noir which marks an item as executing or existing at compile-time. It can be used in several ways: + +- `comptime fn` to define functions which execute exclusively during compile-time. +- `comptime global` to define a global variable which is evaluated at compile-time. + - Unlike runtime globals, `comptime global`s can be mutable. +- `comptime { ... }` to execute a block of statements during compile-time. +- `comptime let` to define a variable whose value is evaluated at compile-time. +- `comptime for` to run a for loop at compile-time. Syntax sugar for `comptime { for .. }`. + +### Scoping + +Note that while in a `comptime` context, any runtime variables _local to the current function_ are never visible. + +### Evaluating + +Evaluation rules of `comptime` follows the normal unconstrained evaluation rules for other Noir code. There are a few things to note though: + +- Certain built-in functions may not be available, although more may be added over time. +- Evaluation order of `comptime {}` blocks within global items is currently unspecified. For example, given the following two functions we can't guarantee +which `println` will execute first. The ordering of the two printouts will be arbitrary, but should be stable across multiple compilations with the same `nargo` version as long as the program is also unchanged. + +```rust +fn one() { + comptime { println("one"); } +} + +fn two() { + comptime { println("two"); } +} +``` + +- Since evaluation order is unspecified, care should be taken when using mutable globals so that they do not rely on a particular ordering. +For example, using globals to generate unique ids should be fine but relying on certain ids always being produced (especially after edits to the program) should be avoided. +- Although the ordering of comptime code is usually unspecified, there are cases where it is: + - Dependencies of a crate will always be evaluated before the dependent crate. + - Any attributes on a function will be run before the function body is resolved. This is to allow the attribute to modify the function if necessary. Note that if the + function itself was called at compile-time previously, it will already be resolved and cannot be modified. To prevent accidentally calling functions you wish to modify + at compile-time, it may be helpful to sort your `comptime` annotation functions into a different submodule crate along with any dependencies they require. + - Unlike raw `comptime {}` blocks, attributes on top-level items in the program do have a set evaluation order. Attributes within a module are evaluated top-down, and attributes + in different modules are evaluated submodule-first. Sibling modules to the same parent module are evaluated in order of the module declarations (`mod foo; mod bar;`) in their + parent module. + +### Lowering + +When a `comptime` value is used in runtime code it must be lowered into a runtime value. This means replacing the expression with the literal that it evaluated to. For example, the code: + +```rust +struct Foo { array: [Field; 2], len: u32 } + +fn main() { + println(comptime { + let mut foo = std::mem::zeroed::(); + foo.array[0] = 4; + foo.len = 1; + foo + }); +} +``` + +will be converted to the following after `comptime` expressions are evaluated: + +```rust +struct Foo { array: [Field; 2], len: u32 } + +fn main() { + println(Foo { array: [4, 0], len: 1 }); +} +``` + +Not all types of values can be lowered. For example, references, `Type`s, and `TypeDefinition`s (among other types) cannot be lowered at all. + +```rust +fn main() { + // There's nothing we could inline here to create a Type value at runtime + // let _ = get_type!(); +} + +comptime fn get_type() -> Type { ... } +``` + +Values of certain types may also change type when they are lowered. For example, a comptime format string will already be +formatted, and thus lowers into a runtime string instead: + +```rust +fn main() { + let foo = comptime { + let i = 2; + f"i = {i}" + }; + assert_eq(foo, "i = 2"); +} +``` + +--- + +## (Quasi) Quote + +Macros in Noir are `comptime` functions which return code as a value which is inserted into the call site when it is lowered there. +A code value in this case is of type `Quoted` and can be created by a `quote { ... }` expression. +More specifically, the code value `quote` creates is a token stream - a representation of source code as a series of words, numbers, string literals, or operators. +For example, the expression `quote { Hi "there reader"! }` would quote three tokens: the word "hi", the string "there reader", and an exclamation mark. +You'll note that snippets that would otherwise be invalid syntax can still be quoted. + +When a `Quoted` value is used in runtime code, it is lowered into a `quote { ... }` expression. Since this expression is only valid +in compile-time code however, we'd get an error if we tried this. Instead, we can use macro insertion to insert each token into the +program at that point, and parse it as an expression. To do this, we have to add a `!` after the function name returning the `Quoted` value. +If the value was created locally and there is no function returning it, `std::meta::unquote!(_)` can be used instead. +Calling such a function at compile-time without `!` will just return the `Quoted` value to be further manipulated. For example: + +```rust title="quote-example" showLineNumbers +comptime fn quote_one() -> Quoted { + quote { 1 } + } + + #[test] + fn returning_versus_macro_insertion() { + comptime { + // let _a: Quoted = quote { 1 }; + let _a: Quoted = quote_one(); + + // let _b: Field = 1; + let _b: Field = quote_one!(); + + // Since integers default to fields, if we + // want a different type we have to explicitly cast + // let _c: i32 = 1 as i32; + let _c: i32 = quote_one!() as i32; + } + } +``` +> Source code: noir_stdlib/src/meta/mod.nr#L131-L151 + + +For those familiar with quoting from other languages (primarily lisps), Noir's `quote` is actually a _quasiquote_. +This means we can escape the quoting by using the unquote operator to splice values in the middle of quoted code. + +In addition to curly braces, you can also use square braces for the quote operator: + +```rust +comptime { + let q1 = quote { 1 }; + let q2 = quote [ 2 ]; + assert_eq(q1, q2); + + // Square braces can be used to quote mismatched curly braces if needed + let _ = quote[}]; +} +``` + +--- + +## Unquote + +The unquote operator `$` is usable within a `quote` expression. +It takes a variable as an argument, evaluates the variable, and splices the resulting value into the quoted token stream at that point. For example, + +```rust +comptime { + let x = 1 + 2; + let y = quote { $x + 4 }; +} +``` + +The value of `y` above will be the token stream containing `3`, `+`, and `4`. We can also use this to combine `Quoted` values into larger token streams: + +```rust +comptime { + let x = quote { 1 + 2 }; + let y = quote { $x + 4 }; +} +``` + +The value of `y` above is now the token stream containing five tokens: `1 + 2 + 4`. + +Note that to unquote something, a variable name _must_ follow the `$` operator in a token stream. +If it is an expression (even a parenthesized one), it will do nothing. Most likely a parse error will be given when the macro is later unquoted. + +Unquoting can also be avoided by escaping the `$` with a backslash: + +```rust +comptime { + let x = quote { 1 + 2 }; + + // y contains the four tokens: `$x + 4` + let y = quote { \$x + 4 }; +} +``` + +### Combining Tokens + +Note that `Quoted` is internally a series of separate tokens, and that all unquoting does is combine these token vectors. +This means that code which appears to append like a string actually appends like a vector internally: + +```rust +comptime { + let x = 3; + let q = quote { foo$x }; // This is [foo, 3], not [foo3] + + // Spaces are ignored in general, they're never part of a token + assert_eq(q, quote { foo 3 }); +} +``` + +If you do want string semantics, you can use format strings then convert back to a `Quoted` value with `.quoted_contents()`. +Note that formatting a quoted value with multiple tokens will always insert a space between each token. If this is +undesired, you'll need to only operate on quoted values containing a single token. To do this, you can iterate +over each token of a larger quoted value with `.tokens()`: + +```rust title="concatenate-example" showLineNumbers +comptime fn concatenate(q1: Quoted, q2: Quoted) -> Quoted { + assert(q1.tokens().len() <= 1); + assert(q2.tokens().len() <= 1); + + f"{q1}{q2}".quoted_contents() + } + + // The CtString type is also useful for a compile-time string of unbounded size + // so that you can append to it in a loop. + comptime fn double_spaced(q: Quoted) -> CtString { + let mut result = "".as_ctstring(); + + for token in q.tokens() { + if result != "".as_ctstring() { + result = result.append_str(" "); + } + result = result.append_fmtstr(f"{token}"); + } + + result + } + + #[test] + fn concatenate_test() { + comptime { + let result = concatenate(quote {foo}, quote {bar}); + assert_eq(result, quote {foobar}); + + let result = double_spaced(quote {foo bar 3}).as_quoted_str!(); + assert_eq(result, "foo bar 3"); + } + } +``` +> Source code: noir_stdlib/src/meta/mod.nr#L266-L299 + + +--- + +## Attributes + +Attributes provide a way to run a `comptime` function on an item in the program. +When you use an attribute, the function with the same name will be called with that item as an argument: + +```rust +#[my_struct_attribute] +struct Foo {} + +comptime fn my_struct_attribute(s: StructDefinition) { + println("Called my_struct_attribute!"); +} + +#[my_function_attribute] +fn foo() {} + +comptime fn my_function_attribute(f: FunctionDefinition) { + println("Called my_function_attribute!"); +} +``` + +Anything returned from one of these functions will be inserted at top-level along with the original item. +Note that expressions are not valid at top-level so you'll get an error trying to return `3` or similar just as if you tried to write a program containing `3; struct Foo {}`. +You can insert other top-level items such as trait impls, structs, or functions this way though. +For example, this is the mechanism used to insert additional trait implementations into the program when deriving a trait impl from a struct: + +```rust title="derive-field-count-example" showLineNumbers +trait FieldCount { + fn field_count() -> u32; + } + + #[derive_field_count] + struct Bar { + x: Field, + y: [Field; 2], + } + + comptime fn derive_field_count(s: StructDefinition) -> Quoted { + let typ = s.as_type(); + let field_count = s.fields_as_written().len(); + quote { + impl FieldCount for $typ { + fn field_count() -> u32 { + $field_count + } + } + } + } +``` +> Source code: noir_stdlib/src/meta/mod.nr#L153-L175 + + +### Calling annotations with additional arguments + +Arguments may optionally be given to attributes. +When this is done, these additional arguments are passed to the attribute function after the item argument. + +```rust title="annotation-arguments-example" showLineNumbers +#[assert_field_is_type(quote { i32 }.as_type())] + struct MyStruct { + my_field: i32, + } + + comptime fn assert_field_is_type(s: StructDefinition, typ: Type) { + // Assert the first field in `s` has type `typ` + let fields = s.fields([]); + assert_eq(fields[0].1, typ); + } +``` +> Source code: noir_stdlib/src/meta/mod.nr#L177-L188 + + +We can also take any number of arguments by adding the `varargs` attribute: + +```rust title="annotation-varargs-example" showLineNumbers +#[assert_three_args(1, 2, 3)] + struct MyOtherStruct { + my_other_field: u32, + } + + #[varargs] + comptime fn assert_three_args(_s: StructDefinition, args: [Field]) { + assert_eq(args.len(), 3); + } +``` +> Source code: noir_stdlib/src/meta/mod.nr#L190-L200 + + +### Attribute Evaluation Order + +Unlike the evaluation order of stray `comptime {}` blocks within functions, attributes have a well-defined evaluation +order. Within a module, attributes are evaluated top to bottom. Between modules, attributes in child modules are evaluated +first. Attributes in sibling modules are resolved following the `mod foo; mod bar;` declaration order within their parent +modules. + +```rust +mod foo; // attributes in foo are run first +mod bar; // followed by attributes in bar + +// followed by any attributes in the parent module +#[derive(Eq)] +struct Baz {} +``` + +Note that because of this evaluation order, you may get an error trying to derive a trait for a struct whose fields +have not yet had the trait derived already: + +```rust +// Error! `Bar` field of `Foo` does not (yet) implement Eq! +#[derive(Eq)] +struct Foo { + bar: Bar +} + +#[derive(Eq)] +struct Bar {} +``` + +In this case, the issue can be resolved by rearranging the structs. + +--- + +## Comptime API + +Although `comptime`, `quote`, and unquoting provide a flexible base for writing macros, +Noir's true metaprogramming ability comes from being able to interact with the compiler through a compile-time API. +This API can be accessed through built-in functions in `std::meta` as well as on methods of several `comptime` types. + +The following is an incomplete list of some `comptime` types along with some useful methods on them. You can see more in the standard library [Metaprogramming section](../standard_library/meta). + +- `Quoted`: A token stream +- `Type`: The type of a Noir type + - `fn implements(self, constraint: TraitConstraint) -> bool` + - Returns true if `self` implements the given trait constraint +- `Expr`: A syntactically valid expression. Can be used to recur on a program's parse tree to inspect how it is structured. + - Methods: + - `fn as_function_call(self) -> Option<(Expr, [Expr])>` + - If this is a function call expression, return `(function, arguments)` + - `fn as_block(self) -> Option<[Expr]>` + - If this is a block, return each statement in the block +- `FunctionDefinition`: A function definition + - Methods: + - `fn parameters(self) -> [(Quoted, Type)]` + - Returns a slice of `(name, type)` pairs for each parameter +- `StructDefinition`: A struct definition + - Methods: + - `fn as_type(self) -> Type` + - Returns this `StructDefinition` as a `Type`. Any generics are kept as-is + - `fn generics(self) -> [Quoted]` + - Return the name of each generic on this struct + - `fn fields(self) -> [(Quoted, Type)]` + - Return the name and type of each field +- `TraitConstraint`: A trait constraint such as `From` +- `TypedExpr`: A type-checked expression. +- `UnresolvedType`: A syntactic notation that refers to a Noir type that hasn't been resolved yet + +There are many more functions available by exploring the `std::meta` module and its submodules. +Using these methods is the key to writing powerful metaprogramming libraries. + +### `#[use_callers_scope]` + +Since certain functions such as `Quoted::as_type`, `Expression::as_type`, or `Quoted::as_trait_constraint` will attempt +to resolve their contents in a particular scope - it can be useful to change the scope they resolve in. By default +these functions will resolve in the current function's scope which is usually the attribute function they are called in. +If you're working on a library however, this may be a completely different module or crate to the item you're trying to +use the attribute on. If you want to be able to use `Quoted::as_type` to refer to types local to the caller's scope for +example, you can annotate your attribute function with `#[use_callers_scope]`. This will ensure your attribute, and any +closures it uses, can refer to anything in the caller's scope. `#[use_callers_scope]` also works recursively. So if both +your attribute function and a helper function it calls use it, then they can both refer to the same original caller. + +--- + +## Example: Derive + +Using all of the above, we can write a `derive` macro that behaves similarly to Rust's but is not built into the language. +From the user's perspective it will look like this: + +```rust +// Example usage +#[derive(Default, Eq, Ord)] +struct MyStruct { my_field: u32 } +``` + +To implement `derive` we'll have to create a `comptime` function that accepts +a variable amount of traits. + +```rust title="derive_example" showLineNumbers +// These are needed for the unconstrained hashmap we're using to store derive functions +use crate::collections::umap::UHashMap; +use crate::hash::BuildHasherDefault; +use crate::hash::poseidon2::Poseidon2Hasher; + +// A derive function is one that given a struct definition can +// create us a quoted trait impl from it. +pub type DeriveFunction = fn(StructDefinition) -> Quoted; + +// We'll keep a global HANDLERS map to keep track of the derive handler for each trait +comptime mut global HANDLERS: UHashMap> = + UHashMap::default(); + +// Given a struct and a slice of traits to derive, create trait impls for each. +// This function is as simple as iterating over the slice, checking if we have a trait +// handler registered for the given trait, calling it, and appending the result. +#[varargs] +pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted { + let mut result = quote {}; + + for trait_to_derive in traits { + let handler = HANDLERS.get(trait_to_derive); + 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 +} +``` +> Source code: noir_stdlib/src/meta/mod.nr#L33-L66 + + +Registering a derive function could be done as follows: + +```rust title="derive_via" showLineNumbers +// To register a handler for a trait, just add it to our handlers map +pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { + HANDLERS.insert(t, f); +} +``` +> Source code: noir_stdlib/src/meta/mod.nr#L68-L75 + + +```rust title="big-derive-usage-example" showLineNumbers +// Finally, to register a handler we call the above function as an annotation + // with our handler function. + #[derive_via(derive_do_nothing)] + trait DoNothing { + fn do_nothing(self); + } + + comptime fn derive_do_nothing(s: StructDefinition) -> Quoted { + // This is simplified since we don't handle generics or where clauses! + // In a real example we'd likely also need to introduce each of + // `s.generics()` as well as a trait constraint for each generic + // to ensure they also implement the trait. + let typ = s.as_type(); + quote { + impl DoNothing for $typ { + fn do_nothing(self) { + // Traits can't tell us what to do + println("something"); + } + } + } + } + + // Since `DoNothing` is a simple trait which: + // 1. Only has one method + // 2. Does not have any generics on the trait itself + // We can use `std::meta::make_trait_impl` to help us out. + // This helper function will generate our impl for us along with any + // necessary where clauses and still provides a flexible interface + // for us to work on each field on the struct. + comptime fn derive_do_nothing_alt(s: StructDefinition) -> Quoted { + let trait_name = quote { DoNothing }; + let method_signature = quote { fn do_nothing(self) }; + + // Call `do_nothing` recursively on each field in the struct + let for_each_field = |field_name| quote { self.$field_name.do_nothing(); }; + + // Some traits like Eq want to join each field expression with something like `&`. + // We don't need that here + let join_fields_with = quote {}; + + // The body function is a spot to insert any extra setup/teardown needed. + // We'll insert our println here. Since we recur on each field, we should see + // one println for the struct itself, followed by a println for every field (recursively). + let body = |body| quote { + println("something"); + $body + }; + crate::meta::make_trait_impl( + s, + trait_name, + method_signature, + for_each_field, + join_fields_with, + body, + ) + } +``` +> Source code: noir_stdlib/src/meta/mod.nr#L202-L260 + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/control_flow.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/control_flow.md new file mode 100644 index 000000000000..57816c38c575 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/control_flow.md @@ -0,0 +1,111 @@ +--- +title: Control Flow +description: + Learn how to use loops and if expressions in the Noir programming language. Discover the syntax + and examples for for loops and if-else statements. +keywords: [Noir programming language, loops, for loop, if-else statements, Rust syntax] +sidebar_position: 2 +--- + +## If Expressions + +Noir supports `if-else` statements. The syntax is most similar to Rust's where it is not required +for the statement's conditional to be surrounded by parentheses. + +```rust +let a = 0; +let mut x: u32 = 0; + +if a == 0 { + if a != 0 { + x = 6; + } else { + x = 2; + } +} else { + x = 5; + assert(x == 5); +} +assert(x == 2); +``` + +## For loops + +`for` loops allow you to repeat a block of code multiple times. + +The following block of code between the braces is run 10 times. + +```rust +for i in 0..10 { + // do something +} +``` + +Alternatively, `start..=end` can be used for a range that is inclusive on both ends. + +The index for loops is of type `u64`. + +### Break and Continue + +In unconstrained code, `break` and `continue` are also allowed in `for` and `loop` loops. These are only allowed +in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations +a loop may have. `break` and `continue` can be used like so: + +```rust +for i in 0 .. 10 { + println("Iteration start") + + if i == 2 { + continue; + } + + if i == 5 { + break; + } + + println(i); +} +println("Loop end") +``` + +When used, `break` will end the current loop early and jump to the statement after the for loop. In the example +above, the `break` will stop the loop and jump to the `println("Loop end")`. + +`continue` will stop the current iteration of the loop, and jump to the start of the next iteration. In the example +above, `continue` will jump to `println("Iteration start")` when used. Note that the loop continues as normal after this. +The iteration variable `i` is still increased by one as normal when `continue` is used. + +`break` and `continue` cannot currently be used to jump out of more than a single loop at a time. + +## Loops + +In unconstrained code, `loop` is allowed for loops that end with a `break`. +A `loop` must contain at least one `break` statement that is reachable during execution. +This is only allowed in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations +a loop may have. + +```rust +let mut i = 10; +loop { + println(i); + i -= 1; + + if i == 0 { + break; + } +} +``` + +## While loops + +In unconstrained code, `while` is allowed for loops that end when a given condition is met. +This is only allowed in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations +a loop may have. + +```rust +let mut i = 0 +while i < 10 { + println(i); + i += 2; +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_bus.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_bus.mdx new file mode 100644 index 000000000000..e55e58622ce8 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_bus.mdx @@ -0,0 +1,23 @@ +--- +title: Data Bus +sidebar_position: 13 +--- +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +The data bus is an optimization that the backend can use to make recursion more efficient. +In order to use it, you must define some inputs of the program entry points (usually the `main()` +function) with the `call_data` modifier, and the return values with the `return_data` modifier. +These modifiers are incompatible with `pub` and `mut` modifiers. + +## Example + +```rust +fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { + let a = z[x]; + a+y +} +``` + +As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/_category_.json new file mode 100644 index 000000000000..5d694210bbf3 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/arrays.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/arrays.md new file mode 100644 index 000000000000..289145a8c4d1 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/arrays.md @@ -0,0 +1,276 @@ +--- +title: Arrays +description: + Dive into the Array data type in Noir. Grasp its methods, practical examples, and best practices for efficiently using Arrays in your Noir code. +keywords: + [ + noir, + array type, + methods, + examples, + indexing, + ] +sidebar_position: 4 +--- + +An array is one way of grouping together values into one compound type. Array types can be inferred +or explicitly specified via the syntax `[; ]`: + +```rust +fn main(x : Field, y : Field) { + let my_arr = [x, y]; + let your_arr: [Field; 2] = [x, y]; +} +``` + +Here, both `my_arr` and `your_arr` are instantiated as an array containing two `Field` elements. + +Array elements can be accessed using indexing: + +```rust +fn main() { + let a = [1, 2, 3, 4, 5]; + + let first = a[0]; + let second = a[1]; +} +``` + +All elements in an array must be of the same type (i.e. homogeneous). That is, an array cannot group +a `Field` value and a `u8` value together for example. + +You can write mutable arrays, like: + +```rust +fn main() { + let mut arr = [1, 2, 3, 4, 5]; + assert(arr[0] == 1); + + arr[0] = 42; + assert(arr[0] == 42); +} +``` + +You can instantiate a new array of a fixed size with the same value repeated for each element. The following example instantiates an array of length 32 where each element is of type Field and has the value 0. + +```rust +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: + +```rust +let array: [Field; 32] = [0; 32]; +let sl = array.as_slice() +``` + +You can define multidimensional arrays: + +```rust +let array : [[Field; 2]; 2]; +let element = array[0][0]; +``` + +However, multidimensional slices are not supported. For example, the following code will error at compile time: + +```rust +let slice : [[Field]] = &[]; +``` + +## Types + +You can create arrays of primitive types or structs. There is not yet support for nested arrays +(arrays of arrays) or arrays of structs that contain arrays. + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for arrays. +Each of these functions are located within the generic impl `impl [T; N] {`. +So anywhere `self` appears, it refers to the variable `self: [T; N]`. + +### len + +Returns the length of an array + +```rust +fn len(self) -> Field +``` + +example + +```rust +fn main() { + let array = [42, 42]; + assert(array.len() == 2); +} +``` + +### sort + +Returns a new sorted array. The original array remains untouched. Notice that this function will +only work for arrays of fields or integers, not for any arbitrary type. This is because the sorting +logic it uses internally is optimized specifically for these values. If you need a sort function to +sort any type, you should use the function `sort_via` described below. + +```rust +fn sort(self) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32]; + let sorted = arr.sort(); + assert(sorted == [32, 42]); +} +``` + +### sort_via + +Sorts the array with a custom comparison function. The ordering function must return true if the first argument should be sorted to be before the second argument or is equal to the second argument. + +Using this method with an operator like `<` that does not return `true` for equal values will result in an assertion failure for arrays with equal elements. + +```rust +fn sort_via(self, ordering: fn(T, T) -> bool) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32] + let sorted_ascending = arr.sort_via(|a, b| a <= b); + assert(sorted_ascending == [32, 42]); // verifies + + let sorted_descending = arr.sort_via(|a, b| a >= b); + assert(sorted_descending == [32, 42]); // does not verify +} +``` + +### map + +Applies a function to each element of the array, returning a new array containing the mapped elements. + +```rust +fn map(self, f: fn(T) -> U) -> [U; N] +``` + +example + +```rust +let a = [1, 2, 3]; +let b = a.map(|a| a * 2); // b is now [2, 4, 6] +``` + +### fold + +Applies a function to each element of the array, returning the final accumulated value. The first +parameter is the initial value. + +```rust +fn fold(self, mut accumulator: U, f: fn(U, T) -> U) -> U +``` + +This is a left fold, so the given function will be applied to the accumulator and first element of +the array, then the second, and so on. For a given call the expected result would be equivalent to: + +```rust +let a1 = [1]; +let a2 = [1, 2]; +let a3 = [1, 2, 3]; + +let f = |a, b| a - b; +a1.fold(10, f) //=> f(10, 1) +a2.fold(10, f) //=> f(f(10, 1), 2) +a3.fold(10, f) //=> f(f(f(10, 1), 2), 3) +``` + +example: + +```rust + +fn main() { + let arr = [2, 2, 2, 2, 2]; + let folded = arr.fold(0, |a, b| a + b); + assert(folded == 10); +} + +``` + +### reduce + +Same as fold, but uses the first element as the starting element. + +Requires `self` to be non-empty. + +```rust +fn reduce(self, f: fn(T, T) -> T) -> T +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let reduced = arr.reduce(|a, b| a + b); + assert(reduced == 10); +} +``` + +### all + +Returns true if all the elements satisfy the given predicate + +```rust +fn all(self, predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let all = arr.all(|a| a == 2); + assert(all); +} +``` + +### any + +Returns true if any of the elements satisfy the given predicate + +```rust +fn any(self, predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 5]; + let any = arr.any(|a| a == 5); + assert(any); +} +``` + +### as_str_unchecked + +Converts a byte array of type `[u8; N]` to a string. Note that this performs no UTF-8 validation - +the given array is interpreted as-is as a string. + +```rust +impl [u8; N] { + pub fn as_str_unchecked(self) -> str +} +``` + +example: + +```rust +fn main() { + let hi = [104, 105].as_str_unchecked(); + assert_eq(hi, "hi"); +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/booleans.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/booleans.md new file mode 100644 index 000000000000..2507af710e71 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/booleans.md @@ -0,0 +1,28 @@ +--- +title: Booleans +description: + Delve into the Boolean data type in Noir. Understand its methods, practical examples, and best practices for using Booleans in your Noir programs. +keywords: + [ + noir, + boolean type, + methods, + examples, + logical operations, + ] +sidebar_position: 2 +--- + + +The `bool` type in Noir has two possible values: `true` and `false`: + +```rust +fn main() { + let t = true; + let f: bool = false; +} +``` + +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. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/fields.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/fields.md new file mode 100644 index 000000000000..7f424516ab95 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/fields.md @@ -0,0 +1,260 @@ +--- +title: Fields +description: + Dive deep into the Field data type in Noir. Understand its methods, practical examples, and best practices to effectively use Fields in your Noir programs. +keywords: + [ + noir, + field type, + methods, + examples, + best practices, + ] +sidebar_position: 0 +--- + +The field type corresponds to the native field type of the proving backend. + +The size of a Noir field depends on the elliptic curve's finite field for the proving backend +adopted. For example, a field would be a 254-bit integer when paired with the default backend that +spans the Grumpkin curve. + +Fields support integer arithmetic and are often used as the default numeric type in Noir: + +```rust +fn main(x : Field, y : Field) { + let z = x + y; +} +``` + +`x`, `y` and `z` are all private fields in this example. Using the `let` keyword we defined a new +private value `z` constrained to be equal to `x + y`. + +If proving efficiency is of priority, fields should be used as a default for solving problems. +Smaller integer types (e.g. `u64`) incur extra range constraints. + +## Methods + +After declaring a Field, you can use these common methods on it: + +### to_le_bits + +Transforms the field into an array of bits, Little Endian. + +```rust title="to_le_bits" showLineNumbers +pub fn to_le_bits(self: Self) -> [u1; N] { +``` +> Source code: noir_stdlib/src/field/mod.nr#L60-L62 + + +example: + +```rust title="to_le_bits_example" showLineNumbers +fn test_to_le_bits() { + let field = 2; + let bits: [u1; 8] = field.to_le_bits(); + assert_eq(bits, [0, 1, 0, 0, 0, 0, 0, 0]); + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L356-L362 + + + +### to_be_bits + +Transforms the field into an array of bits, Big Endian. + +```rust title="to_be_bits" showLineNumbers +pub fn to_be_bits(self: Self) -> [u1; N] { +``` +> Source code: noir_stdlib/src/field/mod.nr#L92-L94 + + +example: + +```rust title="to_be_bits_example" showLineNumbers +fn test_to_be_bits() { + let field = 2; + let bits: [u1; 8] = field.to_be_bits(); + assert_eq(bits, [0, 0, 0, 0, 0, 0, 1, 0]); + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L347-L353 + + + +### to_le_bytes + +Transforms into an array of bytes, Little Endian + +```rust title="to_le_bytes" showLineNumbers +pub fn to_le_bytes(self: Self) -> [u8; N] { +``` +> Source code: noir_stdlib/src/field/mod.nr#L124-L126 + + +example: + +```rust title="to_le_bytes_example" showLineNumbers +fn test_to_le_bytes() { + let field = 2; + let bytes: [u8; 8] = field.to_le_bytes(); + assert_eq(bytes, [2, 0, 0, 0, 0, 0, 0, 0]); + assert_eq(Field::from_le_bytes::<8>(bytes), field); + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L375-L382 + + +### to_be_bytes + +Transforms into an array of bytes, Big Endian + +```rust title="to_be_bytes" showLineNumbers +pub fn to_be_bytes(self: Self) -> [u8; N] { +``` +> Source code: noir_stdlib/src/field/mod.nr#L161-L163 + + +example: + +```rust title="to_be_bytes_example" showLineNumbers +fn test_to_be_bytes() { + let field = 2; + let bytes: [u8; 8] = field.to_be_bytes(); + assert_eq(bytes, [0, 0, 0, 0, 0, 0, 0, 2]); + assert_eq(Field::from_be_bytes::<8>(bytes), field); + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L365-L372 + + + +### to_le_radix + +Decomposes into an array over the specified base, Little Endian + +```rust title="to_le_radix" showLineNumbers +pub fn to_le_radix(self: Self, radix: u32) -> [u8; N] { + // Brillig does not need an immediate radix + if !crate::runtime::is_unconstrained() { + static_assert(1 < radix, "radix must be greater than 1"); + static_assert(radix <= 256, "radix must be less than or equal to 256"); + static_assert(radix & (radix - 1) == 0, "radix must be a power of 2"); + } + self.__to_le_radix(radix) + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L189-L199 + + + +example: + +```rust title="to_le_radix_example" showLineNumbers +fn test_to_le_radix() { + // 259, in base 256, little endian, is [3, 1]. + // i.e. 3 * 256^0 + 1 * 256^1 + let field = 259; + + // The radix (in this example, 256) must be a power of 2. + // The length of the returned byte array can be specified to be + // >= the amount of space needed. + let bytes: [u8; 8] = field.to_le_radix(256); + assert_eq(bytes, [3, 1, 0, 0, 0, 0, 0, 0]); + assert_eq(Field::from_le_bytes::<8>(bytes), field); + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L401-L414 + + + +### to_be_radix + +Decomposes into an array over the specified base, Big Endian + +```rust title="to_be_radix" showLineNumbers +pub fn to_be_radix(self: Self, radix: u32) -> [u8; N] { + // Brillig does not need an immediate radix + if !crate::runtime::is_unconstrained() { + crate::assert_constant(radix); + } + self.__to_be_radix(radix) + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L201-L209 + + +example: + +```rust title="to_be_radix_example" showLineNumbers +fn test_to_be_radix() { + // 259, in base 256, big endian, is [1, 3]. + // i.e. 3 * 256^0 + 1 * 256^1 + let field = 259; + + // The radix (in this example, 256) must be a power of 2. + // The length of the returned byte array can be specified to be + // >= the amount of space needed. + let bytes: [u8; 8] = field.to_be_radix(256); + assert_eq(bytes, [0, 0, 0, 0, 0, 0, 1, 3]); + assert_eq(Field::from_be_bytes::<8>(bytes), field); + } +``` +> Source code: noir_stdlib/src/field/mod.nr#L385-L398 + + + +### pow_32 + +Returns the value to the power of the specified exponent + +```rust +fn pow_32(self, exponent: Field) -> Field +``` + +example: + +```rust +fn main() { + let field = 2 + let pow = field.pow_32(4); + assert(pow == 16); +} +``` + +### assert_max_bit_size + +Adds a constraint to specify that the field can be represented with `bit_size` number of bits + +```rust title="assert_max_bit_size" showLineNumbers +pub fn assert_max_bit_size(self) { +``` +> Source code: noir_stdlib/src/field/mod.nr#L10-L12 + + +example: + +```rust +fn main() { + let field = 2 + field.assert_max_bit_size::<32>(); +} +``` + +### sgn0 + +Parity of (prime) Field element, i.e. sgn0(x mod p) = 0 if x ∈ \{0, ..., p-1\} is even, otherwise sgn0(x mod p) = 1. + +```rust +fn sgn0(self) -> u1 +``` + + +### lt + +Returns true if the field is less than the other field + +```rust +pub fn lt(self, another: Field) -> bool +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/function_types.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/function_types.md new file mode 100644 index 000000000000..f6121af17e24 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/function_types.md @@ -0,0 +1,26 @@ +--- +title: Function types +sidebar_position: 10 +--- + +Noir supports higher-order functions. The syntax for a function type is as follows: + +```rust +fn(arg1_type, arg2_type, ...) -> return_type +``` + +Example: + +```rust +fn assert_returns_100(f: fn() -> Field) { // f takes no args and returns a Field + assert(f() == 100); +} + +fn main() { + assert_returns_100(|| 100); // ok + assert_returns_100(|| 150); // fails +} +``` + +A function type also has an optional capture environment - this is necessary to support closures. +See [Lambdas](../lambdas.md) for more details. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/index.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/index.md new file mode 100644 index 000000000000..0f2db2b2d753 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/index.md @@ -0,0 +1,126 @@ +--- +title: Data Types +description: + Get a clear understanding of the two categories of Noir data types - primitive types and compound + types. Learn about their characteristics, differences, and how to use them in your Noir + programming. +keywords: + [ + noir, + data types, + primitive types, + compound types, + private types, + public types, + ] +--- + +Every value in Noir has a type, which determines which operations are valid for it. + +All values in Noir are fundamentally composed of `Field` elements. For a more approachable +developing experience, abstractions are added on top to introduce different data types in Noir. + +Noir has two category of data types: primitive types (e.g. `Field`, integers, `bool`) and compound +types that group primitive types (e.g. arrays, tuples, structs). Each value can either be private or +public. + +## Private & Public Types + +A **private value** is known only to the Prover, while a **public value** is known by both the +Prover and Verifier. Mark values as `private` when the value should only be known to the prover. All +primitive types (including individual fields of compound types) in Noir are private by default, and +can be marked public when certain values are intended to be revealed to the Verifier. + +> **Note:** For public values defined in Noir programs paired with smart contract verifiers, once +> the proofs are verified on-chain the values can be considered known to everyone that has access to +> that blockchain. + +Public data types are treated no differently to private types apart from the fact that their values +will be revealed in proofs generated. Simply changing the value of a public type will not change the +circuit (where the same goes for changing values of private types as well). + +_Private values_ are also referred to as _witnesses_ sometimes. + +> **Note:** The terms private and public when applied to a type (e.g. `pub Field`) have a different +> meaning than when applied to a function (e.g. `pub fn foo() {}`). +> +> The former is a visibility modifier for the Prover to interpret if a value should be made known to +> the Verifier, while the latter is a visibility modifier for the compiler to interpret if a +> function should be made accessible to external Noir programs like in other languages. + +### pub Modifier + +All data types in Noir are private by default. Types are explicitly declared as public using the +`pub` modifier: + +```rust +fn main(x : Field, y : pub Field) -> pub Field { + x + y +} +``` + +In this example, `x` is **private** while `y` and `x + y` (the return value) are **public**. Note +that visibility is handled **per variable**, so it is perfectly valid to have one input that is +private and another that is public. + +> **Note:** Public types can only be declared through parameters on `main`. + +## Type Aliases + +A type alias is a new name for an existing type. Type aliases are declared with the keyword `type`: + +```rust +type Id = u8; + +fn main() { + let id: Id = 1; + let zero: u8 = 0; + assert(zero + 1 == id); +} +``` + +Type aliases can also be used with [generics](../generics.md): + +```rust +type Id = Size; + +fn main() { + let id: Id = 1; + let zero: u32 = 0; + assert(zero + 1 == id); +} +``` + +Type aliases can even refer to other aliases. An error will be issued if they form a cycle: + +```rust +// Ok! +type A = B; +type B = Field; + +type Bad1 = Bad2; + +// error: Dependency cycle found +type Bad2 = Bad1; +// ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2 +``` + +By default, like functions, type aliases are private to the module they exist in. You can use `pub` +to make the type alias public or `pub(crate)` to make it public to just its crate: + +```rust +// This type alias is now public +pub type Id = u8; +``` + +## Wildcard Type +Noir can usually infer the type of the variable from the context, so specifying the type of a variable is only required when it cannot be inferred. However, specifying a complex type can be tedious, especially when it has multiple generic arguments. Often some of the generic types can be inferred from the context, and Noir only needs a hint to properly infer the other types. We can partially specify a variable's type by using `_` as a marker, indicating where we still want the compiler to infer the type. + +```rust +let a: [_; 4] = foo(b); +``` + + +### BigInt + +You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/integers.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/integers.md new file mode 100644 index 000000000000..b8a5d4980296 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/integers.md @@ -0,0 +1,178 @@ +--- +title: Integers +description: Explore the Integer data type in Noir. Learn about its methods, see real-world examples, and grasp how to efficiently use Integers in your Noir code. +keywords: [noir, integer types, methods, examples, arithmetic] +sidebar_position: 1 +--- + +An integer type is a range constrained field type. +The Noir frontend supports both unsigned and signed integer types. +The allowed sizes are 1, 8, 16, 32 and 64 bits. + +:::info + +When an integer is defined in Noir without a specific type, it will default to `Field`. + +The one exception is for loop indices which default to `u64` since comparisons on `Field`s are not possible. + +::: + +## Unsigned Integers + +An unsigned integer type is specified first with the letter `u` (indicating its unsigned nature) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: u8 = 1; + let y: u8 = 1; + let z = x + y; + assert (z == 2); +} +``` + +The bit size determines the maximum value the integer type can store. For example, a `u8` variable can store a value in the range of 0 to 255 (i.e. $\\2^{8}-1\\$). + +## Signed Integers + +A signed integer type is specified first with the letter `i` (which stands for integer) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: i8 = -1; + let y: i8 = -1; + let z = x + y; + assert (z == -2); +} +``` + +The bit size determines the maximum and minimum range of value the integer type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$). + + +```rust +fn main(x: i16, y: i16) { + // modulo + let c = x % y; + let c = x % -13; +} +``` + +Modulo operation is defined for negative integers thanks to integer division, so that the equality `x = (x/y)*y + (x%y)` holds. + +## 128 bits Unsigned Integers + +The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind: +- You cannot cast between a native integer and `U128` +- There is a higher performance cost when using `U128`, compared to a native type. + +Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. + +```rust +fn main() { + let x = U128::from_integer(23); + let y = U128::from_hex("0x7"); + let z = x + y; + assert(z.to_integer() == 30); +} +``` + +`U128` is implemented with two 64 bits limbs, representing the low and high bits, which explains the performance cost. You should expect `U128` to be twice more costly for addition and four times more costly for multiplication. +You can construct a U128 from its limbs: +```rust +fn main(x: u64, y: u64) { + let z = U128::from_u64s_be(x,y); + assert(z.hi == x as Field); + assert(z.lo == y as Field); +} +``` + +Note that the limbs are stored as Field elements in order to avoid unnecessary conversions. +Apart from this, most operations will work as usual: + +```rust +fn main(x: U128, y: U128) { + // multiplication + let c = x * y; + // addition and subtraction + let c = c - x + y; + // division + let c = x / y; + // bit operation; + let c = x & y | y; + // bit shift + let c = x << y; + // comparisons; + let c = x < y; + let c = x == y; +} +``` + +## Overflows + +Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: + +```rust +fn main(x: u8, y: u8) -> pub u8 { + let z = x + y; + z +} +``` + +With: + +```toml +x = "255" +y = "1" +``` + +Would result in: + +``` +$ nargo execute +error: Assertion failed: 'attempt to add with overflow' +┌─ ~/src/main.nr:9:13 +│ +│ let z = x + y; +│ ----- +│ += Call stack: + ... +``` + +A similar error would happen with signed integers: + +```rust +fn main() -> i8 { + let x: i8 = -118; + let y: i8 = -11; + let z = x + y; + z +} +``` + +Note that if a computation ends up being unused the compiler might remove it and it won't end up producing an overflow: + +```rust +fn main() { + // "255 + 1" would overflow, but `z` is unused so no computation happens + let z: u8 = 255 + 1; +} +``` + +### Wrapping methods + +Although integer overflow is expected to error, some use-cases rely on wrapping. For these use-cases, the standard library provides `wrapping` variants of certain common operations: + +```rust +fn wrapping_add(x: T, y: T) -> T; +fn wrapping_sub(x: T, y: T) -> T; +fn wrapping_mul(x: T, y: T) -> T; +``` + +Example of how it is used: + +```rust + +fn main(x: u8, y: u8) -> pub u8 { + std::wrapping_add(x, y) +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/references.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/references.md new file mode 100644 index 000000000000..a5293d11cfb9 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/references.md @@ -0,0 +1,23 @@ +--- +title: References +sidebar_position: 9 +--- + +Noir supports first-class references. References are a bit like pointers: they point to a specific address that can be followed to access the data stored at that address. You can use Rust-like syntax to use pointers in Noir: the `&` operator references the variable, the `*` operator dereferences it. + +Example: + +```rust +fn main() { + let mut x = 2; + + // you can reference x as &mut and pass it to multiplyBy2 + multiplyBy2(&mut x); +} + +// you can access &mut here +fn multiplyBy2(x: &mut Field) { + // and dereference it with * + *x = *x * 2; +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/slices.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/slices.mdx new file mode 100644 index 000000000000..e8091c62dd86 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/slices.mdx @@ -0,0 +1,358 @@ +--- +title: Slices +description: Explore the Slice data type in Noir. Understand its methods, see real-world examples, and learn how to effectively use Slices in your Noir programs. +keywords: [noir, slice type, methods, examples, subarrays] +sidebar_position: 5 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A slice is a dynamically-sized view into a sequence of elements. They can be resized at runtime, but because they don't own the data, they cannot be returned from a circuit. You can treat slices as arrays without a constrained size. + +```rust +fn main() -> pub u32 { + let mut slice: [Field] = &[0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +To write a slice literal, use a preceding ampersand as in: `&[0; 2]` or +`&[1, 2, 3]`. + +It is important to note that slices are not references to arrays. In Noir, +`&[..]` is more similar to an immutable, growable vector. + +View the corresponding test file [here][test-file]. + +[test-file]: https://github.com/noir-lang/noir/blob/f387ec1475129732f72ba294877efdf6857135ac/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for slices: + +### push_back + +Pushes a new element to the end of the slice, returning a new slice with a length one greater than the original unmodified slice. + +```rust +fn push_back(_self: [T], _elem: T) -> [T] +``` + +example: + +```rust +fn main() -> pub Field { + let mut slice: [Field] = &[0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +View the corresponding test file [here][test-file]. + +### push_front + +Returns a new slice with the specified element inserted at index 0. The existing elements indexes are incremented by 1. + +```rust +fn push_front(_self: Self, _elem: T) -> Self +``` + +Example: + +```rust +let mut new_slice: [Field] = &[]; +new_slice = new_slice.push_front(20); +assert(new_slice[0] == 20); // returns true +``` + +View the corresponding test file [here][test-file]. + +### pop_front + +Returns a tuple of two items, the first element of the slice and the rest of the slice. + +```rust +fn pop_front(_self: Self) -> (T, Self) +``` + +Example: + +```rust +let (first_elem, rest_of_slice) = slice.pop_front(); +``` + +View the corresponding test file [here][test-file]. + +### pop_back + +Returns a tuple of two items, the beginning of the slice with the last element omitted and the last element. + +```rust +fn pop_back(_self: Self) -> (Self, T) +``` + +Example: + +```rust +let (popped_slice, last_elem) = slice.pop_back(); +``` + +View the corresponding test file [here][test-file]. + +### append + +Loops over a slice and adds it to the end of another. + +```rust +fn append(mut self, other: Self) -> Self +``` + +Example: + +```rust +let append = &[1, 2].append(&[3, 4, 5]); +``` + +### insert + +Inserts an element at a specified index and shifts all following elements by 1. + +```rust +fn insert(_self: Self, _index: Field, _elem: T) -> Self +``` + +Example: + +```rust +new_slice = rest_of_slice.insert(2, 100); +assert(new_slice[2] == 100); +``` + +View the corresponding test file [here][test-file]. + +### remove + +Remove an element at a specified index, shifting all elements after it to the left, returning the altered slice and the removed element. + +```rust +fn remove(_self: Self, _index: Field) -> (Self, T) +``` + +Example: + +```rust +let (remove_slice, removed_elem) = slice.remove(3); +``` + +### len + +Returns the length of a slice + +```rust +fn len(self) -> Field +``` + +Example: + +```rust +fn main() { + let slice = &[42, 42]; + assert(slice.len() == 2); +} +``` + +### as_array + +Converts this slice into an array. + +Make sure to specify the size of the resulting array. +Panics if the resulting array length is different than the slice's length. + +```rust +fn as_array(self) -> [T; N] +``` + +Example: + +```rust +fn main() { + let slice = &[5, 6]; + + // Always specify the length of the resulting array! + let array: [Field; 2] = slice.as_array(); + + assert(array[0] == slice[0]); + assert(array[1] == slice[1]); +} +``` + +### map + +Applies a function to each element of the slice, returning a new slice containing the mapped elements. + +```rust +fn map(self, f: fn[Env](T) -> U) -> [U] +``` + +example + +```rust +let a = &[1, 2, 3]; +let b = a.map(|a| a * 2); // b is now &[2, 4, 6] +``` + +### fold + +Applies a function to each element of the slice, returning the final accumulated value. The first +parameter is the initial value. + +```rust +fn fold(self, mut accumulator: U, f: fn[Env](U, T) -> U) -> U +``` + +This is a left fold, so the given function will be applied to the accumulator and first element of +the slice, then the second, and so on. For a given call the expected result would be equivalent to: + +```rust +let a1 = &[1]; +let a2 = &[1, 2]; +let a3 = &[1, 2, 3]; + +let f = |a, b| a - b; +a1.fold(10, f) //=> f(10, 1) +a2.fold(10, f) //=> f(f(10, 1), 2) +a3.fold(10, f) //=> f(f(f(10, 1), 2), 3) +``` + +example: + +```rust + +fn main() { + let slice = &[2, 2, 2, 2, 2]; + let folded = slice.fold(0, |a, b| a + b); + assert(folded == 10); +} + +``` + +### reduce + +Same as fold, but uses the first element as the starting element. + +```rust +fn reduce(self, f: fn[Env](T, T) -> T) -> T +``` + +example: + +```rust +fn main() { + let slice = &[2, 2, 2, 2, 2]; + let reduced = slice.reduce(|a, b| a + b); + assert(reduced == 10); +} +``` + +### filter + +Returns a new slice containing only elements for which the given predicate returns true. + +```rust +fn filter(self, f: fn[Env](T) -> bool) -> Self +``` + +example: + +```rust +fn main() { + let slice = &[1, 2, 3, 4, 5]; + let odds = slice.filter(|x| x % 2 == 1); + assert_eq(odds, &[1, 3, 5]); +} +``` + +### join + +Flatten each element in the slice into one value, separated by `separator`. + +Note that although slices implement `Append`, `join` cannot be used on slice +elements since nested slices are prohibited. + +```rust +fn join(self, separator: T) -> T where T: Append +``` + +example: + +```rust +struct Accumulator { + total: Field, +} + +// "Append" two accumulators by adding them +impl Append for Accumulator { + fn empty() -> Self { + Self { total: 0 } + } + + fn append(self, other: Self) -> Self { + Self { total: self.total + other.total } + } +} + +fn main() { + let slice = &[1, 2, 3, 4, 5].map(|total| Accumulator { total }); + + let result = slice.join(Accumulator::empty()); + assert_eq(result, Accumulator { total: 15 }); + + // We can use a non-empty separator to insert additional elements to sum: + let separator = Accumulator { total: 10 }; + let result = slice.join(separator); + assert_eq(result, Accumulator { total: 55 }); +} +``` + +### all + +Returns true if all the elements satisfy the given predicate + +```rust +fn all(self, predicate: fn[Env](T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let slice = &[2, 2, 2, 2, 2]; + let all = slice.all(|a| a == 2); + assert(all); +} +``` + +### any + +Returns true if any of the elements satisfy the given predicate + +```rust +fn any(self, predicate: fn[Env](T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let slice = &[2, 2, 2, 2, 5]; + let any = slice.any(|a| a == 5); + assert(any); +} + +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/strings.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/strings.md new file mode 100644 index 000000000000..b2257e8bdbbb --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/strings.md @@ -0,0 +1,114 @@ +--- +title: Strings +description: + Discover the String data type in Noir. Learn about its methods, see real-world examples, and understand how to effectively manipulate and use Strings in Noir. +keywords: + [ + noir, + string type, + methods, + examples, + concatenation, + ] +sidebar_position: 3 +--- + + +The string type is a fixed length value defined with `str`. + +You can use strings in `assert()` functions or print them with +`println()`. See more about [Logging](../../standard_library/logging.md). + +```rust + +fn main(message : pub str<11>, hex_as_string : str<4>) { + println(message); + assert(message == "hello world"); + assert(hex_as_string == "0x41"); +} +``` + +You can convert a `str` to a byte array by calling `as_bytes()` +or a vector by calling `as_bytes_vec()`. + +```rust +fn main() { + let message = "hello world"; + let message_bytes = message.as_bytes(); + let mut message_vec = message.as_bytes_vec(); + assert(message_bytes.len() == 11); + assert(message_bytes[0] == 104); + assert(message_bytes[0] == message_vec.get(0)); +} +``` + +## Escape characters + +You can use escape characters for your strings: + +| Escape Sequence | Description | +|-----------------|-----------------| +| `\r` | Carriage Return | +| `\n` | Newline | +| `\t` | Tab | +| `\0` | Null Character | +| `\"` | Double Quote | +| `\\` | Backslash | + +Example: + +```rust +let s = "Hello \"world" // prints "Hello "world" +let s = "hey \tyou"; // prints "hey you" +``` + +## Raw strings + +A raw string begins with the letter `r` and is optionally delimited by a number of hashes `#`. + +Escape characters are *not* processed within raw strings. All contents are interpreted literally. + +Example: + +```rust +let s = r"Hello world"; +let s = r#"Simon says "hello world""#; + +// Any number of hashes may be used (>= 1) as long as the string also terminates with the same number of hashes +let s = r#####"One "#, Two "##, Three "###, Four "####, Five will end the string."#####; +``` + +## Format strings + +A format string begins with the letter `f` and allows inserting the value of local and global variables in it. + +Example: + +```rust +let four = 2 + 2; +let s = f"Two plus two is: {four}"; +println(s); +``` + +The output of the above program is: + +```text +Two plus two is: 4 +``` + +To insert the value of a local or global variable, put it inside `{...}` in the string. + +If you need to write the `{` or `}` characters, use `{{` and `}}` respectively: + +```rust +let four = 2 + 2; + +// Prints "This is not expanded: {four}" +println(f"This is not expanded: {{four}}"); +``` + +More complex expressions are not allowed inside `{...}`: + +```rust +let s = f"Two plus two is: {2 + 2}" // Error: invalid format string +``` \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/structs.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/structs.md new file mode 100644 index 000000000000..4b0a8001226d --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/structs.md @@ -0,0 +1,96 @@ +--- +title: Structs +description: + Explore the Struct data type in Noir. Learn about its methods, see real-world examples, and grasp how to effectively define and use Structs in your Noir programs. +keywords: + [ + noir, + struct type, + methods, + examples, + data structures, + ] +sidebar_position: 8 +--- + +A struct also allows for grouping multiple values of different types. Unlike tuples, we can also +name each field. + +> **Note:** The usage of _field_ here refers to each element of the struct and is unrelated to the +> field type of Noir. + +Defining a struct requires giving it a name and listing each field within as `: ` pairs: + +```rust +struct Animal { + hands: Field, + legs: Field, + eyes: u8, +} +``` + +An instance of a struct can then be created with actual values in `: ` pairs in any +order. Struct fields are accessible using their given names: + +```rust +fn main() { + let legs = 4; + + let dog = Animal { + eyes: 2, + hands: 0, + legs, + }; + + let zero = dog.hands; +} +``` + +Structs can also be destructured in a pattern, binding each field to a new variable: + +```rust +fn main() { + let Animal { hands, legs: feet, eyes } = get_octopus(); + + let ten = hands + feet + eyes as Field; +} + +fn get_octopus() -> Animal { + let octopus = Animal { + hands: 0, + legs: 8, + eyes: 2, + }; + + octopus +} +``` + +The new variables can be bound with names different from the original struct field names, as +showcased in the `legs --> feet` binding in the example above. + +### Visibility + +By default, like functions, structs are private to the module they exist in. You can use `pub` +to make the struct public or `pub(crate)` to make it public to just its crate: + +```rust +// This struct is now public +pub struct Animal { + hands: Field, + legs: Field, + eyes: u8, +} +``` + +The same applies to struct fields: by default they are private to the module they exist in, +but they can be made `pub` or `pub(crate)`: + +```rust +// This struct is now public +pub struct Animal { + hands: Field, // private to its module + pub(crate) legs: Field, // accessible from the entire crate + pub eyes: u8, // accessible from anywhere +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/tuples.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/tuples.md new file mode 100644 index 000000000000..2ec5c9c41135 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/data_types/tuples.md @@ -0,0 +1,48 @@ +--- +title: Tuples +description: + Dive into the Tuple data type in Noir. Understand its methods, practical examples, and best practices for efficiently using Tuples in your Noir code. +keywords: + [ + noir, + tuple type, + methods, + examples, + multi-value containers, + ] +sidebar_position: 7 +--- + +A tuple collects multiple values like an array, but with the added ability to collect values of +different types: + +```rust +fn main() { + let tup: (u8, u64, Field) = (255, 500, 1000); +} +``` + +One way to access tuple elements is via destructuring using pattern matching: + +```rust +fn main() { + let tup = (1, 2); + + let (one, two) = tup; + + let three = one + two; +} +``` + +Another way to access tuple elements is via direct member access, using a period (`.`) followed by +the index of the element we want to access. Index `0` corresponds to the first tuple element, `1` to +the second and so on: + +```rust +fn main() { + let tup = (5, 6, 7, 8); + + let five = tup.0; + let eight = tup.3; +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/functions.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/functions.md new file mode 100644 index 000000000000..f656cdfd97a1 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/functions.md @@ -0,0 +1,226 @@ +--- +title: Functions +description: + Learn how to declare functions and methods in Noir, a programming language with Rust semantics. + This guide covers parameter declaration, return types, call expressions, and more. +keywords: [Noir, Rust, functions, methods, parameter declaration, return types, call expressions] +sidebar_position: 1 +--- + +Functions in Noir follow the same semantics of Rust, though Noir does not support early returns. + +To declare a function the `fn` keyword is used. + +```rust +fn foo() {} +``` + +By default, functions are visible only within the package they are defined. To make them visible outside of that package (for example, as part of a [library](../modules_packages_crates/crates_and_packages.md#libraries)), you should mark them as `pub`: + +```rust +pub fn foo() {} +``` + +You can also restrict the visibility of the function to only the crate it was defined in, by specifying `pub(crate)`: + +```rust +pub(crate) fn foo() {} //foo can only be called within its crate +``` + +All parameters in a function must have a type and all types are known at compile time. The parameter +is pre-pended with a colon and the parameter type. Multiple parameters are separated using a comma. + +```rust +fn foo(x : Field, y : Field){} +``` + +The return type of a function can be stated by using the `->` arrow notation. The function below +states that the foo function must return a `Field`. If the function returns no value, then the arrow +is omitted. + +```rust +fn foo(x : Field, y : Field) -> Field { + x + y +} +``` + +Note that a `return` keyword is unneeded in this case - the last expression in a function's body is +returned. + +## Main function + +If you're writing a binary, the `main` function is the starting point of your program. You can pass all types of expressions to it, as long as they have a fixed size at compile time: + +```rust +fn main(x : Field) // this is fine: passing a Field +fn main(x : [Field; 2]) // this is also fine: passing a Field with known size at compile-time +fn main(x : (Field, bool)) // 👌: passing a (Field, bool) tuple means size 2 +fn main(x : str<5>) // this is fine, as long as you pass a string of size 5 + +fn main(x : Vec) // can't compile, has variable size +fn main(x : [Field]) // can't compile, has variable size +fn main(....// i think you got it by now +``` + +Keep in mind [tests](../../tooling/testing.md) don't differentiate between `main` and any other function. The following snippet passes tests, but won't compile or prove: + +```rust +fn main(x : [Field]) { + assert(x[0] == 1); +} + +#[test] +fn test_one() { + main(&[1, 2]); +} +``` + +```bash +$ nargo test +[testing] Running 1 test functions +[testing] Testing test_one... ok +[testing] All tests passed + +$ nargo check +The application panicked (crashed). +Message: Cannot have variable sized arrays as a parameter to main +``` + +## Call Expressions + +Calling a function in Noir is executed by using the function name and passing in the necessary +arguments. + +Below we show how to call the `foo` function from the `main` function using a call expression: + +```rust +fn main(x : Field, y : Field) { + let z = foo(x); +} + +fn foo(x : Field) -> Field { + x + x +} +``` + +## Methods + +You can define methods in Noir on any struct type in scope. + +```rust +struct MyStruct { + foo: Field, + bar: Field, +} + +impl MyStruct { + fn new(foo: Field) -> MyStruct { + MyStruct { + foo, + bar: 2, + } + } + + fn sum(self) -> Field { + self.foo + self.bar + } +} + +fn main() { + let s = MyStruct::new(40); + assert(s.sum() == 42); +} +``` + +Methods are just syntactic sugar for functions, so if we wanted to we could also call `sum` as +follows: + +```rust +assert(MyStruct::sum(s) == 42); +``` + +It is also possible to specialize which method is chosen depending on the [generic](./generics.md) type that is used. In this example, the `foo` function returns different values depending on its type: + +```rust +struct Foo {} + +impl Foo { + fn foo(self) -> Field { 1 } +} + +impl Foo { + fn foo(self) -> Field { 2 } +} + +fn main() { + let f1: Foo = Foo{}; + let f2: Foo = Foo{}; + assert(f1.foo() + f2.foo() == 3); +} +``` + +Also note that impls with the same method name defined in them cannot overlap. For example, if we already have `foo` defined for `Foo` and `Foo` like we do above, we cannot also define `foo` in an `impl Foo` since it would be ambiguous which version of `foo` to choose. + +```rust +// Including this impl in the same project as the above snippet would +// cause an overlapping impls error +impl Foo { + fn foo(self) -> Field { 3 } +} +``` + +## Lambdas + +Lambdas are anonymous functions. They follow the syntax of Rust - `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +See [Lambdas](./lambdas.md) for more details. + +## Attributes + +Attributes are metadata that can be applied to a function, using the following syntax: `#[attribute(value)]`. + +Supported attributes include: + +- **builtin**: the function is implemented by the compiler, for efficiency purposes. +- **deprecated**: mark the function as _deprecated_. Calling the function will generate a warning: `warning: use of deprecated function` +- **field**: Used to enable conditional compilation of code depending on the field size. See below for more details +- **oracle**: mark the function as _oracle_; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. +- **test**: mark the function as unit tests. See [Tests](../../tooling/testing.md) for more details + +### Field Attribute + +The field attribute defines which field the function is compatible for. The function is conditionally compiled, under the condition that the field attribute matches the Noir native field. +The field can be defined implicitly, by using the name of the elliptic curve usually associated to it - for instance bn254, bls12_381 - or explicitly by using the field (prime) order, in decimal or hexadecimal form. +As a result, it is possible to define multiple versions of a function with each version specialized for a different field attribute. This can be useful when a function requires different parameters depending on the underlying elliptic curve. + +Example: we define the function `foo()` three times below. Once for the default Noir bn254 curve, once for the field $\mathbb F_{23}$, which will normally never be used by Noir, and once again for the bls12_381 curve. + +```rust +#[field(bn254)] +fn foo() -> u32 { + 1 +} + +#[field(23)] +fn foo() -> u32 { + 2 +} + +// This commented code would not compile as foo would be defined twice because it is the same field as bn254 +// #[field(21888242871839275222246405745257275088548364400416034343698204186575808495617)] +// fn foo() -> u32 { +// 2 +// } + +#[field(bls12_381)] +fn foo() -> u32 { + 3 +} +``` + +If the field name is not known to Noir, it will discard the function. Field names are case insensitive. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/generics.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/generics.md new file mode 100644 index 000000000000..7b3906b682e8 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/generics.md @@ -0,0 +1,262 @@ +--- +title: Generics +description: Learn how to use Generics in Noir +keywords: [Noir, Rust, generics, functions, structs] +sidebar_position: 7 +--- + +Generics allow you to use the same functions with multiple different concrete data types. You can +read more about the concept of generics in the Rust documentation +[here](https://doc.rust-lang.org/book/ch10-01-syntax.html). + +Here is a trivial example showing the identity function that supports any type. In Rust, it is +common to refer to the most general type as `T`. We follow the same convention in Noir. + +```rust +fn id(x: T) -> T { + x +} +``` + +## Numeric Generics + +If we want to be generic over array lengths (which are type-level integers), we can use numeric +generics. Using these looks similar to using regular generics, but introducing them into scope +requires declaring them with `let MyGenericName: IntegerType`. This can be done anywhere a normal +generic is declared. Instead of types, these generics resolve to integers at compile-time. +Here's an example of a struct that is generic over the size of the array it contains internally: + +```rust +struct BigInt { + limbs: [u32; N], +} + +impl BigInt { + // `N` is in scope of all methods in the impl + fn first(first: BigInt, second: BigInt) -> Self { + assert(first.limbs != second.limbs); + first + } + + fn second(first: BigInt, second: Self) -> Self { + assert(first.limbs != second.limbs); + second + } +} +``` + +## In Structs + +Generics are useful for specifying types in structs. For example, we can specify that a field in a +struct will be of a certain generic type. In this case `value` is of type `T`. + +```rust +struct RepeatedValue { + value: T, + count: Field, +} + +impl RepeatedValue { + fn print(self) { + for _i in 0 .. self.count { + println(self.value); + } + } +} + +fn main() { + let repeated = RepeatedValue { value: "Hello!", count: 2 }; + repeated.print(); +} +``` + +The `print` function will print `Hello!` an arbitrary number of times, twice in this case. + +## Calling functions on generic parameters + +Since a generic type `T` can represent any type, how can we call functions on the underlying type? +In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" + +This is what [traits](../concepts/traits.md) are for in Noir. Here's an example of a function generic over +any type `T` that implements the `Eq` trait for equality: + +```rust +fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool + where T: Eq +{ + if (array1.len() == 0) | (array2.len() == 0) { + true + } else { + array1[0] == array2[0] + } +} + +fn main() { + assert(first_element_is_equal([1, 2, 3], [1, 5, 6])); + + // We can use first_element_is_equal for arrays of any type + // as long as we have an Eq impl for the types we pass in + let array = [MyStruct::new(), MyStruct::new()]; + assert(first_element_is_equal(array, array)); +} + +struct MyStruct { + foo: Field +} + +impl MyStruct { + fn new() -> Self { + MyStruct { foo: 0 } + } +} + +impl Eq for MyStruct { + fn eq(self, other: MyStruct) -> bool { + self.foo == other.foo + } +} +``` + +You can find more details on traits and trait implementations on the [traits page](../concepts/traits.md). + +## Manually Specifying Generics with the Turbofish Operator + +There are times when the compiler cannot reasonably infer what type should be used for a generic, or when the developer themselves may want to manually distinguish generic type parameters. This is where the `::<>` turbofish operator comes into play. + +The `::<>` operator can follow a variable or path and can be used to manually specify generic arguments within the angle brackets. +The name "turbofish" comes from that `::<>` looks like a little fish. + +Examples: +```rust +fn main() { + let mut slice = []; + slice = slice.push_back(1); + slice = slice.push_back(2); + // Without turbofish a type annotation would be needed on the left hand side + let array = slice.as_array::<2>(); +} +``` + + +```rust +trait MyTrait { + fn ten() -> Self; +} + +impl MyTrait for Field { + fn ten() -> Self { 10 } +} + +struct Foo { + inner: T +} + +impl Foo { + fn generic_method(_self: Self) -> U where U: MyTrait { + U::ten() + } +} + +fn example() { + let foo: Foo = Foo { inner: 1 }; + // Using a type other than `Field` here (e.g. u32) would fail as + // there is no matching impl for `u32: MyTrait`. + // + // Substituting the `10` on the left hand side of this assert + // with `10 as u32` would fail with a type mismatch as we + // are expecting a `Field` from the right hand side. + assert(10 == foo.generic_method::()); +} +``` + +## Arithmetic Generics + +In addition to numeric generics, Noir also allows a limited form of arithmetic on generics. +When you have a numeric generic such as `N`, you can use the following operators on it in a +type position: `+`, `-`, `*`, `/`, and `%`. + +Note that type checking arithmetic generics is a best effort guess from the compiler and there +are many cases of types that are equal that the compiler may not see as such. For example, +we know that `T * (N + M)` should be equal to `T*N + T*M` but the compiler does not currently +apply the distributive law and thus sees these as different types. + +Even with this limitation though, the compiler can handle common cases decently well: + +```rust +trait Serialize { + fn serialize(self) -> [Field; N]; +} + +impl Serialize<1> for Field { + fn serialize(self) -> [Field; 1] { + [self] + } +} + +impl Serialize for [T; N] + where T: Serialize { .. } + +impl Serialize for (T, U) + where T: Serialize, U: Serialize { .. } + +fn main() { + let data = (1, [2, 3, 4]); + assert_eq(data.serialize().len(), 4); +} +``` + +Note that if there is any over or underflow the types will fail to unify: + +```rust title="underflow-example" showLineNumbers +fn pop(array: [Field; N]) -> [Field; N - 1] { + let mut result: [Field; N - 1] = std::mem::zeroed(); + for i in 0..N - 1 { + result[i] = array[i]; + } + result +} + +fn main() { + // error: Could not determine array length `(0 - 1)` + pop([]); +} +``` +> Source code: test_programs/compile_failure/arithmetic_generics_underflow/src/main.nr#L1-L14 + + +This also applies if there is underflow in an intermediate calculation: + +```rust title="intermediate-underflow-example" showLineNumbers +fn main() { + // From main it looks like there's nothing sketchy going on + seems_fine([]); +} + +// Since `seems_fine` says it can receive and return any length N +fn seems_fine(array: [Field; N]) -> [Field; N] { + // But inside `seems_fine` we pop from the array which + // requires the length to be greater than zero. + + // error: Could not determine array length `(0 - 1)` + push_zero(pop(array)) +} + +fn pop(array: [Field; N]) -> [Field; N - 1] { + let mut result: [Field; N - 1] = std::mem::zeroed(); + for i in 0..N - 1 { + result[i] = array[i]; + } + result +} + +fn push_zero(array: [Field; N]) -> [Field; N + 1] { + let mut result: [Field; N + 1] = std::mem::zeroed(); + for i in 0..N { + result[i] = array[i]; + } + // index N is already zeroed + result +} +``` +> Source code: test_programs/compile_failure/arithmetic_generics_intermediate_underflow/src/main.nr#L1-L32 + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/globals.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/globals.md new file mode 100644 index 000000000000..c64b6c53746e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/globals.md @@ -0,0 +1,82 @@ +--- +title: Global Variables +description: + Learn about global variables in Noir. Discover how + to declare, modify, and use them in your programs. +keywords: [noir programming language, globals, global variables, constants] +sidebar_position: 8 +--- + +## Globals + + +Noir supports global variables. The global's type must be specified by the user: + +```rust +global N: Field = 5; + +global TUPLE: (Field, Field) = (3, 2); + +fn main() { + assert(N == 5); + assert(N == TUPLE.0 + TUPLE.1); +} +``` + +:::info + +Globals can be defined as any expression, so long as they don't depend on themselves - otherwise there would be a dependency cycle! For example: + +```rust +global T: u32 = foo(T); // dependency error +``` + +::: + + +If they are initialized to a literal integer, globals can be used to specify an array's length: + +```rust +global N: u32 = 2; + +fn main(y : [Field; N]) { + assert(y[0] == y[1]) +} +``` + +A global from another module can be imported or referenced externally like any other name: + +```rust +global N: Field = 20; + +fn main() { + assert(my_submodule::N != N); +} + +mod my_submodule { + global N: Field = 10; +} +``` + +When a global is used, Noir replaces the name with its definition on each occurrence. +This means globals defined using function calls will repeat the call each time they're used: + +```rust +global RESULT: [Field; 100] = foo(); + +fn foo() -> [Field; 100] { ... } +``` + +This is usually fine since Noir will generally optimize any function call that does not +refer to a program input into a constant. It should be kept in mind however, if the called +function performs side-effects like `println`, as these will still occur on each use. + +### Visibility + +By default, like functions, globals are private to the module they exist in. You can use `pub` +to make the global public or `pub(crate)` to make it public to just its crate: + +```rust +// This global is now public +pub global N: u32 = 5; +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/lambdas.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/lambdas.md new file mode 100644 index 000000000000..be3c7e0b5caa --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/lambdas.md @@ -0,0 +1,81 @@ +--- +title: Lambdas +description: Learn how to use anonymous functions in Noir programming language. +keywords: [Noir programming language, lambda, closure, function, anonymous function] +sidebar_position: 9 +--- + +## Introduction + +Lambdas are anonymous functions. The syntax is `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +A block can be used as the body of a lambda, allowing you to declare local variables inside it: + +```rust +let cool = || { + let x = 100; + let y = 100; + x + y +} + +assert(cool() == 200); +``` + +## Closures + +Inside the body of a lambda, you can use variables defined in the enclosing function. Such lambdas are called **closures**. In this example `x` is defined inside `main` and is accessed from within the lambda: + +```rust +fn main() { + let x = 100; + let closure = || x + 150; + assert(closure() == 250); +} +``` + +## Passing closures to higher-order functions + +It may catch you by surprise that the following code fails to compile: + +```rust +fn foo(f: fn () -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // error :( +} +``` + +The reason is that the closure's capture environment affects its type - we have a closure that captures two Fields and `foo` +expects a regular function as an argument - those are incompatible. +:::note + +Variables contained within the `||` are the closure's parameters, and the expression that follows it is the closure's body. The capture environment is comprised of any variables used in the closure's body that are not parameters. + +E.g. in |x| x + y, y would be a captured variable, but x would not be, since it is a parameter of the closure. + +::: +The syntax for the type of a closure is `fn[env](args) -> ret_type`, where `env` is the capture environment of the closure - +in this example that's `(Field, Field)`. + +The best solution in our case is to make `foo` generic over the environment type of its parameter, so that it can be called +with closures with any environment, as well as with regular functions: + +```rust +fn foo(f: fn[Env]() -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // compiles fine + assert(foo(|| 60) == 60); // compiles fine +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/mutability.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/mutability.md new file mode 100644 index 000000000000..fdeef6a87c53 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/mutability.md @@ -0,0 +1,121 @@ +--- +title: Mutability +description: + Learn about mutable variables in Noir. Discover how + to declare, modify, and use them in your programs. +keywords: [noir programming language, mutability in noir, mutable variables] +sidebar_position: 8 +--- + +Variables in noir can be declared mutable via the `mut` keyword. Mutable variables can be reassigned +to via an assignment expression. + +```rust +let x = 2; +x = 3; // error: x must be mutable to be assigned to + +let mut y = 3; +let y = 4; // OK +``` + +The `mut` modifier can also apply to patterns: + +```rust +let (a, mut b) = (1, 2); +a = 11; // error: a must be mutable to be assigned to +b = 12; // OK + +let mut (c, d) = (3, 4); +c = 13; // OK +d = 14; // OK + +// etc. +let MyStruct { x: mut y } = MyStruct { x: a }; +// y is now in scope +``` + +Note that mutability in noir is local and everything is passed by value, so if a called function +mutates its parameters then the parent function will keep the old value of the parameters. + +```rust +fn main() -> pub Field { + let x = 3; + helper(x); + x // x is still 3 +} + +fn helper(mut x: i32) { + x = 4; +} +``` + +## Non-local mutability + +Non-local mutability can be achieved through the mutable reference type `&mut T`: + +```rust +fn set_to_zero(x: &mut Field) { + *x = 0; +} + +fn main() { + let mut y = 42; + set_to_zero(&mut y); + assert(*y == 0); +} +``` + +When creating a mutable reference, the original variable being referred to (`y` in this +example) must also be mutable. Since mutable references are a reference type, they must +be explicitly dereferenced via `*` to retrieve the underlying value. Note that this yields +a copy of the value, so mutating this copy will not change the original value behind the +reference: + +```rust +fn main() { + let mut x = 1; + let x_ref = &mut x; + + let mut y = *x_ref; + let y_ref = &mut y; + + x = 2; + *x_ref = 3; + + y = 4; + *y_ref = 5; + + assert(x == 3); + assert(*x_ref == 3); + assert(y == 5); + assert(*y_ref == 5); +} +``` + +Note that types in Noir are actually deeply immutable so the copy that occurs when +dereferencing is only a conceptual copy - no additional constraints will occur. + +Mutable references can also be stored within structs. Note that there is also +no lifetime parameter on these unlike rust. This is because the allocated memory +always lasts the entire program - as if it were an array of one element. + +```rust +struct Foo { + x: &mut Field +} + +impl Foo { + fn incr(mut self) { + *self.x += 1; + } +} + +fn main() { + let foo = Foo { x: &mut 0 }; + foo.incr(); + assert(*foo.x == 1); +} +``` + +In general, you should avoid non-local & shared mutability unless it is needed. Sticking +to only local mutability will improve readability and potentially improve compiler optimizations as well. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/ops.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/ops.md new file mode 100644 index 000000000000..c35c36c38a90 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/ops.md @@ -0,0 +1,98 @@ +--- +title: Logical Operations +description: + Learn about the supported arithmetic and logical operations in the Noir programming language. + Discover how to perform operations on private input types, integers, and booleans. +keywords: + [ + Noir programming language, + supported operations, + arithmetic operations, + logical operations, + predicate operators, + bitwise operations, + short-circuiting, + backend, + ] +sidebar_position: 3 +--- + +# Operations + +## Table of Supported Operations + +| Operation | Description | Requirements | +| :-------- | :------------------------------------------------------------: | -------------------------------------: | +| + | Adds two private input types together | Types must be private input | +| - | Subtracts two private input types together | Types must be private input | +| \* | Multiplies two private input types together | Types must be private input | +| / | Divides two private input types together | Types must be private input | +| ^ | XOR two private input types together | Types must be integer | +| & | AND two private input types together | Types must be integer | +| \| | OR two private input types together | Types must be integer | +| \<\< | Left shift an integer by another integer amount | Types must be integer, shift must be u8 | +| >> | Right shift an integer by another integer amount | Types must be integer, shift must be u8 | +| ! | Bitwise not of a value | Type must be integer or boolean | +| \< | returns a bool if one value is less than the other | Upper bound must have a known bit size | +| \<= | returns a bool if one value is less than or equal to the other | Upper bound must have a known bit size | +| > | returns a bool if one value is more than the other | Upper bound must have a known bit size | +| >= | returns a bool if one value is more than or equal to the other | Upper bound must have a known bit size | +| == | returns a bool if one value is equal to the other | Both types must not be constants | +| != | returns a bool if one value is not equal to the other | Both types must not be constants | + +### Predicate Operators + +`<,<=, !=, == , >, >=` are known as predicate/comparison operations because they compare two values. +This differs from the operations such as `+` where the operands are used in _computation_. + +### Bitwise Operations Example + +```rust +fn main(x : Field) { + let y = x as u32; + let z = y & y; +} +``` + +`z` is implicitly constrained to be the result of `y & y`. The `&` operand is used to denote bitwise +`&`. + +> `x & x` would not compile as `x` is a `Field` and not an integer type. + +### Logical Operators + +Noir has no support for the logical operators `||` and `&&`. This is because encoding the +short-circuiting that these operators require can be inefficient for Noir's backend. Instead you can +use the bitwise operators `|` and `&` which operate identically for booleans, just without the +short-circuiting. + +```rust +let my_val = 5; + +let mut flag = 1; +if (my_val > 6) | (my_val == 0) { + flag = 0; +} +assert(flag == 1); + +if (my_val != 10) & (my_val < 50) { + flag = 0; +} +assert(flag == 0); +``` + +### Shorthand operators + +Noir shorthand operators for most of the above operators, namely `+=, -=, *=, /=, %=, &=, |=, ^=, <<=`, and `>>=`. These allow for more concise syntax. For example: + +```rust +let mut i = 0; +i = i + 1; +``` + +could be written as: + +```rust +let mut i = 0; +i += 1; +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/oracles.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/oracles.mdx new file mode 100644 index 000000000000..77a2ac1550ac --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/oracles.mdx @@ -0,0 +1,29 @@ +--- +title: Oracles +description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide. +keywords: + - Noir + - Oracles + - RPC Calls + - Unconstrained Functions + - Programming + - Blockchain +sidebar_position: 6 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation. + +Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md) + +You can declare an Oracle through the `#[oracle()]` flag. Example: + +```rust +#[oracle(get_number_sequence)] +unconstrained fn get_number_sequence(_size: Field) -> [Field] {} +``` + +The timeout for when using an external RPC oracle resolver can be set with the `NARGO_FOREIGN_CALL_TIMEOUT` environment variable. This timeout is in units of milliseconds. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/shadowing.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/shadowing.md new file mode 100644 index 000000000000..5ce6130d2011 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/shadowing.md @@ -0,0 +1,44 @@ +--- +title: Shadowing +sidebar_position: 12 +--- + +Noir allows for inheriting variables' values and re-declaring them with the same name similar to Rust, known as shadowing. + +For example, the following function is valid in Noir: + +```rust +fn main() { + let x = 5; + + { + let x = x * 2; + assert (x == 10); + } + + assert (x == 5); +} +``` + +In this example, a variable x is first defined with the value 5. + +The local scope that follows shadows the original x, i.e. creates a local mutable x based on the value of the original x. It is given a value of 2 times the original x. + +When we return to the main scope, x once again refers to just the original x, which stays at the value of 5. + +## Temporal mutability + +One way that shadowing is useful, in addition to ergonomics across scopes, is for temporarily mutating variables. + +```rust +fn main() { + let age = 30; + // age = age + 5; // Would error as `age` is immutable by default. + + let mut age = age + 5; // Temporarily mutates `age` with a new value. + + let age = age; // Locks `age`'s mutability again. + + assert (age == 35); +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/traits.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/traits.md new file mode 100644 index 000000000000..17cc04a97518 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/traits.md @@ -0,0 +1,611 @@ +--- +title: Traits +description: + Traits in Noir can be used to abstract out a common interface for functions across + several data types. +keywords: [noir programming language, traits, interfaces, generic, protocol] +sidebar_position: 14 +--- + +## Overview + +Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines +the interface of several methods contained within the trait. Types can then implement this trait by providing +implementations for these methods. For example in the program: + +```rust +struct Rectangle { + width: Field, + height: Field, +} + +impl Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +fn log_area(r: Rectangle) { + println(r.area()); +} +``` + +We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this +function to work on `Triangle`s as well?: + +```rust +struct Triangle { + width: Field, + height: Field, +} + +impl Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can +introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`: + +```rust +trait Area { + fn area(self) -> Field; +} + +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing +impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined +by the `Area` trait. + +```rust +impl Area for Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +impl Area for Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Now we have a working program that is generic over any type of Shape that is used! Others can even use this program +as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types. + +## Where Clauses + +As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements +a trait, we can add a where clause to the generic function. + +```rust +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+` +operator. Similarly, we can have multiple trait constraints by separating each with a comma: + +```rust +fn foo(elements: [T], thing: U) where + T: Default + Add + Eq, + U: Bar, +{ + let mut sum = T::default(); + + for element in elements { + sum += element; + } + + if sum == T::default() { + thing.bar(); + } +} +``` + +## Invoking trait methods + +As seen in the previous section, the `area` method was invoked on a type `T` that had a where clause `T: Area`. + +To invoke `area` on a type that directly implements the trait `Area`, the trait must be in scope (imported): + +```rust +use geometry::Rectangle; + +fn main() { + let rectangle = Rectangle { width: 1, height: 2}; + let area = rectangle.area(); // Error: the compiler doesn't know which `area` method this is +} +``` + +The above program errors because there might be multiple traits with an `area` method, all implemented +by `Rectangle`, and it's not clear which one should be used. + +To make the above program compile, the trait must be imported: + +```rust +use geometry::Rectangle; +use geometry::Area; // Bring the Area trait into scope + +fn main() { + let rectangle = Rectangle { width: 1, height: 2}; + let area = rectangle.area(); // OK: will use `area` from `geometry::Area` +} +``` + +An error will also be produced if multiple traits with an `area` method are in scope. If both traits +are needed in a file you can use the fully-qualified path to the trait: + +```rust +use geometry::Rectangle; + +fn main() { + let rectangle = Rectangle { width: 1, height: 2}; + let area = geometry::Area::area(rectangle); +} +``` + +## Generic Implementations + +You can add generics to a trait implementation by adding the generic list after the `impl` keyword: + +```rust +trait Second { + fn second(self) -> Field; +} + +impl Second for (T, Field) { + fn second(self) -> Field { + self.1 + } +} +``` + +You can also implement a trait for every type this way: + +```rust +trait Debug { + fn debug(self); +} + +impl Debug for T { + fn debug(self) { + println(self); + } +} + +fn main() { + 1.debug(); +} +``` + +### Generic Trait Implementations With Where Clauses + +Where clauses can be placed on trait implementations themselves to restrict generics in a similar way. +For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar` +will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types. +For example, here is the implementation for array equality: + +```rust +impl Eq for [T; let N: u32] where T: Eq { + // Test if two arrays have the same elements. + // Because both arrays must have length N, we know their lengths already match. + fn eq(self, other: Self) -> bool { + let mut result = true; + + for i in 0 .. self.len() { + // The T: Eq constraint is needed to call == on the array elements here + result &= self[i] == other[i]; + } + + result + } +} +``` + +Where clauses can also be placed on struct implementations. +For example, here is a method utilizing a generic type that implements the equality trait. + +```rust +struct Foo { + a: u32, + b: T, +} + +impl Foo where T: Eq { + fn eq(self, other: Self) -> bool { + (self.a == other.a) & self.b.eq(other.b) + } +} +``` + +## Generic Traits + +Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in +scope of every item within the trait. + +```rust +trait Into { + // Convert `self` to type `T` + fn into(self) -> T; +} +``` + +When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime +when referencing a generic trait (e.g. in a `where` clause). + +```rust +struct MyStruct { + array: [Field; 2], +} + +impl Into<[Field; 2]> for MyStruct { + fn into(self) -> [Field; 2] { + self.array + } +} + +fn as_array(x: T) -> [Field; 2] + where T: Into<[Field; 2]> +{ + x.into() +} + +fn main() { + let array = [1, 2]; + let my_struct = MyStruct { array }; + + assert_eq(as_array(my_struct), array); +} +``` + +### Associated Types and Constants + +Traits also support associated types and constraints which can be thought of as additional generics that are referred to by name. + +Here's an example of a trait with an associated type `Foo` and a constant `Bar`: + +```rust +trait MyTrait { + type Foo; + + let Bar: u32; +} +``` + +Now when we're implementing `MyTrait` we also have to provide values for `Foo` and `Bar`: + +```rust +impl MyTrait for Field { + type Foo = i32; + + let Bar: u32 = 11; +} +``` + +Since associated constants can also be used in a type position, its values are limited to only other +expression kinds allowed in numeric generics. + +When writing a trait constraint, you can specify all associated types and constants explicitly if +you wish: + +```rust +fn foo(x: T) where T: MyTrait { + ... +} +``` + +Or you can also elide them since there should only be one `Foo` and `Bar` for a given implementation +of `MyTrait` for a type: + +```rust +fn foo(x: T) where T: MyTrait { + ... +} +``` + +If you elide associated types, you can still refer to them via the type as trait syntax ``: + +```rust +fn foo(x: T) where + T: MyTrait, + ::Foo: Default + Eq +{ + let foo_value: ::Foo = Default::default(); + assert_eq(foo_value, foo_value); +} +``` + +## Trait Methods With No `self` + +A trait can contain any number of methods, each of which have access to the `Self` type which represents each type +that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used. +For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type +but doesn't need to take any parameters: + +```rust +trait Default { + fn default() -> Self; +} +``` + +Implementing this trait can be done similarly to any other trait: + +```rust +impl Default for Field { + fn default() -> Field { + 0 + } +} + +struct MyType {} + +impl Default for MyType { + fn default() -> Field { + MyType {} + } +} +``` + +However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`. +Instead, we'll need to refer to the function directly. This can be done either by referring to the +specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later +case, type inference determines the impl that is selected. + +```rust +let my_struct = MyStruct::default(); + +let x: Field = Default::default(); +let result = x + Default::default(); +``` + +## Default Method Implementations + +A trait can also have default implementations of its methods by giving a body to the desired functions. +Note that this body must be valid for all types that may implement the trait. As a result, the only +valid operations on `self` will be operations valid for any type or other operations on the trait itself. + +```rust +trait Numeric { + fn add(self, other: Self) -> Self; + + // Default implementation of double is (self + self) + fn double(self) -> Self { + self.add(self) + } +} +``` + +When implementing a trait with default functions, a type may choose to implement only the required functions: + +```rust +impl Numeric for Field { + fn add(self, other: Field) -> Field { + self + other + } +} +``` + +Or it may implement the optional methods as well: + +```rust +impl Numeric for u32 { + fn add(self, other: u32) -> u32 { + self + other + } + + fn double(self) -> u32 { + self * 2 + } +} +``` + +## Impl Specialization + +When implementing traits for a generic type it is possible to implement the trait for only a certain combination +of generics. This can be either as an optimization or because those specific generics are required to implement the trait. + +```rust +trait Sub { + fn sub(self, other: Self) -> Self; +} + +struct NonZero { + value: T, +} + +impl Sub for NonZero { + fn sub(self, other: Self) -> Self { + let value = self.value - other.value; + assert(value != 0); + NonZero { value } + } +} +``` + +## Overlapping Implementations + +Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous. +This means if a trait `Foo` is already implemented +by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other +type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create +any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given +method call. + +```rust +trait Trait {} + +// Previous impl defined here +impl Trait for (A, B) {} + +// error: Impl for type `(Field, Field)` overlaps with existing impl +impl Trait for (Field, Field) {} +``` + +## Trait Coherence + +Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create +impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same +program. + +The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared +in the crate the impl is in. + +In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does +not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate. +While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its +own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the +library your choices are to either submit a patch to the library or use the newtype pattern. + +### The Newtype Pattern + +The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type +that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create +impls for any trait we need on it. + +```rust +struct Wrapper { + foo: some_library::Foo, +} + +impl Default for Wrapper { + fn default() -> Wrapper { + Wrapper { + foo: some_library::Foo::new(), + } + } +} +``` + +Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated +to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and +unwrapping of values when converting to and from the `Wrapper` and `Foo` types. + +### Trait Inheritance + +Sometimes, you might need one trait to use another trait’s functionality (like "inheritance" in some other languages). In this case, you can specify this relationship by listing any child traits after the parent trait's name and a colon. Now, whenever the parent trait is implemented it will require the child traits to be implemented as well. A parent trait is also called a "super trait." + +```rust +trait Person { + fn name(self) -> String; +} + +// Person is a supertrait of Student. +// Implementing Student requires you to also impl Person. +trait Student: Person { + fn university(self) -> String; +} + +trait Programmer { + fn fav_language(self) -> String; +} + +// CompSciStudent (computer science student) is a subtrait of both Programmer +// and Student. Implementing CompSciStudent requires you to impl both supertraits. +trait CompSciStudent: Programmer + Student { + fn git_username(self) -> String; +} +``` + +### Trait Aliases + +Similar to the proposed Rust feature for [trait aliases](https://github.com/rust-lang/rust/blob/4d215e2426d52ca8d1af166d5f6b5e172afbff67/src/doc/unstable-book/src/language-features/trait-alias.md), +Noir supports aliasing one or more traits and using those aliases wherever +traits would normally be used. + +```rust +trait Foo { + fn foo(self) -> Self; +} + +trait Bar { + fn bar(self) -> Self; +} + +// Equivalent to: +// trait Baz: Foo + Bar {} +// +// impl Baz for T where T: Foo + Bar {} +trait Baz = Foo + Bar; + +// We can use `Baz` to refer to `Foo + Bar` +fn baz(x: T) -> T where T: Baz { + x.foo().bar() +} +``` + +#### Generic Trait Aliases + +Trait aliases can also be generic by placing the generic arguments after the +trait name. These generics are in scope of every item within the trait alias. + +```rust +trait Foo { + fn foo(self) -> Self; +} + +trait Bar { + fn bar(self) -> T; +} + +// Equivalent to: +// trait Baz: Foo + Bar {} +// +// impl Baz for U where U: Foo + Bar {} +trait Baz = Foo + Bar; +``` + +#### Trait Alias Where Clauses + +Trait aliases support where clauses to add trait constraints to any of their +generic arguments, e.g. ensuring `T: Baz` for a trait alias `Qux`. + +```rust +trait Foo { + fn foo(self) -> Self; +} + +trait Bar { + fn bar(self) -> T; +} + +trait Baz { + fn baz(self) -> bool; +} + +// Equivalent to: +// trait Qux: Foo + Bar where T: Baz {} +// +// impl Qux for U where +// U: Foo + Bar, +// T: Baz, +// {} +trait Qux = Foo + Bar where T: Baz; +``` + +Note that while trait aliases support where clauses, +the equivalent traits can fail due to [#6467](https://github.com/noir-lang/noir/issues/6467) + +### Visibility + +By default, like functions, traits and trait aliases are private to the module +they exist in. You can use `pub` to make the trait public or `pub(crate)` to make +it public to just its crate: + +```rust +// This trait is now public +pub trait Trait {} + +// This trait alias is now public +pub trait Baz = Foo + Bar; +``` + +Trait methods have the same visibility as the trait they are in. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/unconstrained.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/unconstrained.md new file mode 100644 index 000000000000..467253bed0d2 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/concepts/unconstrained.md @@ -0,0 +1,104 @@ +--- +title: Unconstrained Functions +description: "Learn about what unconstrained functions in Noir are, how to use them and when you'd want to." + +keywords: [Noir programming language, unconstrained, open] +sidebar_position: 5 +--- + +Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic computation. + +## Why? + +Zero-knowledge (ZK) domain-specific languages (DSL) enable developers to generate ZK proofs from their programs by compiling code down to the constraints of an NP complete language (such as R1CS or PLONKish languages). However, the hard bounds of a constraint system can be very limiting to the functionality of a ZK DSL. + +Enabling a circuit language to perform unconstrained execution is a powerful tool. Said another way, unconstrained execution lets developers generate witnesses from code that does not generate any constraints. Being able to execute logic outside of a circuit is critical for both circuit performance and constructing proofs on information that is external to a circuit. + +Fetching information from somewhere external to a circuit can also be used to enable developers to improve circuit efficiency. + +A ZK DSL does not just prove computation, but proves that some computation was handled correctly. Thus, it is necessary that when we switch from performing some operation directly inside of a circuit to inside of an unconstrained environment that the appropriate constraints are still laid down elsewhere in the circuit. + +## Example + +An in depth example might help drive the point home. This example comes from the excellent [post](https://discord.com/channels/1113924620781883405/1124022445054111926/1128747641853972590) by Tom in the Noir Discord. + +Let's look at how we can optimize a function to turn a `u72` into an array of `u8`s. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u72 & 0xff) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 91 +Backend circuit size: 3619 +``` + +A lot of the operations in this function are optimized away by the compiler (all the bit-shifts turn into divisions by constants). However we can save a bunch of gates by casting to u8 a bit earlier. This automatically truncates the bit-shifted value to fit in a u8 which allows us to remove the AND against 0xff. This saves us ~480 gates in total. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 75 +Backend circuit size: 3143 +``` + +Those are some nice savings already but we can do better. This code is all constrained so we're proving every step of calculating out using num, but we don't actually care about how we calculate this, just that it's correct. This is where brillig comes in. + +It turns out that truncating a u72 into a u8 is hard to do inside a snark, each time we do as u8 we lay down 4 ACIR opcodes which get converted into multiple gates. It's actually much easier to calculate num from out than the other way around. All we need to do is multiply each element of out by a constant and add them all together, both relatively easy operations inside a snark. + +We can then run `u72_to_u8` as unconstrained brillig code in order to calculate out, then use that result in our constrained function and assert that if we were to do the reverse calculation we'd get back num. This looks a little like the below: + +```rust +fn main(num: u72) -> pub [u8; 8] { + // Safety: 'out' is properly constrained below in 'assert(num == reconstructed_num);' + let out = unsafe { u72_to_u8(num) }; + + let mut reconstructed_num: u72 = 0; + for i in 0..8 { + reconstructed_num += (out[i] as u72 << (56 - (8 * i))); + } + assert(num == reconstructed_num); + out +} + +unconstrained fn u72_to_u8(num: u72) -> [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8))) as u8; + } + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 78 +Backend circuit size: 2902 +``` + +This ends up taking off another ~250 gates from our circuit! We've ended up with more ACIR opcodes than before but they're easier for the backend to prove (resulting in fewer gates). + +Note that in order to invoke unconstrained functions we need to wrap them in an `unsafe` block, +to make it clear that the call is unconstrained. +Furthermore, a warning is emitted unless the `unsafe` block is commented with a `// Safety: ...` comment explaining why it is fine to call the unconstrained function. Note that either the `unsafe` block can be commented this way or the statement it exists in (like in the `let` example above). + +Generally we want to use brillig whenever there's something that's easy to verify but hard to compute within the circuit. For example, if you wanted to calculate a square root of a number it'll be a much better idea to calculate this in brillig and then assert that if you square the result you get back your number. + +## Break and Continue + +In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow.md#break-and-continue) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/_category_.json new file mode 100644 index 000000000000..1debcfe76753 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Modules, Packages and Crates", + "position": 2, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/crates_and_packages.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/crates_and_packages.md new file mode 100644 index 000000000000..8881130be634 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/crates_and_packages.md @@ -0,0 +1,43 @@ +--- +title: Crates and Packages +description: Learn how to use Crates and Packages in your Noir project +keywords: [Nargo, dependencies, package management, crates, package] +sidebar_position: 0 +--- + +## Crates + +A crate is the smallest amount of code that the Noir compiler considers at a time. +Crates can contain modules, and the modules may be defined in other files that get compiled with the crate, as we’ll see in the coming sections. + +### Crate Types + +A Noir crate can come in several forms: binaries, libraries or contracts. + +#### Binaries + +_Binary crates_ are programs which you can compile to an ACIR circuit which you can then create proofs against. Each must have a function called `main` that defines the ACIR circuit which is to be proved. + +#### Libraries + +_Library crates_ don't have a `main` function and they don't compile down to ACIR. Instead they define functionality intended to be shared with multiple projects, and eventually included in a binary crate. + +#### Contracts + +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts). + +### Crate Root + +Every crate has a root, which is the source file that the compiler starts, this is also known as the root module. The Noir compiler does not enforce any conditions on the name of the file which is the crate root, however if you are compiling via Nargo the crate root must be called `lib.nr` or `main.nr` for library or binary crates respectively. + +## Packages + +A Nargo _package_ is a collection of one or more crates that provides a set of functionality. A package must include a Nargo.toml file. + +A package _must_ contain either a library or a binary crate, but not both. + +### Differences from Cargo Packages + +One notable difference between Rust's Cargo and Noir's Nargo is that while Cargo allows a package to contain an unlimited number of binary crates and a single library crate, Nargo currently only allows a package to contain a single crate. + +In future this restriction may be lifted to allow a Nargo package to contain both a binary and library crate or multiple binary crates. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/dependencies.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/dependencies.md new file mode 100644 index 000000000000..22186b225988 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/dependencies.md @@ -0,0 +1,122 @@ +--- +title: Dependencies +description: + Learn how to specify and manage dependencies in Nargo, allowing you to upload packages to GitHub + and use them easily in your project. +keywords: [Nargo, dependencies, GitHub, package management, versioning] +sidebar_position: 1 +--- + +Nargo allows you to upload packages to GitHub and use them as dependencies. + +## Specifying a dependency + +Specifying a dependency requires a tag to a specific commit and the git url to the url containing +the package. + +Currently, there are no requirements on the tag contents. If requirements are added, it would follow +semver 2.0 guidelines. + +> Note: Without a `tag` , there would be no versioning and dependencies would change each time you +> compile your project. + +For example, to add the [ecrecover-noir library](https://github.com/colinnielsen/ecrecover-noir) to your project, add it to `Nargo.toml`: + +```toml +# Nargo.toml + +[dependencies] +ecrecover = {tag = "v0.8.0", git = "https://github.com/colinnielsen/ecrecover-noir"} +``` + +If the module is in a subdirectory, you can define a subdirectory in your git repository, for example: + +```toml +# Nargo.toml + +[dependencies] +easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "noir-contracts/contracts/easy_private_token_contract"} +``` + +## Specifying a local dependency + +You can also specify dependencies that are local to your machine. + +For example, this file structure has a library and binary crate + +```tree +├── binary_crate +│   ├── Nargo.toml +│   └── src +│   └── main.nr +└── lib_a + ├── Nargo.toml + └── src + └── lib.nr +``` + +Inside of the binary crate, you can specify: + +```toml +# Nargo.toml + +[dependencies] +lib_a = { path = "../lib_a" } +``` + +## Importing dependencies + +You can import a dependency to a Noir file using the following syntax. For example, to import the +ecrecover-noir library and local lib_a referenced above: + +```rust +use ecrecover; +use lib_a; +``` + +You can also import only the specific parts of dependency that you want to use, like so: + +```rust +use std::hash::sha256; +use std::scalar_mul::fixed_base_embedded_curve; +``` + +Lastly, You can import multiple items in the same line by enclosing them in curly braces: + +```rust +use std::hash::{keccak256, sha256}; +``` + +We don't have a way to consume libraries from inside a [workspace](./workspaces.md) as external dependencies right now. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +## Dependencies of Dependencies + +Note that when you import a dependency, you also get access to all of the dependencies of that package. + +For example, the [phy_vector](https://github.com/resurgencelabs/phy_vector) library imports an [fraction](https://github.com/resurgencelabs/fraction) library. If you're importing the phy_vector library, then you can access the functions in fractions library like so: + +```rust +use phy_vector; + +fn main(x : Field, y : pub Field) { + //... + let f = phy_vector::fraction::toFraction(true, 2, 1); + //... +} +``` + +## Available Libraries + +Noir does not currently have an official package manager. You can find a list of available Noir libraries in the [awesome-noir repo here](https://github.com/noir-lang/awesome-noir#libraries). + +Some libraries that are available today include: + +- [Standard Library](https://github.com/noir-lang/noir/tree/master/noir_stdlib) - the Noir Standard Library +- [Ethereum Storage Proof Verification](https://github.com/aragonzkresearch/noir-trie-proofs) - a library that contains the primitives necessary for RLP decoding (in the form of look-up table construction) and Ethereum state and storage proof verification (or verification of any trie proof involving 32-byte long keys) +- [BigInt](https://github.com/shuklaayush/noir-bigint) - a library that provides a custom BigUint56 data type, allowing for computations on large unsigned integers +- [ECrecover](https://github.com/colinnielsen/ecrecover-noir/tree/main) - a library to verify an ECDSA signature and return the source Ethereum address +- [Sparse Merkle Tree Verifier](https://github.com/vocdoni/smtverifier-noir/tree/main) - a library for verification of sparse Merkle trees +- [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir +- [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/modules.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/modules.md new file mode 100644 index 000000000000..14aa1f0579ac --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/modules.md @@ -0,0 +1,221 @@ +--- +title: Modules +description: + Learn how to organize your files using modules in Noir, following the same convention as Rust's + module system. Examples included. +keywords: [Noir, Rust, modules, organizing files, sub-modules] +sidebar_position: 2 +--- + +Noir's module system follows the same convention as the _newer_ version of Rust's module system. + +## Purpose of Modules + +Modules are used to organize files. Without modules all of your code would need to live in a single +file. In Noir, the compiler does not automatically scan all of your files to detect modules. This +must be done explicitly by the developer. + +## Examples + +### Importing a module in the crate root + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::hello_world(); +} +``` + +Filename : `src/foo.nr` + +```rust +fn from_foo() {} +``` + +In the above snippet, the crate root is the `src/main.nr` file. The compiler sees the module +declaration `mod foo` which prompts it to look for a foo.nr file. + +Visually this module hierarchy looks like the following : + +``` +crate + ├── main + │ + └── foo + └── from_foo + +``` + +The module filename may also be the name of the module as a directory with the contents in a +file named `mod.nr` within that directory. The above example can alternatively be expressed like this: + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::hello_world(); +} +``` + +Filename : `src/foo/mod.nr` + +```rust +fn from_foo() {} +``` + +Note that it's an error to have both files `src/foo.nr` and `src/foo/mod.nr` in the filesystem. + +### Importing a module throughout the tree + +All modules are accessible from the `crate::` namespace. + +``` +crate + ├── bar + ├── foo + └── main + +``` + +In the above snippet, if `bar` would like to use functions in `foo`, it can do so by `use crate::foo::function_name`. + +### Sub-modules + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::from_foo(); +} +``` + +Filename : `src/foo.nr` + +```rust +mod bar; +fn from_foo() {} +``` + +Filename : `src/foo/bar.nr` + +```rust +fn from_bar() {} +``` + +In the above snippet, we have added an extra module to the module tree; `bar`. `bar` is a submodule +of `foo` hence we declare bar in `foo.nr` with `mod bar`. Since `foo` is not the crate root, the +compiler looks for the file associated with the `bar` module in `src/foo/bar.nr` + +Visually the module hierarchy looks as follows: + +``` +crate + ├── main + │ + └── foo + ├── from_foo + └── bar + └── from_bar +``` + +Similar to importing a module in the crate root, modules can be placed in a `mod.nr` file, like this: + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::from_foo(); +} +``` + +Filename : `src/foo/mod.nr` + +```rust +mod bar; +fn from_foo() {} +``` + +Filename : `src/foo/bar/mod.nr` + +```rust +fn from_bar() {} +``` + +### Referencing a parent module + +Given a submodule, you can refer to its parent module using the `super` keyword. + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::from_foo(); +} +``` + +Filename : `src/foo.nr` + +```rust +mod bar; + +fn from_foo() {} +``` + +Filename : `src/foo/bar.nr` + +```rust +// Same as bar::from_foo +use super::from_foo; + +fn from_bar() { + from_foo(); // invokes super::from_foo(), which is bar::from_foo() + super::from_foo(); // also invokes bar::from_foo() +} +``` + +### `use` visibility + +`use` declarations are private to the containing module, by default. However, like functions, +they can be marked as `pub` or `pub(crate)`. Such a use declaration serves to _re-export_ a name. +A public `use` declaration can therefore redirect some public name to a different target definition: +even a definition with a private canonical path, inside a different module. + +An example of re-exporting: + +```rust +mod some_module { + pub use foo::{bar, baz}; + mod foo { + pub fn bar() {} + pub fn baz() {} + } +} + +fn main() { + some_module::bar(); + some_module::baz(); +} +``` + +In this example, the module `some_module` re-exports two public names defined in `foo`. + +### Visibility + +By default, like functions, modules are private to the module (or crate) they exist in. You can use `pub` +to make the module public or `pub(crate)` to make it public to just its crate: + +```rust +// This module is now public and can be seen by other crates. +pub mod foo; +``` \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/workspaces.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/workspaces.md new file mode 100644 index 000000000000..05213be0222e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/modules_packages_crates/workspaces.md @@ -0,0 +1,46 @@ +--- +title: Workspaces +sidebar_position: 3 +--- + +Workspaces are a feature of nargo that allow you to manage multiple related Noir packages in a single repository. A workspace is essentially a group of related projects that share common build output directories and configurations. + +Each Noir project (with its own Nargo.toml file) can be thought of as a package. Each package is expected to contain exactly one "named circuit", being the "name" defined in Nargo.toml with the program logic defined in `./src/main.nr`. + +For a project with the following structure: + +```tree +├── crates +│ ├── a +│ │ ├── Nargo.toml +│ │ └── Prover.toml +│ │ └── src +│ │ └── main.nr +│ └── b +│ ├── Nargo.toml +│ └── Prover.toml +│ └── src +│ └── main.nr +│ +└── Nargo.toml +``` + +You can define a workspace in Nargo.toml like so: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +`members` indicates which packages are included in the workspace. As such, all member packages of a workspace will be processed when the `--workspace` flag is used with various commands or if a `default-member` is not specified. The `--package` option can be used to limit +the scope of some commands to a specific member of the workspace; otherwise these commands run on the package nearest on the path to the +current directory where `nargo` was invoked. + +`default-member` indicates which package various commands process by default. + +Libraries can be defined in a workspace. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +Please note that nesting regular packages is not supported: certain commands work on the workspace level and will use the topmost Nargo.toml file they can find on the path; unless this is a workspace configuration with `members`, the command might run on some unintended package. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/_category_.json new file mode 100644 index 000000000000..af04c0933fdb --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Standard Library", + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/black_box_fns.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/black_box_fns.md new file mode 100644 index 000000000000..e9392b20a92f --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/black_box_fns.md @@ -0,0 +1,31 @@ +--- +title: Black Box Functions +description: Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. +keywords: [noir, black box functions] +--- + +Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. This makes certain zk-snark unfriendly computations cheaper than if they were implemented in Noir. + +The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not. + +## Function list + +Here is a list of the current black box functions: + +- [AES128](./cryptographic_primitives/ciphers.mdx#aes128) +- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) +- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) +- [Blake3](./cryptographic_primitives/hashes.mdx#blake3) +- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) +- [Embedded curve operations (MSM, addition, ...)](./cryptographic_primitives/embedded_curve_ops.mdx) +- AND +- XOR +- RANGE +- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) +- [Recursive proof verification](./recursion.mdx) + +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. + +You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/bn254.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/bn254.md new file mode 100644 index 000000000000..3294f005dbb4 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/bn254.md @@ -0,0 +1,46 @@ +--- +title: Bn254 Field Library +--- + +Noir provides a module in standard library with some optimized functions for bn254 Fr in `std::field::bn254`. + +## decompose + +```rust +fn decompose(x: Field) -> (Field, Field) {} +``` + +Decomposes a single field into two fields, low and high. The low field contains the lower 16 bytes of the input field and the high field contains the upper 16 bytes of the input field. Both field results are range checked to 128 bits. + + +## assert_gt + +```rust +fn assert_gt(a: Field, b: Field) {} +``` + +Asserts that a > b. This will generate less constraints than using `assert(gt(a, b))`. + +## assert_lt + +```rust +fn assert_lt(a: Field, b: Field) {} +``` + +Asserts that a < b. This will generate less constraints than using `assert(lt(a, b))`. + +## gt + +```rust +fn gt(a: Field, b: Field) -> bool {} +``` + +Returns true if a > b. + +## lt + +```rust +fn lt(a: Field, b: Field) -> bool {} +``` + +Returns true if a < b. \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/boundedvec.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/boundedvec.md new file mode 100644 index 000000000000..ba75e45564ab --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/boundedvec.md @@ -0,0 +1,479 @@ +--- +title: Bounded Vectors +keywords: [noir, vector, bounded vector, slice] +sidebar_position: 1 +--- + +A `BoundedVec` is a growable storage similar to a `Vec` except that it +is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented +via slices and thus is not subject to the same restrictions slices are (notably, nested +slices - and thus nested vectors as well - are disallowed). + +Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by +pushing an additional element is also more efficient - the length only needs to be increased +by one. + +For these reasons `BoundedVec` should generally be preferred over `Vec` when there +is a reasonable maximum bound that can be placed on the vector. + +Example: + +```rust +let mut vector: BoundedVec = BoundedVec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +assert(vector.max_len() == 10); +``` + +## Methods + +### new + +```rust +pub fn new() -> Self +``` + +Creates a new, empty vector of length zero. + +Since this container is backed by an array internally, it still needs an initial value +to give each element. To resolve this, each element is zeroed internally. This value +is guaranteed to be inaccessible unless `get_unchecked` is used. + +Example: + +```rust +let empty_vector: BoundedVec = BoundedVec::new(); +assert(empty_vector.len() == 0); +``` + +Note that whenever calling `new` the maximum length of the vector should always be specified +via a type signature: + +```rust title="new_example" showLineNumbers +fn good() -> BoundedVec { + // Ok! MaxLen is specified with a type annotation + let v1: BoundedVec = BoundedVec::new(); + let v2 = BoundedVec::new(); + + // Ok! MaxLen is known from the type of `good`'s return value + v2 +} + +fn bad() { + // Error: Type annotation needed + // The compiler can't infer `MaxLen` from this code. + let mut v3 = BoundedVec::new(); + v3.push(5); +} +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L11-L27 + + +This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions +but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a constraint failure at runtime when the vec is pushed to. + +### get + +```rust +pub fn get(self, index: u64) -> T { +``` + +Retrieves an element from the vector at the given index, starting from zero. + +If the given index is equal to or greater than the length of the vector, this +will issue a constraint failure. + +Example: + +```rust +fn foo(v: BoundedVec) { + let first = v.get(0); + let last = v.get(v.len() - 1); + assert(first != last); +} +``` + +### get_unchecked + +```rust +pub fn get_unchecked(self, index: u64) -> T { +``` + +Retrieves an element from the vector at the given index, starting from zero, without +performing a bounds check. + +Since this function does not perform a bounds check on length before accessing the element, +it is unsafe! Use at your own risk! + +Example: + +```rust title="get_unchecked_example" showLineNumbers +fn sum_of_first_three(v: BoundedVec) -> u32 { + // Always ensure the length is larger than the largest + // index passed to get_unchecked + assert(v.len() > 2); + let first = v.get_unchecked(0); + let second = v.get_unchecked(1); + let third = v.get_unchecked(2); + first + second + third +} +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L54-L64 + + +### set + +```rust +pub fn set(&mut self: Self, index: u64, value: T) { +``` + +Writes an element to the vector at the given index, starting from zero. + +If the given index is equal to or greater than the length of the vector, this will issue a constraint failure. + +Example: + +```rust +fn foo(v: BoundedVec) { + let first = v.get(0); + assert(first != 42); + v.set(0, 42); + let new_first = v.get(0); + assert(new_first == 42); +} +``` + +### set_unchecked + +```rust +pub fn set_unchecked(&mut self: Self, index: u64, value: T) -> T { +``` + +Writes an element to the vector at the given index, starting from zero, without performing a bounds check. + +Since this function does not perform a bounds check on length before accessing the element, it is unsafe! Use at your own risk! + +Example: + +```rust title="set_unchecked_example" showLineNumbers +fn set_unchecked_example() { + let mut vec: BoundedVec = BoundedVec::new(); + vec.extend_from_array([1, 2]); + + // Here we're safely writing within the valid range of `vec` + // `vec` now has the value [42, 2] + vec.set_unchecked(0, 42); + + // We can then safely read this value back out of `vec`. + // Notice that we use the checked version of `get` which would prevent reading unsafe values. + assert_eq(vec.get(0), 42); + + // We've now written past the end of `vec`. + // As this index is still within the maximum potential length of `v`, + // it won't cause a constraint failure. + vec.set_unchecked(2, 42); + println(vec); + + // This will write past the end of the maximum potential length of `vec`, + // it will then trigger a constraint failure. + vec.set_unchecked(5, 42); + println(vec); +} +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L67-L91 + + + +### push + +```rust +pub fn push(&mut self, elem: T) { +``` + +Pushes an element to the end of the vector. This increases the length +of the vector by one. + +Panics if the new length of the vector will be greater than the max length. + +Example: + +```rust title="bounded-vec-push-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + v.push(1); + v.push(2); + + // Panics with failed assertion "push out of bounds" + v.push(3); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L95-L103 + + +### pop + +```rust +pub fn pop(&mut self) -> T +``` + +Pops the element at the end of the vector. This will decrease the length +of the vector by one. + +Panics if the vector is empty. + +Example: + +```rust title="bounded-vec-pop-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + v.push(1); + v.push(2); + + let two = v.pop(); + let one = v.pop(); + + assert(two == 2); + assert(one == 1); + // error: cannot pop from an empty vector + // let _ = v.pop(); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L108-L120 + + +### len + +```rust +pub fn len(self) -> u64 { +``` + +Returns the current length of this vector + +Example: + +```rust title="bounded-vec-len-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + assert(v.len() == 0); + + v.push(100); + assert(v.len() == 1); + + v.push(200); + v.push(300); + v.push(400); + assert(v.len() == 4); + + let _ = v.pop(); + let _ = v.pop(); + assert(v.len() == 2); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L125-L140 + + +### max_len + +```rust +pub fn max_len(_self: BoundedVec) -> u64 { +``` + +Returns the maximum length of this vector. This is always +equal to the `MaxLen` parameter this vector was initialized with. + +Example: + +```rust title="bounded-vec-max-len-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + assert(v.max_len() == 5); + v.push(10); + assert(v.max_len() == 5); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L145-L151 + + +### storage + +```rust +pub fn storage(self) -> [T; MaxLen] { +``` + +Returns the internal array within this vector. +Since arrays in Noir are immutable, mutating the returned storage array will not mutate +the storage held internally by this vector. + +Note that uninitialized elements may be zeroed out! + +Example: + +```rust title="bounded-vec-storage-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + assert(v.storage() == [0, 0, 0, 0, 0]); + + v.push(57); + assert(v.storage() == [57, 0, 0, 0, 0]); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L156-L163 + + +### extend_from_array + +```rust +pub fn extend_from_array(&mut self, array: [T; Len]) +``` + +Pushes each element from the given array to this vector. + +Panics if pushing each element would cause the length of this vector +to exceed the maximum length. + +Example: + +```rust title="bounded-vec-extend-from-array-example" showLineNumbers +let mut vec: BoundedVec = BoundedVec::new(); + vec.extend_from_array([2, 4]); + + assert(vec.len == 2); + assert(vec.get(0) == 2); + assert(vec.get(1) == 4); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L168-L175 + + +### extend_from_bounded_vec + +```rust +pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) +``` + +Pushes each element from the other vector to this vector. The length of +the other vector is left unchanged. + +Panics if pushing each element would cause the length of this vector +to exceed the maximum length. + +Example: + +```rust title="bounded-vec-extend-from-bounded-vec-example" showLineNumbers +let mut v1: BoundedVec = BoundedVec::new(); + let mut v2: BoundedVec = BoundedVec::new(); + + v2.extend_from_array([1, 2, 3]); + v1.extend_from_bounded_vec(v2); + + assert(v1.storage() == [1, 2, 3, 0, 0]); + assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L180-L189 + + +### from_array + +```rust +pub fn from_array(array: [T; Len]) -> Self +``` + +Creates a new vector, populating it with values derived from an array input. +The maximum length of the vector is determined based on the type signature. + +Example: +```rust +let bounded_vec: BoundedVec = BoundedVec::from_array([1, 2, 3]) +``` + +### from_parts + +```rust +pub fn from_parts(mut array: [T; MaxLen], len: u32) -> Self +``` + +Creates a new BoundedVec from the given array and length. +The given length must be less than or equal to the length of the array. + +This function will zero out any elements at or past index `len` of `array`. +This incurs an extra runtime cost of O(MaxLen). If you are sure your array is +zeroed after that index, you can use `from_parts_unchecked` to remove the extra loop. + +Example: + +```rust title="from-parts" showLineNumbers +let vec: BoundedVec = BoundedVec::from_parts([1, 2, 3, 0], 3); + assert_eq(vec.len(), 3); + + // Any elements past the given length are zeroed out, so these + // two BoundedVecs will be completely equal + let vec1: BoundedVec = BoundedVec::from_parts([1, 2, 3, 1], 3); + let vec2: BoundedVec = BoundedVec::from_parts([1, 2, 3, 2], 3); + assert_eq(vec1, vec2); +``` +> Source code: noir_stdlib/src/collections/bounded_vec.nr#L693-L702 + + +### from_parts_unchecked + +```rust +pub fn from_parts_unchecked(array: [T; MaxLen], len: u32) -> Self +``` + +Creates a new BoundedVec from the given array and length. +The given length must be less than or equal to the length of the array. + +This function is unsafe because it expects all elements past the `len` index +of `array` to be zeroed, but does not check for this internally. Use `from_parts` +for a safe version of this function which does zero out any indices past the +given length. Invalidating this assumption can notably cause `BoundedVec::eq` +to give incorrect results since it will check even elements past `len`. + +Example: + +```rust title="from-parts-unchecked" showLineNumbers +let vec: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 0], 3); + assert_eq(vec.len(), 3); + + // invalid use! + let vec1: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 1], 3); + let vec2: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 2], 3); + + // both vecs have length 3 so we'd expect them to be equal, but this + // fails because elements past the length are still checked in eq + assert(vec1 != vec2); +``` +> Source code: noir_stdlib/src/collections/bounded_vec.nr#L707-L718 + + +### map + +```rust +pub fn map(self, f: fn[Env](T) -> U) -> BoundedVec +``` + +Creates a new vector of equal size by calling a closure on each element in this vector. + +Example: + +```rust title="bounded-vec-map-example" showLineNumbers +let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]); + let result = vec.map(|value| value * 2); +``` +> Source code: noir_stdlib/src/collections/bounded_vec.nr#L580-L583 + + +### any + +```rust +pub fn any(self, predicate: fn[Env](T) -> bool) -> bool +``` + +Returns true if the given predicate returns true for any element +in this vector. + +Example: + +```rust title="bounded-vec-any-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + v.extend_from_array([2, 4, 6]); + + let all_even = !v.any(|elem: u32| elem % 2 != 0); + assert(all_even); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L256-L262 + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/hashmap.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/hashmap.md new file mode 100644 index 000000000000..810baad16ba0 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/hashmap.md @@ -0,0 +1,587 @@ +--- +title: HashMap +keywords: [noir, map, hash, hashmap] +sidebar_position: 1 +--- + +`HashMap` is used to efficiently store and look up key-value pairs. + +`HashMap` is a bounded type which can store anywhere from zero to `MaxLen` total elements. +Note that due to hash collisions, the actual maximum number of elements stored by any particular +hashmap is likely lower than `MaxLen`. This is true even with cryptographic hash functions since +every hash value will be performed modulo `MaxLen`. + +Example: + +```rust +// Create a mapping from Fields to u32s with a maximum length of 12 +// using a poseidon2 hasher +use std::hash::poseidon2::Poseidon2Hasher; +let mut map: HashMap> = HashMap::default(); + +map.insert(1, 2); +map.insert(3, 4); + +let two = map.get(1).unwrap(); +``` + +## Methods + +### default + +```rust title="default" showLineNumbers +impl Default for HashMap +where + B: BuildHasher + Default, + H: Hasher + Default, +{ + /// Constructs an empty HashMap. + /// + /// Example: + /// + /// ```noir + /// let hashmap: HashMap> = HashMap::default(); + /// assert(hashmap.is_empty()); + /// ``` + fn default() -> Self { +``` +> Source code: noir_stdlib/src/collections/map.nr#L688-L703 + + +Creates a fresh, empty HashMap. + +When using this function, always make sure to specify the maximum size of the hash map. + +This is the same `default` from the `Default` implementation given further below. It is +repeated here for convenience since it is the recommended way to create a hashmap. + +Example: + +```rust title="default_example" showLineNumbers +let hashmap: HashMap> = HashMap::default(); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L208-L211 + + +Because `HashMap` has so many generic arguments that are likely to be the same throughout +your program, it may be helpful to create a type alias: + +```rust title="type_alias" showLineNumbers +type MyMap = HashMap>; +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L202-L204 + + +### with_hasher + +```rust title="with_hasher" showLineNumbers +pub fn with_hasher(_build_hasher: B) -> Self + where + B: BuildHasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L103-L108 + + +Creates a hashmap with an existing `BuildHasher`. This can be used to ensure multiple +hashmaps are created with the same hasher instance. + +Example: + +```rust title="with_hasher_example" showLineNumbers +let my_hasher: BuildHasherDefault = Default::default(); + let hashmap: HashMap> = + HashMap::with_hasher(my_hasher); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L212-L217 + + +### get + +```rust title="get" showLineNumbers +pub fn get(self, key: K) -> Option + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L472-L479 + + +Retrieves a value from the hashmap, returning `Option::none()` if it was not found. + +Example: + +```rust title="get_example" showLineNumbers +fn get_example(map: HashMap>) { + let x = map.get(12); + + if x.is_some() { + assert(x.unwrap() == 42); + } +} +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L297-L305 + + +### insert + +```rust title="insert" showLineNumbers +pub fn insert(&mut self, key: K, value: V) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L514-L521 + + +Inserts a new key-value pair into the map. If the key was already in the map, its +previous value will be overridden with the newly provided one. + +Example: + +```rust title="insert_example" showLineNumbers +let mut map: HashMap> = HashMap::default(); + map.insert(12, 42); + assert(map.len() == 1); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L218-L222 + + +### remove + +```rust title="remove" showLineNumbers +pub fn remove(&mut self, key: K) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L570-L577 + + +Removes the given key-value pair from the map. If the key was not already present +in the map, this does nothing. + +Example: + +```rust title="remove_example" showLineNumbers +map.remove(12); + assert(map.is_empty()); + + // If a key was not present in the map, remove does nothing + map.remove(12); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L225-L232 + + +### is_empty + +```rust title="is_empty" showLineNumbers +pub fn is_empty(self) -> bool { +``` +> Source code: noir_stdlib/src/collections/map.nr#L167-L169 + + +True if the length of the hash map is empty. + +Example: + +```rust title="is_empty_example" showLineNumbers +assert(map.is_empty()); + + map.insert(1, 2); + assert(!map.is_empty()); + + map.remove(1); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L233-L241 + + +### len + +```rust title="len" showLineNumbers +pub fn len(self) -> u32 { +``` +> Source code: noir_stdlib/src/collections/map.nr#L431-L433 + + +Returns the current length of this hash map. + +Example: + +```rust title="len_example" showLineNumbers +// This is equivalent to checking map.is_empty() + assert(map.len() == 0); + + map.insert(1, 2); + map.insert(3, 4); + map.insert(5, 6); + assert(map.len() == 3); + + // 3 was already present as a key in the hash map, so the length is unchanged + map.insert(3, 7); + assert(map.len() == 3); + + map.remove(1); + assert(map.len() == 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L242-L257 + + +### capacity + +```rust title="capacity" showLineNumbers +pub fn capacity(_self: Self) -> u32 { +``` +> Source code: noir_stdlib/src/collections/map.nr#L453-L455 + + +Returns the maximum capacity of this hashmap. This is always equal to the capacity +specified in the hashmap's type. + +Unlike hashmaps in general purpose programming languages, hashmaps in Noir have a +static capacity that does not increase as the map grows larger. Thus, this capacity +is also the maximum possible element count that can be inserted into the hashmap. +Due to hash collisions (modulo the hashmap length), it is likely the actual maximum +element count will be lower than the full capacity. + +Example: + +```rust title="capacity_example" showLineNumbers +let empty_map: HashMap> = + HashMap::default(); + assert(empty_map.len() == 0); + assert(empty_map.capacity() == 42); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L258-L263 + + +### clear + +```rust title="clear" showLineNumbers +pub fn clear(&mut self) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L123-L125 + + +Clears the hashmap, removing all key-value pairs from it. + +Example: + +```rust title="clear_example" showLineNumbers +assert(!map.is_empty()); + map.clear(); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L264-L268 + + +### contains_key + +```rust title="contains_key" showLineNumbers +pub fn contains_key(self, key: K) -> bool + where + K: Hash + Eq, + B: BuildHasher, + H: Hasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L143-L150 + + +True if the hashmap contains the given key. Unlike `get`, this will not also return +the value associated with the key. + +Example: + +```rust title="contains_key_example" showLineNumbers +if map.contains_key(7) { + let value = map.get(7); + assert(value.is_some()); + } else { + println("No value for key 7!"); + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L269-L276 + + +### entries + +```rust title="entries" showLineNumbers +pub fn entries(self) -> BoundedVec<(K, V), N> { +``` +> Source code: noir_stdlib/src/collections/map.nr#L191-L193 + + +Returns a vector of each key-value pair present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="entries_example" showLineNumbers +let entries = map.entries(); + + // The length of a hashmap may not be compile-time known, so we + // need to loop over its capacity instead + for i in 0..map.capacity() { + if i < entries.len() { + let (key, value) = entries.get(i); + println(f"{key} -> {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L308-L319 + + +### keys + +```rust title="keys" showLineNumbers +pub fn keys(self) -> BoundedVec { +``` +> Source code: noir_stdlib/src/collections/map.nr#L230-L232 + + +Returns a vector of each key present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="keys_example" showLineNumbers +let keys = map.keys(); + + for i in 0..keys.max_len() { + if i < keys.len() { + let key = keys.get_unchecked(i); + let value = map.get(key).unwrap_unchecked(); + println(f"{key} -> {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L320-L330 + + +### values + +```rust title="values" showLineNumbers +pub fn values(self) -> BoundedVec { +``` +> Source code: noir_stdlib/src/collections/map.nr#L267-L269 + + +Returns a vector of each value present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="values_example" showLineNumbers +let values = map.values(); + + for i in 0..values.max_len() { + if i < values.len() { + let value = values.get_unchecked(i); + println(f"Found value {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L331-L340 + + +### iter_mut + +```rust title="iter_mut" showLineNumbers +pub fn iter_mut(&mut self, f: fn(K, V) -> (K, V)) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L304-L311 + + +Iterates through each key-value pair of the HashMap, setting each key-value pair to the +result returned from the given function. + +Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated +through. If this is not desired, use `iter_values_mut` if only values need to be mutated, +or `entries` if neither keys nor values need to be mutated. + +The iteration order is left unspecified. As a result, if two keys are mutated to become +equal, which of the two values that will be present for the key in the resulting map is also unspecified. + +Example: + +```rust title="iter_mut_example" showLineNumbers +// Add 1 to each key in the map, and double the value associated with that key. + map.iter_mut(|k, v| (k + 1, v * 2)); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L344-L347 + + +### iter_keys_mut + +```rust title="iter_keys_mut" showLineNumbers +pub fn iter_keys_mut(&mut self, f: fn(K) -> K) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher, + { +``` +> Source code: noir_stdlib/src/collections/map.nr#L342-L349 + + +Iterates through the HashMap, mutating each key to the result returned from +the given function. + +Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated +through. If only iteration is desired and the keys are not intended to be mutated, +prefer using `entries` instead. + +The iteration order is left unspecified. As a result, if two keys are mutated to become +equal, which of the two values that will be present for the key in the resulting map is also unspecified. + +Example: + +```rust title="iter_keys_mut_example" showLineNumbers +// Double each key, leaving the value associated with that key untouched + map.iter_keys_mut(|k| k * 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L348-L351 + + +### iter_values_mut + +```rust title="iter_values_mut" showLineNumbers +pub fn iter_values_mut(&mut self, f: fn(V) -> V) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L374-L376 + + +Iterates through the HashMap, applying the given function to each value and mutating the +value to equal the result. This function is more efficient than `iter_mut` and `iter_keys_mut` +because the keys are untouched and the underlying hashmap thus does not need to be reordered. + +Example: + +```rust title="iter_values_mut_example" showLineNumbers +// Halve each value + map.iter_values_mut(|v| v / 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L352-L355 + + +### retain + +```rust title="retain" showLineNumbers +pub fn retain(&mut self, f: fn(K, V) -> bool) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L395-L397 + + +Retains only the key-value pairs for which the given function returns true. +Any key-value pairs for which the function returns false will be removed from the map. + +Example: + +```rust title="retain_example" showLineNumbers +map.retain(|k, v| (k != 0) & (v != 0)); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L280-L282 + + +## Trait Implementations + +### default + +```rust title="default" showLineNumbers +impl Default for HashMap +where + B: BuildHasher + Default, + H: Hasher + Default, +{ + /// Constructs an empty HashMap. + /// + /// Example: + /// + /// ```noir + /// let hashmap: HashMap> = HashMap::default(); + /// assert(hashmap.is_empty()); + /// ``` + fn default() -> Self { +``` +> Source code: noir_stdlib/src/collections/map.nr#L688-L703 + + +Constructs an empty HashMap. + +Example: + +```rust title="default_example" showLineNumbers +let hashmap: HashMap> = HashMap::default(); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L208-L211 + + +### eq + +```rust title="eq" showLineNumbers +impl Eq for HashMap +where + K: Eq + Hash, + V: Eq, + B: BuildHasher, + H: Hasher, +{ + /// Checks if two HashMaps are equal. + /// + /// Example: + /// + /// ```noir + /// let mut map1: HashMap> = HashMap::default(); + /// let mut map2: HashMap> = HashMap::default(); + /// + /// map1.insert(1, 2); + /// map1.insert(3, 4); + /// + /// map2.insert(3, 4); + /// map2.insert(1, 2); + /// + /// assert(map1 == map2); + /// ``` + fn eq(self, other: HashMap) -> bool { +``` +> Source code: noir_stdlib/src/collections/map.nr#L636-L661 + + +Checks if two HashMaps are equal. + +Example: + +```rust title="eq_example" showLineNumbers +let mut map1: HashMap> = HashMap::default(); + let mut map2: HashMap> = HashMap::default(); + + map1.insert(1, 2); + map1.insert(3, 4); + + map2.insert(3, 4); + map2.insert(1, 2); + + assert(map1 == map2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L283-L294 + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/index.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/index.md new file mode 100644 index 000000000000..ea84c6d5c21e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/index.md @@ -0,0 +1,5 @@ +--- +title: Containers +description: Container types provided by Noir's standard library for storing and retrieving data +keywords: [containers, data types, vec, hashmap] +--- diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/vec.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/vec.mdx new file mode 100644 index 000000000000..475011922f81 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/containers/vec.mdx @@ -0,0 +1,170 @@ +--- +title: Vectors +description: Delve into the Vec data type in Noir. Learn about its methods, practical examples, and best practices for using Vectors in your Noir code. +keywords: [noir, vector type, methods, examples, dynamic arrays] +sidebar_position: 6 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A vector is a collection type similar to Rust's `Vec` type. In Noir, it is a convenient way to use slices as mutable arrays. + +Example: + +```rust +let mut vector: Vec = Vec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +``` + +## Methods + +### new + +Creates a new, empty vector. + +```rust +pub fn new() -> Self +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` + +### from_slice + +Creates a vector containing each element from a given slice. Mutations to the resulting vector will not affect the original slice. + +```rust +pub fn from_slice(slice: [T]) -> Self +``` + +Example: + +```rust +let slice: [Field] = &[1, 2, 3]; +let vector_from_slice = Vec::from_slice(slice); +assert(vector_from_slice.len() == 3); +``` + +### len + +Returns the number of elements in the vector. + +```rust +pub fn len(self) -> Field +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` + +### get + +Retrieves an element from the vector at a given index. Panics if the index points beyond the vector's end. + +```rust +pub fn get(self, index: Field) -> T +``` + +Example: + +```rust +let vector: Vec = Vec::from_slice(&[10, 20, 30]); +assert(vector.get(1) == 20); +``` + +### set + +```rust +pub fn set(&mut self: Self, index: u64, value: T) { +``` + +Writes an element to the vector at the given index, starting from zero. + +Panics if the index points beyond the vector's end. + +Example: + +```rust +let vector: Vec = Vec::from_slice(&[10, 20, 30]); +assert(vector.get(1) == 20); +vector.set(1, 42); +assert(vector.get(1) == 42); +``` + +### push + +Adds a new element to the vector's end, returning a new vector with a length one greater than the original unmodified vector. + +```rust +pub fn push(&mut self, elem: T) +``` + +Example: + +```rust +let mut vector: Vec = Vec::new(); +vector.push(10); +assert(vector.len() == 1); +``` + +### pop + +Removes an element from the vector's end, returning a new vector with a length one less than the original vector, along with the removed element. Panics if the vector's length is zero. + +```rust +pub fn pop(&mut self) -> T +``` + +Example: + +```rust +let mut vector = Vec::from_slice(&[10, 20]); +let popped_elem = vector.pop(); +assert(popped_elem == 20); +assert(vector.len() == 1); +``` + +### insert + +Inserts an element at a specified index, shifting subsequent elements to the right. + +```rust +pub fn insert(&mut self, index: Field, elem: T) +``` + +Example: + +```rust +let mut vector = Vec::from_slice(&[10, 30]); +vector.insert(1, 20); +assert(vector.get(1) == 20); +``` + +### remove + +Removes an element at a specified index, shifting subsequent elements to the left, and returns the removed element. + +```rust +pub fn remove(&mut self, index: Field) -> T +``` + +Example: + +```rust +let mut vector = Vec::from_slice(&[10, 20, 30]); +let removed_elem = vector.remove(1); +assert(removed_elem == 20); +assert(vector.len() == 2); +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/_category_.json new file mode 100644 index 000000000000..5d694210bbf3 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ciphers.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ciphers.mdx new file mode 100644 index 000000000000..9f82890a92a2 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ciphers.mdx @@ -0,0 +1,36 @@ +--- +title: Ciphers +description: + Learn about the implemented ciphers ready to use for any Noir project +keywords: + [ciphers, Noir project, aes128, encrypt] +sidebar_position: 0 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; + +## aes128 + +Given a plaintext as an array of bytes, returns the corresponding aes128 ciphertext (CBC mode). Input padding is automatically performed using PKCS#7, so that the output length is `input.len() + (16 - input.len() % 16)`. + +```rust title="aes128" showLineNumbers +pub fn aes128_encrypt( + input: [u8; N], + iv: [u8; 16], + key: [u8; 16], +) -> [u8; N + 16 - N % 16] {} +``` +> Source code: noir_stdlib/src/aes128.nr#L2-L8 + + +```rust +fn main() { + let input: [u8; 4] = [0, 12, 3, 15] // Random bytes, will be padded to 16 bytes. + let iv: [u8; 16] = [0; 16]; // Initialisation vector + let key: [u8; 16] = [0; 16] // AES key + let ciphertext = std::aes128::aes128_encrypt(inputs.as_bytes(), iv.as_bytes(), key.as_bytes()); // In this case, the output length will be 16 bytes. +} +``` + + + \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx new file mode 100644 index 000000000000..8d96027b42cb --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx @@ -0,0 +1,98 @@ +--- +title: ECDSA Signature Verification +description: Learn about the cryptographic primitives regarding ECDSA over the secp256k1 and secp256r1 curves +keywords: [cryptographic primitives, Noir project, ecdsa, secp256k1, secp256r1, signatures] +sidebar_position: 3 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; + +Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. + +## ecdsa_secp256k1::verify_signature + +Verifier for ECDSA Secp256k1 signatures. +See ecdsa_secp256k1::verify_signature_slice for a version that accepts slices directly. + +```rust title="ecdsa_secp256k1" showLineNumbers +pub fn verify_signature( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N], +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 + + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + + + +## ecdsa_secp256k1::verify_signature_slice + +Verifier for ECDSA Secp256k1 signatures where the message is a slice. + +```rust title="ecdsa_secp256k1_slice" showLineNumbers +pub fn verify_signature_slice( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8], +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L13-L20 + + + + +## ecdsa_secp256r1::verify_signature + +Verifier for ECDSA Secp256r1 signatures. +See ecdsa_secp256r1::verify_signature_slice for a version that accepts slices directly. + +```rust title="ecdsa_secp256r1" showLineNumbers +pub fn verify_signature( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N], +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 + + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256r1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + + + +## ecdsa_secp256r1::verify_signature + +Verifier for ECDSA Secp256r1 signatures where the message is a slice. + +```rust title="ecdsa_secp256r1_slice" showLineNumbers +pub fn verify_signature_slice( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8], +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L13-L20 + + + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx new file mode 100644 index 000000000000..482a36932b94 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/embedded_curve_ops.mdx @@ -0,0 +1,95 @@ +--- +title: Scalar multiplication +description: See how you can perform scalar multiplication in Noir +keywords: [cryptographic primitives, Noir project, scalar multiplication] +sidebar_position: 1 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; + +The following functions perform operations over the embedded curve whose coordinates are defined by the configured noir field. +For the BN254 scalar field, this is BabyJubJub or Grumpkin. + +:::note +Suffixes `_low` and `_high` denote low and high limbs of a scalar. +::: + +## embedded_curve_ops::multi_scalar_mul + +Performs multi scalar multiplication over the embedded curve. +The function accepts arbitrary amount of point-scalar pairs on the input, it multiplies the individual pairs over +the curve and returns a sum of the resulting points. + +Points represented as x and y coordinates [x1, y1, x2, y2, ...], scalars as low and high limbs [low1, high1, low2, high2, ...]. + +```rust title="multi_scalar_mul" showLineNumbers +pub fn multi_scalar_mul( + points: [EmbeddedCurvePoint; N], + scalars: [EmbeddedCurveScalar; N], +) -> EmbeddedCurvePoint +``` +> Source code: noir_stdlib/src/embedded_curve_ops.nr#L103-L108 + + +example + +```rust +fn main(point_x: Field, point_y: Field, scalar_low: Field, scalar_high: Field) { + let point = std::embedded_curve_ops::multi_scalar_mul([point_x, point_y], [scalar_low, scalar_high]); + println(point); +} +``` + +## embedded_curve_ops::fixed_base_scalar_mul + +Performs fixed base scalar multiplication over the embedded curve (multiplies input scalar with a generator point). +The function accepts a single scalar on the input represented as 2 fields. + +```rust title="fixed_base_scalar_mul" showLineNumbers +pub fn fixed_base_scalar_mul(scalar: EmbeddedCurveScalar) -> EmbeddedCurvePoint +``` +> Source code: noir_stdlib/src/embedded_curve_ops.nr#L120-L122 + + +example + +```rust +fn main(scalar_low: Field, scalar_high: Field) { + let point = std::embedded_curve_ops::fixed_base_scalar_mul(scalar_low, scalar_high); + println(point); +} +``` + +## embedded_curve_ops::embedded_curve_add + +Adds two points on the embedded curve. +This function takes two `EmbeddedCurvePoint` structures as parameters, representing points on the curve, and returns a new `EmbeddedCurvePoint` structure that represents their sum. + +### Parameters: +- `point1` (`EmbeddedCurvePoint`): The first point to add. +- `point2` (`EmbeddedCurvePoint`): The second point to add. + +### Returns: +- `EmbeddedCurvePoint`: The resulting point after the addition of `point1` and `point2`. + +```rust title="embedded_curve_add" showLineNumbers +pub fn embedded_curve_add( + point1: EmbeddedCurvePoint, + point2: EmbeddedCurvePoint, +) -> EmbeddedCurvePoint { +``` +> Source code: noir_stdlib/src/embedded_curve_ops.nr#L136-L141 + + +example + +```rust +fn main() { + let point1 = EmbeddedCurvePoint { x: 1, y: 2 }; + let point2 = EmbeddedCurvePoint { x: 3, y: 4 }; + let result = std::embedded_curve_ops::embedded_curve_add(point1, point2); + println!("Resulting Point: ({}, {})", result.x, result.y); +} +``` + + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/hashes.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/hashes.mdx new file mode 100644 index 000000000000..039ca6b3d494 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -0,0 +1,228 @@ +--- +title: Hash methods +description: + Learn about the cryptographic primitives ready to use for any Noir project, including sha256, + blake2s and pedersen +keywords: + [cryptographic primitives, Noir project, sha256, blake2s, pedersen, hash] +sidebar_position: 0 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; + +## sha256 + +Given an array of bytes, returns the resulting sha256 hash. +Specify a message_size to hash only the first `message_size` bytes of the input. + +```rust title="sha256" showLineNumbers +#[deprecated("sha256 is being deprecated from the stdlib, use https://github.com/noir-lang/sha256 instead")] +pub fn sha256(input: [u8; N]) -> HASH +``` +> Source code: noir_stdlib/src/hash/sha256.nr#L46-L49 + + +example: +```rust title="sha256_var" showLineNumbers +let digest = std::hash::sha256_var([x as u8], 1); +``` +> Source code: test_programs/execution_success/sha256/src/main.nr#L15-L17 + + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::sha256::sha256_var(x, 4); +} +``` + + + + +## blake2s + +Given an array of bytes, returns an array with the Blake2 hash + +```rust title="blake2s" showLineNumbers +pub fn blake2s(input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash/mod.nr#L18-L20 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake2s(x); +} +``` + + + +## blake3 + +Given an array of bytes, returns an array with the Blake3 hash + +```rust title="blake3" showLineNumbers +pub fn blake3(input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash/mod.nr#L24-L26 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake3(x); +} +``` + + + +## pedersen_hash + +Given an array of Fields, returns the Pedersen hash. + +```rust title="pedersen_hash" showLineNumbers +pub fn pedersen_hash(input: [Field; N]) -> Field +``` +> Source code: noir_stdlib/src/hash/mod.nr#L49-L51 + + +example: + +```rust title="pedersen-hash" showLineNumbers +fn main(x: Field, y: Field, expected_hash: Field) { + let hash = std::hash::pedersen_hash([x, y]); + assert_eq(hash, expected_hash); +} +``` +> Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L6 + + + + +## pedersen_commitment + +Given an array of Fields, returns the Pedersen commitment. + +```rust title="pedersen_commitment" showLineNumbers +pub fn pedersen_commitment(input: [Field; N]) -> EmbeddedCurvePoint { +``` +> Source code: noir_stdlib/src/hash/mod.nr#L29-L31 + + +example: + +```rust title="pedersen-commitment" showLineNumbers +fn main(x: Field, y: Field, expected_commitment: std::embedded_curve_ops::EmbeddedCurvePoint) { + let commitment = std::hash::pedersen_commitment([x, y]); + assert_eq(commitment.x, expected_commitment.x); + assert_eq(commitment.y, expected_commitment.y); +} +``` +> Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L7 + + + + +## keccak256 + +Given an array of bytes (`u8`), returns the resulting keccak hash as an array of +32 bytes (`[u8; 32]`). Specify a message_size to hash only the first +`message_size` bytes of the input. + +```rust title="keccak256" showLineNumbers +pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash/mod.nr#L117-L119 + + +example: + +```rust title="keccak256" showLineNumbers +fn main(x: Field, result: [u8; 32]) { + // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field + // The padding is taken care of by the program + let digest = std::hash::keccak256([x as u8], 1); + assert(digest == result); + + //#1399: variable message size + let message_size = 4; + let hash_a = std::hash::keccak256([1, 2, 3, 4], message_size); + let hash_b = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); + + assert(hash_a == hash_b); + + let message_size_big = 8; + let hash_c = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); + + assert(hash_a != hash_c); +} +``` +> Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L20 + + + + +## poseidon + +Given an array of Fields, returns a new Field with the Poseidon Hash. Mind that you need to specify +how many inputs are there to your Poseidon function. + +```rust +// example for hash_1, hash_2 accepts an array of length 2, etc +fn hash_1(input: [Field; 1]) -> Field +``` + +example: + +```rust title="poseidon" showLineNumbers +use std::hash::poseidon; + +fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field) { + let hash1 = poseidon::bn254::hash_2(x1); + assert(hash1 == y1); + + let hash2 = poseidon::bn254::hash_4(x2); + assert(hash2 == y2); +} +``` +> Source code: test_programs/execution_success/poseidon_bn254_hash/src/main.nr#L1-L11 + + +## poseidon 2 + +Given an array of Fields, returns a new Field with the Poseidon2 Hash. Contrary to the Poseidon +function, there is only one hash and you can specify a message_size to hash only the first +`message_size` bytes of the input, + +```rust +// example for hashing the first three elements of the input +Poseidon2::hash(input, 3); +``` + +example: + +```rust title="poseidon2" showLineNumbers +use std::hash::poseidon2; + +fn main(inputs: [Field; 4], expected_hash: Field) { + let hash = poseidon2::Poseidon2::hash(inputs, inputs.len()); + assert_eq(hash, expected_hash); +} +``` +> Source code: test_programs/execution_success/poseidon2/src/main.nr#L1-L8 + + +## hash_to_field + +```rust +fn hash_to_field(_input : [Field]) -> Field {} +``` + +Calculates the `blake2s` hash of the inputs and returns the hash modulo the field modulus to return +a value which can be represented as a `Field`. + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/index.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/index.md new file mode 100644 index 000000000000..650f30165d56 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/cryptographic_primitives/index.md @@ -0,0 +1,14 @@ +--- +title: Cryptographic Primitives +description: + Learn about the cryptographic primitives ready to use for any Noir project +keywords: + [ + cryptographic primitives, + Noir project, + ] +--- + +The Noir team is progressively adding new cryptographic primitives to the standard library. Reach out for news or if you would be interested in adding more of these calculations in Noir. + +Some methods are available thanks to the Aztec backend, not being performed using Noir. When using other backends, these methods may or may not be supplied. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/fmtstr.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/fmtstr.md new file mode 100644 index 000000000000..19809d60261d --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/fmtstr.md @@ -0,0 +1,17 @@ +--- +title: fmtstr +--- + +`fmtstr` is the type resulting from using format string (`f"..."`). + +## Methods + +### quoted_contents + +```rust title="quoted_contents" showLineNumbers +pub comptime fn quoted_contents(self) -> Quoted {} +``` +> Source code: noir_stdlib/src/meta/format_string.nr#L3-L5 + + +Returns the format string contents (that is, without the leading and trailing double quotes) as a `Quoted` value. \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/is_unconstrained.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/is_unconstrained.md new file mode 100644 index 000000000000..34b0698552b7 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/is_unconstrained.md @@ -0,0 +1,69 @@ +--- +title: Is Unconstrained Function +description: + The is_unconstrained function returns whether the context at that point of the program is unconstrained or not. +keywords: + [ + unconstrained + ] +--- + +It's very common for functions in circuits to take unconstrained hints of an expensive computation and then verify it. This is done by running the hint in an unconstrained context and then verifying the result in a constrained context. + +When a function is marked as unconstrained, any subsequent functions that it calls will also be run in an unconstrained context. However, if we are implementing a library function, other users might call it within an unconstrained context or a constrained one. Generally, in an unconstrained context we prefer just computing the result instead of taking a hint of it and verifying it, since that'd mean doing the same computation twice: + +```rust + +fn my_expensive_computation(){ + ... +} + +unconstrained fn my_expensive_computation_hint(){ + my_expensive_computation() +} + +pub fn external_interface(){ + my_expensive_computation_hint(); + // verify my_expensive_computation: If external_interface is called from unconstrained, this is redundant + ... +} + +``` + +In order to improve the performance in an unconstrained context you can use the function at `std::runtime::is_unconstrained() -> bool`: + + +```rust +use dep::std::runtime::is_unconstrained; + +fn my_expensive_computation(){ + ... +} + +unconstrained fn my_expensive_computation_hint(){ + my_expensive_computation() +} + +pub fn external_interface(){ + if is_unconstrained() { + my_expensive_computation(); + } else { + my_expensive_computation_hint(); + // verify my_expensive_computation + ... + } +} + +``` + +The is_unconstrained result is resolved at compile time, so in unconstrained contexts the compiler removes the else branch, and in constrained contexts the compiler removes the if branch, reducing the amount of compute necessary to run external_interface. + +Note that using `is_unconstrained` in a `comptime` context will also return `true`: + +``` +fn main() { + comptime { + assert(is_unconstrained()); + } +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/logging.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/logging.md new file mode 100644 index 000000000000..db75ef9f86fa --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/logging.md @@ -0,0 +1,78 @@ +--- +title: Logging +description: + Learn how to use the println statement for debugging in Noir with this tutorial. Understand the + basics of logging in Noir and how to implement it in your code. +keywords: + [ + noir logging, + println statement, + print statement, + debugging in noir, + noir std library, + logging tutorial, + basic logging in noir, + noir logging implementation, + noir debugging techniques, + rust, + ] +--- + +The standard library provides two familiar statements you can use: `println` and `print`. Despite being a limited implementation of rust's `println!` and `print!` macros, these constructs can be useful for debugging. + +You can print the output of both statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are print statements in your tests). + +It is recommended to use `nargo execute` if you want to debug failing constraints with `println` or `print` statements. This is due to every input in a test being a constant rather than a witness, so we issue an error during compilation while we only print during execution (which comes after compilation). Neither `println`, nor `print` are callable for failed constraints caught at compile time. + +Both `print` and `println` are generic functions which can work on integers, fields, strings, and even structs or expressions. Note however, that slices are currently unsupported. For example: + +```rust +struct Person { + age: Field, + height: Field, +} + +fn main(age: Field, height: Field) { + let person = Person { + age: age, + height: height, + }; + println(person); + println(age + height); + println("Hello world!"); +} +``` + +You can print different types in the same statement (including strings) with a type called `fmtstr`. It can be specified in the same way as a normal string, just prepended with an "f" character: + +```rust + let fmt_str = f"i: {i}, j: {j}"; + println(fmt_str); + + let s = myStruct { y: x, x: y }; + println(s); + + println(f"i: {i}, s: {s}"); + + println(x); + println([x, y]); + + let foo = fooStruct { my_struct: s, foo: 15 }; + println(f"s: {s}, foo: {foo}"); + + println(15); // prints 0x0f, implicit Field + println(-1 as u8); // prints 255 + println(-1 as i8); // prints -1 +``` + +Examples shown above are interchangeable between the two `print` statements: + +```rust +let person = Person { age : age, height : height }; + +println(person); +print(person); + +println("Hello world!"); // Prints with a newline at the end of the input +print("Hello world!"); // Prints the input and keeps cursor on the same line +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/mem.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/mem.md new file mode 100644 index 000000000000..1e9102b32dc3 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/mem.md @@ -0,0 +1,82 @@ +--- +title: Memory Module +description: + This module contains functions which manipulate memory in a low-level way +keywords: + [ + mem, memory, zeroed, transmute, checked_transmute + ] +--- + +# `std::mem::zeroed` + +```rust +fn zeroed() -> T +``` + +Returns a zeroed value of any type. +This function is generally unsafe to use as the zeroed bit pattern is not guaranteed to be valid for all types. +It can however, be useful in cases when the value is guaranteed not to be used such as in a BoundedVec library implementing a growable vector, up to a certain length, backed by an array. +The array can be initialized with zeroed values which are guaranteed to be inaccessible until the vector is pushed to. +Similarly, enumerations in noir can be implemented using this method by providing zeroed values for the unused variants. + +This function currently supports the following types: + +- Field +- Bool +- Uint +- Array +- Slice +- String +- Tuple +- Functions + +Using it on other types could result in unexpected behavior. + +# `std::mem::checked_transmute` + +```rust +fn checked_transmute(value: T) -> U +``` + +Transmutes a value of one type into the same value but with a new type `U`. + +This function is safe to use since both types are asserted to be equal later during compilation after the concrete values for generic types become known. +This function is useful for cases where the compiler may fail a type check that is expected to pass where +a user knows the two types to be equal. For example, when using arithmetic generics there are cases the compiler +does not see as equal, such as `[Field; N*(A + B)]` and `[Field; N*A + N*B]`, which users may know to be equal. +In these cases, `checked_transmute` can be used to cast the value to the desired type while also preserving safety +by checking this equality once `N`, `A`, `B` are fully resolved. + +Note that since this safety check is performed after type checking rather than during, no error is issued if the function +containing `checked_transmute` is never called. + +# `std::mem::array_refcount` + +```rust +fn array_refcount(array: [T; N]) -> u32 {} +``` + +Returns the internal reference count of an array value in unconstrained code. + +Arrays only have reference count in unconstrained code - using this anywhere +else will return zero. + +This function is mostly intended for debugging compiler optimizations but can also be used +to find where array copies may be happening in unconstrained code by placing it before array +mutations. + +# `std::mem::slice_refcount` + +```rust +fn slice_refcount(slice: [T]) -> u32 {} +``` + +Returns the internal reference count of a slice value in unconstrained code. + +Slices only have reference count in unconstrained code - using this anywhere +else will return zero. + +This function is mostly intended for debugging compiler optimizations but can also be used +to find where slice copies may be happening in unconstrained code by placing it before slice +mutations. diff --git a/noir/noir-repo/docs/docs/noir/standard_library/merkle_trees.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/merkle_trees.md similarity index 100% rename from noir/noir-repo/docs/docs/noir/standard_library/merkle_trees.md rename to noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/merkle_trees.md diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/ctstring.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/ctstring.md new file mode 100644 index 000000000000..fe33486487ef --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/ctstring.md @@ -0,0 +1,100 @@ +--- +title: CtString +--- + +`std::meta::ctstring` contains methods on the built-in `CtString` type which is +a compile-time, dynamically-sized string type. Compared to `str` and `fmtstr`, +`CtString` is useful because its size does not need to be specified in its type. This +can be used for formatting items at compile-time or general string handling in `comptime` +code. + +Since `fmtstr`s can be converted into `CtString`s, you can make use of their formatting +abilities in CtStrings by formatting in `fmtstr`s then converting the result to a CtString +afterward. + +## Traits + +### AsCtString + +```rust title="as-ctstring" showLineNumbers +pub trait AsCtString { + comptime fn as_ctstring(self) -> CtString; +} +``` +> Source code: noir_stdlib/src/meta/ctstring.nr#L44-L48 + + +Converts an object into a compile-time string. + +Implementations: + +```rust +impl AsCtString for str { ... } +impl AsCtString for fmtstr { ... } +``` + +## Methods + +### new + +```rust title="new" showLineNumbers +pub comptime fn new() -> Self { +``` +> Source code: noir_stdlib/src/meta/ctstring.nr#L4-L6 + + +Creates an empty `CtString`. + +### append_str + +```rust title="append_str" showLineNumbers +pub comptime fn append_str(self, s: str) -> Self { +``` +> Source code: noir_stdlib/src/meta/ctstring.nr#L12-L14 + + +Returns a new CtString with the given str appended onto the end. + +### append_fmtstr + +```rust title="append_fmtstr" showLineNumbers +pub comptime fn append_fmtstr(self, s: fmtstr) -> Self { +``` +> Source code: noir_stdlib/src/meta/ctstring.nr#L18-L20 + + +Returns a new CtString with the given fmtstr appended onto the end. + +### as_quoted_str + +```rust title="as_quoted_str" showLineNumbers +pub comptime fn as_quoted_str(self) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/ctstring.nr#L27-L29 + + +Returns a quoted string literal from this string's contents. + +There is no direct conversion from a `CtString` to a `str` since +the size would not be known. To get around this, this function can +be used in combination with macro insertion (`!`) to insert this string +literal at this function's call site. + +Example: + +```rust title="as_quoted_str_example" showLineNumbers +let my_ctstring = "foo bar".as_ctstring(); + let my_str = my_ctstring.as_quoted_str!(); + + assert_eq(crate::meta::type_of(my_str), quote { str<7> }.as_type()); +``` +> Source code: noir_stdlib/src/meta/ctstring.nr#L95-L100 + + +## Trait Implementations + +```rust +impl Eq for CtString +impl Hash for CtString +impl Append for CtString +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/expr.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/expr.md new file mode 100644 index 000000000000..b6d395c6700c --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/expr.md @@ -0,0 +1,380 @@ +--- +title: Expr +--- + +`std::meta::expr` contains methods on the built-in `Expr` type for quoted, syntactically valid expressions. + +## Methods + +### as_array + +```rust title="as_array" showLineNumbers +pub comptime fn as_array(self) -> Option<[Expr]> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L10-L12 + + +If this expression is an array, this returns a slice of each element in the array. + +### as_assert + +```rust title="as_assert" showLineNumbers +pub comptime fn as_assert(self) -> Option<(Expr, Option)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L16-L18 + + +If this expression is an assert, this returns the assert expression and the optional message. + +### as_assert_eq + +```rust title="as_assert_eq" showLineNumbers +pub comptime fn as_assert_eq(self) -> Option<(Expr, Expr, Option)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L23-L25 + + +If this expression is an assert_eq, this returns the left-hand-side and right-hand-side +expressions, together with the optional message. + +### as_assign + +```rust title="as_assign" showLineNumbers +pub comptime fn as_assign(self) -> Option<(Expr, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L30-L32 + + +If this expression is an assignment, this returns a tuple with the left hand side +and right hand side in order. + +### as_binary_op + +```rust title="as_binary_op" showLineNumbers +pub comptime fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L37-L39 + + +If this expression is a binary operator operation ` `, +return the left-hand side, operator, and the right-hand side of the operation. + +### as_block + +```rust title="as_block" showLineNumbers +pub comptime fn as_block(self) -> Option<[Expr]> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L44-L46 + + +If this expression is a block `{ stmt1; stmt2; ...; stmtN }`, return +a slice containing each statement. + +### as_bool + +```rust title="as_bool" showLineNumbers +pub comptime fn as_bool(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L50-L52 + + +If this expression is a boolean literal, return that literal. + +### as_cast + +```rust title="as_cast" showLineNumbers +#[builtin(expr_as_cast)] + pub comptime fn as_cast(self) -> Option<(Expr, UnresolvedType)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L56-L59 + + +If this expression is a cast expression (`expr as type`), returns the casted +expression and the type to cast to. + +### as_comptime + +```rust title="as_comptime" showLineNumbers +pub comptime fn as_comptime(self) -> Option<[Expr]> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L64-L66 + + +If this expression is a `comptime { stmt1; stmt2; ...; stmtN }` block, +return each statement in the block. + +### as_constructor + +```rust title="as_constructor" showLineNumbers +pub comptime fn as_constructor(self) -> Option<(UnresolvedType, [(Quoted, Expr)])> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L71-L73 + + +If this expression is a constructor `Type { field1: expr1, ..., fieldN: exprN }`, +return the type and the fields. + +### as_for + +```rust title="as_for" showLineNumbers +pub comptime fn as_for(self) -> Option<(Quoted, Expr, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L78-L80 + + +If this expression is a for statement over a single expression, return the identifier, +the expression and the for loop body. + +### as_for_range + +```rust title="as_for" showLineNumbers +pub comptime fn as_for(self) -> Option<(Quoted, Expr, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L78-L80 + + +If this expression is a for statement over a range, return the identifier, +the range start, the range end and the for loop body. + +### as_function_call + +```rust title="as_function_call" showLineNumbers +pub comptime fn as_function_call(self) -> Option<(Expr, [Expr])> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L92-L94 + + +If this expression is a function call `foo(arg1, ..., argN)`, return +the function and a slice of each argument. + +### as_if + +```rust title="as_if" showLineNumbers +pub comptime fn as_if(self) -> Option<(Expr, Expr, Option)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L100-L102 + + +If this expression is an `if condition { then_branch } else { else_branch }`, +return the condition, then branch, and else branch. If there is no else branch, +`None` is returned for that branch instead. + +### as_index + +```rust title="as_index" showLineNumbers +pub comptime fn as_index(self) -> Option<(Expr, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L107-L109 + + +If this expression is an index into an array `array[index]`, return the +array and the index. + +### as_integer + +```rust title="as_integer" showLineNumbers +pub comptime fn as_integer(self) -> Option<(Field, bool)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L114-L116 + + +If this expression is an integer literal, return the integer as a field +as well as whether the integer is negative (true) or not (false). + +### as_lambda + +```rust title="as_lambda" showLineNumbers +pub comptime fn as_lambda( + self, + ) -> Option<([(Expr, Option)], Option, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L120-L124 + + +If this expression is a lambda, returns the parameters, return type and body. + +### as_let + +```rust title="as_let" showLineNumbers +pub comptime fn as_let(self) -> Option<(Expr, Option, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L129-L131 + + +If this expression is a let statement, returns the let pattern as an `Expr`, +the optional type annotation, and the assigned expression. + +### as_member_access + +```rust title="as_member_access" showLineNumbers +pub comptime fn as_member_access(self) -> Option<(Expr, Quoted)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L136-L138 + + +If this expression is a member access `foo.bar`, return the struct/tuple +expression and the field. The field will be represented as a quoted value. + +### as_method_call + +```rust title="as_method_call" showLineNumbers +pub comptime fn as_method_call(self) -> Option<(Expr, Quoted, [UnresolvedType], [Expr])> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L143-L145 + + +If this expression is a method call `foo.bar::(arg1, ..., argN)`, return +the receiver, method name, a slice of each generic argument, and a slice of each argument. + +### as_repeated_element_array + +```rust title="as_repeated_element_array" showLineNumbers +pub comptime fn as_repeated_element_array(self) -> Option<(Expr, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L150-L152 + + +If this expression is a repeated element array `[elem; length]`, return +the repeated element and the length expressions. + +### as_repeated_element_slice + +```rust title="as_repeated_element_slice" showLineNumbers +pub comptime fn as_repeated_element_slice(self) -> Option<(Expr, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L157-L159 + + +If this expression is a repeated element slice `[elem; length]`, return +the repeated element and the length expressions. + +### as_slice + +```rust title="as_slice" showLineNumbers +pub comptime fn as_slice(self) -> Option<[Expr]> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L164-L166 + + +If this expression is a slice literal `&[elem1, ..., elemN]`, +return each element of the slice. + +### as_tuple + +```rust title="as_tuple" showLineNumbers +pub comptime fn as_tuple(self) -> Option<[Expr]> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L171-L173 + + +If this expression is a tuple `(field1, ..., fieldN)`, +return each element of the tuple. + +### as_unary_op + +```rust title="as_unary_op" showLineNumbers +pub comptime fn as_unary_op(self) -> Option<(UnaryOp, Expr)> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L178-L180 + + +If this expression is a unary operation ` `, +return the unary operator as well as the right-hand side expression. + +### as_unsafe + +```rust title="as_unsafe" showLineNumbers +pub comptime fn as_unsafe(self) -> Option<[Expr]> {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L185-L187 + + +If this expression is an `unsafe { stmt1; ...; stmtN }` block, +return each statement inside in a slice. + +### has_semicolon + +```rust title="has_semicolon" showLineNumbers +pub comptime fn has_semicolon(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L206-L208 + + +`true` if this expression is trailed by a semicolon. E.g. + +``` +comptime { + let expr1 = quote { 1 + 2 }.as_expr().unwrap(); + let expr2 = quote { 1 + 2; }.as_expr().unwrap(); + + assert(expr1.as_binary_op().is_some()); + assert(expr2.as_binary_op().is_some()); + + assert(!expr1.has_semicolon()); + assert(expr2.has_semicolon()); +} +``` + +### is_break + +```rust title="is_break" showLineNumbers +pub comptime fn is_break(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L212-L214 + + +`true` if this expression is `break`. + +### is_continue + +```rust title="is_continue" showLineNumbers +pub comptime fn is_continue(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L218-L220 + + +`true` if this expression is `continue`. + +### modify + +```rust title="modify" showLineNumbers +pub comptime fn modify(self, f: fn[Env](Expr) -> Option) -> Expr { +``` +> Source code: noir_stdlib/src/meta/expr.nr#L229-L231 + + +Applies a mapping function to this expression and to all of its sub-expressions. +`f` will be applied to each sub-expression first, then applied to the expression itself. + +This happens recursively for every expression within `self`. + +For example, calling `modify` on `(&[1], &[2, 3])` with an `f` that returns `Option::some` +for expressions that are integers, doubling them, would return `(&[2], &[4, 6])`. + +### quoted + +```rust title="quoted" showLineNumbers +pub comptime fn quoted(self) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/expr.nr#L266-L268 + + +Returns this expression as a `Quoted` value. It's the same as `quote { $self }`. + +### resolve + +```rust title="resolve" showLineNumbers +pub comptime fn resolve(self, in_function: Option) -> TypedExpr {} +``` +> Source code: noir_stdlib/src/meta/expr.nr#L282-L284 + + +Resolves and type-checks this expression and returns the result as a `TypedExpr`. + +The `in_function` argument specifies where the expression is resolved: +- If it's `none`, the expression is resolved in the function where `resolve` was called +- If it's `some`, the expression is resolved in the given function + +If any names used by this expression are not in scope or if there are any type errors, +this will give compiler errors as if the expression was written directly into +the current `comptime` function. \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/function_def.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/function_def.md new file mode 100644 index 000000000000..7c9615e6ab55 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/function_def.md @@ -0,0 +1,181 @@ +--- +title: FunctionDefinition +--- + +`std::meta::function_def` contains methods on the built-in `FunctionDefinition` type representing +a function definition in the source program. + +## Methods + +### add_attribute + +```rust title="add_attribute" showLineNumbers +pub comptime fn add_attribute(self, attribute: str) {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L3-L5 + + +Adds an attribute to the function. This is only valid +on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +### as_typed_expr + +```rust title="as_typed_expr" showLineNumbers +pub comptime fn as_typed_expr(self) -> TypedExpr {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L8-L10 + + +Returns this function as a `TypedExpr`, which can be unquoted. For example: + +```rust +let typed_expr = some_function.as_typed_expr(); +let _ = quote { $typed_expr(1, 2, 3); }; +``` + +### body + +```rust title="body" showLineNumbers +pub comptime fn body(self) -> Expr {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L13-L15 + + +Returns the body of the function as an expression. This is only valid +on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +### has_named_attribute + +```rust title="has_named_attribute" showLineNumbers +pub comptime fn has_named_attribute(self, name: str) -> bool {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L18-L20 + + +Returns true if this function has a custom attribute with the given name. + +### is_unconstrained + +```rust title="is_unconstrained" showLineNumbers +pub comptime fn is_unconstrained(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L23-L25 + + +Returns true if this function is unconstrained. + +### module + +```rust title="module" showLineNumbers +pub comptime fn module(self) -> Module {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L28-L30 + + +Returns the module where the function is defined. + +### name + +```rust title="name" showLineNumbers +pub comptime fn name(self) -> Quoted {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L33-L35 + + +Returns the name of the function. + +### parameters + +```rust title="parameters" showLineNumbers +pub comptime fn parameters(self) -> [(Quoted, Type)] {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L38-L40 + + +Returns each parameter of the function as a tuple of (parameter pattern, parameter type). + +### return_type + +```rust title="return_type" showLineNumbers +pub comptime fn return_type(self) -> Type {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L43-L45 + + +The return type of the function. + +### set_body + +```rust title="set_body" showLineNumbers +pub comptime fn set_body(self, body: Expr) {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L48-L50 + + +Mutate the function body to a new expression. This is only valid +on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +### set_parameters + +```rust title="set_parameters" showLineNumbers +pub comptime fn set_parameters(self, parameters: [(Quoted, Type)]) {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L53-L55 + + +Mutates the function's parameters to a new set of parameters. This is only valid +on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +Expects a slice of (parameter pattern, parameter type) for each parameter. Requires +each parameter pattern to be a syntactically valid parameter. + +### set_return_type + +```rust title="set_return_type" showLineNumbers +pub comptime fn set_return_type(self, return_type: Type) {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L58-L60 + + +Mutates the function's return type to a new type. This is only valid +on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +### set_return_public + +```rust title="set_return_public" showLineNumbers +pub comptime fn set_return_public(self, public: bool) {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L63-L65 + + +Mutates the function's return visibility to public (if `true` is given) or private (if `false` is given). +This is only valid on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +### set_unconstrained + +```rust title="set_unconstrained" showLineNumbers +pub comptime fn set_unconstrained(self, value: bool) {} +``` +> Source code: noir_stdlib/src/meta/function_def.nr#L71-L73 + + +Mutates the function to be unconstrained (if `true` is given) or not (if `false` is given). +This is only valid on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +## Trait Implementations + +```rust +impl Eq for FunctionDefinition +impl Hash for FunctionDefinition +``` + +Note that each function is assigned a unique ID internally and this is what is used for +equality and hashing. So even functions with identical signatures and bodies may not +be equal in this sense if they were originally different items in the source program. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/index.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/index.md new file mode 100644 index 000000000000..1d94d4a0374e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/index.md @@ -0,0 +1,224 @@ +--- +title: Metaprogramming +description: Noir's Metaprogramming API +keywords: [metaprogramming, comptime, macros, macro, quote, unquote] +--- + +`std::meta` is the entry point for Noir's metaprogramming API. This consists of `comptime` functions +and types used for inspecting and modifying Noir programs. + +## Functions + +### type_of + +```rust title="type_of" showLineNumbers +pub comptime fn type_of(x: T) -> Type {} +``` +> Source code: noir_stdlib/src/meta/mod.nr#L29-L31 + + +Returns the type of a variable at compile-time. + +Example: +```rust +comptime { + let x: i32 = 1; + let x_type: Type = std::meta::type_of(x); + + assert_eq(x_type, quote { i32 }.as_type()); +} +``` + +### unquote + +```rust title="unquote" showLineNumbers +pub comptime fn unquote(code: Quoted) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/mod.nr#L21-L23 + + +Unquotes the passed-in token stream where this function was called. + +Example: +```rust +comptime { + let code = quote { 1 + 2 }; + + // let x = 1 + 2; + let x = unquote!(code); +} +``` + +### derive + +```rust title="derive" showLineNumbers +#[varargs] +pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/mod.nr#L50-L53 + + +Attribute placed on struct definitions. + +Creates a trait impl for each trait passed in as an argument. +To do this, the trait must have a derive handler registered +with `derive_via` beforehand. The traits in the stdlib that +can be derived this way are `Eq`, `Ord`, `Default`, and `Hash`. + +Example: +```rust +#[derive(Eq, Default)] +struct Foo { + x: i32, + y: T, +} + +fn main() { + let foo1 = Foo::default(); + let foo2 = Foo { x: 0, y: &[0] }; + assert_eq(foo1, foo2); +} +``` + +### derive_via + +```rust title="derive_via_signature" showLineNumbers +pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { +``` +> Source code: noir_stdlib/src/meta/mod.nr#L70-L72 + + +Attribute placed on trait definitions. + +Registers a function to create impls for the given trait +when the trait is used in a `derive` call. Users may use +this to register their own functions to enable their traits +to be derived by `derive`. + +Because this function requires a function as an argument which +should produce a trait impl for any given struct, users may find +it helpful to use a function like `std::meta::make_trait_impl` to +help creating these impls. + +Example: +```rust +#[derive_via(derive_do_nothing)] +trait DoNothing { + fn do_nothing(self); +} + +comptime fn derive_do_nothing(s: StructDefinition) -> Quoted { + let typ = s.as_type(); + quote { + impl DoNothing for $typ { + fn do_nothing(self) { + println("Nothing"); + } + } + } +} +``` + +As another example, `derive_eq` in the stdlib is used to derive the `Eq` +trait for any struct. It makes use of `make_trait_impl` to do this: + +```rust title="derive_eq" showLineNumbers +comptime fn derive_eq(s: StructDefinition) -> Quoted { + let signature = quote { fn eq(_self: Self, _other: Self) -> bool }; + let for_each_field = |name| quote { (_self.$name == _other.$name) }; + let body = |fields| { + if s.fields_as_written().len() == 0 { + quote { true } + } else { + fields + } + }; + crate::meta::make_trait_impl( + s, + quote { Eq }, + signature, + for_each_field, + quote { & }, + body, + ) +} +``` +> Source code: noir_stdlib/src/cmp.nr#L10-L30 + + +### make_trait_impl + +```rust title="make_trait_impl" showLineNumbers +pub comptime fn make_trait_impl( + s: StructDefinition, + trait_name: Quoted, + function_signature: Quoted, + for_each_field: fn[Env1](Quoted) -> Quoted, + join_fields_with: Quoted, + body: fn[Env2](Quoted) -> Quoted, +) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/mod.nr#L89-L98 + + +A helper function to more easily create trait impls while deriving traits. + +Note that this function only works for traits which: +1. Have only one method +2. Have no generics on the trait itself. + - E.g. Using this on a trait such as `trait Foo { ... }` will result in the + generated impl incorrectly missing the `T` generic. + +If your trait fits these criteria then `make_trait_impl` is likely the easiest +way to write your derive handler. The arguments are as follows: + +- `s`: The struct to make the impl for +- `trait_name`: The name of the trait to derive. E.g. `quote { Eq }`. +- `function_signature`: The signature of the trait method to derive. E.g. `fn eq(self, other: Self) -> bool`. +- `for_each_field`: An operation to be performed on each field. E.g. `|name| quote { (self.$name == other.$name) }`. +- `join_fields_with`: A separator to join each result of `for_each_field` with. + E.g. `quote { & }`. You can also use an empty `quote {}` for no separator. +- `body`: The result of the field operations is passed into this function for any final processing. + This is the place to insert any setup/teardown code the trait requires. If the trait doesn't require + any such code, you can return the body as-is: `|body| body`. + +Example deriving `Hash`: + +```rust title="derive_hash" showLineNumbers +comptime fn derive_hash(s: StructDefinition) -> Quoted { + let name = quote { Hash }; + let signature = quote { fn hash(_self: Self, _state: &mut H) where H: std::hash::Hasher }; + let for_each_field = |name| quote { _self.$name.hash(_state); }; + crate::meta::make_trait_impl( + s, + name, + signature, + for_each_field, + quote {}, + |fields| fields, + ) +} +``` +> Source code: noir_stdlib/src/hash/mod.nr#L138-L152 + + +Example deriving `Ord`: + +```rust title="derive_ord" showLineNumbers +comptime fn derive_ord(s: StructDefinition) -> Quoted { + let signature = quote { fn cmp(_self: Self, _other: Self) -> std::cmp::Ordering }; + let for_each_field = |name| quote { + if result == std::cmp::Ordering::equal() { + result = _self.$name.cmp(_other.$name); + } + }; + let body = |fields| quote { + let mut result = std::cmp::Ordering::equal(); + $fields + result + }; + crate::meta::make_trait_impl(s, quote { Ord }, signature, for_each_field, quote {}, body) +} +``` +> Source code: noir_stdlib/src/cmp.nr#L221-L236 + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/module.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/module.md new file mode 100644 index 000000000000..f47231972b7f --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/module.md @@ -0,0 +1,82 @@ +--- +title: Module +--- + +`std::meta::module` contains methods on the built-in `Module` type which represents a module in the source program. +Note that this type represents a module generally, it isn't limited to only `mod my_submodule { ... }` +declarations in the source program. + +## Methods + +### add_item + +```rust title="add_item" showLineNumbers +pub comptime fn add_item(self, item: Quoted) {} +``` +> Source code: noir_stdlib/src/meta/module.nr#L3-L5 + + +Adds a top-level item (a function, a struct, a global, etc.) to the module. +Adding multiple items in one go is also valid if the `Quoted` value has multiple items in it. +Note that the items are type-checked as if they are inside the module they are being added to. + +### functions + +```rust title="functions" showLineNumbers +pub comptime fn functions(self) -> [FunctionDefinition] {} +``` +> Source code: noir_stdlib/src/meta/module.nr#L18-L20 + + +Returns each function defined in the module. + +### has_named_attribute + +```rust title="has_named_attribute" showLineNumbers +pub comptime fn has_named_attribute(self, name: str) -> bool {} +``` +> Source code: noir_stdlib/src/meta/module.nr#L8-L10 + + +Returns true if this module has a custom attribute with the given name. + +### is_contract + +```rust title="is_contract" showLineNumbers +pub comptime fn is_contract(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/module.nr#L13-L15 + + +`true` if this module is a contract module (was declared via `contract foo { ... }`). + +### name + +```rust title="name" showLineNumbers +pub comptime fn name(self) -> Quoted {} +``` +> Source code: noir_stdlib/src/meta/module.nr#L28-L30 + + +Returns the name of the module. + +### structs + +```rust title="structs" showLineNumbers +pub comptime fn structs(self) -> [StructDefinition] {} +``` +> Source code: noir_stdlib/src/meta/module.nr#L23-L25 + + +Returns each struct defined in the module. + +## Trait Implementations + +```rust +impl Eq for Module +impl Hash for Module +``` + +Note that each module is assigned a unique ID internally and this is what is used for +equality and hashing. So even modules with identical names and contents may not +be equal in this sense if they were originally different items in the source program. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/op.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/op.md new file mode 100644 index 000000000000..5e3632890aee --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/op.md @@ -0,0 +1,244 @@ +--- +title: UnaryOp and BinaryOp +--- + +`std::meta::op` contains the `UnaryOp` and `BinaryOp` types as well as methods on them. +These types are used to represent a unary or binary operator respectively in Noir source code. + +## Types + +### UnaryOp + +Represents a unary operator. One of `-`, `!`, `&mut`, or `*`. + +### Methods + +#### is_minus + +```rust title="is_minus" showLineNumbers +pub fn is_minus(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L26-L28 + + +Returns `true` if this operator is `-`. + +#### is_not + +```rust title="is_not" showLineNumbers +pub fn is_not(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L32-L34 + + +`true` if this operator is `!` + +#### is_mutable_reference + +```rust title="is_mutable_reference" showLineNumbers +pub fn is_mutable_reference(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L38-L40 + + +`true` if this operator is `&mut` + +#### is_dereference + +```rust title="is_dereference" showLineNumbers +pub fn is_dereference(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L44-L46 + + +`true` if this operator is `*` + +#### quoted + +```rust title="unary_quoted" showLineNumbers +pub comptime fn quoted(self) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/op.nr#L50-L52 + + +Returns this operator as a `Quoted` value. + +### Trait Implementations + +```rust +impl Eq for UnaryOp +impl Hash for UnaryOp +``` + +### BinaryOp + +Represents a binary operator. One of `+`, `-`, `*`, `/`, `%`, `==`, `!=`, `<`, `<=`, `>`, `>=`, `&`, `|`, `^`, `>>`, or `<<`. + +### Methods + +#### is_add + +```rust title="is_add" showLineNumbers +pub fn is_add(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L88-L90 + + +`true` if this operator is `+` + +#### is_subtract + +```rust title="is_subtract" showLineNumbers +pub fn is_subtract(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L94-L96 + + +`true` if this operator is `-` + +#### is_multiply + +```rust title="is_multiply" showLineNumbers +pub fn is_multiply(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L100-L102 + + +`true` if this operator is `*` + +#### is_divide + +```rust title="is_divide" showLineNumbers +pub fn is_divide(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L106-L108 + + +`true` if this operator is `/` + +#### is_modulo + +```rust title="is_modulo" showLineNumbers +pub fn is_modulo(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L178-L180 + + +`true` if this operator is `%` + +#### is_equal + +```rust title="is_equal" showLineNumbers +pub fn is_equal(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L112-L114 + + +`true` if this operator is `==` + +#### is_not_equal + +```rust title="is_not_equal" showLineNumbers +pub fn is_not_equal(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L118-L120 + + +`true` if this operator is `!=` + +#### is_less_than + +```rust title="is_less_than" showLineNumbers +pub fn is_less_than(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L124-L126 + + +`true` if this operator is `<` + +#### is_less_than_or_equal + +```rust title="is_less_than_or_equal" showLineNumbers +pub fn is_less_than_or_equal(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L130-L132 + + +`true` if this operator is `<=` + +#### is_greater_than + +```rust title="is_greater_than" showLineNumbers +pub fn is_greater_than(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L136-L138 + + +`true` if this operator is `>` + +#### is_greater_than_or_equal + +```rust title="is_greater_than_or_equal" showLineNumbers +pub fn is_greater_than_or_equal(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L142-L144 + + +`true` if this operator is `>=` + +#### is_and + +```rust title="is_and" showLineNumbers +pub fn is_and(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L148-L150 + + +`true` if this operator is `&` + +#### is_or + +```rust title="is_or" showLineNumbers +pub fn is_or(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L154-L156 + + +`true` if this operator is `|` + +#### is_shift_right + +```rust title="is_shift_right" showLineNumbers +pub fn is_shift_right(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L166-L168 + + +`true` if this operator is `>>` + +#### is_shift_left + +```rust title="is_shift_left" showLineNumbers +pub fn is_shift_left(self) -> bool { +``` +> Source code: noir_stdlib/src/meta/op.nr#L172-L174 + + +`true` if this operator is `<<` + +#### quoted + +```rust title="binary_quoted" showLineNumbers +pub comptime fn quoted(self) -> Quoted { +``` +> Source code: noir_stdlib/src/meta/op.nr#L184-L186 + + +Returns this operator as a `Quoted` value. + +### Trait Implementations + +```rust +impl Eq for BinaryOp +impl Hash for BinaryOp +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/quoted.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/quoted.md new file mode 100644 index 000000000000..1914f71f4e7d --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/quoted.md @@ -0,0 +1,140 @@ +--- +title: Quoted +--- + +`std::meta::quoted` contains methods on the built-in `Quoted` type which represents +quoted token streams and is the result of the `quote { ... }` expression. + +## Methods + +### as_expr + +```rust title="as_expr" showLineNumbers +pub comptime fn as_expr(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/quoted.nr#L6-L8 + + +Parses the quoted token stream as an expression. Returns `Option::none()` if +the expression failed to parse. + +Example: + +```rust title="as_expr_example" showLineNumbers +#[test] + fn test_expr_as_function_call() { + comptime { + let expr = quote { foo(42) }.as_expr().unwrap(); + let (_function, args) = expr.as_function_call().unwrap(); + assert_eq(args.len(), 1); + assert_eq(args[0].as_integer().unwrap(), (42, false)); + } + } +``` +> Source code: test_programs/noir_test_success/comptime_expr/src/main.nr#L336-L346 + + +### as_module + +```rust title="as_module" showLineNumbers +pub comptime fn as_module(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/quoted.nr#L11-L13 + + +Interprets this token stream as a module path leading to the name of a module. +Returns `Option::none()` if the module isn't found or this token stream cannot be parsed as a path. + +Example: + +```rust title="as_module_example" showLineNumbers +mod baz { + pub mod qux {} +} + +#[test] +fn as_module_test() { + comptime { + let my_mod = quote { baz::qux }.as_module().unwrap(); + assert_eq(my_mod.name(), quote { qux }); + } +} +``` +> Source code: test_programs/compile_success_empty/comptime_module/src/main.nr#L115-L127 + + +### as_trait_constraint + +```rust title="as_trait_constraint" showLineNumbers +pub comptime fn as_trait_constraint(self) -> TraitConstraint {} +``` +> Source code: noir_stdlib/src/meta/quoted.nr#L16-L18 + + +Interprets this token stream as a trait constraint (without an object type). +Note that this function panics instead of returning `Option::none()` if the token +stream does not parse and resolve to a valid trait constraint. + +Example: + +```rust title="implements_example" showLineNumbers +pub fn function_with_where(_x: T) +where + T: SomeTrait, +{ + comptime { + let t = quote { T }.as_type(); + let some_trait_i32 = quote { SomeTrait }.as_trait_constraint(); + assert(t.implements(some_trait_i32)); + + assert(t.get_trait_impl(some_trait_i32).is_none()); + } +} +``` +> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L160-L173 + + +### as_type + +```rust title="as_type" showLineNumbers +pub comptime fn as_type(self) -> Type {} +``` +> Source code: noir_stdlib/src/meta/quoted.nr#L21-L23 + + +Interprets this token stream as a resolved type. Panics if the token +stream doesn't parse to a type or if the type isn't a valid type in scope. + +```rust title="implements_example" showLineNumbers +pub fn function_with_where(_x: T) +where + T: SomeTrait, +{ + comptime { + let t = quote { T }.as_type(); + let some_trait_i32 = quote { SomeTrait }.as_trait_constraint(); + assert(t.implements(some_trait_i32)); + + assert(t.get_trait_impl(some_trait_i32).is_none()); + } +} +``` +> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L160-L173 + + +### tokens + +```rust title="tokens" showLineNumbers +pub comptime fn tokens(self) -> [Quoted] {} +``` +> Source code: noir_stdlib/src/meta/quoted.nr#L26-L28 + + +Returns a slice of the individual tokens that form this token stream. + +## Trait Implementations + +```rust +impl Eq for Quoted +impl Hash for Quoted +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/struct_def.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/struct_def.md new file mode 100644 index 000000000000..5aa557871683 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/struct_def.md @@ -0,0 +1,199 @@ +--- +title: StructDefinition +--- + +`std::meta::struct_def` contains methods on the built-in `StructDefinition` type. +This type corresponds to `struct Name { field1: Type1, ... }` items in the source program. + +## Methods + +### add_attribute + +```rust title="add_attribute" showLineNumbers +pub comptime fn add_attribute(self, attribute: str) {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L5-L7 + + +Adds an attribute to the struct. + +### add_generic + +```rust title="add_generic" showLineNumbers +pub comptime fn add_generic(self, generic_name: str) -> Type {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L10-L12 + + +Adds an generic to the struct. Returns the new generic type. +Errors if the given generic name isn't a single identifier or if +the struct already has a generic with the same name. + +This method should be used carefully, if there is existing code referring +to the struct type it may be checked before this function is called and +see the struct with the original number of generics. This method should +thus be preferred to use on code generated from other macros and structs +that are not used in function signatures. + +Example: + +```rust title="add-generic-example" showLineNumbers +comptime fn add_generic(s: StructDefinition) { + assert_eq(s.generics().len(), 0); + let new_generic = s.add_generic("T"); + + let generics = s.generics(); + assert_eq(generics.len(), 1); + let (typ, numeric) = generics[0]; + assert_eq(typ, new_generic); + assert(numeric.is_none()); + } +``` +> Source code: test_programs/compile_success_empty/comptime_struct_definition/src/main.nr#L35-L46 + + +### as_type + +```rust title="as_type" showLineNumbers +pub comptime fn as_type(self) -> Type {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L17-L19 + + +Returns this struct as a type in the source program. If this struct has +any generics, the generics are also included as-is. + +### generics + +```rust title="generics" showLineNumbers +pub comptime fn generics(self) -> [(Type, Option)] {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L29-L31 + + +Returns each generic on this struct. Each generic is represented as a tuple containing the type, +and an optional containing the numeric type if it's a numeric generic. + +Example: + +``` +#[example] +struct Foo { + bar: [T; K], + baz: Baz, +} + +comptime fn example(foo: StructDefinition) { + assert_eq(foo.generics().len(), 3); + + // Fails because `T` isn't in scope + // let t = quote { T }.as_type(); + // assert_eq(foo.generics()[0].0, t); + assert(foo.generics()[0].1.is_none()); + + // Last generic is numeric, so we have the numeric type available to us + assert(foo.generics()[2].1.is_some()); +} +``` + +### fields + +```rust title="fields" showLineNumbers +pub comptime fn fields(self, generic_args: [Type]) -> [(Quoted, Type)] {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L37-L39 + + +Returns (name, type) pairs of each field in this struct. +Any generic types used in each field type is automatically substituted with the +provided generic arguments. + +### fields_as_written + +```rust title="fields_as_written" showLineNumbers +pub comptime fn fields_as_written(self) -> [(Quoted, Type)] {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L46-L48 + + +Returns (name, type) pairs of each field in this struct. Each type is as-is +with any generic arguments unchanged. Unless the field types are not needed, +users should generally prefer to use `StructDefinition::fields` over this +function if possible. + +### has_named_attribute + +```rust title="has_named_attribute" showLineNumbers +pub comptime fn has_named_attribute(self, name: str) -> bool {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L22-L24 + + +Returns true if this struct has a custom attribute with the given name. + +### module + +```rust title="module" showLineNumbers +pub comptime fn module(self) -> Module {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L51-L53 + + +Returns the module where the struct is defined. + +### name + +```rust title="name" showLineNumbers +pub comptime fn name(self) -> Quoted {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L56-L58 + + +Returns the name of this struct + +Note that the returned quoted value will be just the struct name, it will +not be the full path to the struct, nor will it include any generics. + +### set_fields + +```rust title="set_fields" showLineNumbers +pub comptime fn set_fields(self, new_fields: [(Quoted, Type)]) {} +``` +> Source code: noir_stdlib/src/meta/struct_def.nr#L65-L67 + + +Sets the fields of this struct to the given fields list where each element +is a pair of the field's name and the field's type. Expects each field name +to be a single identifier. Note that this will override any previous fields +on this struct. If those should be preserved, use `.fields()` to retrieve the +current fields on the struct type and append the new fields from there. + +Example: + +```rust +// Change this struct to: +// struct Foo { +// a: u32, +// b: i8, +// } +#[mangle_fields] +struct Foo { x: Field } + +comptime fn mangle_fields(s: StructDefinition) { + s.set_fields(&[ + (quote { a }, quote { u32 }.as_type()), + (quote { b }, quote { i8 }.as_type()), + ]); +} +``` + +## Trait Implementations + +```rust +impl Eq for StructDefinition +impl Hash for StructDefinition +``` + +Note that each struct is assigned a unique ID internally and this is what is used for +equality and hashing. So even structs with identical generics and fields may not +be equal in this sense if they were originally different items in the source program. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_constraint.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_constraint.md new file mode 100644 index 000000000000..3106f732b5a6 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_constraint.md @@ -0,0 +1,17 @@ +--- +title: TraitConstraint +--- + +`std::meta::trait_constraint` contains methods on the built-in `TraitConstraint` type which represents +a trait constraint that can be used to search for a trait implementation. This is similar +syntactically to just the trait itself, but can also contain generic arguments. E.g. `Eq`, `Default`, +`BuildHasher`. + +This type currently has no public methods but it can be used alongside `Type` in `implements` or `get_trait_impl`. + +## Trait Implementations + +```rust +impl Eq for TraitConstraint +impl Hash for TraitConstraint +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_def.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_def.md new file mode 100644 index 000000000000..e661d3af7f1a --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_def.md @@ -0,0 +1,26 @@ +--- +title: TraitDefinition +--- + +`std::meta::trait_def` contains methods on the built-in `TraitDefinition` type. This type +represents trait definitions such as `trait Foo { .. }` at the top-level of a program. + +## Methods + +### as_trait_constraint + +```rust title="as_trait_constraint" showLineNumbers +pub comptime fn as_trait_constraint(_self: Self) -> TraitConstraint {} +``` +> Source code: noir_stdlib/src/meta/trait_def.nr#L6-L8 + + +Converts this trait into a trait constraint. If there are any generics on this +trait, they will be kept as-is without instantiating or replacing them. + +## Trait Implementations + +```rust +impl Eq for TraitDefinition +impl Hash for TraitDefinition +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_impl.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_impl.md new file mode 100644 index 000000000000..355213507b41 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/trait_impl.md @@ -0,0 +1,62 @@ +--- +title: TraitImpl +--- + +`std::meta::trait_impl` contains methods on the built-in `TraitImpl` type which represents a trait +implementation such as `impl Foo for Bar { ... }`. + +## Methods + +### trait_generic_args + +```rust title="trait_generic_args" showLineNumbers +pub comptime fn trait_generic_args(self) -> [Type] {} +``` +> Source code: noir_stdlib/src/meta/trait_impl.nr#L3-L5 + + +Returns any generic arguments on the trait of this trait implementation, if any. + +```rs +impl Foo for Bar { ... } + +comptime { + let bar_type = quote { Bar }.as_type(); + let foo = quote { Foo }.as_trait_constraint(); + + let my_impl: TraitImpl = bar_type.get_trait_impl(foo).unwrap(); + + let generics = my_impl.trait_generic_args(); + assert_eq(generics.len(), 2); + + assert_eq(generics[0].0, quote { i32 }.as_type()); + assert(generics[0].1.is_none()); + assert_eq(generics[1].0, quote { Field }.as_type()); + assert(generics[1].1.is_none()); +} +``` + +### methods + +```rust title="methods" showLineNumbers +pub comptime fn methods(self) -> [FunctionDefinition] {} +``` +> Source code: noir_stdlib/src/meta/trait_impl.nr#L8-L10 + + +Returns each method in this trait impl. + +Example: + +```rs +comptime { + let i32_type = quote { i32 }.as_type(); + let eq = quote { Eq }.as_trait_constraint(); + + let impl_eq_for_i32: TraitImpl = i32_type.get_trait_impl(eq).unwrap(); + let methods = impl_eq_for_i32.methods(); + + assert_eq(methods.len(), 1); + assert_eq(methods[0].name(), quote { eq }); +} +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typ.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typ.md new file mode 100644 index 000000000000..3a85a739a4c8 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typ.md @@ -0,0 +1,264 @@ +--- +title: Type +--- + +`std::meta::typ` contains methods on the built-in `Type` type used for representing +a type in the source program. + +## Functions + +```rust title="fresh_type_variable" showLineNumbers +pub comptime fn fresh_type_variable() -> Type {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L57-L59 + + +Creates and returns an unbound type variable. This is a special kind of type internal +to type checking which will type check with any other type. When it is type checked +against another type it will also be set to that type. For example, if `a` is a type +variable and we have the type equality `(a, i32) = (u8, i32)`, the compiler will set +`a` equal to `u8`. + +Unbound type variables will often be rendered as `_` while printing them. Bound type +variables will appear as the type they are bound to. + +This can be used in conjunction with functions which internally perform type checks +such as `Type::implements` or `Type::get_trait_impl` to potentially grab some of the types used. + +Note that calling `Type::implements` or `Type::get_trait_impl` on a type variable will always +fail. + +Example: + +```rust title="serialize-setup" showLineNumbers +trait Serialize {} + +impl Serialize<1> for Field {} + +impl Serialize for [T; N] +where + T: Serialize, +{} + +impl Serialize for (T, U) +where + T: Serialize, + U: Serialize, +{} +``` +> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L14-L29 + +```rust title="fresh-type-variable-example" showLineNumbers +let typevar1 = std::meta::typ::fresh_type_variable(); + let constraint = quote { Serialize<$typevar1> }.as_trait_constraint(); + let field_type = quote { Field }.as_type(); + + // Search for a trait impl (binding typevar1 to 1 when the impl is found): + assert(field_type.implements(constraint)); + + // typevar1 should be bound to the "1" generic now: + assert_eq(typevar1.as_constant().unwrap(), 1); + + // If we want to do the same with a different type, we need to + // create a new type variable now that `typevar1` is bound + let typevar2 = std::meta::typ::fresh_type_variable(); + let constraint = quote { Serialize<$typevar2> }.as_trait_constraint(); + let array_type = quote { [(Field, Field); 5] }.as_type(); + assert(array_type.implements(constraint)); + + // Now typevar2 should be bound to the serialized pair size 2 times the array length 5 + assert_eq(typevar2.as_constant().unwrap(), 10); +``` +> Source code: test_programs/compile_success_empty/comptime_type/src/main.nr#L129-L149 + + +## Methods + +### as_array + +```rust title="as_array" showLineNumbers +pub comptime fn as_array(self) -> Option<(Type, Type)> {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L76-L78 + + +If this type is an array, return a pair of (element type, size type). + +Example: + +```rust +comptime { + let array_type = quote { [Field; 3] }.as_type(); + let (field_type, three_type) = array_type.as_array().unwrap(); + + assert(field_type.is_field()); + assert_eq(three_type.as_constant().unwrap(), 3); +} +``` + +### as_constant + +```rust title="as_constant" showLineNumbers +pub comptime fn as_constant(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L83-L85 + + +If this type is a constant integer (such as the `3` in the array type `[Field; 3]`), +return the numeric constant. + +### as_integer + +```rust title="as_integer" showLineNumbers +pub comptime fn as_integer(self) -> Option<(bool, u8)> {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L90-L92 + + +If this is an integer type, return a boolean which is `true` +if the type is signed, as well as the number of bits of this integer type. + +### as_mutable_reference + +```rust title="as_mutable_reference" showLineNumbers +comptime fn as_mutable_reference(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L96-L98 + + +If this is a mutable reference type `&mut T`, returns the mutable type `T`. + +### as_slice + +```rust title="as_slice" showLineNumbers +pub comptime fn as_slice(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L102-L104 + + +If this is a slice type, return the element type of the slice. + +### as_str + +```rust title="as_str" showLineNumbers +pub comptime fn as_str(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L108-L110 + + +If this is a `str` type, returns the length `N` as a type. + +### as_struct + +```rust title="as_struct" showLineNumbers +pub comptime fn as_struct(self) -> Option<(StructDefinition, [Type])> {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L114-L116 + + +If this is a struct type, returns the struct in addition to +any generic arguments on this type. + +### as_tuple + +```rust title="as_tuple" showLineNumbers +pub comptime fn as_tuple(self) -> Option<[Type]> {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L120-L122 + + +If this is a tuple type, returns each element type of the tuple. + +### get_trait_impl + +```rust title="get_trait_impl" showLineNumbers +pub comptime fn get_trait_impl(self, constraint: TraitConstraint) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L143-L145 + + +Retrieves the trait implementation that implements the given +trait constraint for this type. If the trait constraint is not +found, `None` is returned. Note that since the concrete trait implementation +for a trait constraint specified in a `where` clause is unknown, +this function will return `None` in these cases. If you only want to know +whether a type implements a trait, use `implements` instead. + +Example: + +```rust +comptime { + let field_type = quote { Field }.as_type(); + let default = quote { Default }.as_trait_constraint(); + + let the_impl: TraitImpl = field_type.get_trait_impl(default).unwrap(); + assert(the_impl.methods().len(), 1); +} +``` + +### implements + +```rust title="implements" showLineNumbers +pub comptime fn implements(self, constraint: TraitConstraint) -> bool {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L166-L168 + + +`true` if this type implements the given trait. Note that unlike +`get_trait_impl` this will also return true for any `where` constraints +in scope. + +Example: + +```rust +fn foo() where T: Default { + comptime { + let field_type = quote { Field }.as_type(); + let default = quote { Default }.as_trait_constraint(); + assert(field_type.implements(default)); + + let t = quote { T }.as_type(); + assert(t.implements(default)); + } +} +``` + +### is_bool + +```rust title="is_bool" showLineNumbers +pub comptime fn is_bool(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L172-L174 + + +`true` if this type is `bool`. + +### is_field + +```rust title="is_field" showLineNumbers +pub comptime fn is_field(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L178-L180 + + +`true` if this type is `Field`. + +### is_unit + +```rust title="is_unit" showLineNumbers +comptime fn is_unit(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/typ.nr#L184-L186 + + +`true` if this type is the unit `()` type. + +## Trait Implementations + +```rust +impl Eq for Type +impl Hash for Type +``` +Note that this is syntactic equality, this is not the same as whether two types will type check +to be the same type. Unless type inference or generics are being used however, users should not +typically have to worry about this distinction unless `std::meta::typ::fresh_type_variable` is used. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typed_expr.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typed_expr.md new file mode 100644 index 000000000000..0db7dbfef610 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/typed_expr.md @@ -0,0 +1,27 @@ +--- +title: TypedExpr +--- + +`std::meta::typed_expr` contains methods on the built-in `TypedExpr` type for resolved and type-checked expressions. + +## Methods + +### get_type + +```rust title="as_function_definition" showLineNumbers +pub comptime fn as_function_definition(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typed_expr.nr#L7-L9 + + +If this expression refers to a function definitions, returns it. Otherwise returns `Option::none()`. + +### get_type + +```rust title="get_type" showLineNumbers +pub comptime fn get_type(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/typed_expr.nr#L13-L15 + + +Returns the type of the expression, or `Option::none()` if there were errors when the expression was previously resolved. \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/unresolved_type.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/unresolved_type.md new file mode 100644 index 000000000000..2826ec5ec0f0 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/meta/unresolved_type.md @@ -0,0 +1,57 @@ +--- +title: UnresolvedType +--- + +`std::meta::unresolved_type` contains methods on the built-in `UnresolvedType` type for the syntax of types. + +## Methods + +### as_mutable_reference + +```rust title="as_mutable_reference" showLineNumbers +comptime fn as_mutable_reference(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/unresolved_type.nr#L8-L10 + + +If this is a mutable reference type `&mut T`, returns the mutable type `T`. + +### as_slice + +```rust title="as_slice" showLineNumbers +comptime fn as_slice(self) -> Option {} +``` +> Source code: noir_stdlib/src/meta/unresolved_type.nr#L14-L16 + + +If this is a slice `&[T]`, returns the element type `T`. + +### is_bool + +```rust title="is_bool" showLineNumbers +comptime fn is_bool(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/unresolved_type.nr#L20-L22 + + +Returns `true` if this type is `bool`. + +### is_field + +```rust title="is_field" showLineNumbers +pub comptime fn is_field(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/unresolved_type.nr#L26-L28 + + +Returns true if this type refers to the Field type. + +### is_unit + +```rust title="is_unit" showLineNumbers +comptime fn is_unit(self) -> bool {} +``` +> Source code: noir_stdlib/src/meta/unresolved_type.nr#L32-L34 + + +Returns true if this type is the unit `()` type. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/options.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/options.md new file mode 100644 index 000000000000..a1bd4e1de5fd --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/options.md @@ -0,0 +1,101 @@ +--- +title: Option Type +--- + +The `Option` type is a way to express that a value might be present (`Some(T))` or absent (`None`). It's a safer way to handle potential absence of values, compared to using nulls in many other languages. + +```rust +struct Option { + None, + Some(T), +} +``` + +The `Option` type, already imported into your Noir program, can be used directly: + +```rust +fn main() { + let none = Option::none(); + let some = Option::some(3); +} +``` + +See [this test](https://github.com/noir-lang/noir/blob/5cbfb9c4a06c8865c98ff2b594464b037d821a5c/crates/nargo_cli/tests/test_data/option/src/main.nr) for a more comprehensive set of examples of each of the methods described below. + +## Methods + +### none + +Constructs a none value. + +### some + +Constructs a some wrapper around a given value. + +### is_none + +Returns true if the Option is None. + +### is_some + +Returns true of the Option is Some. + +### unwrap + +Asserts `self.is_some()` and returns the wrapped value. + +### unwrap_unchecked + +Returns the inner value without asserting `self.is_some()`. This method can be useful within an if condition when we already know that `option.is_some()`. If the option is None, there is no guarantee what value will be returned, only that it will be of type T for an `Option`. + +### unwrap_or + +Returns the wrapped value if `self.is_some()`. Otherwise, returns the given default value. + +### unwrap_or_else + +Returns the wrapped value if `self.is_some()`. Otherwise, calls the given function to return a default value. + +### expect + +Asserts `self.is_some()` with a provided custom message and returns the contained `Some` value. The custom message is expected to be a format string. + +### map + +If self is `Some(x)`, this returns `Some(f(x))`. Otherwise, this returns `None`. + +### map_or + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns the given default value. + +### map_or_else + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns `default()`. + +### and + +Returns None if self is None. Otherwise, this returns `other`. + +### and_then + +If self is None, this returns None. Otherwise, this calls the given function with the Some value contained within self, and returns the result of that call. In some languages this function is called `flat_map` or `bind`. + +### or + +If self is Some, return self. Otherwise, return `other`. + +### or_else + +If self is Some, return self. Otherwise, return `default()`. + +### xor + +If only one of the two Options is Some, return that option. Otherwise, if both options are Some or both are None, None is returned. + +### filter + +Returns `Some(x)` if self is `Some(x)` and `predicate(x)` is true. Otherwise, this returns `None`. + +### flatten + +Flattens an `Option>` into a `Option`. This returns `None` if the outer Option is None. Otherwise, this returns the inner Option. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/recursion.mdx b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/recursion.mdx new file mode 100644 index 000000000000..fcb362780606 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/recursion.mdx @@ -0,0 +1,67 @@ +--- +title: Recursive Proofs +description: Learn about how to write recursive proofs in Noir. +keywords: [recursion, recursive proofs, verification_key, verify_proof] +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox'; + +Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. + +Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) + +## Verifying Recursive Proofs + +```rust +#[foreign(recursive_aggregation)] +pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} +``` + + + +## Example usage + +```rust + +fn main( + verification_key : [Field; 114], + proof : [Field; 93], + public_inputs : [Field; 1], + key_hash : Field, + proof_b : [Field; 93], +) { + std::verify_proof( + verification_key, + proof, + public_inputs, + key_hash + ); + + std::verify_proof( + verification_key, + proof_b, + public_inputs, + key_hash + ); +} +``` + +You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). + +## Parameters + +### `verification_key` + +The verification key for the zk program that is being verified. + +### `proof` + +The proof for the zk program that is being verified. + +### `public_inputs` + +These represent the public inputs of the proof we are verifying. + +### `key_hash` + +A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/traits.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/traits.md new file mode 100644 index 000000000000..9f447ad82a83 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/noir/standard_library/traits.md @@ -0,0 +1,655 @@ +--- +title: Traits +description: Noir's stdlib provides a few commonly used traits. +keywords: [traits, trait, interface, protocol, default, add, eq] +--- + +## `std::default` + +### `std::default::Default` + +```rust title="default-trait" showLineNumbers +pub trait Default { + fn default() -> Self; +} +``` +> Source code: noir_stdlib/src/default.nr#L4-L8 + + +Constructs a default value of a type. + +Implementations: +```rust +impl Default for Field { .. } + +impl Default for i8 { .. } +impl Default for i16 { .. } +impl Default for i32 { .. } +impl Default for i64 { .. } + +impl Default for u8 { .. } +impl Default for u16 { .. } +impl Default for u32 { .. } +impl Default for u64 { .. } + +impl Default for () { .. } +impl Default for bool { .. } + +impl Default for [T; N] + where T: Default { .. } + +impl Default for [T] { .. } + +impl Default for (A, B) + where A: Default, B: Default { .. } + +impl Default for (A, B, C) + where A: Default, B: Default, C: Default { .. } + +impl Default for (A, B, C, D) + where A: Default, B: Default, C: Default, D: Default { .. } + +impl Default for (A, B, C, D, E) + where A: Default, B: Default, C: Default, D: Default, E: Default { .. } +``` + +For primitive integer types, the return value of `default` is `0`. Container +types such as arrays are filled with default values of their element type, +except slices whose length is unknown and thus defaulted to zero. + +--- + +## `std::convert` + +### `std::convert::From` + +```rust title="from-trait" showLineNumbers +pub trait From { + fn from(input: T) -> Self; +} +``` +> Source code: noir_stdlib/src/convert.nr#L1-L5 + + +The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented. + +The Noir standard library provides a number of implementations of `From` between primitive types. +```rust title="from-impls" showLineNumbers +// Unsigned integers + +impl From for u32 { + fn from(value: u8) -> u32 { + value as u32 + } +} + +impl From for u64 { + fn from(value: u8) -> u64 { + value as u64 + } +} +impl From for u64 { + fn from(value: u32) -> u64 { + value as u64 + } +} + +impl From for u128 { + fn from(value: u8) -> u128 { + value as u128 + } +} +impl From for u128 { + fn from(value: u32) -> u128 { + value as u128 + } +} +impl From for u128 { + fn from(value: u64) -> u128 { + value as u128 + } +} + +impl From for Field { + fn from(value: u8) -> Field { + value as Field + } +} +impl From for Field { + fn from(value: u32) -> Field { + value as Field + } +} +impl From for Field { + fn from(value: u64) -> Field { + value as Field + } +} + +impl From for Field { + fn from(value: u128) -> Field { + value as Field + } +} + +// Signed integers + +impl From for i32 { + fn from(value: i8) -> i32 { + value as i32 + } +} + +impl From for i64 { + fn from(value: i8) -> i64 { + value as i64 + } +} +impl From for i64 { + fn from(value: i32) -> i64 { + value as i64 + } +} + +// Booleans +impl From for u8 { + fn from(value: bool) -> u8 { + value as u8 + } +} +impl From for u32 { + fn from(value: bool) -> u32 { + value as u32 + } +} +impl From for u64 { + fn from(value: bool) -> u64 { + value as u64 + } +} +impl From for i8 { + fn from(value: bool) -> i8 { + value as i8 + } +} +impl From for i32 { + fn from(value: bool) -> i32 { + value as i32 + } +} +impl From for i64 { + fn from(value: bool) -> i64 { + value as i64 + } +} +impl From for Field { + fn from(value: bool) -> Field { + value as Field + } +} +``` +> Source code: noir_stdlib/src/convert.nr#L28-L141 + + +#### When to implement `From` + +As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from): + +- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. +- The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. +- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. +- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. + +One additional recommendation specific to Noir is: +- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. + +### `std::convert::Into` + +The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`. + +For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation. + +```rust title="into-trait" showLineNumbers +pub trait Into { + fn into(self) -> T; +} + +impl Into for U +where + T: From, +{ + fn into(self) -> T { + T::from(self) + } +} +``` +> Source code: noir_stdlib/src/convert.nr#L13-L26 + + +`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. + +--- + +## `std::cmp` + +### `std::cmp::Eq` + +```rust title="eq-trait" showLineNumbers +pub trait Eq { + fn eq(self, other: Self) -> bool; +} +``` +> Source code: noir_stdlib/src/cmp.nr#L4-L8 + + +Returns `true` if `self` is equal to `other`. Implementing this trait on a type +allows the type to be used with `==` and `!=`. + +Implementations: +```rust +impl Eq for Field { .. } + +impl Eq for i8 { .. } +impl Eq for i16 { .. } +impl Eq for i32 { .. } +impl Eq for i64 { .. } + +impl Eq for u8 { .. } +impl Eq for u16 { .. } +impl Eq for u32 { .. } +impl Eq for u64 { .. } + +impl Eq for () { .. } +impl Eq for bool { .. } + +impl Eq for [T; N] + where T: Eq { .. } + +impl Eq for [T] + where T: Eq { .. } + +impl Eq for (A, B) + where A: Eq, B: Eq { .. } + +impl Eq for (A, B, C) + where A: Eq, B: Eq, C: Eq { .. } + +impl Eq for (A, B, C, D) + where A: Eq, B: Eq, C: Eq, D: Eq { .. } + +impl Eq for (A, B, C, D, E) + where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } +``` + +### `std::cmp::Ord` + +```rust title="ord-trait" showLineNumbers +pub trait Ord { + fn cmp(self, other: Self) -> Ordering; +} +``` +> Source code: noir_stdlib/src/cmp.nr#L215-L219 + + +`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, +`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. +Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be +used on values of the type. + +`std::cmp` also provides `max` and `min` functions for any type which implements the `Ord` trait. + +Implementations: + +```rust +impl Ord for u8 { .. } +impl Ord for u16 { .. } +impl Ord for u32 { .. } +impl Ord for u64 { .. } + +impl Ord for i8 { .. } +impl Ord for i16 { .. } +impl Ord for i32 { .. } + +impl Ord for i64 { .. } + +impl Ord for () { .. } +impl Ord for bool { .. } + +impl Ord for [T; N] + where T: Ord { .. } + +impl Ord for [T] + where T: Ord { .. } + +impl Ord for (A, B) + where A: Ord, B: Ord { .. } + +impl Ord for (A, B, C) + where A: Ord, B: Ord, C: Ord { .. } + +impl Ord for (A, B, C, D) + where A: Ord, B: Ord, C: Ord, D: Ord { .. } + +impl Ord for (A, B, C, D, E) + where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } +``` + +--- + +## `std::ops` + +### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` + +These traits abstract over addition, subtraction, multiplication, and division respectively. +Implementing these traits for a given type will also allow that type to be used with the corresponding operator +for that trait (`+` for Add, etc) in addition to the normal method names. + +```rust title="add-trait" showLineNumbers +pub trait Add { + fn add(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L1-L5 + +```rust title="sub-trait" showLineNumbers +pub trait Sub { + fn sub(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L65-L69 + +```rust title="mul-trait" showLineNumbers +pub trait Mul { + fn mul(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L129-L133 + +```rust title="div-trait" showLineNumbers +pub trait Div { + fn div(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L193-L197 + + +The implementations block below is given for the `Add` trait, but the same types that implement +`Add` also implement `Sub`, `Mul`, and `Div`. + +Implementations: +```rust +impl Add for Field { .. } + +impl Add for i8 { .. } +impl Add for i16 { .. } +impl Add for i32 { .. } +impl Add for i64 { .. } + +impl Add for u8 { .. } +impl Add for u16 { .. } +impl Add for u32 { .. } +impl Add for u64 { .. } +``` + +### `std::ops::Rem` + +```rust title="rem-trait" showLineNumbers +pub trait Rem { + fn rem(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L257-L261 + + +`Rem::rem(a, b)` is the remainder function returning the result of what is +left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator +to be used with the implementation type. + +Unlike other numeric traits, `Rem` is not implemented for `Field`. + +Implementations: +```rust +impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } +impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } +impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } +impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } + +impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } +impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } +impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } +impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } +``` + +### `std::ops::Neg` + +```rust title="neg-trait" showLineNumbers +pub trait Neg { + fn neg(self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L315-L319 + + +`Neg::neg` is equivalent to the unary negation operator `-`. + +Implementations: +```rust title="neg-trait-impls" showLineNumbers +impl Neg for Field { + fn neg(self) -> Field { + -self + } +} + +impl Neg for i8 { + fn neg(self) -> i8 { + -self + } +} +impl Neg for i16 { + fn neg(self) -> i16 { + -self + } +} +impl Neg for i32 { + fn neg(self) -> i32 { + -self + } +} +impl Neg for i64 { + fn neg(self) -> i64 { + -self + } +} +``` +> Source code: noir_stdlib/src/ops/arith.nr#L321-L348 + + +### `std::ops::Not` + +```rust title="not-trait" showLineNumbers +pub trait Not { + fn not(self: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L1-L5 + + +`Not::not` is equivalent to the unary bitwise NOT operator `!`. + +Implementations: +```rust title="not-trait-impls" showLineNumbers +impl Not for bool { + fn not(self) -> bool { + !self + } +} + +impl Not for u128 { + fn not(self) -> u128 { + !self + } +} +impl Not for u64 { + fn not(self) -> u64 { + !self + } +} +impl Not for u32 { + fn not(self) -> u32 { + !self + } +} +impl Not for u16 { + fn not(self) -> u16 { + !self + } +} +impl Not for u8 { + fn not(self) -> u8 { + !self + } +} +impl Not for u1 { + fn not(self) -> u1 { + !self + } +} + +impl Not for i8 { + fn not(self) -> i8 { + !self + } +} +impl Not for i16 { + fn not(self) -> i16 { + !self + } +} +impl Not for i32 { + fn not(self) -> i32 { + !self + } +} +impl Not for i64 { + fn not(self) -> i64 { + !self + } +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L7-L65 + + +### `std::ops::{ BitOr, BitAnd, BitXor }` + +```rust title="bitor-trait" showLineNumbers +pub trait BitOr { + fn bitor(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L67-L71 + +```rust title="bitand-trait" showLineNumbers +pub trait BitAnd { + fn bitand(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L131-L135 + +```rust title="bitxor-trait" showLineNumbers +pub trait BitXor { + fn bitxor(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L195-L199 + + +Traits for the bitwise operations `|`, `&`, and `^`. + +Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively +to be used with the type. + +The implementations block below is given for the `BitOr` trait, but the same types that implement +`BitOr` also implement `BitAnd` and `BitXor`. + +Implementations: +```rust +impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } + +impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } +impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } +impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } +impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } + +impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } +impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } +impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } +impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } +``` + +### `std::ops::{ Shl, Shr }` + +```rust title="shl-trait" showLineNumbers +pub trait Shl { + fn shl(self, other: u8) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L259-L263 + +```rust title="shr-trait" showLineNumbers +pub trait Shr { + fn shr(self, other: u8) -> Self; +} +``` +> Source code: noir_stdlib/src/ops/bit.nr#L317-L321 + + +Traits for a bit shift left and bit shift right. + +Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. +Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. + +Note that bit shifting is not currently implemented for signed types. + +The implementations block below is given for the `Shl` trait, but the same types that implement +`Shl` also implement `Shr`. + +Implementations: +```rust +impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } +impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } +impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } +impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } +``` + +--- + +## `std::append` + +### `std::append::Append` + +`Append` can abstract over types that can be appended to - usually container types: + +```rust title="append-trait" showLineNumbers +pub trait Append { + fn empty() -> Self; + fn append(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/append.nr#L9-L14 + + +`Append` requires two methods: + +- `empty`: Constructs an empty value of `Self`. +- `append`: Append two values together, returning the result. + +Additionally, it is expected that for any implementation: + +- `T::empty().append(x) == x` +- `x.append(T::empty()) == x` + +Implementations: +```rust +impl Append for [T] +impl Append for Quoted +``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/.nojekyll b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/.nojekyll new file mode 100644 index 000000000000..e2ac6616addc --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/classes/Noir.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/classes/Noir.md new file mode 100644 index 000000000000..ead255bc5047 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/classes/Noir.md @@ -0,0 +1,52 @@ +# Noir + +## Constructors + +### new Noir(circuit) + +```ts +new Noir(circuit): Noir +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `circuit` | `CompiledCircuit` | + +#### Returns + +[`Noir`](Noir.md) + +## Methods + +### execute() + +```ts +execute(inputs, foreignCallHandler?): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | `InputMap` | +| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Allows to execute a circuit to get its witness and return value. + +#### Example + +```typescript +async execute(inputs) +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/and.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/and.md new file mode 100644 index 000000000000..c783283e3965 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/and.md @@ -0,0 +1,22 @@ +# and() + +```ts +and(lhs, rhs): string +``` + +Performs a bitwise AND operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/blake2s256.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/blake2s256.md new file mode 100644 index 000000000000..7882d0da8d50 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/blake2s256.md @@ -0,0 +1,21 @@ +# blake2s256() + +```ts +blake2s256(inputs): Uint8Array +``` + +Calculates the Blake2s256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md new file mode 100644 index 000000000000..5e3cd53e9d36 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256k1\_verify() + +```ts +ecdsa_secp256k1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256k1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md new file mode 100644 index 000000000000..0b20ff689575 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256r1\_verify() + +```ts +ecdsa_secp256r1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256r1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/xor.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/xor.md new file mode 100644 index 000000000000..8d762b895d30 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/functions/xor.md @@ -0,0 +1,22 @@ +# xor() + +```ts +xor(lhs, rhs): string +``` + +Performs a bitwise XOR operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/index.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/index.md new file mode 100644 index 000000000000..4de7a6969910 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/index.md @@ -0,0 +1,47 @@ +# noir_js + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [Noir](classes/Noir.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [ErrorWithPayload](type-aliases/ErrorWithPayload.md) | - | +| [ForeignCallHandler](type-aliases/ForeignCallHandler.md) | A callback which performs an foreign call and returns the response. | +| [ForeignCallInput](type-aliases/ForeignCallInput.md) | - | +| [ForeignCallOutput](type-aliases/ForeignCallOutput.md) | - | +| [WitnessMap](type-aliases/WitnessMap.md) | - | + +### Functions + +| Function | Description | +| :------ | :------ | +| [and](functions/and.md) | Performs a bitwise AND operation between `lhs` and `rhs` | +| [blake2s256](functions/blake2s256.md) | Calculates the Blake2s256 hash of the input bytes | +| [ecdsa\_secp256k1\_verify](functions/ecdsa_secp256k1_verify.md) | Verifies a ECDSA signature over the secp256k1 curve. | +| [ecdsa\_secp256r1\_verify](functions/ecdsa_secp256r1_verify.md) | Verifies a ECDSA signature over the secp256r1 curve. | +| [xor](functions/xor.md) | Performs a bitwise XOR operation between `lhs` and `rhs` | + +## References + +### CompiledCircuit + +Renames and re-exports [InputMap](index.md#inputmap) + +## Variables + +### InputMap + +```ts +InputMap: any; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md new file mode 100644 index 000000000000..e8c2f4aef3d5 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ErrorWithPayload.md @@ -0,0 +1,15 @@ +# ErrorWithPayload + +```ts +type ErrorWithPayload: ExecutionError & object; +``` + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `decodedAssertionPayload` | `any` | - | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md new file mode 100644 index 000000000000..812b8b164818 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md @@ -0,0 +1,24 @@ +# ForeignCallHandler + +```ts +type ForeignCallHandler: (name, inputs) => Promise; +``` + +A callback which performs an foreign call and returns the response. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | The identifier for the type of foreign call being performed. | +| `inputs` | [`ForeignCallInput`](ForeignCallInput.md)[] | An array of hex encoded inputs to the foreign call. | + +## Returns + +`Promise`\<[`ForeignCallOutput`](ForeignCallOutput.md)[]\> + +outputs - An array of hex encoded outputs containing the results of the foreign call. + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md new file mode 100644 index 000000000000..dd95809186a2 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md @@ -0,0 +1,9 @@ +# ForeignCallInput + +```ts +type ForeignCallInput: string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md new file mode 100644 index 000000000000..b71fb78a9469 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md @@ -0,0 +1,9 @@ +# ForeignCallOutput + +```ts +type ForeignCallOutput: string | string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/WitnessMap.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/WitnessMap.md new file mode 100644 index 000000000000..258c46f9d0c9 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/type-aliases/WitnessMap.md @@ -0,0 +1,9 @@ +# WitnessMap + +```ts +type WitnessMap: Map; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/typedoc-sidebar.cjs b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/typedoc-sidebar.cjs new file mode 100644 index 000000000000..4796b5abaa86 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_js/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ErrorWithPayload","label":"ErrorWithPayload"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_js/functions/and","label":"and"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/xor","label":"xor"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/.nojekyll b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/.nojekyll new file mode 100644 index 000000000000..e2ac6616addc --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile.md new file mode 100644 index 000000000000..6faf763b37f7 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile.md @@ -0,0 +1,51 @@ +# compile() + +```ts +compile( + fileManager, + projectPath?, + logFn?, +debugLogFn?): Promise +``` + +Compiles a Noir project + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `fileManager` | `FileManager` | The file manager to use | +| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | +| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | +| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | + +## Returns + +`Promise`\<[`ProgramCompilationArtifacts`](../index.md#programcompilationartifacts)\> + +## Example + +```typescript +// Node.js + +import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager(myProjectPath); +const myCompiledCode = await compile_program(fm); +``` + +```typescript +// Browser + +import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager('/'); +for (const path of files) { + await fm.writeFile(path, await getFileAsStream(path)); +} +const myCompiledCode = await compile_program(fm); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile_contract.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile_contract.md new file mode 100644 index 000000000000..7d0b39a43ef8 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/compile_contract.md @@ -0,0 +1,51 @@ +# compile\_contract() + +```ts +compile_contract( + fileManager, + projectPath?, + logFn?, +debugLogFn?): Promise +``` + +Compiles a Noir project + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `fileManager` | `FileManager` | The file manager to use | +| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | +| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | +| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | + +## Returns + +`Promise`\<[`ContractCompilationArtifacts`](../index.md#contractcompilationartifacts)\> + +## Example + +```typescript +// Node.js + +import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager(myProjectPath); +const myCompiledCode = await compile_contract(fm); +``` + +```typescript +// Browser + +import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager('/'); +for (const path of files) { + await fm.writeFile(path, await getFileAsStream(path)); +} +const myCompiledCode = await compile_contract(fm); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/createFileManager.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/createFileManager.md new file mode 100644 index 000000000000..7e65c1d69c7e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/createFileManager.md @@ -0,0 +1,21 @@ +# createFileManager() + +```ts +createFileManager(dataDir): FileManager +``` + +Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `dataDir` | `string` | root of the file system | + +## Returns + +`FileManager` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md new file mode 100644 index 000000000000..fcea92753412 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md @@ -0,0 +1,21 @@ +# inflateDebugSymbols() + +```ts +inflateDebugSymbols(debugSymbols): any +``` + +Decompresses and decodes the debug symbols + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `debugSymbols` | `string` | The base64 encoded debug symbols | + +## Returns + +`any` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/index.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/index.md new file mode 100644 index 000000000000..b6e0f9d1bc0e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/index.md @@ -0,0 +1,49 @@ +# noir_wasm + +## Exports + +### Functions + +| Function | Description | +| :------ | :------ | +| [compile](functions/compile.md) | Compiles a Noir project | +| [compile\_contract](functions/compile_contract.md) | Compiles a Noir project | +| [createFileManager](functions/createFileManager.md) | Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) | +| [inflateDebugSymbols](functions/inflateDebugSymbols.md) | Decompresses and decodes the debug symbols | + +## References + +### compile\_program + +Renames and re-exports [compile](functions/compile.md) + +## Interfaces + +### ContractCompilationArtifacts + +The compilation artifacts of a given contract. + +#### Properties + +| Property | Type | Description | +| :------ | :------ | :------ | +| `contract` | `ContractArtifact` | The compiled contract. | +| `warnings` | `unknown`[] | Compilation warnings. | + +*** + +### ProgramCompilationArtifacts + +The compilation artifacts of a given program. + +#### Properties + +| Property | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | not part of the compilation output, injected later | +| `program` | `ProgramArtifact` | The compiled contract. | +| `warnings` | `unknown`[] | Compilation warnings. | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs new file mode 100644 index 000000000000..e0870710349c --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"doc","id":"reference/NoirJS/noir_wasm/index","label":"API"},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile","label":"compile"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile_contract","label":"compile_contract"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/createFileManager","label":"createFileManager"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/inflateDebugSymbols","label":"inflateDebugSymbols"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/_category_.json new file mode 100644 index 000000000000..5b6a20a609af --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 4, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/_category_.json new file mode 100644 index 000000000000..27869205ad3c --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Debugger", + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_known_limitations.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_known_limitations.md new file mode 100644 index 000000000000..936d416ac4bc --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_known_limitations.md @@ -0,0 +1,59 @@ +--- +title: Known limitations +description: + An overview of known limitations of the current version of the Noir debugger +keywords: + [ + Nargo, + Noir Debugger, + VS Code, + ] +sidebar_position: 2 +--- + +# Debugger Known Limitations + +There are currently some limits to what the debugger can observe. + +## Mutable references + +The debugger is currently blind to any state mutated via a mutable reference. For example, in: + +``` +let mut x = 1; +let y = &mut x; +*y = 2; +``` + +The update on `x` will not be observed by the debugger. That means, when running `vars` from the debugger REPL, or inspecting the _local variables_ pane in the VS Code debugger, `x` will appear with value 1 despite having executed `*y = 2;`. + +## Variables of type function or mutable references are opaque + +When inspecting variables, any variable of type `Function` or `MutableReference` will render its value as `<>` or `<>`. + +## Debugger instrumentation affects resulting ACIR + +In order to make the state of local variables observable, the debugger compiles Noir circuits interleaving foreign calls that track any mutations to them. While this works (except in the cases described above) and doesn't introduce any behavior changes, it does as a side effect produce bigger bytecode. In particular, when running the command `opcodes` on the REPL debugger, you will notice Unconstrained VM blocks that look like this: + +``` +... +5 BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [], q_c: 2 }), Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(2))], q_c: 0 })] + | outputs=[] + 5.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 5.1 | Mov { destination: RegisterIndex(3), source: RegisterIndex(1) } + 5.2 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 5.3 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 5.4 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 5.5 | Mov { destination: RegisterIndex(3), source: RegisterIndex(3) } + 5.6 | Call { location: 8 } + 5.7 | Stop + 5.8 | ForeignCall { function: "__debug_var_assign", destinations: [], inputs: [RegisterIndex(RegisterIndex(2)), RegisterIndex(RegisterIndex(3))] } +... +``` + +If you are interested in debugging/inspecting compiled ACIR without these synthetic changes, you can invoke the REPL debugger with the `--skip-instrumentation` flag or launch the VS Code debugger with the `skipConfiguration` property set to true in its launch configuration. You can find more details about those in the [Debugger REPL reference](debugger_repl.md) and the [VS Code Debugger reference](debugger_vscode.md). + +:::note +Skipping debugger instrumentation means you won't be able to inspect values of local variables. +::: + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_repl.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_repl.md new file mode 100644 index 000000000000..46e2011304e5 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_repl.md @@ -0,0 +1,360 @@ +--- +title: REPL Debugger +description: + Noir Debugger REPL options and commands. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + REPL, + ] +sidebar_position: 1 +--- + +## Running the REPL debugger + +`nargo debug [OPTIONS] [WITNESS_NAME]` + +Runs the Noir REPL debugger. If a `WITNESS_NAME` is provided the debugger writes the resulting execution witness to a `WITNESS_NAME` file. + +### Options + +| Option | Description | +| --------------------- | ------------------------------------------------------------ | +| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover]| +| `--package ` | The name of the package to debug | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +None of these options are required. + +:::note +Since the debugger starts by compiling the target package, all Noir compiler options are also available. Check out the [compiler reference](../nargo_commands.md#nargo-compile) to learn more about the compiler options. +::: + +## REPL commands + +Once the debugger is running, it accepts the following commands. + +#### `help` (h) + +Displays the menu of available commands. + +``` +> help +Available commands: + + opcodes display ACIR opcodes + into step into to the next opcode + next step until a new source location is reached + out step until a new source location is reached + and the current stack frame is finished + break LOCATION:OpcodeLocation add a breakpoint at an opcode location + over step until a new source location is reached + without diving into function calls + restart restart the debugging session + delete LOCATION:OpcodeLocation delete breakpoint at an opcode location + witness show witness map + witness index:u32 display a single witness from the witness map + witness index:u32 value:String update a witness with the given value + memset index:usize value:String update a memory cell with the given + value + continue continue execution until the end of the + program + vars show variable values available at this point + in execution + stacktrace display the current stack trace + memory show memory (valid when executing unconstrained code) value + step step to the next ACIR opcode + +Other commands: + + help Show this help message + quit Quit repl + +``` + +### Stepping through programs + +#### `next` (n) + +Step until the next Noir source code location. While other commands, such as [`into`](#into-i) and [`step`](#step-s), allow for finer grained control of the program's execution at the opcode level, `next` is source code centric. For example: + +``` +3 ... +4 fn main(x: u32) { +5 assert(entry_point(x) == 2); +6 swap_entry_point(x, x + 1); +7 -> assert(deep_entry_point(x) == 4); +8 multiple_values_entry_point(x); +9 } +``` + + +Using `next` here would cause the debugger to jump to the definition of `deep_entry_point` (if available). + +If you want to step over `deep_entry_point` and go straight to line 8, use [the `over` command](#over) instead. + +#### `over` + +Step until the next source code location, without diving into function calls. For example: + +``` +3 ... +4 fn main(x: u32) { +5 assert(entry_point(x) == 2); +6 swap_entry_point(x, x + 1); +7 -> assert(deep_entry_point(x) == 4); +8 multiple_values_entry_point(x); +9 } +``` + + +Using `over` here would cause the debugger to execute until line 8 (`multiple_values_entry_point(x);`). + +If you want to step into `deep_entry_point` instead, use [the `next` command](#next-n). + +#### `out` + +Step until the end of the current function call. For example: + +``` + 3 ... + 4 fn main(x: u32) { + 5 assert(entry_point(x) == 2); + 6 swap_entry_point(x, x + 1); + 7 -> assert(deep_entry_point(x) == 4); + 8 multiple_values_entry_point(x); + 9 } + 10 + 11 unconstrained fn returns_multiple_values(x: u32) -> (u32, u32, u32, u32) { + 12 ... + ... + 55 + 56 unconstrained fn deep_entry_point(x: u32) -> u32 { + 57 -> level_1(x + 1) + 58 } + +``` + +Running `out` here will resume execution until line 8. + +#### `step` (s) + +Skips to the next ACIR code. A compiled Noir program is a sequence of ACIR opcodes. However, an unconstrained VM opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. + +Using the `step` command at this point would result in the debugger stopping at ACIR opcode 2, `EXPR`, skipping unconstrained computation steps. + +Use [the `into` command](#into-i) instead if you want to follow unconstrained computation step by step. + +#### `into` (i) + +Steps into the next opcode. A compiled Noir program is a sequence of ACIR opcodes. However, a BRILLIG opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. + +Using the `into` command at this point would result in the debugger stopping at opcode 1.0, `Mov ...`, allowing the debugger user to follow unconstrained computation step by step. + +Use [the `step` command](#step-s) instead if you want to skip to the next ACIR code directly. + +#### `continue` (c) + +Continues execution until the next breakpoint, or the end of the program. + +#### `restart` (res) + +Interrupts execution, and restarts a new debugging session from scratch. + +#### `opcodes` (o) + +Display the program's ACIR opcode sequence. For example: + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +### Breakpoints + +#### `break [Opcode]` (or shorthand `b [Opcode]`) + +Sets a breakpoint on the specified opcode index. To get a list of the program opcode numbers, see [the `opcode` command](#opcodes-o). For example: + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +In this example, issuing a `break 1.2` command adds break on opcode 1.2, as denoted by the `*` character: + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | * Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +Running [the `continue` command](#continue-c) at this point would cause the debugger to execute the program until opcode 1.2. + +#### `delete [Opcode]` (or shorthand `d [Opcode]`) + +Deletes a breakpoint at an opcode location. Usage is analogous to [the `break` command](#). + +### Variable inspection + +#### vars + +Show variable values available at this point in execution. + +:::note +The ability to inspect variable values from the debugger depends on compilation to be run in a special debug instrumentation mode. This instrumentation weaves variable tracing code with the original source code. + +So variable value inspection comes at the expense of making the resulting ACIR bytecode bigger and harder to understand and optimize. + +If you find this compromise unacceptable, you can run the debugger with the flag `--skip-debug-instrumentation`. This will compile your circuit without any additional debug information, so the resulting ACIR bytecode will be identical to the one produced by standard Noir compilation. However, if you opt for this, the `vars` command will not be available while debugging. +::: + + +### Stacktrace + +#### `stacktrace` + +Displays the current stack trace. + + +### Witness map + +#### `witness` (w) + +Show witness map. For example: + +``` +_0 = 0 +_1 = 2 +_2 = 1 +``` + +#### `witness [Witness Index]` + +Display a single witness from the witness map. For example: + +``` +> witness 1 +_1 = 2 +``` + +#### `witness [Witness Index] [New value]` + +Overwrite the given index with a new value. For example: + +``` +> witness 1 3 +_1 = 3 +``` + + +### Unconstrained VM memory + +#### `memory` + +Show unconstrained VM memory state. For example: + +``` +> memory +At opcode 1.13: Store { destination_pointer: RegisterIndex(0), source: RegisterIndex(3) } +... +> registers +0 = 0 +1 = 10 +2 = 0 +3 = 1 +4 = 1 +5 = 2³² +6 = 1 +> into +At opcode 1.14: Const { destination: RegisterIndex(5), value: Value { inner: 1 } } +... +> memory +0 = 1 +> +``` + +In the example above: we start with clean memory, then step through a `Store` opcode which stores the value of register 3 (1) into the memory address stored in register 0 (0). Thus now `memory` shows memory address 0 contains value 1. + +:::note +This command is only functional while the debugger is executing unconstrained code. +::: + +#### `memset [Memory address] [New value]` + +Update a memory cell with the given value. For example: + +``` +> memory +0 = 1 +> memset 0 2 +> memory +0 = 2 +> memset 1 4 +> memory +0 = 2 +1 = 4 +> +``` + +:::note +This command is only functional while the debugger is executing unconstrained code. +::: \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_vscode.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_vscode.md new file mode 100644 index 000000000000..c027332b3b04 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/debugger/debugger_vscode.md @@ -0,0 +1,82 @@ +--- +title: VS Code Debugger +description: + VS Code Debugger configuration and features. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + VS Code, + IDE, + ] +sidebar_position: 0 +--- + +# VS Code Noir Debugger Reference + +The Noir debugger enabled by the vscode-noir extension ships with default settings such that the most common scenario should run without any additional configuration steps. + +These defaults can nevertheless be overridden by defining a launch configuration file. This page provides a reference for the properties you can override via a launch configuration file, as well as documenting the Nargo `dap` command, which is a dependency of the VS Code Noir debugger. + + +## Creating and editing launch configuration files + +To create a launch configuration file from VS Code, open the _debug pane_, and click on _create a launch.json file_. + +![Creating a launch configuration file](@site/static/img/debugger/ref1-create-launch.png) + +A `launch.json` file will be created, populated with basic defaults. + +### Noir Debugger launch.json properties + +#### projectFolder + +_String, optional._ + +Absolute path to the Nargo project to debug. By default, it is dynamically determined by looking for the nearest `Nargo.toml` file to the active file at the moment of launching the debugger. + +#### proverName + +_String, optional._ + +Name of the prover input to use. Defaults to `Prover`, which looks for a file named `Prover.toml` at the `projectFolder`. + +#### generateAcir + +_Boolean, optional._ + +If true, generate ACIR opcodes instead of unconstrained opcodes which will be closer to release binaries but less convenient for debugging. Defaults to `false`. + +#### skipInstrumentation + +_Boolean, optional._ + +Skips variables debugging instrumentation of code, making debugging less convenient but the resulting binary smaller and closer to production. Defaults to `false`. + +:::note +Skipping instrumentation causes the debugger to be unable to inspect local variables. +::: + +## `nargo dap [OPTIONS]` + +When run without any option flags, it starts the Nargo Debug Adapter Protocol server, which acts as the debugging backend for the VS Code Noir Debugger. + +All option flags are related to preflight checks. The Debug Adapter Protocol specifies how errors are to be informed from a running DAP server, but it doesn't specify mechanisms to communicate server initialization errors between the DAP server and its client IDE. + +Thus `nargo dap` ships with a _preflight check_ mode. If flag `--preflight-check` and the rest of the `--preflight-*` flags are provided, Nargo will run the same initialization routine except it will not start the DAP server. + +`vscode-noir` will then run `nargo dap` in preflight check mode first before a debugging session starts. If the preflight check ends in error, vscode-noir will present stderr and stdout output from this process through its own Output pane in VS Code. This makes it possible for users to diagnose what pieces of configuration might be wrong or missing in case of initialization errors. + +If the preflight check succeeds, `vscode-noir` proceeds to start the DAP server normally but running `nargo dap` without any additional flags. + +### Options + +| Option | Description | +| --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | +| `--preflight-check` | If present, dap runs in preflight check mode. | +| `--preflight-project-folder ` | Absolute path to the project to debug for preflight check. | +| `--preflight-prover-name ` | Name of prover file to use for preflight check | +| `--preflight-generate-acir` | Optional. If present, compile in ACIR mode while running preflight check. | +| `--preflight-skip-instrumentation` | Optional. If present, compile without introducing debug instrumentation while running preflight check. | +| `-h, --help` | Print help. | diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/nargo_commands.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/nargo_commands.md new file mode 100644 index 000000000000..537363599664 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/nargo_commands.md @@ -0,0 +1,580 @@ +--- +title: Nargo +description: + Noir CLI Commands for Noir Prover and Verifier to create, execute, prove and verify programs, + generate Solidity verifier smart contract and compile into JSON file containing ACIR + representation and ABI of circuit. +keywords: + [ + Nargo, + Noir CLI, + Noir Prover, + Noir Verifier, + generate Solidity verifier, + compile JSON file, + ACIR representation, + ABI of circuit, + TypeScript, + ] +sidebar_position: 0 +--- + +# Command-Line Help for `nargo` + +This document contains the help content for the `nargo` command-line program. + +**Command Overview:** + +* [`nargo`↴](#nargo) +* [`nargo check`↴](#nargo-check) +* [`nargo fmt`↴](#nargo-fmt) +* [`nargo compile`↴](#nargo-compile) +* [`nargo new`↴](#nargo-new) +* [`nargo init`↴](#nargo-init) +* [`nargo execute`↴](#nargo-execute) +* [`nargo debug`↴](#nargo-debug) +* [`nargo test`↴](#nargo-test) +* [`nargo info`↴](#nargo-info) +* [`nargo lsp`↴](#nargo-lsp) +* [`nargo generate-completion-script`↴](#nargo-generate-completion-script) + +## `nargo` + +Noir's package manager + +**Usage:** `nargo ` + +###### **Subcommands:** + +* `check` — Check a local package and all of its dependencies for errors +* `fmt` — Format the Noir files in a workspace +* `compile` — Compile the program and its secret execution trace into ACIR format +* `new` — Create a Noir project in a new directory +* `init` — Create a Noir project in the current directory +* `execute` — Executes a circuit to calculate its return value +* `debug` — Executes a circuit in debug mode +* `test` — Run the tests for this program +* `info` — Provides detailed information on each of a program's function (represented by a single circuit) +* `lsp` — Starts the Noir LSP server +* `generate-completion-script` — Generates a shell completion script for your favorite shell + +###### **Options:** + + + + +## `nargo check` + +Check a local package and all of its dependencies for errors + +**Usage:** `nargo check [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to run the command on. By default run on the first one found moving up along the ancestors of the current directory +* `--workspace` — Run on all packages in the workspace + + Possible values: `true`, `false` + +* `--overwrite` — Force overwrite of existing files + + Possible values: `true`, `false` + +* `--expression-width ` — Specify the backend expression width that should be targeted +* `--bounded-codegen` — 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 + + Default value: `false` + + Possible values: `true`, `false` + +* `--force` — Force a full recompilation + + Possible values: `true`, `false` + +* `--print-acir` — Display the ACIR for compiled circuit + + Possible values: `true`, `false` + +* `--deny-warnings` — Treat all warnings as errors + + Possible values: `true`, `false` + +* `--silence-warnings` — Suppress warnings + + Possible values: `true`, `false` + +* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" +* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check` — Flag to turn on the compiler check for missing Brillig call constraints. Warning: This can degrade compilation speed but will also find some correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check-lookback` — Flag to turn on the lookback feature of the Brillig call constraints check, allowing tracking argument values before the call happens preventing certain rare false positives (leads to a slowdown on large rollout functions) + + Possible values: `true`, `false` + +* `--pedantic-solving` — Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. This is disabled by default + + Default value: `false` + + Possible values: `true`, `false` + + + + +## `nargo fmt` + +Format the Noir files in a workspace + +**Usage:** `nargo fmt [OPTIONS]` + +###### **Options:** + +* `--check` — Run noirfmt in check mode + + Possible values: `true`, `false` + +* `--package ` — The name of the package to run the command on. By default run on the first one found moving up along the ancestors of the current directory +* `--workspace` — Run on all packages in the workspace + + Possible values: `true`, `false` + + + + +## `nargo compile` + +Compile the program and its secret execution trace into ACIR format + +**Usage:** `nargo compile [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to run the command on. By default run on the first one found moving up along the ancestors of the current directory +* `--workspace` — Run on all packages in the workspace + + Possible values: `true`, `false` + +* `--expression-width ` — Specify the backend expression width that should be targeted +* `--bounded-codegen` — 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 + + Default value: `false` + + Possible values: `true`, `false` + +* `--force` — Force a full recompilation + + Possible values: `true`, `false` + +* `--print-acir` — Display the ACIR for compiled circuit + + Possible values: `true`, `false` + +* `--deny-warnings` — Treat all warnings as errors + + Possible values: `true`, `false` + +* `--silence-warnings` — Suppress warnings + + Possible values: `true`, `false` + +* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" +* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check` — Flag to turn on the compiler check for missing Brillig call constraints. Warning: This can degrade compilation speed but will also find some correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check-lookback` — Flag to turn on the lookback feature of the Brillig call constraints check, allowing tracking argument values before the call happens preventing certain rare false positives (leads to a slowdown on large rollout functions) + + Possible values: `true`, `false` + +* `--pedantic-solving` — Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. This is disabled by default + + Default value: `false` + + Possible values: `true`, `false` + + + + +## `nargo new` + +Create a Noir project in a new directory + +**Usage:** `nargo new [OPTIONS] ` + +###### **Arguments:** + +* `` — The path to save the new project + +###### **Options:** + +* `--name ` — Name of the package [default: package directory name] +* `--lib` — Use a library template + + Possible values: `true`, `false` + +* `--bin` — Use a binary template [default] + + Possible values: `true`, `false` + +* `--contract` — Use a contract template + + Possible values: `true`, `false` + + + + +## `nargo init` + +Create a Noir project in the current directory + +**Usage:** `nargo init [OPTIONS]` + +###### **Options:** + +* `--name ` — Name of the package [default: current directory name] +* `--lib` — Use a library template + + Possible values: `true`, `false` + +* `--bin` — Use a binary template [default] + + Possible values: `true`, `false` + +* `--contract` — Use a contract template + + Possible values: `true`, `false` + + + + +## `nargo execute` + +Executes a circuit to calculate its return value + +**Usage:** `nargo execute [OPTIONS] [WITNESS_NAME]` + +###### **Arguments:** + +* `` — Write the execution witness to named file + +Defaults to the name of the package being executed. + +###### **Options:** + +* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover + + Default value: `Prover` +* `--package ` — The name of the package to run the command on. By default run on the first one found moving up along the ancestors of the current directory +* `--workspace` — Run on all packages in the workspace + + Possible values: `true`, `false` + +* `--expression-width ` — Specify the backend expression width that should be targeted +* `--bounded-codegen` — 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 + + Default value: `false` + + Possible values: `true`, `false` + +* `--force` — Force a full recompilation + + Possible values: `true`, `false` + +* `--print-acir` — Display the ACIR for compiled circuit + + Possible values: `true`, `false` + +* `--deny-warnings` — Treat all warnings as errors + + Possible values: `true`, `false` + +* `--silence-warnings` — Suppress warnings + + Possible values: `true`, `false` + +* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" +* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check` — Flag to turn on the compiler check for missing Brillig call constraints. Warning: This can degrade compilation speed but will also find some correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check-lookback` — Flag to turn on the lookback feature of the Brillig call constraints check, allowing tracking argument values before the call happens preventing certain rare false positives (leads to a slowdown on large rollout functions) + + Possible values: `true`, `false` + +* `--pedantic-solving` — Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. This is disabled by default + + Default value: `false` + + Possible values: `true`, `false` + +* `--oracle-resolver ` — JSON RPC url to solve oracle calls + + + +## `nargo debug` + +Executes a circuit in debug mode + +**Usage:** `nargo debug [OPTIONS] [WITNESS_NAME]` + +###### **Arguments:** + +* `` — Write the execution witness to named file + +###### **Options:** + +* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover + + Default value: `Prover` +* `--package ` — The name of the package to execute +* `--expression-width ` — Specify the backend expression width that should be targeted +* `--bounded-codegen` — 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 + + Default value: `false` + + Possible values: `true`, `false` + +* `--force` — Force a full recompilation + + Possible values: `true`, `false` + +* `--print-acir` — Display the ACIR for compiled circuit + + Possible values: `true`, `false` + +* `--deny-warnings` — Treat all warnings as errors + + Possible values: `true`, `false` + +* `--silence-warnings` — Suppress warnings + + Possible values: `true`, `false` + +* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" +* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check` — Flag to turn on the compiler check for missing Brillig call constraints. Warning: This can degrade compilation speed but will also find some correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check-lookback` — Flag to turn on the lookback feature of the Brillig call constraints check, allowing tracking argument values before the call happens preventing certain rare false positives (leads to a slowdown on large rollout functions) + + Possible values: `true`, `false` + +* `--pedantic-solving` — Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. This is disabled by default + + Default value: `false` + + Possible values: `true`, `false` + +* `--acir-mode` — Force ACIR output (disabling instrumentation) + + Possible values: `true`, `false` + +* `--skip-instrumentation ` — Disable vars debug instrumentation (enabled by default) + + Possible values: `true`, `false` + + + + +## `nargo test` + +Run the tests for this program + +**Usage:** `nargo test [OPTIONS] [TEST_NAMES]...` + +###### **Arguments:** + +* `` — If given, only tests with names containing this string will be run + +###### **Options:** + +* `--show-output` — Display output of `println` statements + + Possible values: `true`, `false` + +* `--exact` — Only run tests that match exactly + + Possible values: `true`, `false` + +* `--package ` — The name of the package to run the command on. By default run on the first one found moving up along the ancestors of the current directory +* `--workspace` — Run on all packages in the workspace + + Possible values: `true`, `false` + +* `--expression-width ` — Specify the backend expression width that should be targeted +* `--bounded-codegen` — 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 + + Default value: `false` + + Possible values: `true`, `false` + +* `--force` — Force a full recompilation + + Possible values: `true`, `false` + +* `--print-acir` — Display the ACIR for compiled circuit + + Possible values: `true`, `false` + +* `--deny-warnings` — Treat all warnings as errors + + Possible values: `true`, `false` + +* `--silence-warnings` — Suppress warnings + + Possible values: `true`, `false` + +* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" +* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check` — Flag to turn on the compiler check for missing Brillig call constraints. Warning: This can degrade compilation speed but will also find some correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check-lookback` — Flag to turn on the lookback feature of the Brillig call constraints check, allowing tracking argument values before the call happens preventing certain rare false positives (leads to a slowdown on large rollout functions) + + Possible values: `true`, `false` + +* `--pedantic-solving` — Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. This is disabled by default + + Default value: `false` + + Possible values: `true`, `false` + +* `--oracle-resolver ` — JSON RPC url to solve oracle calls +* `--test-threads ` — Number of threads used for running tests in parallel + + Default value: `4` +* `--format ` — Configure formatting of output + + Possible values: + - `pretty`: + Print verbose output + - `terse`: + Display one character per test + - `json`: + Output a JSON Lines document + +* `-q`, `--quiet` — Display one character per test instead of one line + + Possible values: `true`, `false` + + + + +## `nargo info` + +Provides detailed information on each of a program's function (represented by a single circuit) + +Current information provided per circuit: 1. The number of ACIR opcodes 2. Counts the final number gates in the circuit used by a backend + +**Usage:** `nargo info [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to run the command on. By default run on the first one found moving up along the ancestors of the current directory +* `--workspace` — Run on all packages in the workspace + + Possible values: `true`, `false` + +* `--profile-execution` + + Possible values: `true`, `false` + +* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover + + Default value: `Prover` +* `--expression-width ` — Specify the backend expression width that should be targeted +* `--bounded-codegen` — 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 + + Default value: `false` + + Possible values: `true`, `false` + +* `--force` — Force a full recompilation + + Possible values: `true`, `false` + +* `--print-acir` — Display the ACIR for compiled circuit + + Possible values: `true`, `false` + +* `--deny-warnings` — Treat all warnings as errors + + Possible values: `true`, `false` + +* `--silence-warnings` — Suppress warnings + + Possible values: `true`, `false` + +* `--debug-comptime-in-file ` — Enable printing results of comptime evaluation: provide a path suffix for the module to debug, e.g. "package_name/src/main.nr" +* `--skip-underconstrained-check` — Flag to turn off the compiler check for under constrained values. Warning: This can improve compilation speed but can also lead to correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check` — Flag to turn on the compiler check for missing Brillig call constraints. Warning: This can degrade compilation speed but will also find some correctness errors. This check should always be run on production code + + Possible values: `true`, `false` + +* `--enable-brillig-constraints-check-lookback` — Flag to turn on the lookback feature of the Brillig call constraints check, allowing tracking argument values before the call happens preventing certain rare false positives (leads to a slowdown on large rollout functions) + + Possible values: `true`, `false` + +* `--pedantic-solving` — Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. This is disabled by default + + Default value: `false` + + Possible values: `true`, `false` + + + + +## `nargo lsp` + +Starts the Noir LSP server + +Starts an LSP server which allows IDEs such as VS Code to display diagnostics in Noir source. + +VS Code Noir Language Support: https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir + +**Usage:** `nargo lsp` + + + +## `nargo generate-completion-script` + +Generates a shell completion script for your favorite shell + +**Usage:** `nargo generate-completion-script ` + +###### **Arguments:** + +* `` — The shell to generate completions for. One of: bash, elvish, fish, powershell, zsh + + + +
+ + + This document was generated automatically by + clap-markdown. + + diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/noir_codegen.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/noir_codegen.md new file mode 100644 index 000000000000..e4c362f96100 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/reference/noir_codegen.md @@ -0,0 +1,116 @@ +--- +title: Noir Codegen for TypeScript +description: Learn how to use Noir codegen to generate TypeScript bindings +keywords: [Nargo, Noir, compile, TypeScript] +sidebar_position: 3 +--- + +When using TypeScript, it is extra work to interpret Noir program outputs in a type-safe way. Third party libraries may exist for popular Noir programs, but they are either hard to find or unmaintained. + +Now you can generate TypeScript bindings for your Noir programs in two steps: + +1. Exporting Noir functions using `nargo export` +2. Using the TypeScript module `noir_codegen` to generate TypeScript binding + +**Note:** you can only export functions from a Noir *library* (not binary or contract program types). + +## Installation + +### Your TypeScript project + +If you don't already have a TypeScript project you can add the module with `yarn` (or `npm`), then initialize it: + +```bash +yarn add typescript -D +npx tsc --init +``` + +### Add TypeScript module - `noir_codegen` + +The following command will add the module to your project's devDependencies: + +```bash +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](../getting_started/noir_installation.md). + +If you're in a new project, make a `circuits` folder and create a new Noir library: + +```bash +mkdir circuits && cd circuits +nargo new --lib myNoirLib +``` + +## Usage + +### Export ABI of specified functions + +First go to the `.nr` files in your Noir library, and add the `#[export]` macro to each function that you want to use in TypeScript. + +```rust +#[export] +fn your_function(... +``` + +From your Noir library (where `Nargo.toml` is), run the following command: + +```bash +nargo export +``` + +You will now have an `export` directory with a .json file per exported function. + +You can also specify the directory of Noir programs using `--program-dir`, for example: + +```bash +nargo export --program-dir=./circuits/myNoirLib +``` + +### Generate TypeScript bindings from exported functions + +To use the `noir-codegen` package we added to the TypeScript project: + +```bash +yarn noir-codegen ./export/your_function.json +``` + +This creates an `exports` directory with an `index.ts` file containing all exported functions. + +**Note:** adding `--out-dir` allows you to specify an output dir for your TypeScript bindings to go. Eg: + +```bash +yarn noir-codegen ./export/*.json --out-dir ./path/to/output/dir +``` + +## Example .nr function to .ts output + +Consider a Noir library with this function: + +```rust +#[export] +fn not_equal(x: Field, y: Field) -> bool { + x != y +} +``` + +After the export and codegen steps, you should have an `index.ts` like: + +```typescript +export type Field = string; + + +export const is_equal_circuit: CompiledCircuit = +{"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"private"}],"return_type":{"abi_type":{"kind":"boolean"},"visibility":"private"}},"bytecode":"H4sIAAAAAAAA/7WUMQ7DIAxFQ0Krrr2JjSGYLVcpKrn/CaqqDQN12WK+hPBgmWd/wEyHbF1SS923uhOs3pfoChI+wKXMAXzIKyNj4PB0TFTYc0w5RUjoqeAeEu1wqK0F54RGkWvW44LPzExnlkbMEs4JNZmN8PxS42uHv82T8a3Jeyn2Ks+VLPcO558HmyLMCDOXAXXtpPt4R/Rt9T36ss6dS9HGPx/eG17nGegKBQAA"}; + +export async function is_equal(x: Field, y: Field, foreignCallHandler?: ForeignCallHandler): Promise { + const program = new Noir(is_equal_circuit); + const args: InputMap = { x, y }; + const { returnValue } = await program.execute(args, foreignCallHandler); + return returnValue as boolean; +} +``` + +Now the `is_equal()` function and relevant types are readily available for use in TypeScript. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/cspell.json b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/cspell.json new file mode 100644 index 000000000000..6791ea20f27e --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/cspell.json @@ -0,0 +1,8 @@ +{ + "words": [ + "ACIR", + "flamegraph", + "flamegraphs", + "lookback" + ] +} diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/debugger.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/debugger.md new file mode 100644 index 000000000000..200b5fc423ac --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/debugger.md @@ -0,0 +1,26 @@ +--- +title: Debugger +description: Learn about the Noir Debugger, in its REPL or VS Code versions. +keywords: [Nargo, VSCode, Visual Studio Code, REPL, Debugger] +sidebar_position: 2 +--- + +# Noir Debugger + +There are currently two ways of debugging Noir programs: + +1. From VS Code, via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). +2. Via the REPL debugger, which ships with Nargo. + +In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/noir_installation.md) and vscode-noir: + +- Noir & Nargo ≥0.28.0 +- Noir's VS Code extension ≥0.0.11 + +:::info +At the moment, the debugger supports debugging binary projects, but not contracts. +::: + +We cover the VS Code Noir debugger more in depth in [its VS Code debugger how-to guide](../how_to/debugger/debugging_with_vs_code.md) and [the reference](../reference/debugger/debugger_vscode.md). + +The REPL debugger is discussed at length in [the REPL debugger how-to guide](../how_to/debugger/debugging_with_the_repl.md) and [the reference](../reference/debugger/debugger_repl.md). diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/language_server.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/language_server.md new file mode 100644 index 000000000000..81e0356ef8a1 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/language_server.md @@ -0,0 +1,43 @@ +--- +title: Language Server +description: Learn about the Noir Language Server, how to install the components, and configuration that may be required. +keywords: [Nargo, Language Server, LSP, VSCode, Visual Studio Code] +sidebar_position: 0 +--- + +This section helps you install and configure the Noir Language Server. + +The Language Server Protocol (LSP) has two components, the [Server](#language-server) and the [Client](#language-client). Below we describe each in the context of Noir. + +## Language Server + +The Server component is provided by the Nargo command line tool that you installed at the beginning of this guide. +As long as Nargo is installed and you've used it to run other commands in this guide, it should be good to go! + +If you'd like to verify that the `nargo lsp` command is available, you can run `nargo --help` and look for `lsp` in the list of commands. If you see it, you're using a version of Noir with LSP support. + +## Language Client + +The Client component is usually an editor plugin that launches the Server. It communicates LSP messages between the editor and the Server. For example, when you save a file, the Client will alert the Server, so it can try to compile the project and report any errors. + +Currently, Noir provides a Language Client for Visual Studio Code via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). + +> **Note:** Noir's Language Server Protocol support currently assumes users' VSCode workspace root to be the same as users' Noir project root (i.e. where Nargo.toml lies). +> +> If LSP features seem to be missing / malfunctioning, make sure you are opening your Noir project directly (instead of as a sub-folder) in your VSCode instance. + +When your language server is running correctly and the VSCode plugin is installed, you should see handy codelens buttons for compilation, measuring circuit size, execution, and tests: + +![Compile and Execute](@site/static/img/codelens_compile_execute.png) +![Run test](@site/static/img/codelens_run_test.png) + +You should also see your tests in the `testing` panel: + +![Testing panel](@site/static/img/codelens_testing_panel.png) + +### Configuration + +- **Noir: Enable LSP** - If checked, the extension will launch the Language Server via `nargo lsp` and communicate with it. +- **Noir: Nargo Flags** - Additional flags may be specified if you require them to be added when the extension calls `nargo lsp`. +- **Noir: Nargo Path** - An absolute path to a Nargo binary with the `lsp` command. This may be useful if Nargo is not within the `PATH` of your editor. +- **Noir > Trace: Server** - Setting this to `"messages"` or `"verbose"` will log LSP messages between the Client and Server. Useful for debugging. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/profiler.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/profiler.md new file mode 100644 index 000000000000..1f906226f8ce --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/profiler.md @@ -0,0 +1,115 @@ +--- +title: Noir Profiler +description: Learn about the Noir Profiler, how to generate execution flamegraphs, identify bottlenecks, and visualize optimizations. +keywords: [profiling, profiler, flamegraph] +sidebar_position: 0 +--- + +## Noir Profiler + +`noir-profiler` is a sampling profiler designed to analyze and visualize Noir programs. It assists developers to identify bottlenecks by mapping execution data back to the original source code. + +### Installation + +`noir-profiler` comes out of the box with [noirup](../getting_started/noir_installation.md). Test that you have the profiler installed by running `noir-profiler --version`. + +### Usage + +Let's start by creating a simple Noir program. All this program aims to do is zero out an array past some dynamic index. + +```rust +fn main(ptr: pub u32, mut array: [u32; 32]) -> pub [u32; 32] { + for i in 0..32 { + if i > ptr { + array[i] = 0; + } + } + array +} +``` +You can use these values for the `Prover.toml`: +```toml +ptr = 1 +array = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] +``` + +Running `nargo info` we can get some information about the opcodes produced by this program, but it doesn't give us a lot of info on its own. Compile and execute this program normally using `nargo compile` and `nargo execute`. + +### Generating an ACIR opcode flamegraph + +The program on its own is quite high-level. Let's get a more granular look at what is happening by using `noir-profiler`. + +After compiling the program, run the following: +```sh +noir-profiler opcodes --artifact-path ./target/program.json --output ./target/ +``` +Below you can see an example flamegraph with a total 387 opcodes (using `nargo` version 1.0.0-beta.2): +![ACIR Flamegraph Unoptimized](@site/static/img/tooling/profiler/acir-flamegraph-unoptimized.png) + +You should now have a flamegraph that maps ACIR opcodes to their corresponding locations in the source code. We strongly recommend generating these graphs yourself as you follow this guide. Opening the flamegraph in a browser provides a more interactive experience, allowing you to click into and examine different regions of the graph. Simply viewing the image file won't offer the same level of insight. + +We can see that the majority of opcodes come from the write to `array[i]`. Now that we have some more information about our program's bottlenecks, let's optimize it. + +#### Transform conditional writes into reads + +We can improve our circuit's efficiency using [unconstrained functions](../noir/concepts/unconstrained.md). + +Let's replace expensive array writes with array gets with the new code below: +```rust +fn main(ptr: pub u32, array: [u32; 32]) -> pub [u32; 32] { + // Safety: Sets all elements after `ptr` in `array` to zero. + let zeroed_array = unsafe { zero_out_array(ptr, array) }; + for i in 0..32 { + if i > ptr { + assert_eq(zeroed_array[i], 0); + } else { + assert_eq(zeroed_array[i], array[i]); + } + } + zeroed_array +} + +unconstrained fn zero_out_array(ptr: u32, mut array: [u32; 32]) -> [u32; 32] { + for i in 0..32 { + if i > ptr { + array[i] = 0; + } + } + array +} +``` +We chose to instead write our array inside of the unconstrained function. Then inside of our circuit we assert on every value in the array returned from the unconstrained function. + +This new program produces the following ACIR opcodes flamegraph with a total of 284 opcodes: +![ACIR Flamegraph Optimized](@site/static/img/tooling/profiler/acir-flamegraph-optimized.png) + +In the above image we searched for the ACIR opcodes due to `i > ptr` in the source code. Trigger a search by clicking on "Search" in the top right corner of the flamegraph. In the bottom right corner of the image above, you will note that the flamegraph displays the percentage of all opcodes associated with that search. Searching for `memory::op` in the optimized flamegraph will result in no matches. This is due to no longer using a dynamic array in our circuit. By dynamic array, we are referring to using a dynamic index (values reliant upon witness inputs) when working with arrays. Most of the memory operations, have now been replaced with arithmetic operations as we are reading two arrays from known constant indices. + +### Generate a backend gates flamegraph + +Unfortunately, ACIR opcodes do not give us a full picture of where the cost of this program lies. +The `gates` command also accepts a backend binary. In the [quick start guide](../getting_started/quick_start.md#proving-backend) you can see how to get started with the [Barretenberg proving backend](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg). + +Run the following command: +```sh +noir-profiler gates --artifact-path ./target/program.json --backend-path bb --output ./target +``` +`--backend-path` accepts a path to the backend binary. In the above command we assume that you have the backend binary path saved in your PATH. If you do not, you will have to pass the binary's absolute path. + +This produces the following flamegraph with 3,737 total backend gates (using `bb` version 0.76.4): +![Gates Flamegraph Unoptimized](@site/static/img/tooling/profiler/gates-flamegraph-unoptimized.png) + +Searching for ACIR `memory::op` opcodes, they look to cause about 18.2% of the backend gates. + +You will notice that the majority of the backend gates come from the ACIR range opcodes. This is due to the way UltraHonk handles range constraints, which is the backend used in this example. UltraHonk uses lookup tables internally for its range gates. These can take up the majority of the gates for a small circuit, but whose impact becomes more meaningful in larger circuits. If our array was much larger, range gates would become a much smaller percentage of our total circuit. +Here is an example backend gates flamegraph for the same program in this guide but with an array of size 2048: +![Gates Flamegraph Unoptimized 2048](@site/static/img/tooling/profiler/gates-flamegraph-unoptimized-2048.png) +Every backend implements ACIR opcodes differently, so it is important to profile both the ACIR and the backend gates to get a full picture. + +Now let's generate a graph for our optimized circuit with an array of size 32. We get the following flamegraph that produces 3,062 total backend gates: +![Gates Flamegraph Optimized](@site/static/img/tooling/profiler/gates-flamegraph-optimized.png) + +In the optimized flamegraph, we searched for the backend gates due to `i > ptr` in the source code. The backend gates associated with this call stack were only 3.8% of the total backend gates. If we look back to the ACIR flamegraph, that same code was the cause of 43.3% ACIR opcodes. This discrepancy reiterates the earlier point about profiling both the ACIR opcodes and backend gates. + +For posterity, here is the flamegraph for the same program with a size 2048 array: +![Gates Flamegraph Optimized 2048](@site/static/img/tooling/profiler/gates-flamegraph-optimized-2048.png) diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/security.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/security.md new file mode 100644 index 000000000000..e14481efc317 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/security.md @@ -0,0 +1,61 @@ +--- +title: Security checks +description: Security checks currently provided by the compiler +keywords: [Nargo, Security, Brillig, Unconstrained] +sidebar_position: 2 +--- + +# Security checks + +Two compilation security passes exist currently to ensure soundness of compiled code. Problems they catch are reported as "bugs" (as opposed to errors) in the compiler output. For example: + +``` +**bug**: Brillig function call isn't properly covered by a manual constraint +``` + +### Independent subgraph detection + +This pass examines the instruction flow graph to see if the final function would involve values that don't come from any provided inputs and don't result in the outputs. That would mean there are no constraints ensuring the required continuity. + +This check is enabled by default and can be disabled by passing the `--skip-underconstrained-check` option to `nargo`. + +### Brillig manual constraint coverage + +The results of a Brillig function call must be constrained to ensure security, adhering to these rules: every resulting value (including every array element of a resulting array) has to be involved in a later constraint (i.e. assert, range check) against either one of the arguments of the call, or a constant. In this context, involvement means that a descendant value (e.g. a result of a chain of operations over the value) of a result has to be checked against a descendant value of an argument. For example: + +```rust +unconstrained fn factor(v0: Field) -> [Field; 2] { + ... +} + +fn main f0 (foo: Field) -> [Field; 2] { + let factored = unsafe { factor(foo) }; + assert(factored[0] * factored[1] == foo); + return factored +} +``` + +Here, the results of `factor` are two elements of the returned array. The value `factored[0] * factored[1]` is a descendant of both of them, so both are involved in a constraint against the argument value in the `assert`. Hence, the call to an unconstrained function is properly covered. + +This pass checks if the constraint coverage of Brillig calls is sufficient in these terms. + +The check is at the moment disabled by default due to performance concerns and can be enabled by passing the `--enable-brillig-constraints-check` option to `nargo`. + +#### Lookback option + +Certain false positives of this check can be avoided by providing the `--enable-brillig-constraints-check-lookback` option to `nargo`, which can be slower at compile-time but additionally ensures that descendants of call argument values coming from operations *preceding* the call itself would be followed. For example, consider this case: + +```rust +unconstrained fn unconstrained_add(v0: Field, v1: Field) -> Field { + v0 + v1 +} + +fn main f0 (v0: Field, v1: Field) { + let foo = v0 + v1; + let bar = unsafe { unconstrained_add(v0, v1) }; + assert(foo == bar); + return bar +} +``` + +Normally, the addition operation over `v0` and `v1` happening before the call itself would prevent the call from being (correctly) considered properly constrained. With this option enabled, the false positive goes away at the cost of the check becoming somewhat less performant on large unrolled loops. diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/testing.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/testing.md new file mode 100644 index 000000000000..866677da5679 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tooling/testing.md @@ -0,0 +1,79 @@ +--- +title: Testing in Noir +description: Learn how to use Nargo to test your Noir program in a quick and easy way +keywords: [Nargo, testing, Noir, compile, test] +sidebar_position: 1 +--- + +You can test your Noir programs using Noir circuits. + +Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if +you run `nargo test`. + +For example if you have a program like: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test] +fn test_add() { + assert(add(2,2) == 4); + assert(add(0,1) == 1); + assert(add(1,0) == 1); +} +``` + +Running `nargo test` will test that the `test_add` function can be executed while satisfying all +the constraints which allows you to test that add returns the expected values. Test functions can't +have any arguments currently. + +### Test fail + +You can write tests that are expected to fail by using the decorator `#[test(should_fail)]`. For example: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test(should_fail)] +fn test_add() { + assert(add(2,2) == 5); +} +``` + +You can be more specific and make it fail with a specific reason by using `should_fail_with = ""`: + +```rust +fn main(african_swallow_avg_speed : Field) { + assert(african_swallow_avg_speed == 65, "What is the airspeed velocity of an unladen swallow"); +} + +#[test] +fn test_king_arthur() { + main(65); +} + +#[test(should_fail_with = "What is the airspeed velocity of an unladen swallow")] +fn test_bridgekeeper() { + main(32); +} +``` + +The string given to `should_fail_with` doesn't need to exactly match the failure reason, it just needs to be a substring of it: + +```rust +fn main(african_swallow_avg_speed : Field) { + assert(african_swallow_avg_speed == 65, "What is the airspeed velocity of an unladen swallow"); +} + +#[test] +fn test_king_arthur() { + main(65); +} + +#[test(should_fail_with = "airspeed velocity")] +fn test_bridgekeeper() { + main(32); +} +``` \ No newline at end of file diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tutorials/noirjs_app.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tutorials/noirjs_app.md new file mode 100644 index 000000000000..40e5d94180de --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.3/tutorials/noirjs_app.md @@ -0,0 +1,304 @@ +--- +title: Building a web app with Noir and Barretenberg +description: Learn how to setup a new app that uses Noir to generate and verify zero-knowledge SNARK proofs in a typescript or javascript environment. +keywords: [how to, guide, javascript, typescript, noir, barretenberg, zero-knowledge, proofs, app] +sidebar_position: 0 +pagination_next: noir/concepts/data_types/index +--- + +NoirJS is a Typescript package meant to work both in a browser and a server environment. + +In this tutorial, we will combine NoirJS with Aztec's Barretenberg backend to build a simple web app. From here, you should get an idea on how to proceed with your own Noir projects! + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). + +## Dependencies + +Before we start, we want to make sure we have Node installed. For convenience (and speed), we can just install [Bun](https://bun.sh) as our package manager, and Node will work out-of-the-box: + +```bash +curl -fsSL https://bun.sh/install | bash +``` + +Let's go barebones. Doing the bare minimum is not only simple, but also allows you to easily adapt it to almost any frontend framework. + +Barebones means we can immediately start with the dependencies even on an empty folder 😈: + +```bash +bun i @noir-lang/noir_wasm@1.0.0-beta.2 @noir-lang/noir_js@1.0.0-beta.2 @aztec/bb.js@0.72.1 +``` + +Wait, what are these dependencies? + +- `noir_wasm` is the `wasm` version of the Noir compiler. Although most developers prefer to use `nargo` for compiling, there's nothing wrong with `noir_wasm`. We like `noir_wasm`. +- `noir_js` is the main Noir package. It will execute our program, and generate the witness that will be sent to the backend. +- `bb.js` is the Typescript interface for Aztec's Barretenberg proving backend. It also uses the `wasm` version in order to run on the browser. + +:::info + +In this guide, we will install versions pinned to 1.0.0-beta.2. These work with Barretenberg version 0.72.1, so we are using that one version too. Feel free to try with older or later versions, though! + +::: + +## Setting up our Noir program + +ZK is a powerful technology. An app that reveals computational correctness but doesn't reveal some of its inputs is almost unbelievable, yet Noir makes it as easy as a single line of code. + +:::tip + +It's not just you. We also enjoy syntax highlighting. [Check out the Language Server](../tooling/language_server.md) + +::: + +All you need is a `main.nr` and a `Nargo.toml` file. You can follow the [noirup](../getting_started/noir_installation.md) installation and just run `noirup -v 1.0.0-beta.2`, or just create them by hand: + +```bash +mkdir -p circuit/src +touch circuit/src/main.nr circuit/Nargo.toml +``` + +To make our program interesting, let's give it a real use-case scenario: Bob wants to prove he is older than 18, without disclosing his age. Open `main.nr` and write: + +```rust +fn main(age: u8) { + assert(age >= 18); +} +``` + +This program accepts a private input called age, and simply proves this number is higher than 18. But to run this code, we need to give the compiler a `Nargo.toml` with at least a name and a type: + +```toml +[package] +name = "circuit" +type = "bin" +``` + +This is all that we need to get started with Noir. + +![my heart is ready for you, noir.js](@site/static/img/memes/titanic.jpeg) + +## Setting up our app + +Remember when apps only had one `html` and one `js` file? Well, that's enough for Noir webapps. Let's create them: + +```bash +touch index.html index.js +``` + +And add something useful to our HTML file: + +```html + + + + + + +

Noir app

+
+ + +
+
+

Logs

+

Proof

+
+ + +``` + +It _could_ be a beautiful UI... Depending on which universe you live in. In any case, we're using some scary CSS to make two boxes that will show cool things on the screen. + +As for the JS, real madmen could just `console.log` everything, but let's say we want to see things happening (the true initial purpose of JS... right?). Here's some boilerplate for that. Just paste it in `index.js`: + +```js +const show = (id, content) => { + const container = document.getElementById(id); + container.appendChild(document.createTextNode(content)); + container.appendChild(document.createElement("br")); +}; + +document.getElementById("submit").addEventListener("click", async () => { + try { + // noir goes here + } catch { + show("logs", "Oh 💔"); + } +}); + +``` + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit + └── src + └── main.nr + Nargo.toml + index.js + package.json + index.html + ...etc +``` + +::: + +## Compile compile compile + +Finally we're up for something cool. But before we can execute a Noir program, we need to compile it into ACIR: an abstract representation. Here's where `noir_wasm` comes in. + +`noir_wasm` expects a filesystem so it can resolve dependencies. While we could use the `public` folder, let's just import those using the nice `?url` syntax provided by vite. At the top of the file: + +```js +import { compile, createFileManager } from "@noir-lang/noir_wasm" + +import main from "./circuit/src/main.nr?url"; +import nargoToml from "./circuit/Nargo.toml?url"; +``` + +Compiling on the browser is common enough that `createFileManager` already gives us a nice in-memory filesystem we can use. So all we need to compile is fetching these files, writing them to our filesystem, and compile. Add this function: + +```js +export async function getCircuit() { + const fm = createFileManager("/"); + const { body } = await fetch(main); + const { body: nargoTomlBody } = await fetch(nargoToml); + + fm.writeFile("./src/main.nr", body); + fm.writeFile("./Nargo.toml", nargoTomlBody); + return await compile(fm); +} +``` + +:::tip + +As you can imagine, with `node` it's all conveniently easier since you get native access to `fs`... + +::: + +## Some more JS + +We're starting with the good stuff now. We want to execute our circuit to get the witness, and then feed that witness to Barretenberg. Luckily, both packages are quite easy to work with. Let's import them at the top of the file: + +```js +import { UltraHonkBackend } from '@aztec/bb.js'; +import { Noir } from '@noir-lang/noir_js'; +``` + +And instantiate them inside our try-catch block: + +```ts +// try { +const { program } = await getCircuit(); +const noir = new Noir(program); +const backend = new UltraHonkBackend(program.bytecode); +// } +``` + +:::warning + +WASMs are not always easy to work with. In our case, `vite` likes serving them with the wrong MIME type. There are different fixes but we found the easiest one is just YOLO instantiating the WASMs manually. Paste this at the top of the file, just below the other imports, and it will work just fine: + +```js +import initNoirC from "@noir-lang/noirc_abi"; +import initACVM from "@noir-lang/acvm_js"; +import acvm from "@noir-lang/acvm_js/web/acvm_js_bg.wasm?url"; +import noirc from "@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm?url"; +await Promise.all([initACVM(fetch(acvm)), initNoirC(fetch(noirc))]); +``` + +::: + +## Executing and proving + +Now for the app itself. We're capturing whatever is in the input when people press the submit button. Inside our `try` block, let's just grab that input and get its value. Noir will gladly execute it, and give us a witness: + +```js +const age = document.getElementById("age").value; +show("logs", "Generating witness... ⏳"); +const { witness } = await noir.execute({ age }); +show("logs", "Generated witness... ✅"); + +``` + +:::note + +For the remainder of the tutorial, everything will be happening inside the `try` block + +::: + +Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: + +```js +show("logs", "Generating proof... ⏳"); +const proof = await backend.generateProof(witness); +show("logs", "Generated proof... ✅"); +show("results", proof.proof); +``` + +Our program is technically **done** . You're probably eager to see stuff happening! To serve this in a convenient way, we can use a bundler like `vite` by creating a `vite.config.js` file: + +```bash +touch vite.config.js +``` + +`vite` helps us with a little catch: `bb.js` in particular uses top-level awaits which aren't supported everywhere. So we can add this to the `vite.config.js` to make the bundler optimize them: + +```js +export default { optimizeDeps: { esbuildOptions: { target: "esnext" } } }; +``` + +This should be enough for vite. We don't even need to install it, just run: + +```bash +bunx vite +``` + +If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. + +![Noir Webapp UI](@site/static/img/tutorials/noirjs_webapp/webapp1.png) + +Now, our circuit requires a private input `fn main(age: u8)`, and fails if it is less than 18. Let's see if it works. Submit any number above 18 (as long as it fits in 8 bits) and you should get a valid proof. Otherwise the proof won't even generate correctly. + +By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. + +## Verifying + +Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: + +```js +show('logs', 'Verifying proof... ⌛'); +const isValid = await backend.verifyProof(proof); +show("logs", `Proof is ${isValid ? "valid" : "invalid"}... ✅`); +``` + +You have successfully generated a client-side Noir web app! + +![coded app without math knowledge](@site/static/img/memes/flextape.jpeg) + +## Next steps + +At this point, you have a working ZK app that works on the browser. Actually, it works on a mobile phone too! + +If you want to continue learning by doing, here are some challenges for you: + +- Install [nargo](https://noir-lang.org/docs/getting_started/noir_installation) and write [Noir tests](../tooling/testing) +- Change the circuit to accept a [public input](../noir/concepts/data_types/#private--public-types) as the cutoff age. It could be different depending on the purpose, for example! +- Enjoy Noir's Rust-like syntax and write a struct `Country` that implements a trait `MinAge` with a method `get_min_age`. Then, make a struct `Person` have an `u8` as its age and a country of type `Country`. You can pass a `person` in JS just like a JSON object `person: { age, country: { min_age: 18 }}` + +The world is your stage, just have fun with ZK! You can see how noirjs is used in some common frameworks in the [awesome-noir repo](https://github.com/noir-lang/awesome-noir?tab=readme-ov-file#boilerplates). diff --git a/noir/noir-repo/docs/versioned_sidebars/version-v1.0.0-beta.3-sidebars.json b/noir/noir-repo/docs/versioned_sidebars/version-v1.0.0-beta.3-sidebars.json new file mode 100644 index 000000000000..b9ad026f69ff --- /dev/null +++ b/noir/noir-repo/docs/versioned_sidebars/version-v1.0.0-beta.3-sidebars.json @@ -0,0 +1,93 @@ +{ + "sidebar": [ + { + "type": "doc", + "id": "index" + }, + { + "type": "category", + "label": "Getting Started", + "items": [ + { + "type": "autogenerated", + "dirName": "getting_started" + } + ] + }, + { + "type": "category", + "label": "The Noir Language", + "items": [ + { + "type": "autogenerated", + "dirName": "noir" + } + ] + }, + { + "type": "html", + "value": "
", + "defaultStyle": true + }, + { + "type": "category", + "label": "How To Guides", + "items": [ + { + "type": "autogenerated", + "dirName": "how_to" + } + ] + }, + { + "type": "category", + "label": "Explainers", + "items": [ + { + "type": "autogenerated", + "dirName": "explainers" + } + ] + }, + { + "type": "category", + "label": "Tutorials", + "items": [ + { + "type": "autogenerated", + "dirName": "tutorials" + } + ] + }, + { + "type": "category", + "label": "Reference", + "items": [ + { + "type": "autogenerated", + "dirName": "reference" + } + ] + }, + { + "type": "category", + "label": "Tooling", + "items": [ + { + "type": "autogenerated", + "dirName": "tooling" + } + ] + }, + { + "type": "html", + "value": "
", + "defaultStyle": true + }, + { + "type": "doc", + "id": "migration_notes", + "label": "Migration notes" + } + ] +} diff --git a/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh b/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh index b227f82914e2..89df3ced1814 100755 --- a/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh +++ b/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh @@ -25,15 +25,15 @@ HEX_PUBLIC_INPUTS=$(head -c $PUBLIC_INPUT_BYTES $PROOF_PATH | od -An -v -t x1 | HEX_PROOF=$(tail -c +$(($PUBLIC_INPUT_BYTES + 1)) $PROOF_PATH | od -An -v -t x1 | tr -d $' \n') # Split public inputs into strings where each string represents a `bytes32`. -SPLIT_HEX_PUBLIC_INPUTS=$(sed -e 's/.\{64\}/0x&,/g' <<< $HEX_PUBLIC_INPUTS) +SPLIT_HEX_PUBLIC_INPUTS=$(sed -e 's/.\{64\}/0x&,/g' <<<$HEX_PUBLIC_INPUTS) # Spin up an anvil node to deploy the contract to anvil & DEPLOY_INFO=$(forge create UltraVerifier \ - --rpc-url "127.0.0.1:8545" \ - --private-key "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" \ - --json) + --rpc-url "127.0.0.1:8545" \ + --private-key "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" \ + --json) VERIFIER_ADDRESS=$(echo $DEPLOY_INFO | jq -r '.deployedTo') # Call the verifier contract with our proof. diff --git a/noir/noir-repo/examples/oracle_transcript/Nargo.toml b/noir/noir-repo/examples/oracle_transcript/Nargo.toml new file mode 100644 index 000000000000..3f333c912b09 --- /dev/null +++ b/noir/noir-repo/examples/oracle_transcript/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "oracle_transcript" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] diff --git a/noir/noir-repo/examples/oracle_transcript/Oracle.jsonl b/noir/noir-repo/examples/oracle_transcript/Oracle.jsonl new file mode 100644 index 000000000000..570e95907617 --- /dev/null +++ b/noir/noir-repo/examples/oracle_transcript/Oracle.jsonl @@ -0,0 +1,5 @@ +{"call":{"function":"void_field","inputs":[]},"result":{"values":["000000000000000000000000000000000000000000000000000000000000000a"]}} +{"call":{"function":"void_field","inputs":[]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000014"]}} +{"call":{"function":"field_field","inputs":["0000000000000000000000000000000000000000000000000000000000000002"]},"result":{"values":["000000000000000000000000000000000000000000000000000000000000001e"]}} +{"call":{"function":"field_field","inputs":["0000000000000000000000000000000000000000000000000000000000000003"]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000028"]}} +{"call":{"function":"struct_field","inputs":["000000000000000000000000000000000000000000000000000000000000012c","0000000000000000000000000000000000000000000000000000000000000320",["000000000000000000000000000000000000000000000000000000000000000a","0000000000000000000000000000000000000000000000000000000000000014","000000000000000000000000000000000000000000000000000000000000001e","0000000000000000000000000000000000000000000000000000000000000028"]]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000064"]}} diff --git a/noir/noir-repo/examples/oracle_transcript/Oracle.test.jsonl b/noir/noir-repo/examples/oracle_transcript/Oracle.test.jsonl new file mode 100644 index 000000000000..cebe10d307fc --- /dev/null +++ b/noir/noir-repo/examples/oracle_transcript/Oracle.test.jsonl @@ -0,0 +1,20 @@ +{"call":{"function":"create_mock","inputs":[["0000000000000000000000000000000000000000000000000000000000000076","000000000000000000000000000000000000000000000000000000000000006f","0000000000000000000000000000000000000000000000000000000000000069","0000000000000000000000000000000000000000000000000000000000000064","000000000000000000000000000000000000000000000000000000000000005f","0000000000000000000000000000000000000000000000000000000000000066","0000000000000000000000000000000000000000000000000000000000000069","0000000000000000000000000000000000000000000000000000000000000065","000000000000000000000000000000000000000000000000000000000000006c","0000000000000000000000000000000000000000000000000000000000000064"]]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000000"]}} +{"call":{"function":"set_mock_returns","inputs":["0000000000000000000000000000000000000000000000000000000000000000","000000000000000000000000000000000000000000000000000000000000000a"]},"result":{"values":[]}} +{"call":{"function":"set_mock_times","inputs":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000001"]},"result":{"values":[]}} +{"call":{"function":"create_mock","inputs":[["0000000000000000000000000000000000000000000000000000000000000076","000000000000000000000000000000000000000000000000000000000000006f","0000000000000000000000000000000000000000000000000000000000000069","0000000000000000000000000000000000000000000000000000000000000064","000000000000000000000000000000000000000000000000000000000000005f","0000000000000000000000000000000000000000000000000000000000000066","0000000000000000000000000000000000000000000000000000000000000069","0000000000000000000000000000000000000000000000000000000000000065","000000000000000000000000000000000000000000000000000000000000006c","0000000000000000000000000000000000000000000000000000000000000064"]]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000001"]}} +{"call":{"function":"set_mock_returns","inputs":["0000000000000000000000000000000000000000000000000000000000000001","0000000000000000000000000000000000000000000000000000000000000014"]},"result":{"values":[]}} +{"call":{"function":"set_mock_times","inputs":["0000000000000000000000000000000000000000000000000000000000000001","0000000000000000000000000000000000000000000000000000000000000001"]},"result":{"values":[]}} +{"call":{"function":"create_mock","inputs":[["0000000000000000000000000000000000000000000000000000000000000066","0000000000000000000000000000000000000000000000000000000000000069","0000000000000000000000000000000000000000000000000000000000000065","000000000000000000000000000000000000000000000000000000000000006c","0000000000000000000000000000000000000000000000000000000000000064","000000000000000000000000000000000000000000000000000000000000005f","0000000000000000000000000000000000000000000000000000000000000066","0000000000000000000000000000000000000000000000000000000000000069","0000000000000000000000000000000000000000000000000000000000000065","000000000000000000000000000000000000000000000000000000000000006c","0000000000000000000000000000000000000000000000000000000000000064"]]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000002"]}} +{"call":{"function":"set_mock_params","inputs":["0000000000000000000000000000000000000000000000000000000000000002","0000000000000000000000000000000000000000000000000000000000000002"]},"result":{"values":[]}} +{"call":{"function":"set_mock_returns","inputs":["0000000000000000000000000000000000000000000000000000000000000002","000000000000000000000000000000000000000000000000000000000000001e"]},"result":{"values":[]}} +{"call":{"function":"create_mock","inputs":[["0000000000000000000000000000000000000000000000000000000000000066","0000000000000000000000000000000000000000000000000000000000000069","0000000000000000000000000000000000000000000000000000000000000065","000000000000000000000000000000000000000000000000000000000000006c","0000000000000000000000000000000000000000000000000000000000000064","000000000000000000000000000000000000000000000000000000000000005f","0000000000000000000000000000000000000000000000000000000000000066","0000000000000000000000000000000000000000000000000000000000000069","0000000000000000000000000000000000000000000000000000000000000065","000000000000000000000000000000000000000000000000000000000000006c","0000000000000000000000000000000000000000000000000000000000000064"]]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000003"]}} +{"call":{"function":"set_mock_params","inputs":["0000000000000000000000000000000000000000000000000000000000000003","0000000000000000000000000000000000000000000000000000000000000003"]},"result":{"values":[]}} +{"call":{"function":"set_mock_returns","inputs":["0000000000000000000000000000000000000000000000000000000000000003","0000000000000000000000000000000000000000000000000000000000000028"]},"result":{"values":[]}} +{"call":{"function":"create_mock","inputs":[["0000000000000000000000000000000000000000000000000000000000000073","0000000000000000000000000000000000000000000000000000000000000074","0000000000000000000000000000000000000000000000000000000000000072","0000000000000000000000000000000000000000000000000000000000000075","0000000000000000000000000000000000000000000000000000000000000063","0000000000000000000000000000000000000000000000000000000000000074","000000000000000000000000000000000000000000000000000000000000005f","0000000000000000000000000000000000000000000000000000000000000066","0000000000000000000000000000000000000000000000000000000000000069","0000000000000000000000000000000000000000000000000000000000000065","000000000000000000000000000000000000000000000000000000000000006c","0000000000000000000000000000000000000000000000000000000000000064"]]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000004"]}} +{"call":{"function":"set_mock_params","inputs":["0000000000000000000000000000000000000000000000000000000000000004","000000000000000000000000000000000000000000000000000000000000012c","0000000000000000000000000000000000000000000000000000000000000320",["000000000000000000000000000000000000000000000000000000000000000a","0000000000000000000000000000000000000000000000000000000000000014","000000000000000000000000000000000000000000000000000000000000001e","0000000000000000000000000000000000000000000000000000000000000028"]]},"result":{"values":[]}} +{"call":{"function":"set_mock_returns","inputs":["0000000000000000000000000000000000000000000000000000000000000004","0000000000000000000000000000000000000000000000000000000000000064"]},"result":{"values":[]}} +{"call":{"function":"void_field","inputs":[]},"result":{"values":["000000000000000000000000000000000000000000000000000000000000000a"]}} +{"call":{"function":"void_field","inputs":[]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000014"]}} +{"call":{"function":"field_field","inputs":["0000000000000000000000000000000000000000000000000000000000000002"]},"result":{"values":["000000000000000000000000000000000000000000000000000000000000001e"]}} +{"call":{"function":"field_field","inputs":["0000000000000000000000000000000000000000000000000000000000000003"]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000028"]}} +{"call":{"function":"struct_field","inputs":["000000000000000000000000000000000000000000000000000000000000012c","0000000000000000000000000000000000000000000000000000000000000320",["000000000000000000000000000000000000000000000000000000000000000a","0000000000000000000000000000000000000000000000000000000000000014","000000000000000000000000000000000000000000000000000000000000001e","0000000000000000000000000000000000000000000000000000000000000028"]]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000064"]}} diff --git a/noir/noir-repo/examples/oracle_transcript/Prover.toml b/noir/noir-repo/examples/oracle_transcript/Prover.toml new file mode 100644 index 000000000000..eb8504c2b0c3 --- /dev/null +++ b/noir/noir-repo/examples/oracle_transcript/Prover.toml @@ -0,0 +1,6 @@ + +[input] +x = 2 +y = 3 + +return = 100 diff --git a/noir/noir-repo/examples/oracle_transcript/log_and_exec_transcript.sh b/noir/noir-repo/examples/oracle_transcript/log_and_exec_transcript.sh new file mode 100755 index 000000000000..c6e5066f158a --- /dev/null +++ b/noir/noir-repo/examples/oracle_transcript/log_and_exec_transcript.sh @@ -0,0 +1,22 @@ +#!/bin/bash +set -eu + +cd $(dirname $0) + +# Execute the test to capture oracle calls. +NARGO_TEST_FOREIGN_CALL_LOG=Oracle.test.jsonl nargo test + +# Get rid of the mock setup calls +cat Oracle.test.jsonl \ + | jq --slurp -r -c '.[] | select(.call.function | contains("mock") | not)' \ + > Oracle.jsonl + +# Execute `main` with the Prover.toml and Oracle.jsonl files. +nargo execute --skip-underconstrained-check --oracle-file Oracle.jsonl + +# Also execute through `noir-execute` +noir-execute \ + --artifact-path target/oracle_transcript.json \ + --oracle-file Oracle.jsonl \ + --prover-file Prover.toml \ + --output-dir target diff --git a/noir/noir-repo/examples/oracle_transcript/src/main.nr b/noir/noir-repo/examples/oracle_transcript/src/main.nr new file mode 100644 index 000000000000..585ff2af2b2d --- /dev/null +++ b/noir/noir-repo/examples/oracle_transcript/src/main.nr @@ -0,0 +1,63 @@ +use std::test::OracleMock; + +struct Point { + x: Field, + y: Field, +} + +impl Eq for Point { + fn eq(self, other: Point) -> bool { + (self.x == other.x) & (self.y == other.y) + } +} + +#[oracle(void_field)] +unconstrained fn void_field_oracle() -> Field {} + +unconstrained fn void_field() -> Field { + void_field_oracle() +} + +#[oracle(field_field)] +unconstrained fn field_field_oracle(_x: Field) -> Field {} + +unconstrained fn field_field(x: Field) -> Field { + field_field_oracle(x) +} + +#[oracle(struct_field)] +unconstrained fn struct_field_oracle(_point: Point, _array: [Field; 4]) -> Field {} + +unconstrained fn struct_field(point: Point, array: [Field; 4]) -> Field { + struct_field_oracle(point, array) +} + +fn main(input: Point) -> pub Field { + // Safety: testing context + unsafe { + let a = void_field(); + let b = void_field(); + let c = field_field(input.x); + let d = field_field(input.y); + let p = Point { x: a * c, y: b * d }; + struct_field(p, [a, b, c, d]) + } +} + +/// This test is used to capture an oracle transcript, which can then be replayed +/// during execution. +#[test] +fn test_main() { + // Safety: testing context + unsafe { + let _ = OracleMock::mock("void_field").returns(10).times(1); + let _ = OracleMock::mock("void_field").returns(20).times(1); + let _ = OracleMock::mock("field_field").with_params((2,)).returns(30); + let _ = OracleMock::mock("field_field").with_params((3,)).returns(40); + let _ = OracleMock::mock("struct_field") + .with_params((Point { x: 300, y: 800 }, [10, 20, 30, 40])) + .returns(100); + } + let output = main(Point { x: 2, y: 3 }); + assert_eq(output, 100) +} diff --git a/noir/noir-repo/examples/oracle_transcript/test.sh b/noir/noir-repo/examples/oracle_transcript/test.sh new file mode 100755 index 000000000000..8f43c3b8bb9b --- /dev/null +++ b/noir/noir-repo/examples/oracle_transcript/test.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -eu + +cd $(dirname $0) + +# This file is used for Noir CI and is not required. + +rm -f ./Oracle.* + +./log_and_exec_transcript.sh diff --git a/noir/noir-repo/noir_stdlib/src/cmp.nr b/noir/noir-repo/noir_stdlib/src/cmp.nr index 16bcd4390f78..24621bb37934 100644 --- a/noir/noir-repo/noir_stdlib/src/cmp.nr +++ b/noir/noir-repo/noir_stdlib/src/cmp.nr @@ -35,6 +35,11 @@ impl Eq for Field { } } +impl Eq for u128 { + fn eq(self, other: u128) -> bool { + self == other + } +} impl Eq for u64 { fn eq(self, other: u64) -> bool { self == other @@ -232,6 +237,17 @@ comptime fn derive_ord(s: StructDefinition) -> Quoted { // Note: Field deliberately does not implement Ord +impl Ord for u128 { + fn cmp(self, other: u128) -> Ordering { + if self < other { + Ordering::less() + } else if self > other { + Ordering::greater() + } else { + Ordering::equal() + } + } +} impl Ord for u64 { fn cmp(self, other: u64) -> Ordering { if self < other { diff --git a/noir/noir-repo/noir_stdlib/src/convert.nr b/noir/noir-repo/noir_stdlib/src/convert.nr index 1e5c1484b5fe..7e7348c2b37d 100644 --- a/noir/noir-repo/noir_stdlib/src/convert.nr +++ b/noir/noir-repo/noir_stdlib/src/convert.nr @@ -45,6 +45,22 @@ impl From for u64 { } } +impl From for u128 { + fn from(value: u8) -> u128 { + value as u128 + } +} +impl From for u128 { + fn from(value: u32) -> u128 { + value as u128 + } +} +impl From for u128 { + fn from(value: u64) -> u128 { + value as u128 + } +} + impl From for Field { fn from(value: u8) -> Field { value as Field @@ -61,6 +77,12 @@ impl From for Field { } } +impl From for Field { + fn from(value: u128) -> Field { + value as Field + } +} + // Signed integers impl From for i32 { @@ -142,6 +164,7 @@ comptime fn generate_as_primitive_impls(_: FunctionDefinition) -> Quoted { quote { u16 }, quote { u32 }, quote { u64 }, + quote { u128 }, quote { i8 }, quote { i16 }, quote { i32 }, diff --git a/noir/noir-repo/noir_stdlib/src/default.nr b/noir/noir-repo/noir_stdlib/src/default.nr index 01f10a368b14..229e5e92003f 100644 --- a/noir/noir-repo/noir_stdlib/src/default.nr +++ b/noir/noir-repo/noir_stdlib/src/default.nr @@ -47,6 +47,12 @@ impl Default for u64 { } } +impl Default for u128 { + fn default() -> u128 { + 0 + } +} + impl Default for i8 { fn default() -> i8 { 0 diff --git a/noir/noir-repo/noir_stdlib/src/ecdsa_secp256k1.nr b/noir/noir-repo/noir_stdlib/src/ecdsa_secp256k1.nr index ec39e9997152..1b0f32343b98 100644 --- a/noir/noir-repo/noir_stdlib/src/ecdsa_secp256k1.nr +++ b/noir/noir-repo/noir_stdlib/src/ecdsa_secp256k1.nr @@ -19,4 +19,3 @@ pub fn verify_signature_slice( ) -> bool // docs:end:ecdsa_secp256k1_slice {} - diff --git a/noir/noir-repo/noir_stdlib/src/hash/keccak.nr b/noir/noir-repo/noir_stdlib/src/hash/keccak.nr deleted file mode 100644 index 75be7982e667..000000000000 --- a/noir/noir-repo/noir_stdlib/src/hash/keccak.nr +++ /dev/null @@ -1,155 +0,0 @@ -use crate::runtime::is_unconstrained; - -global BLOCK_SIZE_IN_BYTES: u32 = 136; //(1600 - BITS * 2) / WORD_SIZE; -global WORD_SIZE: u32 = 8; // Limbs are made up of u64s so 8 bytes each. -global LIMBS_PER_BLOCK: u32 = BLOCK_SIZE_IN_BYTES / WORD_SIZE; -global NUM_KECCAK_LANES: u32 = 25; - -#[foreign(keccakf1600)] -pub fn keccakf1600(input: [u64; 25]) -> [u64; 25] {} - -#[no_predicates] -#[deprecated("keccak256 is being deprecated from the stdlib, use https://github.com/noir-lang/keccak256 instead")] -pub(crate) fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] { - assert(N >= message_size); - - // Copy input to block bytes. For that we'll need at least input bytes (N) - // but we want it to be padded to a multiple of BLOCK_SIZE_IN_BYTES. - let mut block_bytes = [0; ((N / BLOCK_SIZE_IN_BYTES) + 1) * BLOCK_SIZE_IN_BYTES]; - if is_unconstrained() { - for i in 0..message_size { - block_bytes[i] = input[i]; - } - } else { - for i in 0..N { - if i < message_size { - block_bytes[i] = input[i]; - } - } - } - - //1. format_input_lanes - let max_blocks = (N + BLOCK_SIZE_IN_BYTES) / BLOCK_SIZE_IN_BYTES; - //maximum number of bytes to hash - let real_max_blocks = (message_size + BLOCK_SIZE_IN_BYTES) / BLOCK_SIZE_IN_BYTES; - let real_blocks_bytes = real_max_blocks * BLOCK_SIZE_IN_BYTES; - - block_bytes[message_size] = 1; - block_bytes[real_blocks_bytes - 1] = 0x80; - - // populate a vector of 64-bit limbs from our byte array - let mut sliced_buffer = - [0; (((N / BLOCK_SIZE_IN_BYTES) + 1) * BLOCK_SIZE_IN_BYTES) / WORD_SIZE]; - for i in 0..sliced_buffer.len() { - let limb_start = WORD_SIZE * i; - - let mut sliced = 0; - let mut v = 1; - for k in 0..WORD_SIZE { - sliced += v * (block_bytes[limb_start + k] as Field); - v *= 256; - } - - sliced_buffer[i] = sliced as u64; - } - - //2. sponge_absorb - let mut state: [u64; NUM_KECCAK_LANES] = [0; NUM_KECCAK_LANES]; - // When in an unconstrained runtime we can take advantage of runtime loop bounds, - // thus allowing us to simplify the loop body. - if is_unconstrained() { - for i in 0..real_max_blocks { - if (i == 0) { - for j in 0..LIMBS_PER_BLOCK { - state[j] = sliced_buffer[j]; - } - } else { - for j in 0..LIMBS_PER_BLOCK { - state[j] = state[j] ^ sliced_buffer[i * LIMBS_PER_BLOCK + j]; - } - } - state = keccakf1600(state); - } - } else { - // `real_max_blocks` is guaranteed to at least be `1` - // We peel out the first block as to avoid a conditional inside of the loop. - // Otherwise, a dynamic predicate can cause a blowup in a constrained runtime. - for j in 0..LIMBS_PER_BLOCK { - state[j] = sliced_buffer[j]; - } - state = keccakf1600(state); - for i in 1..max_blocks { - if i < real_max_blocks { - for j in 0..LIMBS_PER_BLOCK { - state[j] = state[j] ^ sliced_buffer[i * LIMBS_PER_BLOCK + j]; - } - state = keccakf1600(state); - } - } - } - - //3. sponge_squeeze - let mut result = [0; 32]; - for i in 0..4 { - let lane = state[i] as Field; - let lane_le: [u8; 8] = lane.to_le_bytes(); - for j in 0..8 { - result[8 * i + j] = lane_le[j]; - } - } - result -} - -mod tests { - use super::keccak256; - - #[test] - fn smoke_test() { - let input = [0xbd]; - let result = [ - 0x5a, 0x50, 0x2f, 0x9f, 0xca, 0x46, 0x7b, 0x26, 0x6d, 0x5b, 0x78, 0x33, 0x65, 0x19, - 0x37, 0xe8, 0x05, 0x27, 0x0c, 0xa3, 0xf3, 0xaf, 0x1c, 0x0d, 0xd2, 0x46, 0x2d, 0xca, - 0x4b, 0x3b, 0x1a, 0xbf, - ]; - assert_eq(keccak256(input, input.len()), result); - } - - #[test] - fn hash_hello_world() { - let input = "Hello world!".as_bytes(); - let result = [ - 0xec, 0xd0, 0xe1, 0x8, 0xa9, 0x8e, 0x19, 0x2a, 0xf1, 0xd2, 0xc2, 0x50, 0x55, 0xf4, 0xe3, - 0xbe, 0xd7, 0x84, 0xb5, 0xc8, 0x77, 0x20, 0x4e, 0x73, 0x21, 0x9a, 0x52, 0x3, 0x25, 0x1f, - 0xea, 0xab, - ]; - assert_eq(keccak256(input, input.len()), result); - } - - #[test] - fn var_size_hash() { - let input = [ - 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, - 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, - 223, - ]; - let result = [ - 226, 37, 115, 94, 94, 196, 72, 116, 194, 105, 79, 233, 65, 12, 30, 94, 181, 131, 170, - 219, 171, 166, 236, 88, 143, 67, 255, 160, 248, 214, 39, 129, - ]; - assert_eq(keccak256(input, 13), result); - } - - #[test] - fn hash_longer_than_136_bytes() { - let input = "123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789" - .as_bytes(); - assert(input.len() > 136); - - let result = [ - 0x1d, 0xca, 0xeb, 0xdf, 0xd9, 0xd6, 0x24, 0x67, 0x1c, 0x18, 0x16, 0xda, 0xd, 0x8a, 0xeb, - 0xa8, 0x75, 0x71, 0x2c, 0xc, 0x89, 0xe0, 0x25, 0x2, 0xe8, 0xb6, 0x5e, 0x16, 0x5, 0x55, - 0xe4, 0x40, - ]; - assert_eq(keccak256(input, input.len()), result); - } -} diff --git a/noir/noir-repo/noir_stdlib/src/hash/mod.nr b/noir/noir-repo/noir_stdlib/src/hash/mod.nr index ee78d04c91b6..7a492d373cc9 100644 --- a/noir/noir-repo/noir_stdlib/src/hash/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/hash/mod.nr @@ -1,18 +1,28 @@ pub mod poseidon; pub mod poseidon2; -pub mod keccak; -pub mod sha256; -pub mod sha512; use crate::default::Default; use crate::embedded_curve_ops::{ EmbeddedCurvePoint, EmbeddedCurveScalar, multi_scalar_mul, multi_scalar_mul_array_return, }; use crate::meta::derive_via; -use crate::uint128::U128; -// Kept for backwards compatibility -pub use sha256::{digest, sha256, sha256_compression, sha256_var}; +#[foreign(sha256_compression)] +// docs:start:sha256_compression +pub fn sha256_compression(input: [u32; 16], state: [u32; 8]) -> [u32; 8] {} +// docs:end:sha256_compression + +#[foreign(keccakf1600)] +// docs:start:keccakf1600 +pub fn keccakf1600(input: [u64; 25]) -> [u64; 25] {} +// docs:end:keccakf1600 + +pub mod keccak { + #[deprecated("This function has been moved to std::hash::keccakf1600")] + pub fn keccakf1600(input: [u64; 25]) -> [u64; 25] { + super::keccakf1600(input) + } +} #[foreign(blake2s)] // docs:start:blake2s @@ -114,13 +124,6 @@ pub fn hash_to_field(inputs: [Field]) -> Field { sum } -// docs:start:keccak256 -pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] -// docs:end:keccak256 -{ - crate::hash::keccak::keccak256(input, message_size) -} - #[foreign(poseidon2_permutation)] pub fn poseidon2_permutation(_input: [Field; N], _state_length: u32) -> [Field; N] {} @@ -241,6 +244,15 @@ impl Hash for u64 { } } +impl Hash for u128 { + fn hash(self, state: &mut H) + where + H: Hasher, + { + H::write(state, self as Field); + } +} + impl Hash for i8 { fn hash(self, state: &mut H) where @@ -293,16 +305,6 @@ impl Hash for () { {} } -impl Hash for U128 { - fn hash(self, state: &mut H) - where - H: Hasher, - { - H::write(state, self.lo as Field); - H::write(state, self.hi as Field); - } -} - impl Hash for [T; N] where T: Hash, diff --git a/noir/noir-repo/noir_stdlib/src/hash/sha256.nr b/noir/noir-repo/noir_stdlib/src/hash/sha256.nr deleted file mode 100644 index a8bd71a21111..000000000000 --- a/noir/noir-repo/noir_stdlib/src/hash/sha256.nr +++ /dev/null @@ -1,845 +0,0 @@ -use crate::runtime::is_unconstrained; - -// Implementation of SHA-256 mapping a byte array of variable length to -// 32 bytes. - -// A message block is up to 64 bytes taken from the input. -global BLOCK_SIZE: u32 = 64; - -// The first index in the block where the 8 byte message size will be written. -global MSG_SIZE_PTR: u32 = 56; - -// Size of the message block when packed as 4-byte integer array. -global INT_BLOCK_SIZE: u32 = 16; - -// A `u32` integer consists of 4 bytes. -global INT_SIZE: u32 = 4; - -// Index of the integer in the `INT_BLOCK` where the length is written. -global INT_SIZE_PTR: u32 = MSG_SIZE_PTR / INT_SIZE; - -// Magic numbers for bit shifting. -// Works with actual bit shifting as well as the compiler turns them into * and / -// but circuit execution appears to be 10% faster this way. -global TWO_POW_8: u32 = 256; -global TWO_POW_16: u32 = TWO_POW_8 * 256; -global TWO_POW_24: u32 = TWO_POW_16 * 256; -global TWO_POW_32: u64 = TWO_POW_24 as u64 * 256; - -// Index of a byte in a 64 byte block; ie. 0..=63 -type BLOCK_BYTE_PTR = u32; - -// The foreign function to compress blocks works on 16 pieces of 4-byte integers, instead of 64 bytes. -type INT_BLOCK = [u32; INT_BLOCK_SIZE]; - -// A message block is a slice of the original message of a fixed size, -// potentially padded with zeros, with neighbouring 4 bytes packed into integers. -type MSG_BLOCK = INT_BLOCK; - -// The hash is 32 bytes. -type HASH = [u8; 32]; - -// The state accumulates the blocks. -// Its overall size is the same as the `HASH`. -type STATE = [u32; 8]; - -// docs:start:sha256 -#[deprecated("sha256 is being deprecated from the stdlib, use https://github.com/noir-lang/sha256 instead")] -pub fn sha256(input: [u8; N]) -> HASH -// docs:end:sha256 -{ - digest(input) -} - -#[foreign(sha256_compression)] -pub fn sha256_compression(_input: INT_BLOCK, _state: STATE) -> STATE {} - -// SHA-256 hash function -#[no_predicates] -#[deprecated("sha256 is being deprecated from the stdlib, use https://github.com/noir-lang/sha256 instead")] -pub fn digest(msg: [u8; N]) -> HASH { - sha256_var(msg, N as u64) -} - -// Variable size SHA-256 hash -#[deprecated("sha256 is being deprecated from the stdlib, use https://github.com/noir-lang/sha256 instead")] -pub fn sha256_var(msg: [u8; N], message_size: u64) -> HASH { - let message_size = message_size as u32; - let num_blocks = N / BLOCK_SIZE; - let mut msg_block: MSG_BLOCK = [0; INT_BLOCK_SIZE]; - // Intermediate hash, starting with the canonical initial value - let mut h: STATE = [ - 1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, - 1541459225, - ]; - // Pointer into msg_block on a 64 byte scale - let mut msg_byte_ptr = 0; - for i in 0..num_blocks { - let msg_start = BLOCK_SIZE * i; - // Safety: the msg_block is checked below in verify_msg_block - let (new_msg_block, new_msg_byte_ptr) = - unsafe { build_msg_block(msg, message_size, msg_start) }; - - if msg_start < message_size { - msg_block = new_msg_block; - } - - if !is_unconstrained() { - // Verify the block we are compressing was appropriately constructed - let new_msg_byte_ptr = verify_msg_block(msg, message_size, msg_block, msg_start); - if msg_start < message_size { - msg_byte_ptr = new_msg_byte_ptr; - } - } else if msg_start < message_size { - msg_byte_ptr = new_msg_byte_ptr; - } - - // If the block is filled, compress it. - // An un-filled block is handled after this loop. - if (msg_start < message_size) & (msg_byte_ptr == BLOCK_SIZE) { - h = sha256_compression(msg_block, h); - } - } - - let modulo = N % BLOCK_SIZE; - // Handle setup of the final msg block. - // This case is only hit if the msg is less than the block size, - // or our message cannot be evenly split into blocks. - if modulo != 0 { - let msg_start = BLOCK_SIZE * num_blocks; - // Safety: the msg_block is checked below in verify_msg_block - let (new_msg_block, new_msg_byte_ptr) = - unsafe { build_msg_block(msg, message_size, msg_start) }; - - if msg_start < message_size { - msg_block = new_msg_block; - } - - if !is_unconstrained() { - let new_msg_byte_ptr = verify_msg_block(msg, message_size, msg_block, msg_start); - if msg_start < message_size { - msg_byte_ptr = new_msg_byte_ptr; - verify_msg_block_padding(msg_block, msg_byte_ptr); - } - } else if msg_start < message_size { - msg_byte_ptr = new_msg_byte_ptr; - } - } - - // If we had modulo == 0 then it means the last block was full, - // and we can reset the pointer to zero to overwrite it. - if msg_byte_ptr == BLOCK_SIZE { - msg_byte_ptr = 0; - } - - // Pad the rest such that we have a [u32; 2] block at the end representing the length - // of the message, and a block of 1 0 ... 0 following the message (i.e. [1 << 7, 0, ..., 0]). - // Here we rely on the fact that everything beyond the available input is set to 0. - msg_block = update_block_item( - msg_block, - msg_byte_ptr, - |msg_item| set_item_byte_then_zeros(msg_item, msg_byte_ptr, 1 << 7), - ); - msg_byte_ptr = msg_byte_ptr + 1; - let last_block = msg_block; - - // If we don't have room to write the size, compress the block and reset it. - if msg_byte_ptr > MSG_SIZE_PTR { - h = sha256_compression(msg_block, h); - // `attach_len_to_msg_block` will zero out everything after the `msg_byte_ptr`. - msg_byte_ptr = 0; - } - - // Safety: the msg_len is checked below in verify_msg_len - msg_block = unsafe { attach_len_to_msg_block(msg_block, msg_byte_ptr, message_size) }; - - if !is_unconstrained() { - verify_msg_len(msg_block, last_block, msg_byte_ptr, message_size); - } - - hash_final_block(msg_block, h) -} - -// Take `BLOCK_SIZE` number of bytes from `msg` starting at `msg_start`. -// Returns the block and the length that has been copied rather than padded with zeros. -unconstrained fn build_msg_block( - msg: [u8; N], - message_size: u32, - msg_start: u32, -) -> (MSG_BLOCK, BLOCK_BYTE_PTR) { - let mut msg_block: MSG_BLOCK = [0; INT_BLOCK_SIZE]; - - // We insert `BLOCK_SIZE` bytes (or up to the end of the message) - let block_input = if msg_start + BLOCK_SIZE > message_size { - if message_size < msg_start { - // This function is sometimes called with `msg_start` past the end of the message. - // In this case we return an empty block and zero pointer to signal that the result should be ignored. - 0 - } else { - message_size - msg_start - } - } else { - BLOCK_SIZE - }; - - // Figure out the number of items in the int array that we have to pack. - // e.g. if the input is [0,1,2,3,4,5] then we need to pack it as 2 items: [0123, 4500] - let mut int_input = block_input / INT_SIZE; - if block_input % INT_SIZE != 0 { - int_input = int_input + 1; - }; - - for i in 0..int_input { - let mut msg_item: u32 = 0; - // Always construct the integer as 4 bytes, even if it means going beyond the input. - for j in 0..INT_SIZE { - let k = i * INT_SIZE + j; - let msg_byte = if k < block_input { - msg[msg_start + k] - } else { - 0 - }; - msg_item = lshift8(msg_item, 1) + msg_byte as u32; - } - msg_block[i] = msg_item; - } - - // Returning the index as if it was a 64 byte array. - // We have to project it down to 16 items and bit shifting to get a byte back if we need it. - (msg_block, block_input) -} - -// Verify the block we are compressing was appropriately constructed by `build_msg_block` -// and matches the input data. Returns the index of the first unset item. -// If `message_size` is less than `msg_start` then this is called with the old non-empty block; -// in that case we can skip verification, ie. no need to check that everything is zero. -fn verify_msg_block( - msg: [u8; N], - message_size: u32, - msg_block: MSG_BLOCK, - msg_start: u32, -) -> BLOCK_BYTE_PTR { - let mut msg_byte_ptr = 0; - let mut msg_end = msg_start + BLOCK_SIZE; - if msg_end > N { - msg_end = N; - } - // We might have to go beyond the input to pad the fields. - if msg_end % INT_SIZE != 0 { - msg_end = msg_end + INT_SIZE - msg_end % INT_SIZE; - } - - // Reconstructed packed item. - let mut msg_item: u32 = 0; - - // Inclusive at the end so that we can compare the last item. - let mut i: u32 = 0; - for k in msg_start..=msg_end { - if k % INT_SIZE == 0 { - // If we consumed some input we can compare against the block. - if (msg_start < message_size) & (k > msg_start) { - assert_eq(msg_block[i], msg_item as u32); - i = i + 1; - msg_item = 0; - } - } - // Shift the accumulator - msg_item = lshift8(msg_item, 1); - // If we have input to consume, add it at the rightmost position. - if k < message_size & k < msg_end { - msg_item = msg_item + msg[k] as u32; - msg_byte_ptr = msg_byte_ptr + 1; - } - } - - msg_byte_ptr -} - -// Verify the block we are compressing was appropriately padded with zeros by `build_msg_block`. -// This is only relevant for the last, potentially partially filled block. -fn verify_msg_block_padding(msg_block: MSG_BLOCK, msg_byte_ptr: BLOCK_BYTE_PTR) { - // Check all the way to the end of the block. - verify_msg_block_zeros(msg_block, msg_byte_ptr, INT_BLOCK_SIZE); -} - -// Verify that a region of ints in the message block are (partially) zeroed, -// up to an (exclusive) maximum which can either be the end of the block -// or just where the size is to be written. -fn verify_msg_block_zeros( - msg_block: MSG_BLOCK, - mut msg_byte_ptr: BLOCK_BYTE_PTR, - max_int_byte_ptr: u32, -) { - // This variable is used to get around the compiler under-constrained check giving a warning. - // We want to check against a constant zero, but if it does not come from the circuit inputs - // or return values the compiler check will issue a warning. - let zero = msg_block[0] - msg_block[0]; - - // First integer which is supposed to be (partially) zero. - let mut int_byte_ptr = msg_byte_ptr / INT_SIZE; - - // Check partial zeros. - let modulo = msg_byte_ptr % INT_SIZE; - if modulo != 0 { - let zeros = INT_SIZE - modulo; - let mask = if zeros == 3 { - TWO_POW_24 - } else if zeros == 2 { - TWO_POW_16 - } else { - TWO_POW_8 - }; - assert_eq(msg_block[int_byte_ptr] % mask, zero); - int_byte_ptr = int_byte_ptr + 1; - } - - // Check the rest of the items. - for i in 0..max_int_byte_ptr { - if i >= int_byte_ptr { - assert_eq(msg_block[i], zero); - } - } -} - -// Verify that up to the byte pointer the two blocks are equal. -// At the byte pointer the new block can be partially zeroed. -fn verify_msg_block_equals_last( - msg_block: MSG_BLOCK, - last_block: MSG_BLOCK, - mut msg_byte_ptr: BLOCK_BYTE_PTR, -) { - // msg_byte_ptr is the position at which they are no longer have to be the same. - // First integer which is supposed to be (partially) zero contains that pointer. - let mut int_byte_ptr = msg_byte_ptr / INT_SIZE; - - // Check partial zeros. - let modulo = msg_byte_ptr % INT_SIZE; - if modulo != 0 { - // Reconstruct the partially zero item from the last block. - let last_field = last_block[int_byte_ptr]; - let mut msg_item: u32 = 0; - // Reset to where they are still equal. - msg_byte_ptr = msg_byte_ptr - modulo; - for i in 0..INT_SIZE { - msg_item = lshift8(msg_item, 1); - if i < modulo { - msg_item = msg_item + get_item_byte(last_field, msg_byte_ptr) as u32; - msg_byte_ptr = msg_byte_ptr + 1; - } - } - assert_eq(msg_block[int_byte_ptr], msg_item); - } - - for i in 0..INT_SIZE_PTR { - if i < int_byte_ptr { - assert_eq(msg_block[i], last_block[i]); - } - } -} - -// Apply a function on the block item which the pointer indicates. -fn update_block_item( - mut msg_block: MSG_BLOCK, - msg_byte_ptr: BLOCK_BYTE_PTR, - f: fn[Env](u32) -> u32, -) -> MSG_BLOCK { - let i = msg_byte_ptr / INT_SIZE; - msg_block[i] = f(msg_block[i]); - msg_block -} - -// Set the rightmost `zeros` number of bytes to 0. -fn set_item_zeros(item: u32, zeros: u8) -> u32 { - lshift8(rshift8(item, zeros), zeros) -} - -// Replace one byte in the item with a value, and set everything after it to zero. -fn set_item_byte_then_zeros(msg_item: u32, msg_byte_ptr: BLOCK_BYTE_PTR, msg_byte: u8) -> u32 { - let zeros = INT_SIZE - msg_byte_ptr % INT_SIZE; - let zeroed_item = set_item_zeros(msg_item, zeros as u8); - let new_item = byte_into_item(msg_byte, msg_byte_ptr); - zeroed_item + new_item -} - -// Get a byte of a message item according to its overall position in the `BLOCK_SIZE` space. -fn get_item_byte(mut msg_item: u32, msg_byte_ptr: BLOCK_BYTE_PTR) -> u8 { - // How many times do we have to shift to the right to get to the position we want? - let max_shifts = INT_SIZE - 1; - let shifts = max_shifts - msg_byte_ptr % INT_SIZE; - msg_item = rshift8(msg_item, shifts as u8); - // At this point the byte we want is in the rightmost position. - msg_item as u8 -} - -// Project a byte into a position in a field based on the overall block pointer. -// For example putting 1 into pointer 5 would be 100, because overall we would -// have [____, 0100] with indexes [0123,4567]. -fn byte_into_item(msg_byte: u8, msg_byte_ptr: BLOCK_BYTE_PTR) -> u32 { - let mut msg_item = msg_byte as u32; - // How many times do we have to shift to the left to get to the position we want? - let max_shifts = INT_SIZE - 1; - let shifts = max_shifts - msg_byte_ptr % INT_SIZE; - lshift8(msg_item, shifts as u8) -} - -// Construct a field out of 4 bytes. -fn make_item(b0: u8, b1: u8, b2: u8, b3: u8) -> u32 { - let mut item = b0 as u32; - item = lshift8(item, 1) + b1 as u32; - item = lshift8(item, 1) + b2 as u32; - item = lshift8(item, 1) + b3 as u32; - item -} - -// Shift by 8 bits to the left between 0 and 4 times. -// Checks `is_unconstrained()` to just use a bitshift if we're running in an unconstrained context, -// otherwise multiplies by 256. -fn lshift8(item: u32, shifts: u8) -> u32 { - if is_unconstrained() { - if item == 0 { - 0 - } else { - // Brillig wouldn't shift 0<<4 without overflow. - item << (8 * shifts) - } - } else { - // We can do a for loop up to INT_SIZE or an if-else. - if shifts == 0 { - item - } else if shifts == 1 { - item * TWO_POW_8 - } else if shifts == 2 { - item * TWO_POW_16 - } else if shifts == 3 { - item * TWO_POW_24 - } else { - // Doesn't make sense, but it's most likely called on 0 anyway. - 0 - } - } -} - -// Shift by 8 bits to the right between 0 and 4 times. -// Checks `is_unconstrained()` to just use a bitshift if we're running in an unconstrained context, -// otherwise divides by 256. -fn rshift8(item: u32, shifts: u8) -> u32 { - if is_unconstrained() { - item >> (8 * shifts) - } else { - // Division wouldn't work on `Field`. - if shifts == 0 { - item - } else if shifts == 1 { - item / TWO_POW_8 - } else if shifts == 2 { - item / TWO_POW_16 - } else if shifts == 3 { - item / TWO_POW_24 - } else { - 0 - } - } -} - -// Zero out all bytes between the end of the message and where the length is appended, -// then write the length into the last 8 bytes of the block. -unconstrained fn attach_len_to_msg_block( - mut msg_block: MSG_BLOCK, - mut msg_byte_ptr: BLOCK_BYTE_PTR, - message_size: u32, -) -> MSG_BLOCK { - // We assume that `msg_byte_ptr` is less than 57 because if not then it is reset to zero before calling this function. - // In any case, fill blocks up with zeros until the last 64 bits (i.e. until msg_byte_ptr = 56). - // There can be one item which has to be partially zeroed. - let modulo = msg_byte_ptr % INT_SIZE; - if modulo != 0 { - // Index of the block in which we find the item we need to partially zero. - let i = msg_byte_ptr / INT_SIZE; - let zeros = INT_SIZE - modulo; - msg_block[i] = set_item_zeros(msg_block[i], zeros as u8); - msg_byte_ptr = msg_byte_ptr + zeros; - } - - // The rest can be zeroed without bit shifting anything. - for i in (msg_byte_ptr / INT_SIZE)..INT_SIZE_PTR { - msg_block[i] = 0; - } - - // Set the last two 4 byte ints as the first/second half of the 8 bytes of the length. - let len = 8 * message_size; - let len_bytes: [u8; 8] = (len as Field).to_be_bytes(); - for i in 0..=1 { - let shift = i * 4; - msg_block[INT_SIZE_PTR + i] = make_item( - len_bytes[shift], - len_bytes[shift + 1], - len_bytes[shift + 2], - len_bytes[shift + 3], - ); - } - msg_block -} - -// Verify that the message length was correctly written by `attach_len_to_msg_block`, -// and that everything between the byte pointer and the size pointer was zeroed, -// and that everything before the byte pointer was untouched. -fn verify_msg_len( - msg_block: MSG_BLOCK, - last_block: MSG_BLOCK, - msg_byte_ptr: BLOCK_BYTE_PTR, - message_size: u32, -) { - // Check zeros up to the size pointer. - verify_msg_block_zeros(msg_block, msg_byte_ptr, INT_SIZE_PTR); - - // Check that up to the pointer we match the last block. - verify_msg_block_equals_last(msg_block, last_block, msg_byte_ptr); - - // We verify the message length was inserted correctly by reversing the byte decomposition. - let mut reconstructed_len: u64 = 0; - for i in INT_SIZE_PTR..INT_BLOCK_SIZE { - reconstructed_len = reconstructed_len * TWO_POW_32; - reconstructed_len = reconstructed_len + msg_block[i] as u64; - } - let len = 8 * message_size as u64; - assert_eq(reconstructed_len, len); -} - -// Perform the final compression, then transform the `STATE` into `HASH`. -fn hash_final_block(msg_block: MSG_BLOCK, mut state: STATE) -> HASH { - let mut out_h: HASH = [0; 32]; // Digest as sequence of bytes - // Hash final padded block - state = sha256_compression(msg_block, state); - - // Return final hash as byte array - for j in 0..8 { - let h_bytes: [u8; 4] = (state[j] as Field).to_be_bytes(); - for k in 0..4 { - out_h[4 * j + k] = h_bytes[k]; - } - } - - out_h -} - -mod tests { - use super::{ - attach_len_to_msg_block, build_msg_block, byte_into_item, get_item_byte, make_item, - set_item_byte_then_zeros, set_item_zeros, - }; - use super::INT_BLOCK; - use super::sha256_var; - - #[test] - fn smoke_test() { - let input = [0xbd]; - let result = [ - 0x68, 0x32, 0x57, 0x20, 0xaa, 0xbd, 0x7c, 0x82, 0xf3, 0x0f, 0x55, 0x4b, 0x31, 0x3d, - 0x05, 0x70, 0xc9, 0x5a, 0xcc, 0xbb, 0x7d, 0xc4, 0xb5, 0xaa, 0xe1, 0x12, 0x04, 0xc0, - 0x8f, 0xfe, 0x73, 0x2b, - ]; - assert_eq(sha256_var(input, input.len() as u64), result); - } - - #[test] - fn msg_just_over_block() { - let input = [ - 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, - 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, - 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, - 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, - ]; - let result = [ - 91, 122, 146, 93, 52, 109, 133, 148, 171, 61, 156, 70, 189, 238, 153, 7, 222, 184, 94, - 24, 65, 114, 192, 244, 207, 199, 87, 232, 192, 224, 171, 207, - ]; - assert_eq(sha256_var(input, input.len() as u64), result); - } - - #[test] - fn msg_multiple_over_block() { - let input = [ - 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, - 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, - 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, - 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 115, 45, 97, 115, - 99, 105, 105, 13, 10, 109, 105, 109, 101, 45, 118, 101, 114, 115, 105, 111, 110, 58, 49, - 46, 48, 32, 40, 77, 97, 99, 32, 79, 83, 32, 88, 32, 77, 97, 105, 108, 32, 49, 54, 46, - 48, 32, 92, 40, 51, 55, 51, 49, 46, 53, 48, 48, 46, 50, 51, 49, 92, 41, 41, 13, 10, 115, - 117, 98, 106, 101, 99, 116, 58, 72, 101, 108, 108, 111, 13, 10, 109, 101, 115, 115, 97, - 103, 101, 45, 105, 100, 58, 60, 56, 70, 56, 49, 57, 68, 51, 50, 45, 66, 54, 65, 67, 45, - 52, 56, 57, 68, 45, 57, 55, 55, 70, 45, 52, 51, 56, 66, 66, 67, 52, 67, 65, 66, 50, 55, - 64, 109, 101, 46, 99, 111, 109, 62, 13, 10, 100, 97, 116, 101, 58, 83, 97, 116, 44, 32, - 50, 54, 32, 65, 117, 103, 32, 50, 48, 50, 51, 32, 49, 50, 58, 50, 53, 58, 50, 50, 32, - 43, 48, 52, 48, 48, 13, 10, 116, 111, 58, 122, 107, 101, 119, 116, 101, 115, 116, 64, - 103, 109, 97, 105, 108, 46, 99, 111, 109, 13, 10, 100, 107, 105, 109, 45, 115, 105, 103, - 110, 97, 116, 117, 114, 101, 58, 118, 61, 49, 59, 32, 97, 61, 114, 115, 97, 45, 115, - 104, 97, 50, 53, 54, 59, 32, 99, 61, 114, 101, 108, 97, 120, 101, 100, 47, 114, 101, - 108, 97, 120, 101, 100, 59, 32, 100, 61, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, - 59, 32, 115, 61, 49, 97, 49, 104, 97, 105, 59, 32, 116, 61, 49, 54, 57, 51, 48, 51, 56, - 51, 51, 55, 59, 32, 98, 104, 61, 55, 120, 81, 77, 68, 117, 111, 86, 86, 85, 52, 109, 48, - 87, 48, 87, 82, 86, 83, 114, 86, 88, 77, 101, 71, 83, 73, 65, 83, 115, 110, 117, 99, 75, - 57, 100, 74, 115, 114, 99, 43, 118, 85, 61, 59, 32, 104, 61, 102, 114, 111, 109, 58, 67, - 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 77, 105, 109, 101, 45, 86, 101, - 114, 115, 105, 111, 110, 58, 83, 117, 98, 106, 101, 99, - ]; - let result = [ - 116, 90, 151, 31, 78, 22, 138, 180, 211, 189, 69, 76, 227, 200, 155, 29, 59, 123, 154, - 60, 47, 153, 203, 129, 157, 251, 48, 2, 79, 11, 65, 47, - ]; - assert_eq(sha256_var(input, input.len() as u64), result); - } - - #[test] - fn msg_just_under_block() { - let input = [ - 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, - 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, - 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, - 108, 97, 105, 110, 59, - ]; - let result = [ - 143, 140, 76, 173, 222, 123, 102, 68, 70, 149, 207, 43, 39, 61, 34, 79, 216, 252, 213, - 165, 74, 16, 110, 74, 29, 64, 138, 167, 30, 1, 9, 119, - ]; - assert_eq(sha256_var(input, input.len() as u64), result); - } - - #[test] - fn msg_big_not_block_multiple() { - let input = [ - 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, - 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, - 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, - 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 115, 45, 97, 115, - 99, 105, 105, 13, 10, 109, 105, 109, 101, 45, 118, 101, 114, 115, 105, 111, 110, 58, 49, - 46, 48, 32, 40, 77, 97, 99, 32, 79, 83, 32, 88, 32, 77, 97, 105, 108, 32, 49, 54, 46, - 48, 32, 92, 40, 51, 55, 51, 49, 46, 53, 48, 48, 46, 50, 51, 49, 92, 41, 41, 13, 10, 115, - 117, 98, 106, 101, 99, 116, 58, 72, 101, 108, 108, 111, 13, 10, 109, 101, 115, 115, 97, - 103, 101, 45, 105, 100, 58, 60, 56, 70, 56, 49, 57, 68, 51, 50, 45, 66, 54, 65, 67, 45, - 52, 56, 57, 68, 45, 57, 55, 55, 70, 45, 52, 51, 56, 66, 66, 67, 52, 67, 65, 66, 50, 55, - 64, 109, 101, 46, 99, 111, 109, 62, 13, 10, 100, 97, 116, 101, 58, 83, 97, 116, 44, 32, - 50, 54, 32, 65, 117, 103, 32, 50, 48, 50, 51, 32, 49, 50, 58, 50, 53, 58, 50, 50, 32, - 43, 48, 52, 48, 48, 13, 10, 116, 111, 58, 122, 107, 101, 119, 116, 101, 115, 116, 64, - 103, 109, 97, 105, 108, 46, 99, 111, 109, 13, 10, 100, 107, 105, 109, 45, 115, 105, 103, - 110, 97, 116, 117, 114, 101, 58, 118, 61, 49, 59, 32, 97, 61, 114, 115, 97, 45, 115, - 104, 97, 50, 53, 54, 59, 32, 99, 61, 114, 101, 108, 97, 120, 101, 100, 47, 114, 101, - 108, 97, 120, 101, 100, 59, 32, 100, 61, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, - 59, 32, 115, 61, 49, 97, 49, 104, 97, 105, 59, 32, 116, 61, 49, 54, 57, 51, 48, 51, 56, - 51, 51, 55, 59, 32, 98, 104, 61, 55, 120, 81, 77, 68, 117, 111, 86, 86, 85, 52, 109, 48, - 87, 48, 87, 82, 86, 83, 114, 86, 88, 77, 101, 71, 83, 73, 65, 83, 115, 110, 117, 99, 75, - 57, 100, 74, 115, 114, 99, 43, 118, 85, 61, 59, 32, 104, 61, 102, 114, 111, 109, 58, 67, - 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 77, 105, 109, 101, 45, 86, 101, - 114, 115, 105, 111, 110, 58, 83, 117, 98, 106, 101, 99, 116, 58, 77, 101, 115, 115, 97, - 103, 101, 45, 73, 100, 58, 68, 97, 116, 101, 58, 116, 111, 59, 32, 98, 61, - ]; - let result = [ - 112, 144, 73, 182, 208, 98, 9, 238, 54, 229, 61, 145, 222, 17, 72, 62, 148, 222, 186, - 55, 192, 82, 220, 35, 66, 47, 193, 200, 22, 38, 26, 186, - ]; - assert_eq(sha256_var(input, input.len() as u64), result); - } - - #[test] - fn msg_big_with_padding() { - let input = [ - 48, 130, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 48, 130, 1, 17, - 48, 37, 2, 1, 1, 4, 32, 176, 223, 31, 133, 108, 84, 158, 102, 70, 11, 165, 175, 196, 12, - 201, 130, 25, 131, 46, 125, 156, 194, 28, 23, 55, 133, 157, 164, 135, 136, 220, 78, 48, - 37, 2, 1, 2, 4, 32, 190, 82, 180, 235, 222, 33, 79, 50, 152, 136, 142, 35, 116, 224, 6, - 242, 156, 141, 128, 248, 10, 61, 98, 86, 248, 45, 207, 210, 90, 232, 175, 38, 48, 37, 2, - 1, 3, 4, 32, 0, 194, 104, 108, 237, 246, 97, 230, 116, 198, 69, 110, 26, 87, 17, 89, - 110, 199, 108, 250, 36, 21, 39, 87, 110, 102, 250, 213, 174, 131, 171, 174, 48, 37, 2, - 1, 11, 4, 32, 136, 155, 87, 144, 111, 15, 152, 127, 85, 25, 154, 81, 20, 58, 51, 75, - 193, 116, 234, 0, 60, 30, 29, 30, 183, 141, 72, 247, 255, 203, 100, 124, 48, 37, 2, 1, - 12, 4, 32, 41, 234, 106, 78, 31, 11, 114, 137, 237, 17, 92, 71, 134, 47, 62, 78, 189, - 233, 201, 214, 53, 4, 47, 189, 201, 133, 6, 121, 34, 131, 64, 142, 48, 37, 2, 1, 13, 4, - 32, 91, 222, 210, 193, 62, 222, 104, 82, 36, 41, 138, 253, 70, 15, 148, 208, 156, 45, - 105, 171, 241, 195, 185, 43, 217, 162, 146, 201, 222, 89, 238, 38, 48, 37, 2, 1, 14, 4, - 32, 76, 123, 216, 13, 51, 227, 72, 245, 59, 193, 238, 166, 103, 49, 23, 164, 171, 188, - 194, 197, 156, 187, 249, 28, 198, 95, 69, 15, 182, 56, 54, 38, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]; - let result = [ - 32, 85, 108, 174, 127, 112, 178, 182, 8, 43, 134, 123, 192, 211, 131, 66, 184, 240, 212, - 181, 240, 180, 106, 195, 24, 117, 54, 129, 19, 10, 250, 53, - ]; - let message_size = 297; - assert_eq(sha256_var(input, message_size), result); - } - - #[test] - fn msg_big_no_padding() { - let input = [ - 48, 130, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 48, 130, 1, 17, - 48, 37, 2, 1, 1, 4, 32, 176, 223, 31, 133, 108, 84, 158, 102, 70, 11, 165, 175, 196, 12, - 201, 130, 25, 131, 46, 125, 156, 194, 28, 23, 55, 133, 157, 164, 135, 136, 220, 78, 48, - 37, 2, 1, 2, 4, 32, 190, 82, 180, 235, 222, 33, 79, 50, 152, 136, 142, 35, 116, 224, 6, - 242, 156, 141, 128, 248, 10, 61, 98, 86, 248, 45, 207, 210, 90, 232, 175, 38, 48, 37, 2, - 1, 3, 4, 32, 0, 194, 104, 108, 237, 246, 97, 230, 116, 198, 69, 110, 26, 87, 17, 89, - 110, 199, 108, 250, 36, 21, 39, 87, 110, 102, 250, 213, 174, 131, 171, 174, 48, 37, 2, - 1, 11, 4, 32, 136, 155, 87, 144, 111, 15, 152, 127, 85, 25, 154, 81, 20, 58, 51, 75, - 193, 116, 234, 0, 60, 30, 29, 30, 183, 141, 72, 247, 255, 203, 100, 124, 48, 37, 2, 1, - 12, 4, 32, 41, 234, 106, 78, 31, 11, 114, 137, 237, 17, 92, 71, 134, 47, 62, 78, 189, - 233, 201, 214, 53, 4, 47, 189, 201, 133, 6, 121, 34, 131, 64, 142, 48, 37, 2, 1, 13, 4, - 32, 91, 222, 210, 193, 62, 222, 104, 82, 36, 41, 138, 253, 70, 15, 148, 208, 156, 45, - 105, 171, 241, 195, 185, 43, 217, 162, 146, 201, 222, 89, 238, 38, 48, 37, 2, 1, 14, 4, - 32, 76, 123, 216, 13, 51, 227, 72, 245, 59, 193, 238, 166, 103, 49, 23, 164, 171, 188, - 194, 197, 156, 187, 249, 28, 198, 95, 69, 15, 182, 56, 54, 38, - ]; - let result = [ - 32, 85, 108, 174, 127, 112, 178, 182, 8, 43, 134, 123, 192, 211, 131, 66, 184, 240, 212, - 181, 240, 180, 106, 195, 24, 117, 54, 129, 19, 10, 250, 53, - ]; - assert_eq(sha256_var(input, input.len() as u64), result); - } - - #[test] - fn same_msg_len_variable_padding() { - let input = [ - 29, 81, 165, 84, 243, 114, 101, 37, 242, 146, 127, 99, 69, 145, 39, 72, 213, 39, 253, - 179, 218, 37, 217, 201, 172, 93, 198, 50, 249, 70, 15, 30, 162, 112, 187, 40, 140, 9, - 236, 53, 32, 44, 38, 163, 113, 254, 192, 197, 44, 89, 71, 130, 169, 242, 17, 211, 214, - 72, 19, 178, 186, 168, 147, 127, 99, 101, 252, 227, 8, 147, 150, 85, 97, 158, 17, 107, - 218, 244, 82, 113, 247, 91, 208, 214, 60, 244, 87, 137, 173, 201, 130, 18, 66, 56, 198, - 149, 207, 189, 175, 120, 123, 224, 177, 167, 251, 159, 143, 110, 68, 183, 189, 70, 126, - 32, 35, 164, 44, 30, 44, 12, 65, 18, 62, 239, 242, 2, 248, 104, 2, 178, 64, 28, 126, 36, - 137, 24, 14, 116, 91, 98, 90, 159, 218, 102, 45, 11, 110, 223, 245, 184, 52, 99, 59, - 245, 136, 175, 3, 72, 164, 146, 145, 116, 22, 66, 24, 49, 193, 121, 3, 60, 37, 41, 97, - 3, 190, 66, 195, 225, 63, 46, 3, 118, 4, 208, 15, 1, 40, 254, 235, 151, 123, 70, 180, - 170, 44, 172, 90, 4, 254, 53, 239, 116, 246, 67, 56, 129, 61, 22, 169, 213, 65, 27, 216, - 116, 162, 239, 214, 207, 126, 177, 20, 100, 25, 48, 143, 84, 215, 70, 197, 53, 65, 70, - 86, 172, 61, 62, 9, 212, 167, 169, 133, 41, 126, 213, 196, 33, 192, 238, 0, 63, 246, - 215, 58, 128, 110, 101, 92, 3, 170, 214, 130, 149, 52, 81, 125, 118, 233, 3, 118, 193, - 104, 207, 120, 115, 77, 253, 191, 122, 0, 107, 164, 207, 113, 81, 169, 36, 201, 228, 74, - 134, 131, 218, 178, 35, 30, 216, 101, 2, 103, 174, 87, 95, 50, 50, 215, 157, 5, 210, - 188, 54, 211, 78, 45, 199, 96, 121, 241, 241, 176, 226, 194, 134, 130, 89, 217, 210, - 186, 32, 140, 39, 91, 103, 212, 26, 87, 32, 72, 144, 228, 230, 117, 99, 188, 50, 15, 69, - 79, 179, 50, 12, 106, 86, 218, 101, 73, 142, 243, 29, 250, 122, 228, 233, 29, 255, 22, - 121, 114, 125, 103, 41, 250, 241, 179, 126, 158, 198, 116, 209, 65, 94, 98, 228, 175, - 169, 96, 3, 9, 233, 133, 214, 55, 161, 164, 103, 80, 85, 24, 186, 64, 167, 92, 131, 53, - 101, 202, 47, 25, 104, 118, 155, 14, 12, 12, 25, 116, 45, 221, 249, 28, 246, 212, 200, - 157, 167, 169, 56, 197, 181, 4, 245, 146, 1, 140, 234, 191, 212, 228, 125, 87, 81, 86, - 119, 30, 63, 129, 143, 32, 96, - ]; - - // Prepare inputs of different lengths - let mut input_511 = [0; 511]; - let mut input_512 = [0; 512]; // Next block - let mut input_575 = [0; 575]; - let mut input_576 = [0; 576]; // Next block - for i in 0..input.len() { - input_511[i] = input[i]; - input_512[i] = input[i]; - input_575[i] = input[i]; - input_576[i] = input[i]; - } - - // Compute hashes of all inputs (with same message length) - let fixed_length_hash = super::sha256(input); - let var_full_length_hash = sha256_var(input, input.len() as u64); - let var_length_hash_511 = sha256_var(input_511, input.len() as u64); - let var_length_hash_512 = sha256_var(input_512, input.len() as u64); - let var_length_hash_575 = sha256_var(input_575, input.len() as u64); - let var_length_hash_576 = sha256_var(input_576, input.len() as u64); - - // All of the above should have produced the same hash - assert_eq(var_full_length_hash, fixed_length_hash); - assert_eq(var_length_hash_511, fixed_length_hash); - assert_eq(var_length_hash_512, fixed_length_hash); - assert_eq(var_length_hash_575, fixed_length_hash); - assert_eq(var_length_hash_576, fixed_length_hash); - } - - #[test] - fn test_get_item_byte() { - let fld = make_item(10, 20, 30, 40); - assert_eq(fld, 0x0a141e28); - assert_eq(get_item_byte(fld, 0), 10); - assert_eq(get_item_byte(fld, 4), 10); - assert_eq(get_item_byte(fld, 6), 30); - } - - #[test] - fn test_byte_into_item() { - let fld = make_item(0, 20, 0, 0); - assert_eq(byte_into_item(20, 1), fld); - assert_eq(byte_into_item(20, 5), fld); - } - - #[test] - fn test_set_item_zeros() { - let fld0 = make_item(10, 20, 30, 40); - let fld1 = make_item(10, 0, 0, 0); - assert_eq(set_item_zeros(fld0, 3), fld1); - assert_eq(set_item_zeros(fld0, 4), 0); - assert_eq(set_item_zeros(0, 4), 0); - } - - #[test] - fn test_set_item_byte_then_zeros() { - let fld0 = make_item(10, 20, 30, 40); - let fld1 = make_item(10, 50, 0, 0); - assert_eq(set_item_byte_then_zeros(fld0, 1, 50), fld1); - } - - #[test] - fn test_build_msg_block_start_0() { - let input = [ - 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, - 101, 115, 46, 48, - ]; - assert_eq(input.len(), 22); - - // Safety: testing context - let (msg_block, msg_byte_ptr) = unsafe { build_msg_block(input, input.len(), 0) }; - assert_eq(msg_byte_ptr, input.len()); - assert_eq(msg_block[0], make_item(input[0], input[1], input[2], input[3])); - assert_eq(msg_block[1], make_item(input[4], input[5], input[6], input[7])); - assert_eq(msg_block[5], make_item(input[20], input[21], 0, 0)); - assert_eq(msg_block[6], 0); - } - - #[test] - fn test_build_msg_block_start_1() { - let input = [ - 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, - 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, - 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, - 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, - ]; - assert_eq(input.len(), 68); - // Safety: test context - let (msg_block, msg_byte_ptr) = unsafe { build_msg_block(input, input.len(), 64) }; - assert_eq(msg_byte_ptr, 4); - assert_eq(msg_block[0], make_item(input[64], input[65], input[66], input[67])); - assert_eq(msg_block[1], 0); - } - - #[test] - fn test_attach_len_to_msg_block() { - let input: INT_BLOCK = [ - 2152555847, 1397309779, 1936618851, 1262052426, 1936876331, 1985297723, 543702374, - 1919905082, 1131376244, 1701737517, 1417244773, 978151789, 1697470053, 1920166255, - 1849316213, 1651139939, - ]; - // Safety: testing context - let msg_block = unsafe { attach_len_to_msg_block(input, 1, 448) }; - assert_eq(msg_block[0], ((1 << 7) as u32) * 256 * 256 * 256); - assert_eq(msg_block[1], 0); - assert_eq(msg_block[15], 3584); - } -} diff --git a/noir/noir-repo/noir_stdlib/src/hash/sha512.nr b/noir/noir-repo/noir_stdlib/src/hash/sha512.nr deleted file mode 100644 index 5630139c1f12..000000000000 --- a/noir/noir-repo/noir_stdlib/src/hash/sha512.nr +++ /dev/null @@ -1,165 +0,0 @@ -// Implementation of SHA-512 mapping a byte array of variable length to -// 64 bytes. -// Internal functions act on 64-bit unsigned integers for simplicity. -// Auxiliary mappings; names as in FIPS PUB 180-4 -fn rotr64(a: u64, b: u8) -> u64 // 64-bit right rotation -{ - // None of the bits overlap between `(a >> b)` and `(a << (64 - b))` - // Addition is then equivalent to OR, with fewer constraints. - (a >> b) + (a << (64 - b)) -} - -fn sha_ch(x: u64, y: u64, z: u64) -> u64 { - (x & y) ^ (!x & z) -} - -fn sha_maj(x: u64, y: u64, z: u64) -> u64 { - (x & y) ^ (x & z) ^ (y & z) -} - -fn sha_bigma0(x: u64) -> u64 { - rotr64(x, 28) ^ rotr64(x, 34) ^ rotr64(x, 39) -} - -fn sha_bigma1(x: u64) -> u64 { - rotr64(x, 14) ^ rotr64(x, 18) ^ rotr64(x, 41) -} - -fn sha_sigma0(x: u64) -> u64 { - rotr64(x, 1) ^ rotr64(x, 8) ^ (x >> 7) -} - -fn sha_sigma1(x: u64) -> u64 { - rotr64(x, 19) ^ rotr64(x, 61) ^ (x >> 6) -} - -fn sha_w(msg: [u64; 16]) -> [u64; 80] // Expanded message blocks -{ - let mut w: [u64; 80] = [0; 80]; - - for j in 0..16 { - w[j] = msg[j]; - } - - for j in 16..80 { - w[j] = crate::wrapping_add( - crate::wrapping_add(sha_sigma1(w[j - 2]), w[j - 7]), - crate::wrapping_add(sha_sigma0(w[j - 15]), w[j - 16]), - ); - } - w -} - -// SHA-512 compression function -#[no_predicates] -fn sha_c(msg: [u64; 16], hash: [u64; 8]) -> [u64; 8] { - // noir-fmt:ignore - let K: [u64; 80] = [4794697086780616226, 8158064640168781261, 13096744586834688815, 16840607885511220156, 4131703408338449720, 6480981068601479193, 10538285296894168987, 12329834152419229976, 15566598209576043074, 1334009975649890238, 2608012711638119052, 6128411473006802146, 8268148722764581231, 9286055187155687089, 11230858885718282805, 13951009754708518548, 16472876342353939154, 17275323862435702243, 1135362057144423861, 2597628984639134821, 3308224258029322869, 5365058923640841347, 6679025012923562964, 8573033837759648693, 10970295158949994411, 12119686244451234320, 12683024718118986047, 13788192230050041572, 14330467153632333762, 15395433587784984357, 489312712824947311, 1452737877330783856, 2861767655752347644, 3322285676063803686, 5560940570517711597, 5996557281743188959, 7280758554555802590, 8532644243296465576, 9350256976987008742, 10552545826968843579, 11727347734174303076, 12113106623233404929, 14000437183269869457, 14369950271660146224, 15101387698204529176, 15463397548674623760, 17586052441742319658, 1182934255886127544, 1847814050463011016, 2177327727835720531, 2830643537854262169, 3796741975233480872, 4115178125766777443, 5681478168544905931, 6601373596472566643, 7507060721942968483, 8399075790359081724, 8693463985226723168, 9568029438360202098, 10144078919501101548, 10430055236837252648, 11840083180663258601, 13761210420658862357, 14299343276471374635, 14566680578165727644, 15097957966210449927, 16922976911328602910, 17689382322260857208, 500013540394364858, 748580250866718886, 1242879168328830382, 1977374033974150939, 2944078676154940804, 3659926193048069267, 4368137639120453308, 4836135668995329356, 5532061633213252278, 6448918945643986474, 6902733635092675308, 7801388544844847127]; // first 64 bits of fractional parts of cube roots of first 80 primes - let mut out_h: [u64; 8] = hash; - let w = sha_w(msg); - for j in 0..80 { - let out1 = crate::wrapping_add(out_h[7], sha_bigma1(out_h[4])); - let out2 = crate::wrapping_add(out1, sha_ch(out_h[4], out_h[5], out_h[6])); - let t1 = crate::wrapping_add(crate::wrapping_add(out2, K[j]), w[j]); - let t2 = crate::wrapping_add(sha_bigma0(out_h[0]), sha_maj(out_h[0], out_h[1], out_h[2])); - out_h[7] = out_h[6]; - out_h[6] = out_h[5]; - out_h[5] = out_h[4]; - out_h[4] = crate::wrapping_add(out_h[3], t1); - out_h[3] = out_h[2]; - out_h[2] = out_h[1]; - out_h[1] = out_h[0]; - out_h[0] = crate::wrapping_add(t1, t2); - } - - out_h -} -// Convert 128-byte array to array of 16 u64s -fn msg_u8_to_u64(msg: [u8; 128]) -> [u64; 16] { - let mut msg64: [u64; 16] = [0; 16]; - - for i in 0..16 { - let mut msg_field: Field = 0; - for j in 0..8 { - msg_field = msg_field * 256 + msg[128 - 8 * (i + 1) + j] as Field; - } - msg64[15 - i] = msg_field as u64; - } - - msg64 -} -// SHA-512 hash function -pub fn digest(msg: [u8; N]) -> [u8; 64] { - let mut msg_block: [u8; 128] = [0; 128]; - // noir-fmt:ignore - let mut h: [u64; 8] = [7640891576956012808, 13503953896175478587, 4354685564936845355, 11912009170470909681, 5840696475078001361, 11170449401992604703, 2270897969802886507, 6620516959819538809]; // Intermediate hash, starting with the canonical initial value - let mut c: [u64; 8] = [0; 8]; // Compression of current message block as sequence of u64 - let mut out_h: [u8; 64] = [0; 64]; // Digest as sequence of bytes - let mut i: u64 = 0; // Message byte pointer - for k in 0..msg.len() { - // Populate msg_block - msg_block[i] = msg[k]; - i = i + 1; - if i == 128 { - // Enough to hash block - c = sha_c(msg_u8_to_u64(msg_block), h); - for j in 0..8 { - h[j] = crate::wrapping_add(h[j], c[j]); - } - - i = 0; - } - } - // Pad the rest such that we have a [u64; 2] block at the end representing the length - // of the message, and a block of 1 0 ... 0 following the message (i.e. [1 << 7, 0, ..., 0]). - msg_block[i] = 1 << 7; - i += 1; - // If i >= 113, there aren't enough bits in the current message block to accomplish this, so - // the 1 and 0s fill up the current block, which we then compress accordingly. - if i >= 113 { - // Not enough bits (128) to store length. Fill up with zeros. - if i < 128 { - for _i in 113..128 { - if i <= 127 { - msg_block[i] = 0; - i += 1; - } - } - } - c = sha_c(msg_u8_to_u64(msg_block), h); - for j in 0..8 { - h[j] = crate::wrapping_add(h[j], c[j]); - } - - i = 0; - } - - let len = 8 * msg.len(); - let len_bytes: [u8; 16] = (len as Field).to_le_bytes(); - for _i in 0..128 { - // In any case, fill blocks up with zeros until the last 128 (i.e. until i = 112). - if i < 112 { - msg_block[i] = 0; - i += 1; - } else if i < 128 { - for j in 0..16 { - msg_block[127 - j] = len_bytes[j]; - } - i += 16; // Done. - } - } - // Hash final padded block - c = sha_c(msg_u8_to_u64(msg_block), h); - for j in 0..8 { - h[j] = crate::wrapping_add(h[j], c[j]); - } - // Return final hash as byte array - for j in 0..8 { - let h_bytes: [u8; 8] = (h[7 - j] as Field).to_le_bytes(); - for k in 0..8 { - out_h[63 - 8 * j - k] = h_bytes[k]; - } - } - - out_h -} diff --git a/noir/noir-repo/noir_stdlib/src/lib.nr b/noir/noir-repo/noir_stdlib/src/lib.nr index d5c360792d91..cd54162a5045 100644 --- a/noir/noir-repo/noir_stdlib/src/lib.nr +++ b/noir/noir-repo/noir_stdlib/src/lib.nr @@ -2,12 +2,9 @@ pub mod hash; pub mod aes128; pub mod array; pub mod slice; -pub mod merkle; pub mod ecdsa_secp256k1; pub mod ecdsa_secp256r1; pub mod embedded_curve_ops; -pub mod sha256; -pub mod sha512; pub mod field; pub mod collections; pub mod compat; @@ -19,7 +16,6 @@ pub mod cmp; pub mod ops; pub mod default; pub mod prelude; -pub mod uint128; pub mod runtime; pub mod meta; pub mod append; @@ -121,8 +117,46 @@ where pub fn as_witness(x: Field) {} mod tests { + use super::wrapping_mul; + #[test(should_fail_with = "custom message")] fn test_static_assert_custom_message() { super::static_assert(1 == 2, "custom message"); } + + #[test(should_fail)] + fn test_wrapping_mul() { + // This currently fails. + // See: https://github.com/noir-lang/noir/issues/7528 + let zero: u128 = 0; + let one: u128 = 1; + let two_pow_64: u128 = 0x10000000000000000; + let u128_max: u128 = 0xffffffffffffffffffffffffffffffff; + + // 1*0==0 + assert_eq(zero, wrapping_mul(zero, one)); + + // 0*1==0 + assert_eq(zero, wrapping_mul(one, zero)); + + // 1*1==1 + assert_eq(one, wrapping_mul(one, one)); + + // 0 * ( 1 << 64 ) == 0 + assert_eq(zero, wrapping_mul(zero, two_pow_64)); + + // ( 1 << 64 ) * 0 == 0 + assert_eq(zero, wrapping_mul(two_pow_64, zero)); + + // 1 * ( 1 << 64 ) == 1 << 64 + assert_eq(two_pow_64, wrapping_mul(two_pow_64, one)); + + // ( 1 << 64 ) * 1 == 1 << 64 + assert_eq(two_pow_64, wrapping_mul(one, two_pow_64)); + + // ( 1 << 64 ) * ( 1 << 64 ) == 1 << 64 + assert_eq(zero, wrapping_mul(two_pow_64, two_pow_64)); + // -1 * -1 == 1 + assert_eq(one, wrapping_mul(u128_max, u128_max)); + } } diff --git a/noir/noir-repo/noir_stdlib/src/merkle.nr b/noir/noir-repo/noir_stdlib/src/merkle.nr deleted file mode 100644 index 34cfcdb17877..000000000000 --- a/noir/noir-repo/noir_stdlib/src/merkle.nr +++ /dev/null @@ -1,19 +0,0 @@ -// Regular merkle tree means a append-only merkle tree (Explain why this is the only way to have privacy and alternatives if you don't want it) -// Currently we assume that it is a binary tree, so depth k implies a width of 2^k -// XXX: In the future we can add an arity parameter -// Returns the merkle root of the tree from the provided leaf, its hashpath, using a pedersen hash function. -#[deprecated("This function will be removed from the stdlib in version 1.0.0-beta.4")] -pub fn compute_merkle_root(leaf: Field, index: Field, hash_path: [Field; N]) -> Field { - let index_bits: [u1; N] = index.to_le_bits(); - let mut current = leaf; - for i in 0..N { - let path_bit = index_bits[i] as bool; - let (hash_left, hash_right) = if path_bit { - (hash_path[i], current) - } else { - (current, hash_path[i]) - }; - current = crate::hash::pedersen_hash([hash_left, hash_right]); - } - current -} diff --git a/noir/noir-repo/noir_stdlib/src/meta/op.nr b/noir/noir-repo/noir_stdlib/src/meta/op.nr index 67c9b4cdb643..7dc846e3318b 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/op.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/op.nr @@ -222,4 +222,3 @@ impl BinaryOp { } } } - diff --git a/noir/noir-repo/noir_stdlib/src/ops/arith.nr b/noir/noir-repo/noir_stdlib/src/ops/arith.nr index 195ae7775803..3c5f011662c8 100644 --- a/noir/noir-repo/noir_stdlib/src/ops/arith.nr +++ b/noir/noir-repo/noir_stdlib/src/ops/arith.nr @@ -10,6 +10,11 @@ impl Add for Field { } } +impl Add for u128 { + fn add(self, other: u128) -> u128 { + self + other + } +} impl Add for u64 { fn add(self, other: u64) -> u64 { self + other @@ -69,6 +74,11 @@ impl Sub for Field { } } +impl Sub for u128 { + fn sub(self, other: u128) -> u128 { + self - other + } +} impl Sub for u64 { fn sub(self, other: u64) -> u64 { self - other @@ -128,6 +138,11 @@ impl Mul for Field { } } +impl Mul for u128 { + fn mul(self, other: u128) -> u128 { + self * other + } +} impl Mul for u64 { fn mul(self, other: u64) -> u64 { self * other @@ -187,6 +202,11 @@ impl Div for Field { } } +impl Div for u128 { + fn div(self, other: u128) -> u128 { + self / other + } +} impl Div for u64 { fn div(self, other: u64) -> u64 { self / other @@ -240,6 +260,11 @@ pub trait Rem { } // docs:end:rem-trait +impl Rem for u128 { + fn rem(self, other: u128) -> u128 { + self % other + } +} impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other @@ -321,4 +346,3 @@ impl Neg for i64 { } } // docs:end:neg-trait-impls - diff --git a/noir/noir-repo/noir_stdlib/src/ops/bit.nr b/noir/noir-repo/noir_stdlib/src/ops/bit.nr index 70c52ac38b00..3b1f99430cd9 100644 --- a/noir/noir-repo/noir_stdlib/src/ops/bit.nr +++ b/noir/noir-repo/noir_stdlib/src/ops/bit.nr @@ -11,6 +11,11 @@ impl Not for bool { } } +impl Not for u128 { + fn not(self) -> u128 { + !self + } +} impl Not for u64 { fn not(self) -> u64 { !self @@ -71,6 +76,11 @@ impl BitOr for bool { } } +impl BitOr for u128 { + fn bitor(self, other: u128) -> u128 { + self | other + } +} impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other @@ -130,6 +140,11 @@ impl BitAnd for bool { } } +impl BitAnd for u128 { + fn bitand(self, other: u128) -> u128 { + self & other + } +} impl BitAnd for u64 { fn bitand(self, other: u64) -> u64 { self & other @@ -189,6 +204,11 @@ impl BitXor for bool { } } +impl BitXor for u128 { + fn bitxor(self, other: u128) -> u128 { + self ^ other + } +} impl BitXor for u64 { fn bitxor(self, other: u64) -> u64 { self ^ other @@ -242,8 +262,8 @@ pub trait Shl { } // docs:end:shl-trait -impl Shl for u32 { - fn shl(self, other: u8) -> u32 { +impl Shl for u128 { + fn shl(self, other: u8) -> u128 { self << other } } @@ -252,6 +272,11 @@ impl Shl for u64 { self << other } } +impl Shl for u32 { + fn shl(self, other: u8) -> u32 { + self << other + } +} impl Shl for u16 { fn shl(self, other: u8) -> u16 { self << other @@ -295,6 +320,11 @@ pub trait Shr { } // docs:end:shr-trait +impl Shr for u128 { + fn shr(self, other: u8) -> u128 { + self >> other + } +} impl Shr for u64 { fn shr(self, other: u8) -> u64 { self >> other @@ -341,4 +371,3 @@ impl Shr for i64 { self >> other } } - diff --git a/noir/noir-repo/noir_stdlib/src/prelude.nr b/noir/noir-repo/noir_stdlib/src/prelude.nr index a4a6c35b615e..7aa60456b6dd 100644 --- a/noir/noir-repo/noir_stdlib/src/prelude.nr +++ b/noir/noir-repo/noir_stdlib/src/prelude.nr @@ -7,4 +7,3 @@ pub use crate::default::Default; pub use crate::meta::{derive, derive_via}; pub use crate::option::Option; pub use crate::panic::panic; -pub use crate::uint128::U128; diff --git a/noir/noir-repo/noir_stdlib/src/sha256.nr b/noir/noir-repo/noir_stdlib/src/sha256.nr deleted file mode 100644 index 534c954d3dc1..000000000000 --- a/noir/noir-repo/noir_stdlib/src/sha256.nr +++ /dev/null @@ -1,10 +0,0 @@ -// This file is kept for backwards compatibility. -#[deprecated("sha256 is being deprecated from the stdlib, use https://github.com/noir-lang/sha256 instead")] -pub fn digest(msg: [u8; N]) -> [u8; 32] { - crate::hash::sha256::digest(msg) -} - -#[deprecated("sha256 is being deprecated from the stdlib, use https://github.com/noir-lang/sha256 instead")] -pub fn sha256_var(msg: [u8; N], message_size: u64) -> [u8; 32] { - crate::hash::sha256::sha256_var(msg, message_size) -} diff --git a/noir/noir-repo/noir_stdlib/src/sha512.nr b/noir/noir-repo/noir_stdlib/src/sha512.nr deleted file mode 100644 index 27b53f4395f6..000000000000 --- a/noir/noir-repo/noir_stdlib/src/sha512.nr +++ /dev/null @@ -1,5 +0,0 @@ -// This file is kept for backwards compatibility. -#[deprecated] -pub fn digest(msg: [u8; N]) -> [u8; 64] { - crate::hash::sha512::digest(msg) -} diff --git a/noir/noir-repo/noir_stdlib/src/uint128.nr b/noir/noir-repo/noir_stdlib/src/uint128.nr deleted file mode 100644 index f41958e0e306..000000000000 --- a/noir/noir-repo/noir_stdlib/src/uint128.nr +++ /dev/null @@ -1,572 +0,0 @@ -use crate::cmp::{Eq, Ord, Ordering}; -use crate::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Not, Rem, Shl, Shr, Sub}; -use crate::static_assert; -use super::{convert::AsPrimitive, default::Default}; - -global pow64: Field = 18446744073709551616; //2^64; -global pow63: Field = 9223372036854775808; // 2^63; -pub struct U128 { - pub(crate) lo: Field, - pub(crate) hi: Field, -} - -impl U128 { - - pub fn from_u64s_le(lo: u64, hi: u64) -> U128 { - // in order to handle multiplication, we need to represent the product of two u64 without overflow - assert(crate::field::modulus_num_bits() as u32 > 128); - U128 { lo: lo as Field, hi: hi as Field } - } - - pub fn from_u64s_be(hi: u64, lo: u64) -> U128 { - U128::from_u64s_le(lo, hi) - } - - pub fn zero() -> U128 { - U128 { lo: 0, hi: 0 } - } - - pub fn one() -> U128 { - U128 { lo: 1, hi: 0 } - } - pub fn from_le_bytes(bytes: [u8; 16]) -> U128 { - let mut lo = 0; - let mut base = 1; - for i in 0..8 { - lo += (bytes[i] as Field) * base; - base *= 256; - } - let mut hi = 0; - base = 1; - for i in 8..16 { - hi += (bytes[i] as Field) * base; - base *= 256; - } - U128 { lo, hi } - } - - pub fn to_be_bytes(self: Self) -> [u8; 16] { - let lo: [u8; 8] = self.lo.to_be_bytes(); - let hi: [u8; 8] = self.hi.to_be_bytes(); - let mut bytes = [0; 16]; - for i in 0..8 { - bytes[i] = hi[i]; - bytes[i + 8] = lo[i]; - } - bytes - } - - pub fn to_le_bytes(self: Self) -> [u8; 16] { - let lo: [u8; 8] = self.lo.to_le_bytes(); - let hi: [u8; 8] = self.hi.to_le_bytes(); - let mut bytes = [0; 16]; - for i in 0..8 { - bytes[i] = lo[i]; - bytes[i + 8] = hi[i]; - } - bytes - } - - pub fn from_hex(hex: str) -> U128 { - let bytes = hex.as_bytes(); - // string must starts with "0x" - assert((bytes[0] == 48) & (bytes[1] == 120), "Invalid hexadecimal string"); - static_assert(N < 35, "Input does not fit into a U128"); - - let mut lo = 0; - let mut hi = 0; - let mut base = 1; - if N <= 18 { - for i in 0..N - 2 { - lo += U128::decode_ascii(bytes[N - i - 1]) * base; - base = base * 16; - } - } else { - for i in 0..16 { - lo += U128::decode_ascii(bytes[N - i - 1]) * base; - base = base * 16; - } - base = 1; - for i in 17..N - 1 { - hi += U128::decode_ascii(bytes[N - i]) * base; - base = base * 16; - } - } - U128 { lo: lo as Field, hi: hi as Field } - } - - unconstrained fn unconstrained_check_is_upper_ascii(ascii: u8) -> bool { - ((ascii >= 65) & (ascii <= 90)) // Between 'A' and 'Z' - } - - pub(crate) fn decode_ascii(ascii: u8) -> Field { - ( - if ascii < 58 { - ascii - 48 - } else { - // Safety: optionally adds 32 and then check (below) the result is in 'a..f' range - let ascii = - ascii + 32 * (unsafe { U128::unconstrained_check_is_upper_ascii(ascii) as u8 }); - assert(ascii >= 97); // enforce >= 'a' - assert(ascii <= 102); // enforce <= 'f' - ascii - 87 - } - ) as Field - } - - // TODO: Replace with a faster version. - // A circuit that uses this function can be slow to compute - // (we're doing up to 127 calls to compute the quotient) - unconstrained fn unconstrained_div(self: Self, b: U128) -> (U128, U128) { - if b == U128::zero() { - // Return 0,0 to avoid eternal loop - (U128::zero(), U128::zero()) - } else if self < b { - (U128::zero(), self) - } else if self == b { - (U128::one(), U128::zero()) - } else { - let (q, r) = if b.hi as u64 >= pow63 as u64 { - // The result of multiplication by 2 would overflow - (U128::zero(), self) - } else { - self.unconstrained_div(b * U128::from_u64s_le(2, 0)) - }; - let q_mul_2 = q * U128::from_u64s_le(2, 0); - if r < b { - (q_mul_2, r) - } else { - (q_mul_2 + U128::one(), r - b) - } - } - } - - pub fn from_integer(i: T) -> U128 - where - T: AsPrimitive, - { - let f = i.as_(); - // Reject values which would overflow a u128 - f.assert_max_bit_size::<128>(); - let lo = f as u64 as Field; - let hi = (f - lo) / pow64; - U128 { lo, hi } - } - - pub fn to_integer(self) -> T - where - Field: AsPrimitive, - { - AsPrimitive::as_(self.lo + self.hi * pow64) - } - - fn wrapping_mul(self: Self, b: U128) -> U128 { - let low = self.lo * b.lo; - let lo = low as u64 as Field; - let carry = (low - lo) / pow64; - let high = self.lo * b.hi + self.hi * b.lo + carry; - let hi = high as u64 as Field; - U128 { lo, hi } - } -} - -impl Add for U128 { - fn add(self: Self, b: U128) -> U128 { - let low = self.lo + b.lo; - let lo = low as u64 as Field; - let carry = (low - lo) / pow64; - let high = self.hi + b.hi + carry; - let hi = high as u64 as Field; - assert(hi == high, "attempt to add with overflow"); - U128 { lo, hi } - } -} - -impl Sub for U128 { - fn sub(self: Self, b: U128) -> U128 { - let low = pow64 + self.lo - b.lo; - let lo = low as u64 as Field; - let borrow = (low == lo) as Field; - let high = self.hi - b.hi - borrow; - let hi = high as u64 as Field; - assert(hi == high, "attempt to subtract with underflow"); - U128 { lo, hi } - } -} - -impl Mul for U128 { - fn mul(self: Self, b: U128) -> U128 { - assert(self.hi * b.hi == 0, "attempt to multiply with overflow"); - let low = self.lo * b.lo; - let lo = low as u64 as Field; - let carry = (low - lo) / pow64; - let high = if crate::field::modulus_num_bits() as u32 > 196 { - (self.lo + self.hi) * (b.lo + b.hi) - low + carry - } else { - self.lo * b.hi + self.hi * b.lo + carry - }; - let hi = high as u64 as Field; - assert(hi == high, "attempt to multiply with overflow"); - U128 { lo, hi } - } -} - -impl Div for U128 { - fn div(self: Self, b: U128) -> U128 { - // Safety: euclidian division is asserted to be correct: assert(a == b * q + r); and assert(r < b); - // Furthermore, U128 addition and multiplication ensures that b * q + r does not overflow - unsafe { - let (q, r) = self.unconstrained_div(b); - let a = b * q + r; - assert_eq(self, a); - assert(r < b); - q - } - } -} - -impl Rem for U128 { - fn rem(self: Self, b: U128) -> U128 { - // Safety: cf div() above - unsafe { - let (q, r) = self.unconstrained_div(b); - let a = b * q + r; - assert_eq(self, a); - assert(r < b); - - r - } - } -} - -impl Eq for U128 { - fn eq(self: Self, b: U128) -> bool { - (self.lo == b.lo) & (self.hi == b.hi) - } -} - -impl Ord for U128 { - fn cmp(self, other: Self) -> Ordering { - let hi_ordering = (self.hi as u64).cmp((other.hi as u64)); - let lo_ordering = (self.lo as u64).cmp((other.lo as u64)); - - if hi_ordering == Ordering::equal() { - lo_ordering - } else { - hi_ordering - } - } -} - -impl Not for U128 { - fn not(self) -> U128 { - U128 { lo: (!(self.lo as u64)) as Field, hi: (!(self.hi as u64)) as Field } - } -} - -impl BitOr for U128 { - fn bitor(self, other: U128) -> U128 { - U128 { - lo: ((self.lo as u64) | (other.lo as u64)) as Field, - hi: ((self.hi as u64) | (other.hi as u64)) as Field, - } - } -} - -impl BitAnd for U128 { - fn bitand(self, other: U128) -> U128 { - U128 { - lo: ((self.lo as u64) & (other.lo as u64)) as Field, - hi: ((self.hi as u64) & (other.hi as u64)) as Field, - } - } -} - -impl BitXor for U128 { - fn bitxor(self, other: U128) -> U128 { - U128 { - lo: ((self.lo as u64) ^ (other.lo as u64)) as Field, - hi: ((self.hi as u64) ^ (other.hi as u64)) as Field, - } - } -} - -impl Shl for U128 { - fn shl(self, other: u8) -> U128 { - assert(other < 128, "attempt to shift left with overflow"); - let exp_bits: [u1; 7] = (other as Field).to_be_bits(); - - let mut r: Field = 2; - let mut y: Field = 1; - for i in 1..8 { - let bit = exp_bits[7 - i] as Field; - y = bit * (r * y) + (1 - bit) * y; - r *= r; - } - self.wrapping_mul(U128::from_integer(y)) - } -} - -impl Shr for U128 { - fn shr(self, other: u8) -> U128 { - assert(other < 128, "attempt to shift right with overflow"); - let exp_bits: [u1; 7] = (other as Field).to_be_bits(); - - let mut r: Field = 2; - let mut y: Field = 1; - for i in 1..8 { - let bit = exp_bits[7 - i] as Field; - y = bit * (r * y) + (1 - bit) * y; - r *= r; - } - self / U128::from_integer(y) - } -} - -impl Default for U128 { - fn default() -> Self { - U128::zero() - } -} - -mod tests { - use crate::default::Default; - use crate::ops::Not; - use crate::uint128::{pow63, pow64, U128}; - - #[test] - fn test_not(lo: u64, hi: u64) { - let num = U128::from_u64s_le(lo, hi); - let not_num = num.not(); - - assert_eq(not_num.hi, (hi.not() as Field)); - assert_eq(not_num.lo, (lo.not() as Field)); - - let not_not_num = not_num.not(); - assert_eq(num, not_not_num); - } - #[test] - fn test_construction() { - // Check little-endian u64 is inversed with big-endian u64 construction - let a = U128::from_u64s_le(2, 1); - let b = U128::from_u64s_be(1, 2); - assert_eq(a, b); - // Check byte construction is equivalent - let c = U128::from_le_bytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let d = U128::from_u64s_le(0x0706050403020100, 0x0f0e0d0c0b0a0908); - assert_eq(c, d); - } - #[test] - fn test_byte_decomposition() { - let a = U128::from_u64s_le(0x0706050403020100, 0x0f0e0d0c0b0a0908); - // Get big-endian and little-endian byte decompostions - let le_bytes_a = a.to_le_bytes(); - let be_bytes_a = a.to_be_bytes(); - - // Check equivalence - for i in 0..16 { - assert_eq(le_bytes_a[i], be_bytes_a[15 - i]); - } - // Reconstruct U128 from byte decomposition - let b = U128::from_le_bytes(le_bytes_a); - // Check that it's the same element - assert_eq(a, b); - } - #[test] - fn test_hex_constuction() { - let a = U128::from_u64s_le(0x1, 0x2); - let b = U128::from_hex("0x20000000000000001"); - assert_eq(a, b); - - let c = U128::from_hex("0xffffffffffffffffffffffffffffffff"); - let d = U128::from_u64s_le(0xffffffffffffffff, 0xffffffffffffffff); - assert_eq(c, d); - - let e = U128::from_hex("0x00000000000000000000000000000000"); - let f = U128::from_u64s_le(0, 0); - assert_eq(e, f); - } - - // Ascii decode tests - - #[test] - fn test_ascii_decode_correct_range() { - // '0'..'9' range - for i in 0..10 { - let decoded = U128::decode_ascii(48 + i); - assert_eq(decoded, i as Field); - } - // 'A'..'F' range - for i in 0..6 { - let decoded = U128::decode_ascii(65 + i); - assert_eq(decoded, (i + 10) as Field); - } - // 'a'..'f' range - for i in 0..6 { - let decoded = U128::decode_ascii(97 + i); - assert_eq(decoded, (i + 10) as Field); - } - } - - #[test(should_fail)] - fn test_ascii_decode_range_less_than_48_fails_0() { - crate::println(U128::decode_ascii(0)); - } - #[test(should_fail)] - fn test_ascii_decode_range_less_than_48_fails_1() { - crate::println(U128::decode_ascii(47)); - } - - #[test(should_fail)] - fn test_ascii_decode_range_58_64_fails_0() { - let _ = U128::decode_ascii(58); - } - #[test(should_fail)] - fn test_ascii_decode_range_58_64_fails_1() { - let _ = U128::decode_ascii(64); - } - #[test(should_fail)] - fn test_ascii_decode_range_71_96_fails_0() { - let _ = U128::decode_ascii(71); - } - #[test(should_fail)] - fn test_ascii_decode_range_71_96_fails_1() { - let _ = U128::decode_ascii(96); - } - #[test(should_fail)] - fn test_ascii_decode_range_greater_than_102_fails() { - let _ = U128::decode_ascii(103); - } - - #[test(should_fail)] - fn test_ascii_decode_regression() { - // This code will actually fail because of ascii_decode, - // but in the past it was possible to create a value > (1<<128) - let a = U128::from_hex("0x~fffffffffffffffffffffffffffffff"); - let b: Field = a.to_integer(); - let c: [u8; 17] = b.to_le_bytes(); - assert(c[16] != 0); - } - - #[test] - fn test_unconstrained_div() { - // Test the potential overflow case - let a = U128::from_u64s_le(0x0, 0xffffffffffffffff); - let b = U128::from_u64s_le(0x0, 0xfffffffffffffffe); - let c = U128::one(); - let d = U128::from_u64s_le(0x0, 0x1); - // Safety: testing context - unsafe { - let (q, r) = a.unconstrained_div(b); - assert_eq(q, c); - assert_eq(r, d); - } - - let a = U128::from_u64s_le(2, 0); - let b = U128::one(); - // Check the case where a is a multiple of b - // Safety: testing context - unsafe { - let (c, d) = a.unconstrained_div(b); - assert_eq((c, d), (a, U128::zero())); - } - - // Check where b is a multiple of a - // Safety: testing context - unsafe { - let (c, d) = b.unconstrained_div(a); - assert_eq((c, d), (U128::zero(), b)); - } - - // Dividing by zero returns 0,0 - let a = U128::from_u64s_le(0x1, 0x0); - let b = U128::zero(); - // Safety: testing context - unsafe { - let (c, d) = a.unconstrained_div(b); - assert_eq((c, d), (U128::zero(), U128::zero())); - } - // Dividing 1<<127 by 1<<127 (special case) - let a = U128::from_u64s_le(0x0, pow63 as u64); - let b = U128::from_u64s_le(0x0, pow63 as u64); - // Safety: testing context - unsafe { - let (c, d) = a.unconstrained_div(b); - assert_eq((c, d), (U128::one(), U128::zero())); - } - } - - #[test] - fn integer_conversions() { - // Maximum - let start: Field = 0xffffffffffffffffffffffffffffffff; - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - - // Minimum - let start: Field = 0x0; - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - - // Low limb - let start: Field = 0xffffffffffffffff; - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - - // High limb - let start: Field = 0xffffffffffffffff0000000000000000; - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - } - - #[test] - fn integer_conversions_fuzz(lo: u64, hi: u64) { - let start: Field = (lo as Field) + pow64 * (hi as Field); - let a = U128::from_integer(start); - let end = a.to_integer(); - assert_eq(start, end); - } - - #[test] - fn test_wrapping_mul() { - // 1*0==0 - assert_eq(U128::zero(), U128::zero().wrapping_mul(U128::one())); - - // 0*1==0 - assert_eq(U128::zero(), U128::one().wrapping_mul(U128::zero())); - - // 1*1==1 - assert_eq(U128::one(), U128::one().wrapping_mul(U128::one())); - - // 0 * ( 1 << 64 ) == 0 - assert_eq(U128::zero(), U128::zero().wrapping_mul(U128::from_u64s_le(0, 1))); - - // ( 1 << 64 ) * 0 == 0 - assert_eq(U128::zero(), U128::from_u64s_le(0, 1).wrapping_mul(U128::zero())); - - // 1 * ( 1 << 64 ) == 1 << 64 - assert_eq(U128::from_u64s_le(0, 1), U128::from_u64s_le(0, 1).wrapping_mul(U128::one())); - - // ( 1 << 64 ) * 1 == 1 << 64 - assert_eq(U128::from_u64s_le(0, 1), U128::one().wrapping_mul(U128::from_u64s_le(0, 1))); - - // ( 1 << 64 ) * ( 1 << 64 ) == 1 << 64 - assert_eq(U128::zero(), U128::from_u64s_le(0, 1).wrapping_mul(U128::from_u64s_le(0, 1))); - // -1 * -1 == 1 - assert_eq( - U128::one(), - U128::from_u64s_le(0xffffffffffffffff, 0xffffffffffffffff).wrapping_mul( - U128::from_u64s_le(0xffffffffffffffff, 0xffffffffffffffff), - ), - ); - } - - #[test] - fn test_default() { - assert_eq(U128::default(), U128::zero()); - } -} diff --git a/noir/noir-repo/rust-toolchain.toml b/noir/noir-repo/rust-toolchain.toml index e647d5cbf460..ed1915d2613a 100644 --- a/noir/noir-repo/rust-toolchain.toml +++ b/noir/noir-repo/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.75.0" +channel = "1.85.0" components = [ "rust-src" ] -targets = [ "wasm32-unknown-unknown", "wasm32-wasi", "aarch64-apple-darwin" ] +targets = [ "wasm32-unknown-unknown", "wasm32-wasip1", "aarch64-apple-darwin" ] profile = "default" diff --git a/noir/noir-repo/scripts/bump-aztec-packges-commit.sh b/noir/noir-repo/scripts/bump-aztec-packages-commit.sh similarity index 100% rename from noir/noir-repo/scripts/bump-aztec-packges-commit.sh rename to noir/noir-repo/scripts/bump-aztec-packages-commit.sh diff --git a/noir/noir-repo/scripts/install_bb.sh b/noir/noir-repo/scripts/install_bb.sh index 72170af78d8d..e95bd50a0f08 100755 --- a/noir/noir-repo/scripts/install_bb.sh +++ b/noir/noir-repo/scripts/install_bb.sh @@ -1,11 +1,11 @@ #!/bin/bash -VERSION="0.72.1" +VERSION="0.77.1" BBUP_PATH=~/.bb/bbup if ! [ -f $BBUP_PATH ]; then - curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/master/barretenberg/cpp/installation/install | bash + curl -L https://bbup.aztec.network | bash fi $BBUP_PATH -v $VERSION diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256/Prover.toml b/noir/noir-repo/test_programs/benchmarks/bench_sha256/Prover.toml deleted file mode 100644 index 66779dea9d7b..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256/Prover.toml +++ /dev/null @@ -1 +0,0 @@ -input = [1,2] diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_sha256/src/main.nr deleted file mode 100644 index c94d359239dd..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256/src/main.nr +++ /dev/null @@ -1,4 +0,0 @@ - -fn main(input: [u8; 2]) -> pub [u8; 32] { - std::hash::sha256(input) -} \ No newline at end of file diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Prover.toml b/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Prover.toml deleted file mode 100644 index 542b4a08dd6f..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Prover.toml +++ /dev/null @@ -1,102 +0,0 @@ -input = [ - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], -] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/src/main.nr deleted file mode 100644 index 6e4bfc27c8f2..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/src/main.nr +++ /dev/null @@ -1,10 +0,0 @@ -global SIZE: u32 = 100; - -fn main(input: [[u8; 2]; SIZE]) -> pub [[u8; 32]; SIZE] { - let mut results: [[u8; 32]; SIZE] = [[0; 32]; SIZE]; - for i in 0..SIZE { - results[i] = std::hash::sha256(input[i]); - } - - results -} diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Nargo.toml b/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Nargo.toml deleted file mode 100644 index c1dc76df3942..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "bench_sha256_30" -version = "0.1.0" -type = "bin" -authors = [""] - -[dependencies] diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Prover.toml b/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Prover.toml deleted file mode 100644 index 7792a9ab8e36..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/Prover.toml +++ /dev/null @@ -1,32 +0,0 @@ -input = [ - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], - [1,2], -] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/src/main.nr deleted file mode 100644 index 0a4288114e34..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_30/src/main.nr +++ /dev/null @@ -1,10 +0,0 @@ -global SIZE: u32 = 30; - -fn main(input: [[u8; 2]; SIZE]) -> pub [[u8; 32]; SIZE] { - let mut results: [[u8; 32]; SIZE] = [[0; 32]; SIZE]; - for i in 0..SIZE { - results[i] = std::hash::sha256(input[i]); - } - - results -} diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Prover.toml b/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Prover.toml deleted file mode 100644 index ba4bbc1540dc..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Prover.toml +++ /dev/null @@ -1,191 +0,0 @@ -# 2*64+60=188 bytes -input = [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 72, - 73, - 74, - 75, - 76, - 77, - 78, - 79, - 80, - 81, - 82, - 83, - 84, - 85, - 86, - 87, - 88, - 89, - 90, - 91, - 92, - 93, - 94, - 95, - 96, - 97, - 98, - 99, - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 72, - 73, - 74, - 75, - 76, - 77, - 78, - 79, - 80, - 81, - 82, - 83, - 84, - 85, - 86, - 87, -] diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/src/main.nr b/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/src/main.nr deleted file mode 100644 index c47bdc2a5610..000000000000 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/src/main.nr +++ /dev/null @@ -1,7 +0,0 @@ -// Input size long enough that we have to compress a few times -// and then pad the last block out. -global INPUT_SIZE: u32 = 2 * 64 + 60; - -fn main(input: [u8; INPUT_SIZE]) -> pub [u8; 32] { - std::hash::sha256(input) -} diff --git a/noir/noir-repo/test_programs/compilation_report.sh b/noir/noir-repo/test_programs/compilation_report.sh index 6f7ef254477b..aa1bbef39dec 100755 --- a/noir/noir-repo/test_programs/compilation_report.sh +++ b/noir/noir-repo/test_programs/compilation_report.sh @@ -6,7 +6,7 @@ current_dir=$(pwd) base_path="$current_dir/execution_success" # Tests to be profiled for compilation report -tests_to_profile=("sha256_regression" "regression_4709" "ram_blowup_regression" "global_var_regression_entry_points") +tests_to_profile=("regression_4709" "ram_blowup_regression" "global_var_regression_entry_points") echo "[ " > $current_dir/compilation_report.json diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Nargo.toml b/noir/noir-repo/test_programs/compile_failure/broken_impl/Nargo.toml similarity index 51% rename from noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Nargo.toml rename to noir/noir-repo/test_programs/compile_failure/broken_impl/Nargo.toml index d0c90d75088e..a200adc27b07 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_100/Nargo.toml +++ b/noir/noir-repo/test_programs/compile_failure/broken_impl/Nargo.toml @@ -1,7 +1,7 @@ [package] -name = "bench_sha256_100" -version = "0.1.0" +name = "broken_impl" type = "bin" authors = [""] +compiler_version = ">=0.26.0" [dependencies] diff --git a/noir/noir-repo/test_programs/compile_failure/broken_impl/src/main.nr b/noir/noir-repo/test_programs/compile_failure/broken_impl/src/main.nr new file mode 100644 index 000000000000..033b0e741ed6 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_failure/broken_impl/src/main.nr @@ -0,0 +1,2 @@ +// This used to crash the compiler +impl< Foo for diff --git a/noir/noir-repo/test_programs/compile_success_empty/brillig_integer_binary_operations/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/brillig_integer_binary_operations/src/main.nr index 032fbf457b63..2c02582c14d9 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/brillig_integer_binary_operations/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/brillig_integer_binary_operations/src/main.nr @@ -77,4 +77,3 @@ unconstrained fn shr(x: u32, y: u8) -> u32 { unconstrained fn shl(x: u32, y: u8) -> u32 { x << y } - diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_as_field/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_as_field/src/main.nr deleted file mode 100644 index e13db54b80bd..000000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_as_field/src/main.nr +++ /dev/null @@ -1,6 +0,0 @@ -fn main() { - comptime { - let _: U128 = U128::from_integer(1); - } -} - diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_as_field/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/comptime_as_primitive/Nargo.toml similarity index 100% rename from noir/noir-repo/test_programs/compile_success_empty/comptime_as_field/Nargo.toml rename to noir/noir-repo/test_programs/compile_success_empty/comptime_as_primitive/Nargo.toml diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_as_primitive/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_as_primitive/src/main.nr new file mode 100644 index 000000000000..392ccf2ebfc6 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_as_primitive/src/main.nr @@ -0,0 +1,7 @@ +fn main() { + comptime { + let x: u64 = 1; + let y = x as Field; + let _ = y as u128; + } +} 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 index 8cdd15aaa0ec..adee6e6fe935 100644 --- 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 @@ -26,4 +26,3 @@ fn hello_world() {} comptime fn call(x: Quoted) -> Quoted { quote { $x() } } - diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/Nargo.toml deleted file mode 100644 index 38a46ba0dbec..000000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/Nargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "comptime_from_field" -type = "bin" -authors = [""] - -[dependencies] diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/src/main.nr deleted file mode 100644 index 722c5c7eb329..000000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_from_field/src/main.nr +++ /dev/null @@ -1,6 +0,0 @@ -fn main() { - comptime { - let _: Field = U128::from_hex("0x0").to_integer(); - } -} - diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_function_definition/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_function_definition/src/main.nr index 4ce641cbe7df..57bcd2ba90ec 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_function_definition/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_function_definition/src/main.nr @@ -116,7 +116,7 @@ mod test_as_typed_expr_1 { comptime fn foo(module: Module) -> Quoted { let method = module.functions().filter(|f| f.name() == quote { method })[0]; let func = method.as_typed_expr(); - quote { + quote { pub fn bar() -> i32 { $func(1) } @@ -140,7 +140,7 @@ mod test_as_typed_expr_2 { comptime fn foo(module: Module) -> Quoted { let method = module.functions().filter(|f| f.name() == quote { method })[0]; let func = method.as_typed_expr(); - quote { + quote { pub fn bar() -> u32 { // Safety: test program unsafe { $func([1, 2, 3, 0]) } @@ -166,7 +166,7 @@ mod test_as_typed_expr_3 { comptime fn foo(module: Module) -> Quoted { let method = module.functions().filter(|f| f.name() == quote { method })[0]; let func = method.as_typed_expr(); - quote { + quote { pub fn bar() -> u32 { // Safety: test program comptime { $func(([1, 2, 3, 0], "a")) } diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/Nargo.toml deleted file mode 100644 index 47c8654804d6..000000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "comptime_keccak" -type = "bin" -authors = [""] -compiler_version = ">=0.33.0" - -[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/src/main.nr deleted file mode 100644 index dc4e88b7ab2d..000000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/src/main.nr +++ /dev/null @@ -1,32 +0,0 @@ -// Tests a very simple program. -// -// The features being tested is keccak256 in brillig -fn main() { - comptime { - let x = 0xbd; - let result = [ - 0x5a, 0x50, 0x2f, 0x9f, 0xca, 0x46, 0x7b, 0x26, 0x6d, 0x5b, 0x78, 0x33, 0x65, 0x19, - 0x37, 0xe8, 0x05, 0x27, 0x0c, 0xa3, 0xf3, 0xaf, 0x1c, 0x0d, 0xd2, 0x46, 0x2d, 0xca, - 0x4b, 0x3b, 0x1a, 0xbf, - ]; - // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field - // The padding is taken care of by the program - let digest = keccak256([x as u8], 1); - assert(digest == result); - //#1399: variable message size - let message_size = 4; - let hash_a = keccak256([1, 2, 3, 4], message_size); - let hash_b = keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); - - assert(hash_a == hash_b); - - let message_size_big = 8; - let hash_c = keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); - - assert(hash_a != hash_c); - } -} - -comptime fn keccak256(data: [u8; N], msg_len: u32) -> [u8; 32] { - std::hash::keccak256(data, msg_len) -} 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 11025d450246..5573e8ad798b 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 @@ -31,4 +31,3 @@ pub fn neg_at_comptime() { let _result = -value; } } - diff --git a/noir/noir-repo/test_programs/compile_success_empty/conditional_regression_to_bits/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/conditional_regression_to_bits/src/main.nr index dd80d2c576f2..ef0eb4cf0689 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/conditional_regression_to_bits/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/conditional_regression_to_bits/src/main.nr @@ -23,4 +23,3 @@ fn main() { } assert(c1 == 1); } - diff --git a/noir/noir-repo/test_programs/compile_success_empty/enums/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/enums/src/main.nr index 03fc10a1c35a..3b12af1c15d5 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/enums/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/enums/src/main.nr @@ -3,6 +3,7 @@ fn main() { foo_tests(); option_tests(); abc_tests(); + match_on_structs(); } fn primitive_tests() { @@ -19,8 +20,20 @@ fn primitive_tests() { false => fail(), true => (), } + + // Ensure we can match on MIN and use globals as constants + let i64_min = I64_MIN; + match i64_min { + 9_223_372_036_854_775_807 => fail(), + -9_223_372_036_854_775_807 => fail(), + 0 => fail(), + I64_MIN => (), + _ => fail(), + } } +global I64_MIN: i64 = -9_223_372_036_854_775_808; + enum Foo { A(Field, Field), B(u32), @@ -103,7 +116,7 @@ fn abc_tests() { // Mut is only to throw the optimizer off a bit so we can see // the `eq`s that get generated before they're removed because each of these are constant let mut tuple = (ABC::A, ABC::B); - match tuple { + let _ = match tuple { (ABC::A, _) => 1, (_, ABC::A) => 2, (_, ABC::B) => 3, @@ -114,3 +127,27 @@ fn abc_tests() { _ => 0, }; } + +fn match_on_structs() { + let foo = MyStruct { x: 10, y: 20 }; + match foo { + MyStruct { x, y } => { + assert_eq(x, 10); + assert_eq(y, 20); + }, + } + + match MyOption::Some(foo) { + MyOption::Some(MyStruct { x: x2, y: y2 }) => { + assert_eq(x2, 10); + assert_eq(y2, 20); + }, + MyOption::None => fail(), + MyOption::Maybe => fail(), + } +} + +struct MyStruct { + x: i32, + y: Field, +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/eq_derivation_with_numeric_generics/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/eq_derivation_with_numeric_generics/src/main.nr index 3307aeb15adc..e2f3cb46d11d 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/eq_derivation_with_numeric_generics/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/eq_derivation_with_numeric_generics/src/main.nr @@ -9,4 +9,3 @@ fn main() { let bar = Foo { a: [0; 10], b: 28 }; assert(foo != bar); } - diff --git a/noir/noir-repo/test_programs/compile_success_empty/trait_override_implementation/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/trait_override_implementation/src/main.nr index 12a625b08432..589232d00d8e 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/trait_override_implementation/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/trait_override_implementation/src/main.nr @@ -80,4 +80,3 @@ fn main(x: Field) { assert(bar_mut.f4() == 4, "14"); assert(bar_mut.f5() == 50, "15"); } - diff --git a/noir/noir-repo/test_programs/compile_success_empty/unquote_struct/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/unquote_struct/src/main.nr index 12c683a94a85..c7820c094b7a 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/unquote_struct/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/unquote_struct/src/main.nr @@ -1,3 +1,5 @@ +use std::meta::unquote; + fn main() { let foo = Foo { x: 4, y: 4 }; foo.assert_equal(); @@ -21,3 +23,16 @@ comptime fn output_struct(f: FunctionDefinition) -> Quoted { } } } + +// The following code proves that `Bar::` can be unquoted +// into a constructor position without losing its generic types. + +pub struct Bar {} + +pub fn bar() { + let _ = comptime { + let x = quote { Bar:: }; + let q = quote { $x {} }; + unquote!(q) + }; +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/unquote_trait_constraint_in_trait_impl_position/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/unquote_trait_constraint_in_trait_impl_position/src/main.nr index 92ddf99f1c0b..8bf3863bbe3f 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/unquote_trait_constraint_in_trait_impl_position/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/unquote_trait_constraint_in_trait_impl_position/src/main.nr @@ -21,4 +21,3 @@ comptime fn foo(_: FunctionDefinition) -> Quoted { } } } - diff --git a/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Nargo.toml b/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Nargo.toml new file mode 100644 index 000000000000..c04a74f24df4 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "u128_multiplication_overflow" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] diff --git a/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Prover.toml b/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Prover.toml new file mode 100644 index 000000000000..90212f582036 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/Prover.toml @@ -0,0 +1,2 @@ +x = "257295058452732708167448229940904157557" +y = "85070591730234615865843651857942052864" diff --git a/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/src/main.nr b/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/src/main.nr new file mode 100644 index 000000000000..45884059a881 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/u128_multiplication_overflow/src/main.nr @@ -0,0 +1,7 @@ +fn main(x: u128, y: u128) -> pub u128 { + // Multiplying 257295058452732708167448229940904157557 * 85070591730234615865843651857942052864 + // using bn254 field elements wraps the maximum field element value, + // resulting in 31631953497925087476338759149270597631 which is less than u128::MAX. + // We want this to be caught as an overflow. + x * y +} diff --git a/noir/noir-repo/test_programs/execution_report.sh b/noir/noir-repo/test_programs/execution_report.sh index 5c916ef6bd75..c7b12d996811 100755 --- a/noir/noir-repo/test_programs/execution_report.sh +++ b/noir/noir-repo/test_programs/execution_report.sh @@ -6,7 +6,7 @@ current_dir=$(pwd) base_path="$current_dir/execution_success" # Tests to be profiled for execution report -tests_to_profile=("sha256_regression" "regression_4709" "ram_blowup_regression" "global_var_regression_entry_points") +tests_to_profile=("regression_4709" "ram_blowup_regression" "global_var_regression_entry_points") echo "[" > $current_dir/execution_report.json diff --git a/noir/noir-repo/test_programs/execution_success/6/Prover.toml b/noir/noir-repo/test_programs/execution_success/6/Prover.toml index 1c52aef063c9..d370032bd530 100644 --- a/noir/noir-repo/test_programs/execution_success/6/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/6/Prover.toml @@ -3,37 +3,4 @@ # used : https://emn178.github.io/online-tools/sha256.html x = [104, 101, 108, 108, 111] -result = [ - 0x2c, - 0xf2, - 0x4d, - 0xba, - 0x5f, - 0xb0, - 0xa3, - 0x0e, - 0x26, - 0xe8, - 0x3b, - 0x2a, - 0xc5, - 0xb9, - 0xe2, - 0x9e, - 0x1b, - 0x16, - 0x1e, - 0x5c, - 0x1f, - 0xa7, - 0x42, - 0x5e, - 0x73, - 0x04, - 0x33, - 0x62, - 0x93, - 0x8b, - 0x98, - 0x24, -] +result = [234, 143, 22, 61, 179, 134, 130, 146, 94, 68, 145, 197, 229, 141, 75, 179, 80, 110, 248, 193, 78, 183, 138, 134, 233, 8, 197, 98, 74, 103, 32, 15] diff --git a/noir/noir-repo/test_programs/execution_success/6/src/main.nr b/noir/noir-repo/test_programs/execution_success/6/src/main.nr index 5b71174614f5..e950e309df2d 100644 --- a/noir/noir-repo/test_programs/execution_success/6/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/6/src/main.nr @@ -1,11 +1,11 @@ -// Sha256 circuit where the input is 5 bytes -// not five field elements since sha256 operates over +// blake3 circuit where the input is 5 bytes +// not five field elements since blake3 operates over // bytes. // // If you do not cast, it will take all the bytes from the field element! fn main(x: [u8; 5], result: pub [u8; 32]) { - let mut digest = std::hash::sha256(x); + let mut digest = std::hash::blake3(x); digest[0] = 5 as u8; - digest = std::hash::sha256(x); + digest = std::hash::blake3(x); assert(digest == result); } diff --git a/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/Prover.toml b/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/Prover.toml index cc60eb8a8baa..f852a79a1033 100644 --- a/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/Prover.toml @@ -1,4 +1,4 @@ index = "1" leaf = ["51", "109", "224", "175", "60", "42", "79", "222", "117", "255", "174", "79", "126", "242", "74", "34", "100", "35", "20", "200", "109", "89", "191", "219", "41", "10", "118", "217", "165", "224", "215", "109"] path = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63"] -root = [79, 230, 126, 184, 98, 125, 226, 58, 117, 45, 140, 15, 72, 118, 89, 173, 117, 161, 166, 0, 214, 125, 13, 16, 113, 81, 173, 156, 97, 15, 57, 216] +root = [186, 47, 168, 70, 152, 149, 203, 90, 138, 188, 96, 15, 111, 179, 82, 106, 198, 166, 172, 38, 110, 187, 182, 64, 29, 101, 171, 221, 89, 105, 243, 22] diff --git a/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr b/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr index 260d609928b8..603ae704d704 100644 --- a/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr @@ -18,7 +18,7 @@ fn compute_root(leaf: [u8; 32], path: [u8; 64], _index: u32, root: [u8; 32]) { hash_input[j + b] = path[offset + j]; } - current = std::hash::sha256(hash_input); + current = std::hash::blake3(hash_input); index = index >> 1; } diff --git a/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/Prover.toml b/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/Prover.toml index 1f2915324144..85b480415b1b 100644 --- a/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/Prover.toml @@ -1,5 +1,5 @@ y = "3" -hash_result = [50, 53, 90, 252, 105, 236, 223, 30, 135, 229, 193, 172, 51, 139, 8, 32, 188, 104, 151, 115, 129, 168, 27, 71, 203, 47, 40, 228, 89, 177, 129, 100] +hash_result = [77, 43, 36, 42, 132, 232, 186, 191, 119, 43, 192, 121, 66, 137, 143, 205, 13, 23, 80, 25, 162, 45, 100, 31, 178, 150, 138, 4, 72, 73, 120, 70] [[x]] a = "1" @@ -20,4 +20,4 @@ a = "7" b = ["8", "9", "22"] [x.bar] -inner = ["106", "107", "108"] \ No newline at end of file +inner = ["106", "107", "108"] diff --git a/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/src/main.nr b/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/src/main.nr index 15a2747eaa9d..14f110a23a0b 100644 --- a/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/array_dynamic_nested_blackbox_input/src/main.nr @@ -15,6 +15,6 @@ fn main(mut x: [Foo; 3], y: pub Field, hash_result: pub [u8; 32]) { // Make sure that we are passing a dynamic array to the black box function call // by setting the array using a dynamic index here hash_input[y - 1] = 0; - let hash = std::hash::sha256(hash_input); + let hash = std::hash::blake3(hash_input); assert_eq(hash, hash_result); } diff --git a/noir/noir-repo/test_programs/execution_success/bit_and/src/main.nr b/noir/noir-repo/test_programs/execution_success/bit_and/src/main.nr index 5a0aa17e3ede..2e2ce62def7c 100644 --- a/noir/noir-repo/test_programs/execution_success/bit_and/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/bit_and/src/main.nr @@ -13,4 +13,3 @@ fn main(x: Field, y: Field, a: Field, b: Field) { let b_as_u8 = b as u8; assert((a_as_u8 & b_as_u8) == a_as_u8); } - diff --git a/noir/noir-repo/test_programs/execution_success/bool_not/src/main.nr b/noir/noir-repo/test_programs/execution_success/bool_not/src/main.nr index 935d8cc074de..d848225b3f26 100644 --- a/noir/noir-repo/test_programs/execution_success/bool_not/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/bool_not/src/main.nr @@ -1,4 +1,3 @@ fn main(x: u1) { assert(!x == 0); } - diff --git a/noir/noir-repo/test_programs/execution_success/bool_or/src/main.nr b/noir/noir-repo/test_programs/execution_success/bool_or/src/main.nr index 6cb959e61e64..87bbb757fbcd 100644 --- a/noir/noir-repo/test_programs/execution_success/bool_or/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/bool_or/src/main.nr @@ -3,4 +3,3 @@ fn main(x: u1, y: u1) { assert(x | y | x == 1); } - diff --git a/noir/noir-repo/test_programs/execution_success/brillig_calls_conditionals/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_calls_conditionals/src/main.nr index e8bce638ebe8..8d0e78bd6235 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_calls_conditionals/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_calls_conditionals/src/main.nr @@ -36,4 +36,3 @@ unconstrained fn entry_point(x: u32) -> u32 { result } - diff --git a/noir/noir-repo/test_programs/execution_success/brillig_cow_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_cow_regression/src/main.nr index 69273bc3dcac..4b70f2961b66 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_cow_regression/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_cow_regression/src/main.nr @@ -172,6 +172,6 @@ unconstrained fn main(kernel_data: DataToHash) -> pub [Field; NUM_FIELDS_PER_SHA } } - let sha_digest = std::hash::sha256(hash_input_flattened); - U256::from_bytes32(sha_digest).to_u128_limbs() + let blake3_digest = std::hash::blake3(hash_input_flattened); + U256::from_bytes32(blake3_digest).to_u128_limbs() } diff --git a/noir/noir-repo/test_programs/execution_success/brillig_nested_arrays/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_nested_arrays/src/main.nr index 3f5e5a0f8eb5..7835bca55a75 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_nested_arrays/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_nested_arrays/src/main.nr @@ -41,4 +41,3 @@ fn main(x: Field, y: Field) { create_and_assert_inside_brillig(x, y); } } - diff --git a/noir/noir-repo/test_programs/execution_success/brillig_pedersen/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_pedersen/src/main.nr index 55f81882bd4d..e4d2d2d0e784 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_pedersen/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_pedersen/src/main.nr @@ -24,4 +24,3 @@ unconstrained fn main( let hash = std::hash::pedersen_commitment_with_separator([state], 0); assert(std::hash::pedersen_commitment_with_separator([43], 0).x == hash.x); } - diff --git a/noir/noir-repo/test_programs/execution_success/cast_bool/src/main.nr b/noir/noir-repo/test_programs/execution_success/cast_bool/src/main.nr index 422d3b98f83f..f6742fb62f3f 100644 --- a/noir/noir-repo/test_programs/execution_success/cast_bool/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/cast_bool/src/main.nr @@ -3,4 +3,3 @@ fn main(x: Field, y: Field) { let t = z as u8; assert(t == 1); } - diff --git a/noir/noir-repo/test_programs/execution_success/conditional_1/Prover.toml b/noir/noir-repo/test_programs/execution_success/conditional_1/Prover.toml index baad8be126a5..b06d750fda0b 100644 --- a/noir/noir-repo/test_programs/execution_success/conditional_1/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/conditional_1/Prover.toml @@ -3,7 +3,7 @@ a=0 x = [104, 101, 108, 108, 111] result = [ - 0x2c, + 234, 0xf2, 0x4d, 0xba, diff --git a/noir/noir-repo/test_programs/execution_success/conditional_1/src/main.nr b/noir/noir-repo/test_programs/execution_success/conditional_1/src/main.nr index eedb8a697d1d..e0292c733bd1 100644 --- a/noir/noir-repo/test_programs/execution_success/conditional_1/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/conditional_1/src/main.nr @@ -53,7 +53,7 @@ fn main(a: u32, mut c: [u32; 4], x: [u8; 5], result: pub [u8; 32]) { let mut y = 0; if a == 0 { - let digest = std::hash::sha256(x); + let digest = std::hash::blake3(x); y = digest[0]; } else { y = 5; diff --git a/noir/noir-repo/test_programs/execution_success/conditional_regression_661/src/main.nr b/noir/noir-repo/test_programs/execution_success/conditional_regression_661/src/main.nr index 1036acc6da4b..a8459515634b 100644 --- a/noir/noir-repo/test_programs/execution_success/conditional_regression_661/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/conditional_regression_661/src/main.nr @@ -25,4 +25,3 @@ fn issue_661_bar(a: [u32; 4]) -> [u32; 4] { b[0] = a[0] + 1; b } - diff --git a/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/Prover.toml b/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/Prover.toml index baad8be126a5..5f098ae39d81 100644 --- a/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/Prover.toml @@ -2,37 +2,4 @@ c=[2, 4, 3, 0, ] a=0 x = [104, 101, 108, 108, 111] -result = [ - 0x2c, - 0xf2, - 0x4d, - 0xba, - 0x5f, - 0xb0, - 0xa3, - 0x0e, - 0x26, - 0xe8, - 0x3b, - 0x2a, - 0xc5, - 0xb9, - 0xe2, - 0x9e, - 0x1b, - 0x16, - 0x1e, - 0x5c, - 0x1f, - 0xa7, - 0x42, - 0x5e, - 0x73, - 0x04, - 0x33, - 0x62, - 0x93, - 0x8b, - 0x98, - 0x24, -] +result = [234, 143, 22, 61, 179, 134, 130, 146, 94, 68, 145, 197, 229, 141, 75, 179, 80, 110, 248, 193, 78, 183, 138, 134, 233, 8, 197, 98, 74, 103, 32, 15] diff --git a/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/src/main.nr b/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/src/main.nr index de5ad20a6423..9312655c8f36 100644 --- a/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/conditional_regression_short_circuit/src/main.nr @@ -24,9 +24,9 @@ fn bar(x: Field) { } fn call_intrinsic(x: [u8; 5], result: [u8; 32]) { - let mut digest = std::hash::sha256(x); + let mut digest = std::hash::blake3(x); digest[0] = 5 as u8; - digest = std::hash::sha256(x); + digest = std::hash::blake3(x); assert(digest == result); } 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 ecc7794cf9ea..02c2a7d06832 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 @@ -9,4 +9,3 @@ fn main(mut x: u32, y: call_data(0) u32, z: call_data(0) [u32; 4]) -> return_dat unconstrained fn foo(x: u32) -> u32 { x + 1 } - diff --git a/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/Prover.toml b/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/Prover.toml index 412c7b36e4c2..e78fc19cb71b 100644 --- a/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/Prover.toml @@ -33,46 +33,6 @@ hashed_message = [ 0xc1, 0xe2, ] -message = [ - 0x49, - 0x6e, - 0x73, - 0x74, - 0x72, - 0x75, - 0x63, - 0x74, - 0x69, - 0x6f, - 0x6e, - 0x73, - 0x20, - 0x75, - 0x6e, - 0x63, - 0x6c, - 0x65, - 0x61, - 0x72, - 0x2c, - 0x20, - 0x61, - 0x73, - 0x6b, - 0x20, - 0x61, - 0x67, - 0x61, - 0x69, - 0x6e, - 0x20, - 0x6c, - 0x61, - 0x74, - 0x65, - 0x72, - 0x2e, -] pub_key_x = [ 0xa0, 0x43, diff --git a/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/src/main.nr b/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/src/main.nr index 00d420089fc9..1c94bf8961e5 100644 --- a/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/ecdsa_secp256k1/src/main.nr @@ -1,14 +1,4 @@ -fn main( - message: [u8; 38], - hashed_message: [u8; 32], - pub_key_x: [u8; 32], - pub_key_y: [u8; 32], - signature: [u8; 64], -) { - // Hash the message, since secp256k1 expects a hashed_message - let expected = std::hash::sha256(message); - assert(hashed_message == expected); - +fn main(hashed_message: [u8; 32], pub_key_x: [u8; 32], pub_key_y: [u8; 32], signature: [u8; 64]) { let valid_signature = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); assert(valid_signature); diff --git a/noir/noir-repo/test_programs/execution_success/fold_complex_outputs/src/main.nr b/noir/noir-repo/test_programs/execution_success/fold_complex_outputs/src/main.nr index b433c8b416cc..b83a88c4a7d6 100644 --- a/noir/noir-repo/test_programs/execution_success/fold_complex_outputs/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/fold_complex_outputs/src/main.nr @@ -69,4 +69,3 @@ fn map_fields(mut input: ParentStruct) -> ParentStruct { input } - diff --git a/noir/noir-repo/test_programs/execution_success/global_var_entry_point_used_in_another_entry/src/main.nr b/noir/noir-repo/test_programs/execution_success/global_var_entry_point_used_in_another_entry/src/main.nr index 364c27cc4ab7..d57416772717 100644 --- a/noir/noir-repo/test_programs/execution_success/global_var_entry_point_used_in_another_entry/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/global_var_entry_point_used_in_another_entry/src/main.nr @@ -41,4 +41,3 @@ unconstrained fn entry_point_one_diff_global(x: Field, y: Field) { let z = THREE + x + y; assert(z == 4); } - diff --git a/noir/noir-repo/test_programs/execution_success/if_else_chain/src/main.nr b/noir/noir-repo/test_programs/execution_success/if_else_chain/src/main.nr index 2705d5b31110..2c80acd273cd 100644 --- a/noir/noir-repo/test_programs/execution_success/if_else_chain/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/if_else_chain/src/main.nr @@ -12,4 +12,3 @@ fn main(a: u32, mut c: [u32; 4]) { assert(c[0] == 10); } } - diff --git a/noir/noir-repo/test_programs/execution_success/keccak256/Prover.toml b/noir/noir-repo/test_programs/execution_success/keccak256/Prover.toml deleted file mode 100644 index d65c4011d3f8..000000000000 --- a/noir/noir-repo/test_programs/execution_success/keccak256/Prover.toml +++ /dev/null @@ -1,35 +0,0 @@ -x = 0xbd -result = [ - 0x5a, - 0x50, - 0x2f, - 0x9f, - 0xca, - 0x46, - 0x7b, - 0x26, - 0x6d, - 0x5b, - 0x78, - 0x33, - 0x65, - 0x19, - 0x37, - 0xe8, - 0x05, - 0x27, - 0x0c, - 0xa3, - 0xf3, - 0xaf, - 0x1c, - 0x0d, - 0xd2, - 0x46, - 0x2d, - 0xca, - 0x4b, - 0x3b, - 0x1a, - 0xbf, -] diff --git a/noir/noir-repo/test_programs/execution_success/keccak256/src/main.nr b/noir/noir-repo/test_programs/execution_success/keccak256/src/main.nr deleted file mode 100644 index 1e13fa028b79..000000000000 --- a/noir/noir-repo/test_programs/execution_success/keccak256/src/main.nr +++ /dev/null @@ -1,20 +0,0 @@ -// docs:start:keccak256 -fn main(x: Field, result: [u8; 32]) { - // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field - // The padding is taken care of by the program - let digest = std::hash::keccak256([x as u8], 1); - assert(digest == result); - - //#1399: variable message size - let message_size = 4; - let hash_a = std::hash::keccak256([1, 2, 3, 4], message_size); - let hash_b = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); - - assert(hash_a == hash_b); - - let message_size_big = 8; - let hash_c = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); - - assert(hash_a != hash_c); -} -// docs:end:keccak256 diff --git a/noir/noir-repo/test_programs/execution_success/merkle_insert/src/main.nr b/noir/noir-repo/test_programs/execution_success/merkle_insert/src/main.nr index 25a455c90b8e..42a261ae6c6a 100644 --- a/noir/noir-repo/test_programs/execution_success/merkle_insert/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/merkle_insert/src/main.nr @@ -6,8 +6,23 @@ fn main( leaf: Field, index: Field, ) { - assert(old_root == std::merkle::compute_merkle_root(old_leaf, index, old_hash_path)); + assert(old_root == compute_merkle_root(old_leaf, index, old_hash_path)); - let calculated_root = std::merkle::compute_merkle_root(leaf, index, old_hash_path); + let calculated_root = compute_merkle_root(leaf, index, old_hash_path); assert(new_root == calculated_root); } + +fn compute_merkle_root(leaf: Field, index: Field, hash_path: [Field; N]) -> Field { + let index_bits: [u1; N] = index.to_le_bits(); + let mut current = leaf; + for i in 0..N { + let path_bit = index_bits[i] as bool; + let (hash_left, hash_right) = if path_bit { + (hash_path[i], current) + } else { + (current, hash_path[i]) + }; + current = std::hash::pedersen_hash([hash_left, hash_right]); + } + current +} diff --git a/noir/noir-repo/test_programs/execution_success/nested_array_dynamic/src/main.nr b/noir/noir-repo/test_programs/execution_success/nested_array_dynamic/src/main.nr index 2c53822d6b94..4ac6b4124ae5 100644 --- a/noir/noir-repo/test_programs/execution_success/nested_array_dynamic/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/nested_array_dynamic/src/main.nr @@ -73,4 +73,3 @@ fn main(mut x: [Foo; 4], y: pub Field) { foo_parents[y - 2].foos[y - 3].b = foo_parents[y - 2].foos[y - 2].b; assert(foo_parents[1].foos[0].b == [20, 19, 5000]); } - diff --git a/noir/noir-repo/test_programs/execution_success/nested_arrays_from_brillig/src/main.nr b/noir/noir-repo/test_programs/execution_success/nested_arrays_from_brillig/src/main.nr index 387454b9e21e..66021005ce3a 100644 --- a/noir/noir-repo/test_programs/execution_success/nested_arrays_from_brillig/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/nested_arrays_from_brillig/src/main.nr @@ -24,4 +24,3 @@ fn main(values: [Field; 6]) { let notes = unsafe { create_inside_brillig(values) }; assert(access_nested(notes) == (2 + 4 + 3 + 1)); } - diff --git a/noir/noir-repo/test_programs/execution_success/pedersen_check/src/main.nr b/noir/noir-repo/test_programs/execution_success/pedersen_check/src/main.nr index c71b2b570da4..00afa4b8e276 100644 --- a/noir/noir-repo/test_programs/execution_success/pedersen_check/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/pedersen_check/src/main.nr @@ -17,4 +17,3 @@ fn main(x: Field, y: Field, salt: Field, out_x: Field, out_y: Field, out_hash: F let hash = std::hash::pedersen_commitment([state]); assert(std::hash::pedersen_commitment([43]).x == hash.x); } - diff --git a/noir/noir-repo/test_programs/execution_success/pedersen_commitment/src/main.nr b/noir/noir-repo/test_programs/execution_success/pedersen_commitment/src/main.nr index c2225edcdf11..e9a7ea4816f1 100644 --- a/noir/noir-repo/test_programs/execution_success/pedersen_commitment/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/pedersen_commitment/src/main.nr @@ -5,4 +5,3 @@ fn main(x: Field, y: Field, expected_commitment: std::embedded_curve_ops::Embedd assert_eq(commitment.y, expected_commitment.y); } // docs:end:pedersen-commitment - diff --git a/noir/noir-repo/test_programs/execution_success/pedersen_hash/src/main.nr b/noir/noir-repo/test_programs/execution_success/pedersen_hash/src/main.nr index de981d44bca5..c55e947930b4 100644 --- a/noir/noir-repo/test_programs/execution_success/pedersen_hash/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/pedersen_hash/src/main.nr @@ -4,4 +4,3 @@ fn main(x: Field, y: Field, expected_hash: Field) { assert_eq(hash, expected_hash); } // docs:end:pedersen-hash - diff --git a/noir/noir-repo/test_programs/execution_success/ram_blowup_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/ram_blowup_regression/src/main.nr index 6deb54dd21d7..5f63bf03e558 100644 --- a/noir/noir-repo/test_programs/execution_success/ram_blowup_regression/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/ram_blowup_regression/src/main.nr @@ -20,9 +20,9 @@ pub fn field_from_bytes_32_trunc(bytes32: [u8; 32]) -> Field { low + high * v } -pub fn sha256_to_field(bytes_to_hash: [u8; N]) -> Field { - let sha256_hashed = std::hash::sha256(bytes_to_hash); - let hash_in_a_field = field_from_bytes_32_trunc(sha256_hashed); +pub fn blake3_to_field(bytes_to_hash: [u8; N]) -> Field { + let blake3_hashed = std::hash::blake3(bytes_to_hash); + let hash_in_a_field = field_from_bytes_32_trunc(blake3_hashed); hash_in_a_field } @@ -36,6 +36,6 @@ fn main(tx_effects_hash_input: [Field; TX_EFFECTS_HASH_INPUT_FIELDS]) -> pub Fie } } - let sha_digest = sha256_to_field(hash_input_flattened); - sha_digest + let blake3_digest = blake3_to_field(hash_input_flattened); + blake3_digest } diff --git a/noir/noir-repo/test_programs/execution_success/reference_only_used_as_alias/src/main.nr b/noir/noir-repo/test_programs/execution_success/reference_only_used_as_alias/src/main.nr index c8dd38d9aca4..cf84d1030205 100644 --- a/noir/noir-repo/test_programs/execution_success/reference_only_used_as_alias/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/reference_only_used_as_alias/src/main.nr @@ -90,4 +90,3 @@ fn main(input: [Field; 4], randomness: Field, context_input: u32) { let event0 = ExampleEvent0 { value0: input[0], value1: input[1] }; event0.emit(encode_event_with_randomness(&mut context, randomness)); } - diff --git a/noir/noir-repo/test_programs/execution_success/regression_4449/Prover.toml b/noir/noir-repo/test_programs/execution_success/regression_4449/Prover.toml index 81af476bcc98..12b95c0dbfa1 100644 --- a/noir/noir-repo/test_programs/execution_success/regression_4449/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/regression_4449/Prover.toml @@ -1,3 +1,3 @@ x = 0xbd -result = [204, 59, 83, 197, 18, 1, 128, 43, 247, 28, 104, 225, 106, 13, 20, 187, 42, 26, 67, 150, 48, 75, 238, 168, 121, 247, 142, 160, 71, 222, 97, 188] \ No newline at end of file +result = [3, 128, 126, 121, 21, 242, 202, 74, 58, 183, 180, 171, 169, 186, 245, 81, 206, 26, 69, 29, 25, 207, 152, 152, 52, 33, 40, 106, 200, 237, 90, 156] diff --git a/noir/noir-repo/test_programs/execution_success/regression_4449/src/main.nr b/noir/noir-repo/test_programs/execution_success/regression_4449/src/main.nr index 3fda39bd8741..88b80dabc7f5 100644 --- a/noir/noir-repo/test_programs/execution_success/regression_4449/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/regression_4449/src/main.nr @@ -5,7 +5,7 @@ fn main(x: u8, result: [u8; 32]) { for i in 0..70 { let y = x + i; let a = [y, x, 32, 0, y + 1, y - 1, y - 2, 5]; - digest = std::sha256::digest(a); + digest = std::hash::blake3(a); } assert(digest == result); diff --git a/noir/noir-repo/test_programs/execution_success/sha256/Nargo.toml b/noir/noir-repo/test_programs/execution_success/regression_7195/Nargo.toml similarity index 50% rename from noir/noir-repo/test_programs/execution_success/sha256/Nargo.toml rename to noir/noir-repo/test_programs/execution_success/regression_7195/Nargo.toml index 255d2156ef67..d9ef0fa7388d 100644 --- a/noir/noir-repo/test_programs/execution_success/sha256/Nargo.toml +++ b/noir/noir-repo/test_programs/execution_success/regression_7195/Nargo.toml @@ -1,6 +1,6 @@ [package] -name = "sha256" +name = "regression_7195" type = "bin" authors = [""] -[dependencies] +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/regression_7195/Prover.toml b/noir/noir-repo/test_programs/execution_success/regression_7195/Prover.toml new file mode 100644 index 000000000000..8c12ebba6cf7 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/regression_7195/Prover.toml @@ -0,0 +1,2 @@ +x = "1" +y = "2" diff --git a/noir/noir-repo/test_programs/execution_success/regression_7195/src/main.nr b/noir/noir-repo/test_programs/execution_success/regression_7195/src/main.nr new file mode 100644 index 000000000000..004b388d40da --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/regression_7195/src/main.nr @@ -0,0 +1,21 @@ +fn bar(y: Field) { + assert(y != 0); +} + +fn foo(x: Field) { + // Safety: test + let y = unsafe { baz(x) }; + bar(y); +} + +unconstrained fn baz(x: Field) -> Field { + x +} + +fn main(x: Field, y: pub Field) { + // Safety: test + let x = unsafe { baz(x) }; + foo(x); + foo(y); + assert(x != y); +} diff --git a/noir/noir-repo/test_programs/execution_success/sha2_byte/Nargo.toml b/noir/noir-repo/test_programs/execution_success/regression_7451/Nargo.toml similarity index 68% rename from noir/noir-repo/test_programs/execution_success/sha2_byte/Nargo.toml rename to noir/noir-repo/test_programs/execution_success/regression_7451/Nargo.toml index efd691fce585..92740ca82f3e 100644 --- a/noir/noir-repo/test_programs/execution_success/sha2_byte/Nargo.toml +++ b/noir/noir-repo/test_programs/execution_success/regression_7451/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "sha2_byte" +name = "regression_7451" type = "bin" authors = [""] diff --git a/noir/noir-repo/test_programs/execution_success/regression_7451/Prover.toml b/noir/noir-repo/test_programs/execution_success/regression_7451/Prover.toml new file mode 100644 index 000000000000..7b70754b0690 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/regression_7451/Prover.toml @@ -0,0 +1 @@ +x="1" diff --git a/noir/noir-repo/test_programs/execution_success/regression_7451/src/main.nr b/noir/noir-repo/test_programs/execution_success/regression_7451/src/main.nr new file mode 100644 index 000000000000..c8d990a9c82e --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/regression_7451/src/main.nr @@ -0,0 +1,12 @@ +fn main(x: u8) { + // Safety: test code + let predicate = unsafe { return_true() }; + let tmp: u8 = if predicate { 0xff } else { 0 }; + + let y = tmp & x; + assert_eq(y, 1); +} + +unconstrained fn return_true() -> bool { + true +} diff --git a/noir/noir-repo/test_programs/execution_success/sha256/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256/Prover.toml deleted file mode 100644 index b4bf41623709..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256/Prover.toml +++ /dev/null @@ -1,38 +0,0 @@ - -x = 0xbd -result = [ - 0x68, - 0x32, - 0x57, - 0x20, - 0xaa, - 0xbd, - 0x7c, - 0x82, - 0xf3, - 0x0f, - 0x55, - 0x4b, - 0x31, - 0x3d, - 0x05, - 0x70, - 0xc9, - 0x5a, - 0xcc, - 0xbb, - 0x7d, - 0xc4, - 0xb5, - 0xaa, - 0xe1, - 0x12, - 0x04, - 0xc0, - 0x8f, - 0xfe, - 0x73, - 0x2b, -] -input = [0, 0] -toggle = false \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256/src/main.nr deleted file mode 100644 index 8e5e46b9837e..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256/src/main.nr +++ /dev/null @@ -1,27 +0,0 @@ -// Sha256 example -// -// Calls Sha256 from the standard library. -// -// The Compiler sees this special function and creates an ACIR gate -// -// The ACIR SHA256 gate is passed to PLONK who should -// know how to create the necessary constraints. -// -// Not yet here: For R1CS, it is more about manipulating arithmetic gates to get performance -// This can be done in ACIR! -fn main(x: Field, result: [u8; 32], input: [u8; 2], toggle: bool) { - // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field - // The padding is taken care of by the program - // docs:start:sha256_var - let digest = std::hash::sha256_var([x as u8], 1); - // docs:end:sha256_var - assert(digest == result); - - let digest = std::hash::sha256([x as u8]); - assert(digest == result); - - // variable size - let size: Field = 1 + toggle as Field; - let var_sha = std::hash::sha256_var(input, size as u64); - assert(var_sha == std::hash::sha256_var(input, 1)); -} diff --git a/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Nargo.toml deleted file mode 100644 index f7076311e1dc..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "sha256_brillig_performance_regression" -type = "bin" -authors = [""] -compiler_version = ">=0.33.0" - -[dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Prover.toml deleted file mode 100644 index 5bb7f3542573..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/Prover.toml +++ /dev/null @@ -1,16 +0,0 @@ -input_amount = "1" -minimum_output_amount = "2" -secret_hash_for_L1_to_l2_message = "3" -uniswap_fee_tier = "4" - -[aztec_recipient] -inner = "5" - -[caller_on_L1] -inner = "6" - -[input_asset_bridge_portal_address] -inner = "7" - -[output_asset_bridge_portal_address] -inner = "8" diff --git a/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/src/main.nr deleted file mode 100644 index 42cc6d4ff3bc..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_brillig_performance_regression/src/main.nr +++ /dev/null @@ -1,104 +0,0 @@ -// Performance regression extracted from an aztec protocol contract. - -unconstrained fn main( - input_asset_bridge_portal_address: EthAddress, - input_amount: Field, - uniswap_fee_tier: Field, - output_asset_bridge_portal_address: EthAddress, - minimum_output_amount: Field, - aztec_recipient: AztecAddress, - secret_hash_for_L1_to_l2_message: Field, - caller_on_L1: EthAddress, -) -> pub Field { - let mut hash_bytes = [0; 260]; // 8 fields of 32 bytes each + 4 bytes fn selector - let input_token_portal_bytes: [u8; 32] = - input_asset_bridge_portal_address.to_field().to_be_bytes(); - let in_amount_bytes: [u8; 32] = input_amount.to_be_bytes(); - let uniswap_fee_tier_bytes: [u8; 32] = uniswap_fee_tier.to_be_bytes(); - let output_token_portal_bytes: [u8; 32] = - output_asset_bridge_portal_address.to_field().to_be_bytes(); - let amount_out_min_bytes: [u8; 32] = minimum_output_amount.to_be_bytes(); - let aztec_recipient_bytes: [u8; 32] = aztec_recipient.to_field().to_be_bytes(); - let secret_hash_for_L1_to_l2_message_bytes: [u8; 32] = - secret_hash_for_L1_to_l2_message.to_be_bytes(); - let caller_on_L1_bytes: [u8; 32] = caller_on_L1.to_field().to_be_bytes(); - - // The purpose of including the following selector is to make the message unique to that specific call. Note that - // it has nothing to do with calling the function. - let selector = comptime { - std::hash::keccak256( - "swap_public(address,uint256,uint24,address,uint256,bytes32,bytes32,address)".as_bytes(), - 75, - ) - }; - - hash_bytes[0] = selector[0]; - hash_bytes[1] = selector[1]; - hash_bytes[2] = selector[2]; - hash_bytes[3] = selector[3]; - - for i in 0..32 { - hash_bytes[i + 4] = input_token_portal_bytes[i]; - hash_bytes[i + 36] = in_amount_bytes[i]; - hash_bytes[i + 68] = uniswap_fee_tier_bytes[i]; - hash_bytes[i + 100] = output_token_portal_bytes[i]; - hash_bytes[i + 132] = amount_out_min_bytes[i]; - hash_bytes[i + 164] = aztec_recipient_bytes[i]; - hash_bytes[i + 196] = secret_hash_for_L1_to_l2_message_bytes[i]; - hash_bytes[i + 228] = caller_on_L1_bytes[i]; - } - - let content_hash = sha256_to_field(hash_bytes); - content_hash -} - -// Convert a 32 byte array to a field element by truncating the final byte -pub fn field_from_bytes_32_trunc(bytes32: [u8; 32]) -> Field { - // Convert it to a field element - let mut v = 1; - let mut high = 0 as Field; - let mut low = 0 as Field; - - for i in 0..15 { - // covers bytes 16..30 (31 is truncated and ignored) - low = low + (bytes32[15 + 15 - i] as Field) * v; - v = v * 256; - // covers bytes 0..14 - high = high + (bytes32[14 - i] as Field) * v; - } - // covers byte 15 - low = low + (bytes32[15] as Field) * v; - - low + high * v -} - -pub fn sha256_to_field(bytes_to_hash: [u8; N]) -> Field { - let sha256_hashed = std::hash::sha256(bytes_to_hash); - let hash_in_a_field = field_from_bytes_32_trunc(sha256_hashed); - - hash_in_a_field -} - -pub trait ToField { - fn to_field(self) -> Field; -} - -pub struct EthAddress { - inner: Field, -} - -impl ToField for EthAddress { - fn to_field(self) -> Field { - self.inner - } -} - -pub struct AztecAddress { - pub inner: Field, -} - -impl ToField for AztecAddress { - fn to_field(self) -> Field { - self.inner - } -} diff --git a/noir/noir-repo/test_programs/execution_success/sha256_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/sha256_regression/Nargo.toml deleted file mode 100644 index ce98d000bcb7..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_regression/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "sha256_regression" -type = "bin" -authors = [""] -compiler_version = ">=0.33.0" - -[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256_regression/Prover.toml deleted file mode 100644 index ea0a0f2e8a71..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_regression/Prover.toml +++ /dev/null @@ -1,14 +0,0 @@ -msg_just_over_block = [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116] -msg_multiple_of_block = [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 115, 45, 97, 115, 99, 105, 105, 13, 10, 109, 105, 109, 101, 45, 118, 101, 114, 115, 105, 111, 110, 58, 49, 46, 48, 32, 40, 77, 97, 99, 32, 79, 83, 32, 88, 32, 77, 97, 105, 108, 32, 49, 54, 46, 48, 32, 92, 40, 51, 55, 51, 49, 46, 53, 48, 48, 46, 50, 51, 49, 92, 41, 41, 13, 10, 115, 117, 98, 106, 101, 99, 116, 58, 72, 101, 108, 108, 111, 13, 10, 109, 101, 115, 115, 97, 103, 101, 45, 105, 100, 58, 60, 56, 70, 56, 49, 57, 68, 51, 50, 45, 66, 54, 65, 67, 45, 52, 56, 57, 68, 45, 57, 55, 55, 70, 45, 52, 51, 56, 66, 66, 67, 52, 67, 65, 66, 50, 55, 64, 109, 101, 46, 99, 111, 109, 62, 13, 10, 100, 97, 116, 101, 58, 83, 97, 116, 44, 32, 50, 54, 32, 65, 117, 103, 32, 50, 48, 50, 51, 32, 49, 50, 58, 50, 53, 58, 50, 50, 32, 43, 48, 52, 48, 48, 13, 10, 116, 111, 58, 122, 107, 101, 119, 116, 101, 115, 116, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 13, 10, 100, 107, 105, 109, 45, 115, 105, 103, 110, 97, 116, 117, 114, 101, 58, 118, 61, 49, 59, 32, 97, 61, 114, 115, 97, 45, 115, 104, 97, 50, 53, 54, 59, 32, 99, 61, 114, 101, 108, 97, 120, 101, 100, 47, 114, 101, 108, 97, 120, 101, 100, 59, 32, 100, 61, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 59, 32, 115, 61, 49, 97, 49, 104, 97, 105, 59, 32, 116, 61, 49, 54, 57, 51, 48, 51, 56, 51, 51, 55, 59, 32, 98, 104, 61, 55, 120, 81, 77, 68, 117, 111, 86, 86, 85, 52, 109, 48, 87, 48, 87, 82, 86, 83, 114, 86, 88, 77, 101, 71, 83, 73, 65, 83, 115, 110, 117, 99, 75, 57, 100, 74, 115, 114, 99, 43, 118, 85, 61, 59, 32, 104, 61, 102, 114, 111, 109, 58, 67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 77, 105, 109, 101, 45, 86, 101, 114, 115, 105, 111, 110, 58, 83, 117, 98, 106, 101, 99] -msg_just_under_block = [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59] -msg_big_not_block_multiple = [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 115, 45, 97, 115, 99, 105, 105, 13, 10, 109, 105, 109, 101, 45, 118, 101, 114, 115, 105, 111, 110, 58, 49, 46, 48, 32, 40, 77, 97, 99, 32, 79, 83, 32, 88, 32, 77, 97, 105, 108, 32, 49, 54, 46, 48, 32, 92, 40, 51, 55, 51, 49, 46, 53, 48, 48, 46, 50, 51, 49, 92, 41, 41, 13, 10, 115, 117, 98, 106, 101, 99, 116, 58, 72, 101, 108, 108, 111, 13, 10, 109, 101, 115, 115, 97, 103, 101, 45, 105, 100, 58, 60, 56, 70, 56, 49, 57, 68, 51, 50, 45, 66, 54, 65, 67, 45, 52, 56, 57, 68, 45, 57, 55, 55, 70, 45, 52, 51, 56, 66, 66, 67, 52, 67, 65, 66, 50, 55, 64, 109, 101, 46, 99, 111, 109, 62, 13, 10, 100, 97, 116, 101, 58, 83, 97, 116, 44, 32, 50, 54, 32, 65, 117, 103, 32, 50, 48, 50, 51, 32, 49, 50, 58, 50, 53, 58, 50, 50, 32, 43, 48, 52, 48, 48, 13, 10, 116, 111, 58, 122, 107, 101, 119, 116, 101, 115, 116, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 13, 10, 100, 107, 105, 109, 45, 115, 105, 103, 110, 97, 116, 117, 114, 101, 58, 118, 61, 49, 59, 32, 97, 61, 114, 115, 97, 45, 115, 104, 97, 50, 53, 54, 59, 32, 99, 61, 114, 101, 108, 97, 120, 101, 100, 47, 114, 101, 108, 97, 120, 101, 100, 59, 32, 100, 61, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 59, 32, 115, 61, 49, 97, 49, 104, 97, 105, 59, 32, 116, 61, 49, 54, 57, 51, 48, 51, 56, 51, 51, 55, 59, 32, 98, 104, 61, 55, 120, 81, 77, 68, 117, 111, 86, 86, 85, 52, 109, 48, 87, 48, 87, 82, 86, 83, 114, 86, 88, 77, 101, 71, 83, 73, 65, 83, 115, 110, 117, 99, 75, 57, 100, 74, 115, 114, 99, 43, 118, 85, 61, 59, 32, 104, 61, 102, 114, 111, 109, 58, 67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 77, 105, 109, 101, 45, 86, 101, 114, 115, 105, 111, 110, 58, 83, 117, 98, 106, 101, 99, 116, 58, 77, 101, 115, 115, 97, 103, 101, 45, 73, 100, 58, 68, 97, 116, 101, 58, 116, 111, 59, 32, 98, 61] -msg_big_with_padding = [48, 130, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 48, 130, 1, 17, 48, 37, 2, 1, 1, 4, 32, 176, 223, 31, 133, 108, 84, 158, 102, 70, 11, 165, 175, 196, 12, 201, 130, 25, 131, 46, 125, 156, 194, 28, 23, 55, 133, 157, 164, 135, 136, 220, 78, 48, 37, 2, 1, 2, 4, 32, 190, 82, 180, 235, 222, 33, 79, 50, 152, 136, 142, 35, 116, 224, 6, 242, 156, 141, 128, 248, 10, 61, 98, 86, 248, 45, 207, 210, 90, 232, 175, 38, 48, 37, 2, 1, 3, 4, 32, 0, 194, 104, 108, 237, 246, 97, 230, 116, 198, 69, 110, 26, 87, 17, 89, 110, 199, 108, 250, 36, 21, 39, 87, 110, 102, 250, 213, 174, 131, 171, 174, 48, 37, 2, 1, 11, 4, 32, 136, 155, 87, 144, 111, 15, 152, 127, 85, 25, 154, 81, 20, 58, 51, 75, 193, 116, 234, 0, 60, 30, 29, 30, 183, 141, 72, 247, 255, 203, 100, 124, 48, 37, 2, 1, 12, 4, 32, 41, 234, 106, 78, 31, 11, 114, 137, 237, 17, 92, 71, 134, 47, 62, 78, 189, 233, 201, 214, 53, 4, 47, 189, 201, 133, 6, 121, 34, 131, 64, 142, 48, 37, 2, 1, 13, 4, 32, 91, 222, 210, 193, 62, 222, 104, 82, 36, 41, 138, 253, 70, 15, 148, 208, 156, 45, 105, 171, 241, 195, 185, 43, 217, 162, 146, 201, 222, 89, 238, 38, 48, 37, 2, 1, 14, 4, 32, 76, 123, 216, 13, 51, 227, 72, 245, 59, 193, 238, 166, 103, 49, 23, 164, 171, 188, 194, 197, 156, 187, 249, 28, 198, 95, 69, 15, 182, 56, 54, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] -msg_big_no_padding = [48, 130, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 48, 130, 1, 17, 48, 37, 2, 1, 1, 4, 32, 176, 223, 31, 133, 108, 84, 158, 102, 70, 11, 165, 175, 196, 12, 201, 130, 25, 131, 46, 125, 156, 194, 28, 23, 55, 133, 157, 164, 135, 136, 220, 78, 48, 37, 2, 1, 2, 4, 32, 190, 82, 180, 235, 222, 33, 79, 50, 152, 136, 142, 35, 116, 224, 6, 242, 156, 141, 128, 248, 10, 61, 98, 86, 248, 45, 207, 210, 90, 232, 175, 38, 48, 37, 2, 1, 3, 4, 32, 0, 194, 104, 108, 237, 246, 97, 230, 116, 198, 69, 110, 26, 87, 17, 89, 110, 199, 108, 250, 36, 21, 39, 87, 110, 102, 250, 213, 174, 131, 171, 174, 48, 37, 2, 1, 11, 4, 32, 136, 155, 87, 144, 111, 15, 152, 127, 85, 25, 154, 81, 20, 58, 51, 75, 193, 116, 234, 0, 60, 30, 29, 30, 183, 141, 72, 247, 255, 203, 100, 124, 48, 37, 2, 1, 12, 4, 32, 41, 234, 106, 78, 31, 11, 114, 137, 237, 17, 92, 71, 134, 47, 62, 78, 189, 233, 201, 214, 53, 4, 47, 189, 201, 133, 6, 121, 34, 131, 64, 142, 48, 37, 2, 1, 13, 4, 32, 91, 222, 210, 193, 62, 222, 104, 82, 36, 41, 138, 253, 70, 15, 148, 208, 156, 45, 105, 171, 241, 195, 185, 43, 217, 162, 146, 201, 222, 89, 238, 38, 48, 37, 2, 1, 14, 4, 32, 76, 123, 216, 13, 51, 227, 72, 245, 59, 193, 238, 166, 103, 49, 23, 164, 171, 188, 194, 197, 156, 187, 249, 28, 198, 95, 69, 15, 182, 56, 54, 38] -message_size = 297 - -# Results matched against ethers library -result_just_over_block = [91, 122, 146, 93, 52, 109, 133, 148, 171, 61, 156, 70, 189, 238, 153, 7, 222, 184, 94, 24, 65, 114, 192, 244, 207, 199, 87, 232, 192, 224, 171, 207] -result_multiple_of_block = [116, 90, 151, 31, 78, 22, 138, 180, 211, 189, 69, 76, 227, 200, 155, 29, 59, 123, 154, 60, 47, 153, 203, 129, 157, 251, 48, 2, 79, 11, 65, 47] -result_just_under_block = [143, 140, 76, 173, 222, 123, 102, 68, 70, 149, 207, 43, 39, 61, 34, 79, 216, 252, 213, 165, 74, 16, 110, 74, 29, 64, 138, 167, 30, 1, 9, 119] -result_big = [112, 144, 73, 182, 208, 98, 9, 238, 54, 229, 61, 145, 222, 17, 72, 62, 148, 222, 186, 55, 192, 82, 220, 35, 66, 47, 193, 200, 22, 38, 26, 186] -result_big_with_padding = [32, 85, 108, 174, 127, 112, 178, 182, 8, 43, 134, 123, 192, 211, 131, 66, 184, 240, 212, 181, 240, 180, 106, 195, 24, 117, 54, 129, 19, 10, 250, 53] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256_regression/src/main.nr deleted file mode 100644 index dbbcc07e501e..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_regression/src/main.nr +++ /dev/null @@ -1,39 +0,0 @@ -// A bunch of different test cases for sha256_var in the stdlib -fn main( - msg_just_over_block: [u8; 68], - result_just_over_block: pub [u8; 32], - msg_multiple_of_block: [u8; 448], - result_multiple_of_block: pub [u8; 32], - // We want to make sure we are testing a message with a size >= 57 but < 64 - msg_just_under_block: [u8; 60], - result_just_under_block: pub [u8; 32], - msg_big_not_block_multiple: [u8; 472], - result_big: pub [u8; 32], - // This message is only 297 elements and we want to hash only a variable amount - msg_big_with_padding: [u8; 700], - // This is the same as `msg_big_with_padding` but with no padding - msg_big_no_padding: [u8; 297], - message_size: u64, - result_big_with_padding: pub [u8; 32], -) { - let hash = std::hash::sha256_var(msg_just_over_block, msg_just_over_block.len() as u64); - assert_eq(hash, result_just_over_block); - - let hash = std::hash::sha256_var(msg_multiple_of_block, msg_multiple_of_block.len() as u64); - assert_eq(hash, result_multiple_of_block); - - let hash = std::hash::sha256_var(msg_just_under_block, msg_just_under_block.len() as u64); - assert_eq(hash, result_just_under_block); - - let hash = std::hash::sha256_var( - msg_big_not_block_multiple, - msg_big_not_block_multiple.len() as u64, - ); - assert_eq(hash, result_big); - - let hash_padding = std::hash::sha256_var(msg_big_with_padding, message_size); - assert_eq(hash_padding, result_big_with_padding); - - let hash_no_padding = std::hash::sha256_var(msg_big_no_padding, message_size); - assert_eq(hash_no_padding, result_big_with_padding); -} diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Nargo.toml deleted file mode 100644 index a80677c585d7..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "sha256_var_padding_regression" -type = "bin" -authors = [""] -compiler_version = ">=0.34.0" - -[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Prover.toml deleted file mode 100644 index 7b20e8701281..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -preimage = [29, 81, 165, 84, 243, 114, 101, 37, 242, 146, 127, 99, 69, 145, 39, 72, 213, 39, 253, 179, 218, 37, 217, 201, 172, 93, 198, 50, 249, 70, 15, 30, 162, 112, 187, 40, 140, 9, 236, 53, 32, 44, 38, 163, 113, 254, 192, 197, 44, 89, 71, 130, 169, 242, 17, 211, 214, 72, 19, 178, 186, 168, 147, 127, 99, 101, 252, 227, 8, 147, 150, 85, 97, 158, 17, 107, 218, 244, 82, 113, 247, 91, 208, 214, 60, 244, 87, 137, 173, 201, 130, 18, 66, 56, 198, 149, 207, 189, 175, 120, 123, 224, 177, 167, 251, 159, 143, 110, 68, 183, 189, 70, 126, 32, 35, 164, 44, 30, 44, 12, 65, 18, 62, 239, 242, 2, 248, 104, 2, 178, 64, 28, 126, 36, 137, 24, 14, 116, 91, 98, 90, 159, 218, 102, 45, 11, 110, 223, 245, 184, 52, 99, 59, 245, 136, 175, 3, 72, 164, 146, 145, 116, 22, 66, 24, 49, 193, 121, 3, 60, 37, 41, 97, 3, 190, 66, 195, 225, 63, 46, 3, 118, 4, 208, 15, 1, 40, 254, 235, 151, 123, 70, 180, 170, 44, 172, 90, 4, 254, 53, 239, 116, 246, 67, 56, 129, 61, 22, 169, 213, 65, 27, 216, 116, 162, 239, 214, 207, 126, 177, 20, 100, 25, 48, 143, 84, 215, 70, 197, 53, 65, 70, 86, 172, 61, 62, 9, 212, 167, 169, 133, 41, 126, 213, 196, 33, 192, 238, 0, 63, 246, 215, 58, 128, 110, 101, 92, 3, 170, 214, 130, 149, 52, 81, 125, 118, 233, 3, 118, 193, 104, 207, 120, 115, 77, 253, 191, 122, 0, 107, 164, 207, 113, 81, 169, 36, 201, 228, 74, 134, 131, 218, 178, 35, 30, 216, 101, 2, 103, 174, 87, 95, 50, 50, 215, 157, 5, 210, 188, 54, 211, 78, 45, 199, 96, 121, 241, 241, 176, 226, 194, 134, 130, 89, 217, 210, 186, 32, 140, 39, 91, 103, 212, 26, 87, 32, 72, 144, 228, 230, 117, 99, 188, 50, 15, 69, 79, 179, 50, 12, 106, 86, 218, 101, 73, 142, 243, 29, 250, 122, 228, 233, 29, 255, 22, 121, 114, 125, 103, 41, 250, 241, 179, 126, 158, 198, 116, 209, 65, 94, 98, 228, 175, 169, 96, 3, 9, 233, 133, 214, 55, 161, 164, 103, 80, 85, 24, 186, 64, 167, 92, 131, 53, 101, 202, 47, 25, 104, 118, 155, 14, 12, 12, 25, 116, 45, 221, 249, 28, 246, 212, 200, 157, 167, 169, 56, 197, 181, 4, 245, 146, 1, 140, 234, 191, 212, 228, 125, 87, 81, 86, 119, 30, 63, 129, 143, 32, 96] -result = [205, 74, 73, 134, 202, 93, 199, 152, 171, 244, 133, 193, 132, 40, 42, 9, 248, 11, 99, 200, 135, 58, 220, 227, 45, 253, 183, 241, 69, 69, 80, 219] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/src/main.nr deleted file mode 100644 index 13f87a0efc51..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_padding_regression/src/main.nr +++ /dev/null @@ -1,29 +0,0 @@ -// Test to check sha256_var produces same results irrespective of number of padding bytes after message.length -// Ref: https://github.com/noir-lang/noir/issues/6163, https://gist.github.com/jp4g/d5953faae9eadb2909357474f7901e58 -fn main(preimage: [u8; 448], result: [u8; 32]) { - // Construct arrays of different lengths - let mut preimage_511 = [0; 511]; - let mut preimage_512 = [0; 512]; // Next block - let mut preimage_575 = [0; 575]; - let mut preimage_576 = [0; 576]; // Next block - for i in 0..preimage.len() { - preimage_511[i] = preimage[i]; - preimage_512[i] = preimage[i]; - preimage_575[i] = preimage[i]; - preimage_576[i] = preimage[i]; - } - let fixed_length_hash = std::hash::sha256::digest(preimage); - let var_full_length_hash = std::hash::sha256::sha256_var(preimage, preimage.len() as u64); - let var_length_hash_511 = std::hash::sha256::sha256_var(preimage_511, preimage.len() as u64); - let var_length_hash_512 = std::hash::sha256::sha256_var(preimage_512, preimage.len() as u64); - let var_length_hash_575 = std::hash::sha256::sha256_var(preimage_575, preimage.len() as u64); - let var_length_hash_576 = std::hash::sha256::sha256_var(preimage_576, preimage.len() as u64); - - // All of the above should have produced the same hash (result) - assert(fixed_length_hash == result); - assert(var_full_length_hash == result); - assert(var_length_hash_511 == result); - assert(var_length_hash_512 == result); - assert(var_length_hash_575 == result); - assert(var_length_hash_576 == result); -} diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Nargo.toml deleted file mode 100644 index 3e141ee5d5f6..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "sha256_var_size_regression" -type = "bin" -authors = [""] -compiler_version = ">=0.33.0" - -[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Prover.toml deleted file mode 100644 index df632a428584..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Prover.toml +++ /dev/null @@ -1,3 +0,0 @@ -enable = [true, false] -foo = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] -toggle = false diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/src/main.nr deleted file mode 100644 index 4278cdda8a30..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/src/main.nr +++ /dev/null @@ -1,17 +0,0 @@ -global NUM_HASHES: u32 = 2; - -fn main(foo: [u8; 95], toggle: bool, enable: [bool; NUM_HASHES]) { - let mut result = [[0; 32]; NUM_HASHES]; - let mut const_result = [[0; 32]; NUM_HASHES]; - let size: Field = 93 + toggle as Field * 2; - for i in 0..NUM_HASHES { - if enable[i] { - result[i] = std::sha256::sha256_var(foo, size as u64); - const_result[i] = std::sha256::sha256_var(foo, 93); - } - } - - for i in 0..NUM_HASHES { - assert_eq(result[i], const_result[i]); - } -} diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Nargo.toml deleted file mode 100644 index e8f3e6bbe643..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "sha256_var_witness_const_regression" -type = "bin" -authors = [""] -compiler_version = ">=0.33.0" - -[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Prover.toml deleted file mode 100644 index 7b91051c1a0c..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -input = [0, 0] -toggle = false \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/src/main.nr deleted file mode 100644 index 97c4435d41d2..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/src/main.nr +++ /dev/null @@ -1,9 +0,0 @@ -fn main(input: [u8; 2], toggle: bool) { - let size: Field = 1 + toggle as Field; - assert(!toggle); - - let variable_sha = std::sha256::sha256_var(input, size as u64); - let constant_sha = std::sha256::sha256_var(input, 1); - - assert_eq(variable_sha, constant_sha); -} diff --git a/noir/noir-repo/test_programs/execution_success/sha2_byte/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha2_byte/Prover.toml deleted file mode 100644 index 2f82f14a58d4..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha2_byte/Prover.toml +++ /dev/null @@ -1,5 +0,0 @@ -# Values obtainable from https://emn178.github.io/online-tools/sha256.html and https://emn178.github.io/online-tools/sha512.html -x = 0xbd -result256 = [0x68, 0x32, 0x57, 0x20, 0xaa, 0xbd, 0x7c, 0x82, 0xf3, 0x0f, 0x55, 0x4b, 0x31, 0x3d, 0x05, 0x70, 0xc9, 0x5a, 0xcc, 0xbb, 0x7d, 0xc4, 0xb5, 0xaa, 0xe1, 0x12, 0x04, 0xc0, 0x8f, 0xfe, 0x73, 0x2b] -result512 = [0x29, 0x6e, 0x22, 0x67, 0xd7, 0x4c, 0x27, 0x8d, 0xaa, 0xaa, 0x94, 0x0d, 0x17, 0xb0, 0xcf, 0xb7, 0x4a, 0x50, 0x83, 0xf8, 0xe0, 0x69, 0x72, 0x6d, 0x8c, 0x84, 0x1c, 0xbe, 0x59, 0x6e, 0x04, 0x31, 0xcb, 0x77, 0x41, 0xa5, 0xb5, 0x0f, 0x71, 0x66, 0x6c, 0xfd, 0x54, 0xba, 0xcb, 0x7b, 0x00, 0xae, 0xa8, 0x91, 0x49, 0x9c, 0xf4, 0xef, 0x6a, 0x03, 0xc8, 0xa8, 0x3f, 0xe3, 0x7c, 0x3f, 0x7b, 0xaf] - diff --git a/noir/noir-repo/test_programs/execution_success/sha2_byte/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha2_byte/src/main.nr deleted file mode 100644 index a1663642c69b..000000000000 --- a/noir/noir-repo/test_programs/execution_success/sha2_byte/src/main.nr +++ /dev/null @@ -1,8 +0,0 @@ -// Test Noir implementations of SHA256 and SHA512 on a one-byte message. -fn main(x: Field, result256: [u8; 32], result512: [u8; 64]) { - let digest256 = std::hash::sha256([x as u8]); - assert(digest256 == result256); - - let digest512 = std::hash::sha512::digest([x as u8]); - assert(digest512 == result512); -} diff --git a/noir/noir-repo/test_programs/execution_success/keccak256/Nargo.toml b/noir/noir-repo/test_programs/execution_success/shift_right_overflow/Nargo.toml similarity index 63% rename from noir/noir-repo/test_programs/execution_success/keccak256/Nargo.toml rename to noir/noir-repo/test_programs/execution_success/shift_right_overflow/Nargo.toml index 7e48c3b342cb..a883299fbc26 100644 --- a/noir/noir-repo/test_programs/execution_success/keccak256/Nargo.toml +++ b/noir/noir-repo/test_programs/execution_success/shift_right_overflow/Nargo.toml @@ -1,6 +1,5 @@ [package] -name = "keccak256" +name = "shift_right_overflow" type = "bin" authors = [""] - [dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/shift_right_overflow/Prover.toml b/noir/noir-repo/test_programs/execution_success/shift_right_overflow/Prover.toml new file mode 100644 index 000000000000..57cb8b2eac81 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/shift_right_overflow/Prover.toml @@ -0,0 +1 @@ +x = 9 diff --git a/noir/noir-repo/test_programs/execution_success/shift_right_overflow/src/main.nr b/noir/noir-repo/test_programs/execution_success/shift_right_overflow/src/main.nr new file mode 100644 index 000000000000..c5d23ab5cd97 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/shift_right_overflow/src/main.nr @@ -0,0 +1,5 @@ +fn main(x: u8) { + // This would previously overflow in ACIR. Now it returns zero. + let value = 1 >> x; + assert_eq(value, 0); +} diff --git a/noir/noir-repo/test_programs/execution_success/simple_shield/src/main.nr b/noir/noir-repo/test_programs/execution_success/simple_shield/src/main.nr index 35b50150986d..82b53dd2cc9e 100644 --- a/noir/noir-repo/test_programs/execution_success/simple_shield/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/simple_shield/src/main.nr @@ -20,7 +20,22 @@ fn main( // Compute output note nullifier let receiver_note_commitment = std::hash::pedersen_commitment([to_pubkey_x, to_pubkey_y]); // Check that the input note nullifier is in the root - assert(note_root == std::merkle::compute_merkle_root(note_commitment.x, index, note_hash_path)); + assert(note_root == compute_merkle_root(note_commitment.x, index, note_hash_path)); [nullifier.x, receiver_note_commitment.x] } + +fn compute_merkle_root(leaf: Field, index: Field, hash_path: [Field; N]) -> Field { + let index_bits: [u1; N] = index.to_le_bits(); + let mut current = leaf; + for i in 0..N { + let path_bit = index_bits[i] as bool; + let (hash_left, hash_right) = if path_bit { + (hash_path[i], current) + } else { + (current, hash_path[i]) + }; + current = std::hash::pedersen_hash([hash_left, hash_right]); + } + current +} diff --git a/noir/noir-repo/test_programs/execution_success/slice_dynamic_index/src/main.nr b/noir/noir-repo/test_programs/execution_success/slice_dynamic_index/src/main.nr index 2ab633eb98c0..c6fd64d58059 100644 --- a/noir/noir-repo/test_programs/execution_success/slice_dynamic_index/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/slice_dynamic_index/src/main.nr @@ -305,4 +305,3 @@ fn dynamic_slice_merge_push_then_pop(mut slice: [Field], x: Field, y: Field) { let (_, elem) = slice.pop_back(); assert(elem == y); } - 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 index 67901f10f299..ba20fdd61f1f 100644 --- 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 @@ -805,4 +805,3 @@ fn main() { // 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/struct_fields_ordering/src/main.nr b/noir/noir-repo/test_programs/execution_success/struct_fields_ordering/src/main.nr index 1a2e2d462e2d..fe26ac31424a 100644 --- a/noir/noir-repo/test_programs/execution_success/struct_fields_ordering/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/struct_fields_ordering/src/main.nr @@ -9,4 +9,3 @@ fn main(y: pub myStruct) { assert(y.foo == 5); assert(y.bar == 7); } - diff --git a/noir/noir-repo/test_programs/execution_success/submodules/src/main.nr b/noir/noir-repo/test_programs/execution_success/submodules/src/main.nr index f937af746274..b58e64b4990c 100644 --- a/noir/noir-repo/test_programs/execution_success/submodules/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/submodules/src/main.nr @@ -12,4 +12,3 @@ mod my_submodule { pub fn my_helper() {} } - diff --git a/noir/noir-repo/test_programs/execution_success/u128/Nargo.toml b/noir/noir-repo/test_programs/execution_success/u128/Nargo.toml deleted file mode 100644 index c1dcd84db046..000000000000 --- a/noir/noir-repo/test_programs/execution_success/u128/Nargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "u128" -type = "bin" -authors = [""] - -[dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/u128/Prover.toml b/noir/noir-repo/test_programs/execution_success/u128/Prover.toml deleted file mode 100644 index 961db9825a70..000000000000 --- a/noir/noir-repo/test_programs/execution_success/u128/Prover.toml +++ /dev/null @@ -1,7 +0,0 @@ -x = "3" -y = "4" -z = "7" -hexa ="0x1f03a" -[big_int] -lo = 1 -hi = 2 \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/u128/src/main.nr b/noir/noir-repo/test_programs/execution_success/u128/src/main.nr deleted file mode 100644 index 4ea934378143..000000000000 --- a/noir/noir-repo/test_programs/execution_success/u128/src/main.nr +++ /dev/null @@ -1,42 +0,0 @@ -fn main(mut x: u32, y: u32, z: u32, big_int: U128, hexa: str<7>) { - let a = U128::from_u64s_le(x as u64, x as u64); - let b = U128::from_u64s_le(y as u64, x as u64); - let c = a + b; - assert(c.lo == z as Field); - assert(c.hi == 2 * x as Field); - assert(U128::from_hex(hexa).lo == 0x1f03a); - let t1 = U128::from_hex("0x9d9c7a87771f03a23783f9d9c7a8777"); - let t2 = U128::from_hex("0x45a26c708BFCF39041"); - let t = t1 + t2; - assert(t.lo == 0xc5e4b029996e17b8); - assert(t.hi == 0x09d9c7a87771f07f); - let t3 = U128::from_le_bytes(t.to_le_bytes()); - assert(t == t3); - - let t4 = t - t2; - assert(t4 == t1); - - let t5 = U128::from_u64s_le(0, 1); - let t6 = U128::from_u64s_le(1, 0); - assert((t5 - t6).hi == 0); - - assert( - (U128::from_hex("0x71f03a23783f9d9c7a8777") * U128::from_hex("0x8BFCF39041")).hi - == U128::from_hex("0x3e4e0471b873470e247c824e61445537").hi, - ); - let q = U128::from_hex("0x3e4e0471b873470e247c824e61445537") / U128::from_hex("0x8BFCF39041"); - assert(q == U128::from_hex("0x71f03a23783f9d9c7a8777")); - - assert(big_int.hi == 2); - - let mut small_int = U128::from_integer(x); - assert(small_int.lo == x as Field); - assert(x == small_int.to_integer()); - let shift = small_int << (x as u8); - assert(shift == U128::from_integer(x << (x as u8))); - assert(shift >> (x as u8) == small_int); - assert(shift >> 127 == U128::from_integer(0)); - assert(shift << 127 == U128::from_integer(0)); - assert(U128::from_integer(3).to_integer() == 3); -} - diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256/Nargo.toml b/noir/noir-repo/test_programs/execution_success/u128_type/Nargo.toml similarity index 52% rename from noir/noir-repo/test_programs/benchmarks/bench_sha256/Nargo.toml rename to noir/noir-repo/test_programs/execution_success/u128_type/Nargo.toml index 488b94ca8586..f86639a9e745 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256/Nargo.toml +++ b/noir/noir-repo/test_programs/execution_success/u128_type/Nargo.toml @@ -1,7 +1,7 @@ [package] -name = "bench_sha256" -version = "0.1.0" +name = "u128_type" type = "bin" authors = [""] +compiler_version = ">=0.23.0" [dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/u128_type/Prover.toml b/noir/noir-repo/test_programs/execution_success/u128_type/Prover.toml new file mode 100644 index 000000000000..75da077f2e6d --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/u128_type/Prover.toml @@ -0,0 +1,3 @@ +x = 12345 +y = 2345678 +z = 2 diff --git a/noir/noir-repo/test_programs/execution_success/u128_type/src/main.nr b/noir/noir-repo/test_programs/execution_success/u128_type/src/main.nr new file mode 100644 index 000000000000..acfa3d28eec2 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/u128_type/src/main.nr @@ -0,0 +1,22 @@ +fn main(x: u128, y: u128, z: u8) { + let const_x = 12345; + let const_y = 2345678; + let const_z = 2; + + assert_eq(x + y, const_x + const_y); + assert_eq(y - x, const_y - const_x); + assert_eq(x * y, const_x * const_y); + assert_eq(y / x, const_y / const_x); + assert_eq(y % x, const_y % const_x); + assert_eq(!x, !const_x); + assert_eq(x ^ y, const_x ^ const_y); + assert_eq(x & y, const_x & const_y); + assert_eq(x | y, const_x | const_y); + assert_eq(x >> z, const_x >> const_z); + assert_eq(x << z, const_x << const_z); + assert_eq(x < y, const_x < const_y); + assert_eq(x <= y, const_x <= const_y); + assert_eq(x != y, const_x != const_y); + assert_eq(y > x, const_y > const_x); + assert_eq(y >= x, const_y >= const_x); +} diff --git a/noir/noir-repo/test_programs/execution_success/wrapping_operations/src/main.nr b/noir/noir-repo/test_programs/execution_success/wrapping_operations/src/main.nr index d8345884c828..0c4be84a7659 100644 --- a/noir/noir-repo/test_programs/execution_success/wrapping_operations/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/wrapping_operations/src/main.nr @@ -3,4 +3,3 @@ fn main(x: u8, y: u8) { assert(std::wrapping_add(y, 1) == x); assert(std::wrapping_mul(y, y) == 1); } - diff --git a/noir/noir-repo/test_programs/memory_report.sh b/noir/noir-repo/test_programs/memory_report.sh index eb83004affdb..ff64449fcf5e 100755 --- a/noir/noir-repo/test_programs/memory_report.sh +++ b/noir/noir-repo/test_programs/memory_report.sh @@ -8,7 +8,7 @@ PARSE_MEMORY=$(realpath "$(dirname "$0")/parse_memory.sh") # Tests to be profiled for memory report -tests_to_profile=("keccak256" "workspace" "regression_4709" "ram_blowup_regression" "global_var_regression_entry_points") +tests_to_profile=("workspace" "regression_4709" "ram_blowup_regression" "global_var_regression_entry_points") current_dir=$(pwd) base_path="$current_dir/execution_success" diff --git a/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr index fa2c55e69347..cfd985a55eaa 100644 --- a/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr @@ -269,4 +269,3 @@ fn test_vec_any_not_default() { vec.extend_from_array([2, 4]); assert(!vec.any(|v| v == default_value)); } - diff --git a/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr index 019f757591ee..de6fd15f068e 100644 --- a/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr @@ -20,4 +20,3 @@ unconstrained fn test_overflow_mul() { let b: u8 = 2; assert_eq(a * b, 0); } - diff --git a/noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/src/main.nr index 446692c485bb..3b1f2963b9f9 100644 --- a/noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/comptime_blackbox/src/main.nr @@ -117,13 +117,16 @@ fn test_ecdsa_secp256r1() { /// Test that sha256_compression is implemented. #[test] -fn test_sha256() { +fn test_sha256_compression() { + let input: [u32; 16] = [0xbd; 16]; + let state: [u32; 8] = [0; 8]; + let hash = comptime { - let input: [u8; 1] = [0xbd]; - std::hash::sha256(input) + let input: [u32; 16] = [0xbd; 16]; + let state: [u32; 8] = [0; 8]; + std::hash::sha256_compression(input, state) }; - assert_eq(hash[0], 0x68); - assert_eq(hash[31], 0x2b); + assert_eq(hash, std::hash::sha256_compression(input, state)); } /// Test that `embedded_curve_add` and `multi_scalar_mul` are implemented. diff --git a/noir/noir-repo/test_programs/noir_test_success/global_eval/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/global_eval/src/main.nr index 87a2d50a9162..da962bf52032 100644 --- a/noir/noir-repo/test_programs/noir_test_success/global_eval/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/global_eval/src/main.nr @@ -1,20 +1,9 @@ -use std::uint128::U128; - // These definitions require `to_be_bits` and `to_le_bits` to be supported at comptime. global BITS_BE_13: [u1; 4] = (13 as Field).to_be_bits(); global BITS_LE_13: [u1; 4] = (13 as Field).to_le_bits(); -// Examples from #6691 which use the above behind the scenes. -global POW64_A: Field = 2.pow_32(64); -global POW64_B: Field = (U128::one() << 64).to_integer(); - #[test] fn test_be_and_le_bits() { assert_eq(BITS_BE_13, [1, 1, 0, 1]); assert_eq(BITS_LE_13, [1, 0, 1, 1]); } - -#[test] -fn test_pow64() { - assert_eq(POW64_A, POW64_B); -} diff --git a/noir/noir-repo/test_programs/noir_test_success/mock_oracle/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/mock_oracle/src/main.nr index bdffd62bb809..3bde9a0b3ef8 100644 --- a/noir/noir-repo/test_programs/noir_test_success/mock_oracle/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/mock_oracle/src/main.nr @@ -151,4 +151,3 @@ fn test_mock_struct_field() { assert_eq(20, struct_field(point, another_array)); } } - diff --git a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Nargo.toml b/noir/noir-repo/test_programs/noir_test_success/u128_type/Nargo.toml similarity index 52% rename from noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Nargo.toml rename to noir/noir-repo/test_programs/noir_test_success/u128_type/Nargo.toml index ae66d7ed5a60..f86639a9e745 100644 --- a/noir/noir-repo/test_programs/benchmarks/bench_sha256_long/Nargo.toml +++ b/noir/noir-repo/test_programs/noir_test_success/u128_type/Nargo.toml @@ -1,7 +1,7 @@ [package] -name = "bench_sha256_long" -version = "0.1.0" +name = "u128_type" type = "bin" authors = [""] +compiler_version = ">=0.23.0" [dependencies] diff --git a/noir/noir-repo/test_programs/noir_test_success/u128_type/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/u128_type/src/main.nr new file mode 100644 index 000000000000..c0bca7b3d7aa --- /dev/null +++ b/noir/noir-repo/test_programs/noir_test_success/u128_type/src/main.nr @@ -0,0 +1,49 @@ +#[test] +fn test_u128_sub() { + let a: u128 = 671967750576550571863734675757137222; + let b = 671967750576550571863734675757137221; + let c = a - 1; + assert(c == b); +} + +#[test] +fn test_u128_add() { + let a: u128 = 671967750576550571863734675757137222; + let b: u128 = 671967750576550571863734675757137221; + let c: u128 = b + 1; + assert(c == a); +} + +#[test] +fn test_u128_mul() { + let a: u128 = 671967750576550571863734675757137222; + let b: u128 = 335983875288275285931867337878568611; + let c: u128 = b * 2; + assert(c == a); +} + +#[test] +fn test_u128_div() { + let a: u128 = 671967750576550571863734675757137222; + let b: u128 = 335983875288275285931867337878568611; + let c = a / 2; + assert(c == b); +} + +#[test] +fn test_u128_not() { + let a: u128 = 1; + let b = !a; + assert_eq(b, 340282366920938463463374607431768211454); + + let a: u128 = 340282366920938463463374607431768211454; + let b = !a; + assert_eq(b, 1); +} + +#[test] +fn test_max_u128() { + let a: u128 = 340282366920938463463374607431768211455; + let c = 170141183460469231731687303715884105727; + assert_eq(a / 2, c); +} diff --git a/noir/noir-repo/tooling/acvm_cli/src/cli/execute_cmd.rs b/noir/noir-repo/tooling/acvm_cli/src/cli/execute_cmd.rs index 6f6fba7cd1e7..c1d4533230e7 100644 --- a/noir/noir-repo/tooling/acvm_cli/src/cli/execute_cmd.rs +++ b/noir/noir-repo/tooling/acvm_cli/src/cli/execute_cmd.rs @@ -1,14 +1,14 @@ use std::io::{self, Write}; use std::path::PathBuf; +use acir::FieldElement; use acir::circuit::Program; use acir::native_types::{WitnessMap, WitnessStack}; -use acir::FieldElement; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; use nargo::PrintOutput; -use nargo::{foreign_calls::DefaultForeignCallBuilder, ops::execute_program}; +use nargo::foreign_calls::DefaultForeignCallBuilder; use noir_artifact_cli::errors::CliError; use noir_artifact_cli::fs::artifact::read_bytecode_from_file; use noir_artifact_cli::fs::witness::save_witness_to_dir; @@ -56,7 +56,7 @@ fn run_command(args: ExecuteCommand) -> Result { )?; if args.output_witness.is_some() { save_witness_to_dir( - output_witness, + &output_witness, &args.output_witness.unwrap(), &args.working_directory, )?; @@ -80,7 +80,8 @@ pub(crate) fn execute_program_from_witness( ) -> Result, CliError> { let program: Program = Program::deserialize_program(bytecode).map_err(CliError::CircuitDeserializationError)?; - execute_program( + + nargo::ops::execute_program( &program, inputs_map, &Bn254BlackBoxSolver(pedantic_solving), diff --git a/noir/noir-repo/tooling/acvm_cli/src/fs/witness.rs b/noir/noir-repo/tooling/acvm_cli/src/fs/witness.rs index 5c2d2a9ad058..d1b88758cc74 100644 --- a/noir/noir-repo/tooling/acvm_cli/src/fs/witness.rs +++ b/noir/noir-repo/tooling/acvm_cli/src/fs/witness.rs @@ -1,8 +1,8 @@ //! These witness functions are only used by the ACVM CLI and we'll most likely deprecate them. use std::{collections::BTreeMap, path::Path}; -use acir::{native_types::Witness, FieldElement}; -use acvm::acir::{native_types::WitnessMap, AcirField}; +use acir::{FieldElement, native_types::Witness}; +use acvm::acir::{AcirField, native_types::WitnessMap}; use noir_artifact_cli::errors::{CliError, FilesystemError}; diff --git a/noir/noir-repo/tooling/acvm_cli/src/main.rs b/noir/noir-repo/tooling/acvm_cli/src/main.rs index a30360a947ce..2ac8a96eaba7 100644 --- a/noir/noir-repo/tooling/acvm_cli/src/main.rs +++ b/noir/noir-repo/tooling/acvm_cli/src/main.rs @@ -8,7 +8,7 @@ mod cli; use std::env; use tracing_appender::rolling; -use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter}; +use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan}; mod fs; @@ -20,18 +20,19 @@ fn main() { .with_span_events(FmtSpan::ACTIVE) .with_writer(debug_file) .with_ansi(false) - .with_env_filter(EnvFilter::from_default_env()) + .with_env_filter(EnvFilter::from_env("NOIR_LOG")) .init(); } else { tracing_subscriber::fmt() .with_span_events(FmtSpan::ACTIVE) + .with_writer(std::io::stderr) .with_ansi(true) .with_env_filter(EnvFilter::from_env("NOIR_LOG")) .init(); } if let Err(report) = cli::start_cli() { - eprintln!("{report}"); + eprintln!("{report:#}"); std::process::exit(1); } } diff --git a/noir/noir-repo/tooling/artifact_cli/src/bin/execute.rs b/noir/noir-repo/tooling/artifact_cli/src/bin/execute.rs index b66177a3d572..5d67ce0a958d 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/bin/execute.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/bin/execute.rs @@ -1,10 +1,10 @@ #![forbid(unsafe_code)] #![warn(unreachable_pub)] -use clap::{command, Parser, Subcommand}; +use clap::{Parser, Subcommand, command}; use color_eyre::eyre; use const_format::formatcp; -use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter}; +use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan}; use noir_artifact_cli::commands::execute_cmd; @@ -35,6 +35,7 @@ pub fn start_cli() -> eyre::Result<()> { fn main() { tracing_subscriber::fmt() .with_span_events(FmtSpan::ACTIVE) + .with_writer(std::io::stderr) .with_ansi(true) .with_env_filter(EnvFilter::from_env("NOIR_LOG")) .init(); diff --git a/noir/noir-repo/tooling/artifact_cli/src/commands/execute_cmd.rs b/noir/noir-repo/tooling/artifact_cli/src/commands/execute_cmd.rs index 9a117329c151..6ce966282696 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/commands/execute_cmd.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/commands/execute_cmd.rs @@ -1,18 +1,18 @@ -use std::{collections::BTreeMap, path::PathBuf}; +use std::path::PathBuf; -use acir::{circuit::Program, native_types::WitnessStack, FieldElement}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; -use color_eyre::eyre::{self, bail}; use crate::{ - errors::CliError, - fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}, Artifact, + errors::CliError, + execution::{self, ExecutionResults}, +}; +use nargo::{ + PrintOutput, + foreign_calls::{DefaultForeignCallBuilder, layers, transcript::ReplayForeignCallExecutor}, }; -use nargo::{foreign_calls::DefaultForeignCallBuilder, NargoError, PrintOutput}; -use noirc_abi::{input_parser::InputValue, Abi}; -use noirc_artifacts::debug::DebugArtifact; +use noirc_driver::CompiledProgram; use super::parse_and_normalize_path; @@ -21,106 +21,106 @@ use super::parse_and_normalize_path; pub struct ExecuteCommand { /// Path to the JSON build artifact (either a program or a contract). #[clap(long, short, value_parser = parse_and_normalize_path)] - artifact_path: PathBuf, + pub artifact_path: PathBuf, /// Path to the Prover.toml file which contains the inputs and the /// optional return value in ABI format. #[clap(long, short, value_parser = parse_and_normalize_path)] - prover_file: PathBuf, + pub prover_file: PathBuf, /// Path to the directory where the output witness should be saved. /// If empty then the results are discarded. #[clap(long, short, value_parser = parse_and_normalize_path)] - output_dir: Option, + pub output_dir: Option, /// Write the execution witness to named file /// /// Defaults to the name of the circuit being executed. #[clap(long, short)] - witness_name: Option, + pub witness_name: Option, /// Name of the function to execute, if the artifact is a contract. #[clap(long)] - contract_fn: Option, + pub contract_fn: Option, + + /// Path to the oracle transcript that is to be replayed during the + /// execution in response to foreign calls. The format is expected + /// to be JSON Lines, with each request/response on a separate line. + /// + /// Note that a transcript might be invalid if the inputs change and + /// the circuit takes a different path during execution. + #[clap(long, conflicts_with = "oracle_resolver")] + pub oracle_file: Option, /// JSON RPC url to solve oracle calls. + #[clap(long, conflicts_with = "oracle_file")] + pub oracle_resolver: Option, + + /// Root directory for the RPC oracle resolver. + #[clap(long, value_parser = parse_and_normalize_path)] + pub oracle_root_dir: Option, + + /// Package name for the RPC oracle resolver #[clap(long)] - oracle_resolver: Option, + pub oracle_package_name: Option, /// Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. #[clap(long, default_value_t = false)] - pedantic_solving: bool, + pub pedantic_solving: bool, } -pub fn run(args: ExecuteCommand) -> eyre::Result<()> { +pub fn run(args: ExecuteCommand) -> Result<(), CliError> { let artifact = Artifact::read_from_file(&args.artifact_path)?; + let artifact_name = args.artifact_path.file_stem().and_then(|s| s.to_str()).unwrap_or_default(); - let circuit = match artifact { - Artifact::Program(program) => Circuit { - name: None, - abi: program.abi, - bytecode: program.bytecode, - debug_symbols: program.debug_symbols, - file_map: program.file_map, - }, + let (circuit, circuit_name): (CompiledProgram, String) = match artifact { + Artifact::Program(program) => (program.into(), artifact_name.to_string()), Artifact::Contract(contract) => { - let names = - contract.functions.iter().map(|f| f.name.clone()).collect::>().join(","); + let names = || contract.functions.iter().map(|f| f.name.clone()).collect::>(); let Some(ref name) = args.contract_fn else { - bail!("--contract-fn missing; options: [{names}]"); + return Err(CliError::MissingContractFn { names: names() }); }; - let Some(function) = contract.functions.into_iter().find(|f| f.name == *name) else { - bail!("unknown --contract-fn '{name}'; options: [{names}]"); + let Some(program) = contract.function_as_compiled_program(name) else { + return Err(CliError::UnknownContractFn { name: name.clone(), names: names() }); }; - Circuit { - name: Some(name.clone()), - abi: function.abi, - bytecode: function.bytecode, - debug_symbols: function.debug_symbols, - file_map: contract.file_map, - } + (program, format!("{artifact_name}::{name}")) } }; match execute(&circuit, &args) { - Ok(solved) => { - save_witness(circuit, args, solved)?; - } - Err(CliError::CircuitExecutionError(err)) => { - show_diagnostic(circuit, err); + Ok(results) => { + execution::save_and_check_witness( + &circuit, + results, + &circuit_name, + args.output_dir.as_deref(), + args.witness_name.as_deref(), + )?; } Err(e) => { - bail!("failed to execute the circuit: {e}"); + if let CliError::CircuitExecutionError(ref err) = e { + execution::show_diagnostic(&circuit, err); + } + // Still returning the error to facilitate command forwarding, to indicate that the command failed. + return Err(e); } } Ok(()) } -/// Parameters necessary to execute a circuit, display execution failures, etc. -struct Circuit { - name: Option, - abi: Abi, - bytecode: Program, - debug_symbols: noirc_errors::debug_info::ProgramDebugInfo, - file_map: BTreeMap, -} - -struct SolvedWitnesses { - expected_return: Option, - actual_return: Option, - witness_stack: WitnessStack, -} - /// Execute a circuit and return the output witnesses. -fn execute(circuit: &Circuit, args: &ExecuteCommand) -> Result { - let (input_map, expected_return) = read_inputs_from_file(&args.prover_file, &circuit.abi)?; - - let initial_witness = circuit.abi.encode(&input_map, None)?; +fn execute(circuit: &CompiledProgram, args: &ExecuteCommand) -> Result { + // Build a custom foreign call executor that replays the Oracle transcript, + // and use it as a base for the default executor. Using it as the innermost rather + // than top layer so that any extra `print` added for debugging is handled by the + // default, rather than trying to match it to the transcript. + let transcript_executor = match args.oracle_file { + Some(ref path) => layers::Either::Left(ReplayForeignCallExecutor::from_file(path)?), + None => layers::Either::Right(layers::Empty), + }; - // TODO: Build a custom foreign call executor that reads from the Oracle transcript, - // and use it as a base for the default executor; see `DefaultForeignCallBuilder::build_with_base` let mut foreign_call_executor = DefaultForeignCallBuilder { output: PrintOutput::Stdout, enable_mocks: false, @@ -128,82 +128,9 @@ fn execute(circuit: &Circuit, args: &ExecuteCommand) -> Result) { - if let Some(diagnostic) = nargo::errors::try_to_diagnose_runtime_error( - &err, - &circuit.abi, - &circuit.debug_symbols.debug_infos, - ) { - let debug_artifact = DebugArtifact { - debug_symbols: circuit.debug_symbols.debug_infos, - file_map: circuit.file_map, - }; - diagnostic.report(&debug_artifact, false); - } -} + .build_with_base(transcript_executor); -/// Print information about the witness and compare to expectations, -/// returning errors if something isn't right. -fn save_witness( - circuit: Circuit, - args: ExecuteCommand, - solved: SolvedWitnesses, -) -> eyre::Result<()> { - let artifact = args.artifact_path.file_stem().and_then(|s| s.to_str()).unwrap_or_default(); - let name = circuit - .name - .as_ref() - .map(|name| format!("{artifact}.{name}")) - .unwrap_or_else(|| artifact.to_string()); - - println!("[{}] Circuit witness successfully solved", name); - - if let Some(ref witness_dir) = args.output_dir { - let witness_path = save_witness_to_dir( - solved.witness_stack, - &args.witness_name.unwrap_or_else(|| name.clone()), - witness_dir, - )?; - println!("[{}] Witness saved to {}", name, witness_path.display()); - } + let blackbox_solver = Bn254BlackBoxSolver(args.pedantic_solving); - // Check that the circuit returned a non-empty result if the ABI expects a return value. - if let Some(ref expected) = circuit.abi.return_type { - if solved.actual_return.is_none() { - bail!("Missing return witness; expected a value of type {expected:?}"); - } - } - - // Check that if the prover file contained a `return` entry then that's what we got. - if let Some(expected) = solved.expected_return { - match solved.actual_return { - None => { - bail!("Missing return witness;\nexpected:\n{expected:?}"); - } - Some(actual) if actual != expected => { - bail!("Unexpected return witness;\nexpected:\n{expected:?}\ngot:\n{actual:?}"); - } - _ => {} - } - } - - Ok(()) + execution::execute(circuit, &blackbox_solver, &mut foreign_call_executor, &args.prover_file) } diff --git a/noir/noir-repo/tooling/artifact_cli/src/commands/mod.rs b/noir/noir-repo/tooling/artifact_cli/src/commands/mod.rs index 78f3b19292f2..9049b3695b7a 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/commands/mod.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/commands/mod.rs @@ -1,3 +1,4 @@ +//! This module is for commands that we might want to invoke from `nargo` as-is. use std::path::PathBuf; use color_eyre::eyre; diff --git a/noir/noir-repo/tooling/artifact_cli/src/errors.rs b/noir/noir-repo/tooling/artifact_cli/src/errors.rs index 5f302f78695a..1dfaac850b03 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/errors.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/errors.rs @@ -1,6 +1,10 @@ use acir::FieldElement; -use nargo::NargoError; -use noirc_abi::errors::{AbiError, InputParserError}; +use nargo::{NargoError, foreign_calls::transcript::TranscriptError}; +use noirc_abi::{ + AbiReturnType, + errors::{AbiError, InputParserError}, + input_parser::InputValue, +}; use std::path::PathBuf; use thiserror::Error; @@ -35,6 +39,10 @@ pub enum CliError { #[error("Failed to deserialize inputs")] InputDeserializationError(#[from] InputParserError), + /// Error related to oracle transcript deserialization + #[error(transparent)] + TranscriptError(#[from] TranscriptError), + /// Error related to ABI encoding #[error(transparent)] AbiError(#[from] AbiError), @@ -61,4 +69,16 @@ pub enum CliError { #[error("Failed to serialize output witness: {0}")] OutputWitnessSerializationFailed(#[from] toml::ser::Error), + + #[error("Unexpected return value: expected {expected:?}; got {actual:?}")] + UnexpectedReturn { expected: InputValue, actual: Option }, + + #[error("Missing return witnesses; expected {expected:?}")] + MissingReturn { expected: AbiReturnType }, + + #[error("Missing contract function name; options: {names:?}")] + MissingContractFn { names: Vec }, + + #[error("Unknown contract function '{name}'; options: {names:?}")] + UnknownContractFn { name: String, names: Vec }, } diff --git a/noir/noir-repo/tooling/artifact_cli/src/execution.rs b/noir/noir-repo/tooling/artifact_cli/src/execution.rs new file mode 100644 index 000000000000..2e19ab551613 --- /dev/null +++ b/noir/noir-repo/tooling/artifact_cli/src/execution.rs @@ -0,0 +1,135 @@ +use std::path::Path; + +use acir::{FieldElement, native_types::WitnessStack}; +use acvm::BlackBoxFunctionSolver; +use nargo::{NargoError, foreign_calls::ForeignCallExecutor}; +use noirc_abi::input_parser::InputValue; +use noirc_artifacts::debug::DebugArtifact; +use noirc_driver::CompiledProgram; + +use crate::{ + errors::CliError, + fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}, +}; + +/// Results of a circuit execution. +#[derive(Clone, Debug)] +pub struct ExecutionResults { + pub witness_stack: WitnessStack, + pub return_values: ReturnValues, +} + +/// The decoded `return` witnesses. +#[derive(Clone, Debug)] +pub struct ReturnValues { + /// The `return` value from the `Prover.toml` file, if present. + pub expected_return: Option, + /// The `return` value from the circuit execution. + pub actual_return: Option, +} + +/// Execute a circuit and return the output witnesses. +pub fn execute( + circuit: &CompiledProgram, + blackbox_solver: &B, + foreign_call_executor: &mut E, + prover_file: &Path, +) -> Result +where + B: BlackBoxFunctionSolver, + E: ForeignCallExecutor, +{ + let (input_map, expected_return) = read_inputs_from_file(prover_file, &circuit.abi)?; + + let initial_witness = circuit.abi.encode(&input_map, None)?; + + let witness_stack = nargo::ops::execute_program( + &circuit.program, + initial_witness, + blackbox_solver, + foreign_call_executor, + )?; + + let main_witness = + &witness_stack.peek().expect("Should have at least one witness on the stack").witness; + + let (_, actual_return) = circuit.abi.decode(main_witness)?; + + Ok(ExecutionResults { + witness_stack, + return_values: ReturnValues { actual_return, expected_return }, + }) +} + +/// Print an error stack trace, if possible. +pub fn show_diagnostic(circuit: &CompiledProgram, err: &NargoError) { + if let Some(diagnostic) = + nargo::errors::try_to_diagnose_runtime_error(err, &circuit.abi, &circuit.debug) + { + let debug_artifact = DebugArtifact { + debug_symbols: circuit.debug.clone(), + file_map: circuit.file_map.clone(), + }; + + diagnostic.report(&debug_artifact, false); + } +} + +/// Print some information and save the witness if an output directory is specified, +/// then checks if the expected return values were the ones we expected. +pub fn save_and_check_witness( + circuit: &CompiledProgram, + results: ExecutionResults, + circuit_name: &str, + witness_dir: Option<&Path>, + witness_name: Option<&str>, +) -> Result<(), CliError> { + println!("[{}] Circuit witness successfully solved", circuit_name); + // Save first, so that we can potentially look at the output if the expectations fail. + if let Some(witness_dir) = witness_dir { + save_witness(&results.witness_stack, circuit_name, witness_dir, witness_name)?; + } + check_witness(circuit, results.return_values) +} + +/// Save the witness stack to a file. +pub fn save_witness( + witness_stack: &WitnessStack, + circuit_name: &str, + witness_dir: &Path, + witness_name: Option<&str>, +) -> Result<(), CliError> { + let witness_name = witness_name.unwrap_or(circuit_name); + let witness_path = save_witness_to_dir(witness_stack, witness_name, witness_dir)?; + println!("[{}] Witness saved to {}", circuit_name, witness_path.display()); + Ok(()) +} + +/// Compare return values to expectations, returning errors if something unexpected was returned. +pub fn check_witness( + circuit: &CompiledProgram, + return_values: ReturnValues, +) -> Result<(), CliError> { + // Check that the circuit returned a non-empty result if the ABI expects a return value. + if let Some(ref expected) = circuit.abi.return_type { + if return_values.actual_return.is_none() { + return Err(CliError::MissingReturn { expected: expected.clone() }); + } + } + + // Check that if the prover file contained a `return` entry then that's what we got. + if let Some(expected) = return_values.expected_return { + match return_values.actual_return { + None => { + return Err(CliError::UnexpectedReturn { expected, actual: None }); + } + Some(actual) => { + if actual != expected { + return Err(CliError::UnexpectedReturn { expected, actual: Some(actual) }); + } + } + } + } + + Ok(()) +} diff --git a/noir/noir-repo/tooling/artifact_cli/src/fs/artifact.rs b/noir/noir-repo/tooling/artifact_cli/src/fs/artifact.rs index 856fc472fc73..1a55764a9fed 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/fs/artifact.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/fs/artifact.rs @@ -1,16 +1,18 @@ -use std::path::Path; +use std::path::{Path, PathBuf}; use crate::{ - errors::{CliError, FilesystemError}, Artifact, + errors::{CliError, FilesystemError}, }; use noirc_artifacts::contract::ContractArtifact; use noirc_artifacts::program::ProgramArtifact; +use noirc_driver::CrateName; +use serde::de::Error; impl Artifact { /// Try to parse an artifact as a binary program or a contract pub fn read_from_file(path: &Path) -> Result { - let json = std::fs::read(path).map_err(FilesystemError::from)?; + let json = std::fs::read(path.with_extension("json")).map_err(FilesystemError::from)?; let as_program = || serde_json::from_slice::(&json).map(Artifact::Program); let as_contract = @@ -35,3 +37,55 @@ pub fn read_bytecode_from_file( .map_err(|e| FilesystemError::InvalidBytecodeFile(file_path, e.to_string()))?; Ok(bytecode) } + +/// Read a `ProgramArtifact`. Returns error if it turns out to be a `ContractArtifact`. +pub fn read_program_from_file(path: &Path) -> Result { + match Artifact::read_from_file(path)? { + Artifact::Program(program) => Ok(program), + Artifact::Contract(contract) => { + let msg = format!( + "expected a program artifact but found a contract in {}: {}", + path.display(), + contract.name + ); + Err(CliError::ArtifactDeserializationError(serde_json::Error::custom(msg))) + } + } +} + +pub fn save_program_to_file( + program_artifact: &ProgramArtifact, + crate_name: &CrateName, + output_dir: &Path, +) -> Result { + let circuit_name: String = crate_name.into(); + save_build_artifact_to_file(program_artifact, &circuit_name, output_dir) +} + +pub fn save_contract_to_file( + compiled_contract: &ContractArtifact, + circuit_name: &str, + output_dir: &Path, +) -> Result { + save_build_artifact_to_file(compiled_contract, circuit_name, output_dir) +} + +fn save_build_artifact_to_file( + build_artifact: &T, + artifact_name: &str, + output_dir: &Path, +) -> Result { + let artifact_path = output_dir.join(artifact_name).with_extension("json"); + let bytes = serde_json::to_vec(build_artifact)?; + write_to_file(&bytes, &artifact_path)?; + Ok(artifact_path) +} + +/// Create the parent directory if needed and write the bytes to a file. +pub fn write_to_file(bytes: &[u8], path: &Path) -> Result<(), FilesystemError> { + if let Some(dir) = path.parent() { + std::fs::create_dir_all(dir)?; + } + std::fs::write(path, bytes)?; + Ok(()) +} diff --git a/noir/noir-repo/tooling/artifact_cli/src/fs/inputs.rs b/noir/noir-repo/tooling/artifact_cli/src/fs/inputs.rs index f115af92041b..753343af8c65 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/fs/inputs.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/fs/inputs.rs @@ -1,6 +1,6 @@ use noirc_abi::{ - input_parser::{Format, InputValue}, Abi, InputMap, MAIN_RETURN_NAME, + input_parser::{Format, InputValue}, }; use std::{collections::BTreeMap, path::Path}; diff --git a/noir/noir-repo/tooling/artifact_cli/src/fs/witness.rs b/noir/noir-repo/tooling/artifact_cli/src/fs/witness.rs index f486a53f14d8..fee31ec1f224 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/fs/witness.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/fs/witness.rs @@ -1,16 +1,16 @@ use std::path::{Path, PathBuf}; -use acir::{native_types::WitnessStackError, FieldElement}; +use acir::{FieldElement, native_types::WitnessStackError}; use acvm::acir::native_types::WitnessStack; -use crate::errors::FilesystemError; +use crate::errors::{CliError, FilesystemError}; /// Write `witness.gz` to the output directory. pub fn save_witness_to_dir( - witnesses: WitnessStack, + witnesses: &WitnessStack, witness_name: &str, witness_dir: &Path, -) -> Result { +) -> Result { std::fs::create_dir_all(witness_dir)?; let witness_path = witness_dir.join(witness_name).with_extension("gz"); diff --git a/noir/noir-repo/tooling/artifact_cli/src/lib.rs b/noir/noir-repo/tooling/artifact_cli/src/lib.rs index 2cd2341b7b70..ac4316f5801a 100644 --- a/noir/noir-repo/tooling/artifact_cli/src/lib.rs +++ b/noir/noir-repo/tooling/artifact_cli/src/lib.rs @@ -2,6 +2,7 @@ use noirc_artifacts::{contract::ContractArtifact, program::ProgramArtifact}; pub mod commands; pub mod errors; +pub mod execution; pub mod fs; /// A parsed JSON build artifact. diff --git a/noir/noir-repo/tooling/debugger/src/context.rs b/noir/noir-repo/tooling/debugger/src/context.rs index 9741749df643..79e03672e8d7 100644 --- a/noir/noir-repo/tooling/debugger/src/context.rs +++ b/noir/noir-repo/tooling/debugger/src/context.rs @@ -5,22 +5,22 @@ use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; use acvm::acir::native_types::{Witness, WitnessMap, WitnessStack}; use acvm::brillig_vm::MemoryValue; use acvm::pwg::{ - ACVMStatus, AcirCallWaitInfo, BrilligSolver, BrilligSolverStatus, ForeignCallWaitInfo, - OpcodeNotSolvable, StepResult, ACVM, + ACVM, ACVMStatus, AcirCallWaitInfo, BrilligSolver, BrilligSolverStatus, ForeignCallWaitInfo, + OpcodeNotSolvable, StepResult, }; use acvm::{BlackBoxFunctionSolver, FieldElement}; use codespan_reporting::files::{Files, SimpleFile}; use fm::FileId; -use nargo::errors::{ExecutionError, Location}; use nargo::NargoError; +use nargo::errors::{ExecutionError, Location}; use noirc_artifacts::debug::{DebugArtifact, StackFrame}; use noirc_driver::DebugFile; use thiserror::Error; use std::collections::BTreeMap; -use std::collections::{hash_set::Iter, HashSet}; +use std::collections::{HashSet, hash_set::Iter}; /// A Noir program is composed by /// `n` ACIR circuits @@ -429,6 +429,12 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { .filter(|v: &Vec| !v.is_empty()) } + /// Returns the `FileId` of the file associated with the innermost function on the call stack. + pub(super) fn get_current_file(&mut self) -> Option { + self.get_current_source_location() + .and_then(|locations| locations.last().map(|location| location.file)) + } + /// Returns the (possible) stack of source locations corresponding to the /// given opcode location. Due to compiler inlining it's possible for this /// function to return multiple source locations. An empty vector means that @@ -788,11 +794,11 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } pub(super) fn get_variables(&self) -> Vec> { - return self.foreign_call_executor.get_variables(); + self.foreign_call_executor.get_variables() } pub(super) fn current_stack_frame(&self) -> Option> { - return self.foreign_call_executor.current_stack_frame(); + self.foreign_call_executor.current_stack_frame() } fn breakpoint_reached(&self) -> bool { @@ -954,13 +960,13 @@ mod tests { use crate::foreign_calls::DefaultDebugForeignCallExecutor; use acvm::{ acir::{ + AcirField, brillig::{HeapVector, IntegerBitSize}, circuit::{ brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}, opcodes::{AcirFunctionId, BlockId, BlockType}, }, native_types::Expression, - AcirField, }, blackbox_solver::StubbedBlackBoxSolver, brillig_vm::brillig::{ diff --git a/noir/noir-repo/tooling/debugger/src/dap.rs b/noir/noir-repo/tooling/debugger/src/dap.rs index 0d2095954e07..1df27d8ea6fd 100644 --- a/noir/noir-repo/tooling/debugger/src/dap.rs +++ b/noir/noir-repo/tooling/debugger/src/dap.rs @@ -1,8 +1,8 @@ use std::collections::BTreeMap; use std::io::{Read, Write}; -use acvm::acir::circuit::brillig::BrilligBytecode; use acvm::acir::circuit::Circuit; +use acvm::acir::circuit::brillig::BrilligBytecode; use acvm::acir::native_types::WitnessMap; use acvm::{BlackBoxFunctionSolver, FieldElement}; use nargo::PrintOutput; @@ -466,14 +466,14 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< } fn map_source_breakpoints(&mut self, args: &SetBreakpointsArguments) -> Vec { - let Some(ref source) = &args.source.path else { + let Some(source) = &args.source.path else { return vec![]; }; let Some(file_id) = self.find_file_id(source) else { eprintln!("WARN: file ID for source {source} not found"); return vec![]; }; - let Some(ref breakpoints) = &args.breakpoints else { + let Some(breakpoints) = &args.breakpoints else { return vec![]; }; let mut breakpoints_to_set: Vec<(DebugLocation, i64)> = vec![]; diff --git a/noir/noir-repo/tooling/debugger/src/foreign_calls.rs b/noir/noir-repo/tooling/debugger/src/foreign_calls.rs index b92e22844ea9..efae3df407a2 100644 --- a/noir/noir-repo/tooling/debugger/src/foreign_calls.rs +++ b/noir/noir-repo/tooling/debugger/src/foreign_calls.rs @@ -1,13 +1,13 @@ use acvm::{ + AcirField, FieldElement, acir::brillig::{ForeignCallParam, ForeignCallResult}, pwg::ForeignCallWaitInfo, - AcirField, FieldElement, }; use nargo::{ + PrintOutput, foreign_calls::{ - layers::Layer, DefaultForeignCallBuilder, ForeignCallError, ForeignCallExecutor, + DefaultForeignCallBuilder, ForeignCallError, ForeignCallExecutor, layers::Layer, }, - PrintOutput, }; use noirc_artifacts::debug::{DebugArtifact, DebugVars, StackFrame}; use noirc_errors::debug_info::{DebugFnId, DebugVarId}; @@ -66,7 +66,7 @@ impl DefaultDebugForeignCallExecutor { pub fn from_artifact<'a>( output: PrintOutput<'a>, artifact: &DebugArtifact, - ) -> impl DebugForeignCallExecutor + 'a { + ) -> impl DebugForeignCallExecutor + use<'a> { let mut ex = Self::default(); ex.load_artifact(artifact); Self::make(output, ex) diff --git a/noir/noir-repo/tooling/debugger/src/lib.rs b/noir/noir-repo/tooling/debugger/src/lib.rs index 37ac088ca355..f0dc859beb3e 100644 --- a/noir/noir-repo/tooling/debugger/src/lib.rs +++ b/noir/noir-repo/tooling/debugger/src/lib.rs @@ -19,8 +19,9 @@ pub fn run_repl_session>( solver: &B, program: CompiledProgram, initial_witness: WitnessMap, + raw_source_printing: bool, ) -> Result>, NargoError> { - repl::run(solver, program, initial_witness) + repl::run(solver, program, initial_witness, raw_source_printing) } pub fn run_dap_loop>( diff --git a/noir/noir-repo/tooling/debugger/src/repl.rs b/noir/noir-repo/tooling/debugger/src/repl.rs index eda3cbfd895b..081561469853 100644 --- a/noir/noir-repo/tooling/debugger/src/repl.rs +++ b/noir/noir-repo/tooling/debugger/src/repl.rs @@ -1,12 +1,12 @@ use crate::context::{DebugCommandResult, DebugContext, DebugLocation}; +use acvm::AcirField; use acvm::acir::brillig::BitSize; use acvm::acir::circuit::brillig::{BrilligBytecode, BrilligFunctionId}; use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; use acvm::acir::native_types::{Witness, WitnessMap, WitnessStack}; -use acvm::brillig_vm::brillig::Opcode as BrilligOpcode; use acvm::brillig_vm::MemoryValue; -use acvm::AcirField; +use acvm::brillig_vm::brillig::Opcode as BrilligOpcode; use acvm::{BlackBoxFunctionSolver, FieldElement}; use nargo::{NargoError, PrintOutput}; use noirc_driver::CompiledProgram; @@ -14,7 +14,7 @@ use noirc_driver::CompiledProgram; use crate::foreign_calls::DefaultDebugForeignCallExecutor; use noirc_artifacts::debug::DebugArtifact; -use easy_repl::{command, CommandStatus, Repl}; +use easy_repl::{CommandStatus, Repl, command}; use noirc_printable_type::PrintableValueDisplay; use std::cell::RefCell; @@ -32,6 +32,10 @@ pub struct ReplDebugger<'a, B: BlackBoxFunctionSolver> { // Brillig functions referenced from the ACIR circuits above unconstrained_functions: &'a [BrilligBytecode], + + // whether to print the source without highlighting, pretty-printing, + // or line numbers + raw_source_printing: bool, } impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { @@ -41,6 +45,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { debug_artifact: &'a DebugArtifact, initial_witness: WitnessMap, unconstrained_functions: &'a [BrilligBytecode], + raw_source_printing: bool, ) -> Self { let foreign_call_executor = Box::new(DefaultDebugForeignCallExecutor::from_artifact( PrintOutput::Stdout, @@ -68,6 +73,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { initial_witness, last_result, unconstrained_functions, + raw_source_printing, } } @@ -97,7 +103,11 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { } } let locations = self.context.get_source_location_for_debug_location(&location); - print_source_code_location(self.debug_artifact, &locations); + print_source_code_location( + self.debug_artifact, + &locations, + self.raw_source_printing, + ); } } } @@ -125,7 +135,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { } } let locations = self.context.get_source_location_for_debug_location(debug_location); - print_source_code_location(self.debug_artifact, &locations); + print_source_code_location(self.debug_artifact, &locations, self.raw_source_printing); } pub fn show_current_call_stack(&self) { @@ -233,6 +243,24 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { } } + fn add_breakpoint_at_line(&mut self, line_number: i64) { + let Some(current_file) = self.context.get_current_file() else { + println!("No current file."); + return; + }; + + let best_location = + self.context.find_opcode_for_source_location(¤t_file, line_number); + + match best_location { + Some(location) => { + println!("Added breakpoint at line {}", line_number); + self.add_breakpoint_at(location) + } + None => println!("No opcode at line {}", line_number), + } + } + fn delete_breakpoint_at(&mut self, location: DebugLocation) { if self.context.delete_breakpoint(&location) { println!("Breakpoint at {location} deleted"); @@ -427,6 +455,7 @@ pub fn run>( blackbox_solver: &B, program: CompiledProgram, initial_witness: WitnessMap, + raw_source_printing: bool, ) -> Result>, NargoError> { let circuits = &program.program.functions; let debug_artifact = @@ -438,6 +467,7 @@ pub fn run>( debug_artifact, initial_witness, unconstrained_functions, + raw_source_printing, )); let ref_context = &context; @@ -524,6 +554,16 @@ pub fn run>( } }, ) + .add( + "break", + command! { + "add a breakpoint at a line of the current file", + (line_number: i64) => |line_number| { + ref_context.borrow_mut().add_breakpoint_at_line(line_number); + Ok(CommandStatus::Done) + } + }, + ) .add( "break", command! { diff --git a/noir/noir-repo/tooling/debugger/src/source_code_printer.rs b/noir/noir-repo/tooling/debugger/src/source_code_printer.rs index b3682c9016fb..a756de8d98ba 100644 --- a/noir/noir-repo/tooling/debugger/src/source_code_printer.rs +++ b/noir/noir-repo/tooling/debugger/src/source_code_printer.rs @@ -30,7 +30,11 @@ struct LocationPrintContext { // Given a DebugArtifact and an OpcodeLocation, prints all the source code // locations the OpcodeLocation maps to, with some surrounding context and // visual aids to highlight the location itself. -pub(super) fn print_source_code_location(debug_artifact: &DebugArtifact, locations: &[Location]) { +pub(super) fn print_source_code_location( + debug_artifact: &DebugArtifact, + locations: &[Location], + raw_source_printing: bool, +) { let locations = locations.iter(); for loc in locations { @@ -41,9 +45,11 @@ pub(super) fn print_source_code_location(debug_artifact: &DebugArtifact, locatio for line in lines { match line { PrintedLine::Skip => {} - PrintedLine::Ellipsis { line_number } => print_ellipsis(line_number), + PrintedLine::Ellipsis { line_number } => { + print_ellipsis(line_number, raw_source_printing) + } PrintedLine::Content { line_number, cursor, content, highlight } => { - print_content(line_number, cursor, content, highlight) + print_content(line_number, cursor, content, highlight, raw_source_printing) } } } @@ -57,11 +63,29 @@ fn print_location_path(debug_artifact: &DebugArtifact, loc: Location) { println!("At {}:{line_number}:{column_number}", debug_artifact.name(loc.file).unwrap()); } -fn print_ellipsis(line_number: usize) { +fn print_ellipsis(line_number: usize, raw_source_printing: bool) { + if raw_source_printing { + println!("..."); + return; + } + println!("{:>3} {:2} {}", line_number.dimmed(), "", "...".dimmed()); } -fn print_content(line_number: usize, cursor: &str, content: &str, highlight: Option>) { +fn print_content( + line_number: usize, + cursor: &str, + content: &str, + highlight: Option>, + raw_source_printing: bool, +) { + if raw_source_printing { + if cursor == "->" && highlight.is_some() { + println!("{}", content); + } + return; + } + match highlight { Some(highlight) => { println!( @@ -220,17 +244,17 @@ fn render_location<'a>( #[cfg(test)] mod tests { - use crate::source_code_printer::render_location; use crate::source_code_printer::PrintedLine::Content; + use crate::source_code_printer::render_location; use acvm::acir::circuit::OpcodeLocation; use fm::FileManager; use noirc_artifacts::debug::DebugArtifact; - use noirc_errors::{debug_info::DebugInfo, Location, Span}; + use noirc_errors::{Location, Span, debug_info::DebugInfo}; use std::collections::BTreeMap; use std::ops::Range; use std::path::Path; use std::path::PathBuf; - use tempfile::{tempdir, TempDir}; + use tempfile::{TempDir, tempdir}; // Returns the absolute path to the file fn create_dummy_file(dir: &TempDir, file_name: &Path) -> PathBuf { diff --git a/noir/noir-repo/tooling/debugger/tests/debug.rs b/noir/noir-repo/tooling/debugger/tests/debug.rs index eb43cf9cc6dd..079857420853 100644 --- a/noir/noir-repo/tooling/debugger/tests/debug.rs +++ b/noir/noir-repo/tooling/debugger/tests/debug.rs @@ -49,4 +49,115 @@ mod tests { // Exit the bash session. dbg_session.send_line("exit").expect("Failed to quit bash session"); } + + #[test] + fn debugger_expected_call_stack() { + let nargo_bin = + cargo_bin("nargo").into_os_string().into_string().expect("Cannot parse nargo path"); + + let timeout_seconds = 30; + let mut dbg_session = + spawn_bash(Some(timeout_seconds * 1000)).expect("Could not start bash session"); + + let test_program_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../test_programs/execution_success/regression_7195"); + let test_program_dir = test_program_path.display(); + + // Start debugger and test that it loads for the given program. + dbg_session + .execute( + &format!( + "{nargo_bin} debug --raw-source-printing true --program-dir {test_program_dir} --force-brillig --expression-width 3" + ), + ".*\\Starting debugger.*", + ) + .expect("Could not start debugger"); + + let num_steps = 16; + for _ in 1..=num_steps { + // While running the debugger, issue a "next" cmd, + // which should run to the program to the next source line given + // we haven't set any breakpoints. + // ">" is the debugger's prompt, so finding one + // after running "next" indicates that the + // debugger has not panicked for this step. + dbg_session + .send_line("next") + .expect("Debugger panicked while attempting to step through program."); + dbg_session + .exp_string(">") + .expect("Failed while waiting for debugger to step through program."); + } + + let mut lines = vec![]; + while let Ok(line) = dbg_session.read_line() { + if !(line.starts_with(">next") || line.starts_with("At ") || line.starts_with("...")) { + lines.push(line); + } + } + + let lines_expected_to_contain: Vec<&str> = vec![ + "> next", + " let x = unsafe { baz(x) };", + "unconstrained fn baz(x: Field) -> Field {", + "> next", + " let x = unsafe { baz(x) };", + "unconstrained fn baz(x: Field) -> Field {", + "> next", + " let x = unsafe { baz(x) };", + "}", + "> next", + " let x = unsafe { baz(x) };", + "> next", + " foo(x);", + "fn foo(x: Field) {", + "> next", + " foo(x);", + "fn foo(x: Field) {", + "> next", + " foo(x);", + " let y = unsafe { baz(x) };", + "unconstrained fn baz(x: Field) -> Field {", + "> next", + " foo(x);", + " let y = unsafe { baz(x) };", + "unconstrained fn baz(x: Field) -> Field {", + "> next", + " foo(x);", + " let y = unsafe { baz(x) };", + "}", + "> next", + " foo(x);", + " let y = unsafe { baz(x) };", + "> next", + " foo(x);", + " bar(y);", + "fn bar(y: Field) {", + "> next", + " foo(x);", + " bar(y);", + "fn bar(y: Field) {", + "> next", + " foo(x);", + " bar(y);", + " assert(y != 0);", + ]; + + for (line, line_expected_to_contain) in lines.into_iter().zip(lines_expected_to_contain) { + let ascii_line: String = line.chars().filter(char::is_ascii).collect(); + let line_expected_to_contain = line_expected_to_contain.trim_start(); + assert!( + ascii_line.contains(line_expected_to_contain), + "{:?}\ndid not contain\n{:?}", + ascii_line, + line_expected_to_contain, + ); + } + + // Run the "quit" command + dbg_session.send_line("quit").expect("Failed to quit debugger"); + + // Exit the bash session. + dbg_session.send_line("exit").expect("Failed to quit bash session"); + } } diff --git a/noir/noir-repo/tooling/fuzzer/src/dictionary/mod.rs b/noir/noir-repo/tooling/fuzzer/src/dictionary/mod.rs index 7fe1c4cd602c..4f15a336a3e9 100644 --- a/noir/noir-repo/tooling/fuzzer/src/dictionary/mod.rs +++ b/noir/noir-repo/tooling/fuzzer/src/dictionary/mod.rs @@ -6,16 +6,16 @@ use std::collections::HashSet; use acvm::{ + AcirField, acir::{ circuit::{ + Circuit, Opcode, Program, brillig::{BrilligBytecode, BrilligInputs}, opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum}, - Circuit, Opcode, Program, }, native_types::Expression, }, brillig_vm::brillig::Opcode as BrilligOpcode, - AcirField, }; /// Constructs a [HashSet] of values pulled from a [Program] which are likely to be correspond diff --git a/noir/noir-repo/tooling/fuzzer/src/lib.rs b/noir/noir-repo/tooling/fuzzer/src/lib.rs index 324be323fc21..471f3da88f65 100644 --- a/noir/noir-repo/tooling/fuzzer/src/lib.rs +++ b/noir/noir-repo/tooling/fuzzer/src/lib.rs @@ -4,11 +4,11 @@ //! Code is used under the MIT license. use acvm::{ + FieldElement, acir::{ circuit::Program, native_types::{WitnessMap, WitnessStack}, }, - FieldElement, }; use dictionary::build_dictionary_from_program; use noirc_abi::InputMap; diff --git a/noir/noir-repo/tooling/fuzzer/src/strategies/int.rs b/noir/noir-repo/tooling/fuzzer/src/strategies/int.rs index 22dded7c7b76..5fb8d469a926 100644 --- a/noir/noir-repo/tooling/fuzzer/src/strategies/int.rs +++ b/noir/noir-repo/tooling/fuzzer/src/strategies/int.rs @@ -54,20 +54,12 @@ impl IntStrategy { /// Maximum allowed positive number. fn type_max(&self) -> i128 { - if self.bits < 128 { - (1i128 << (self.bits - 1)) - 1 - } else { - i128::MAX - } + if self.bits < 128 { (1i128 << (self.bits - 1)) - 1 } else { i128::MAX } } /// Minimum allowed negative number. fn type_min(&self) -> i128 { - if self.bits < 128 { - -(1i128 << (self.bits - 1)) - } else { - i128::MIN - } + if self.bits < 128 { -(1i128 << (self.bits - 1)) } else { i128::MIN } } } diff --git a/noir/noir-repo/tooling/fuzzer/src/strategies/mod.rs b/noir/noir-repo/tooling/fuzzer/src/strategies/mod.rs index 99c7ca56f2e7..4c8181ea804a 100644 --- a/noir/noir-repo/tooling/fuzzer/src/strategies/mod.rs +++ b/noir/noir-repo/tooling/fuzzer/src/strategies/mod.rs @@ -4,7 +4,7 @@ use proptest::prelude::*; use acvm::{AcirField, FieldElement}; -use noirc_abi::{input_parser::InputValue, Abi, AbiType, InputMap, Sign}; +use noirc_abi::{Abi, AbiType, InputMap, Sign, input_parser::InputValue}; use std::collections::{BTreeMap, HashSet}; use uint::UintStrategy; diff --git a/noir/noir-repo/tooling/fuzzer/src/strategies/uint.rs b/noir/noir-repo/tooling/fuzzer/src/strategies/uint.rs index 402e65597524..904a5cef2878 100644 --- a/noir/noir-repo/tooling/fuzzer/src/strategies/uint.rs +++ b/noir/noir-repo/tooling/fuzzer/src/strategies/uint.rs @@ -78,11 +78,7 @@ impl UintStrategy { /// Maximum integer that fits in the given bit width. fn type_max(&self) -> u128 { - if self.bits < 128 { - (1 << self.bits) - 1 - } else { - u128::MAX - } + if self.bits < 128 { (1 << self.bits) - 1 } else { u128::MAX } } } diff --git a/noir/noir-repo/tooling/inspector/Cargo.toml b/noir/noir-repo/tooling/inspector/Cargo.toml index 2124f7e9a282..d7ab641e4837 100644 --- a/noir/noir-repo/tooling/inspector/Cargo.toml +++ b/noir/noir-repo/tooling/inspector/Cargo.toml @@ -26,3 +26,4 @@ const_format.workspace = true acir.workspace = true noirc_artifacts.workspace = true noirc_artifacts_info.workspace = true +noir_artifact_cli.workspace = true diff --git a/noir/noir-repo/tooling/inspector/src/cli/info_cmd.rs b/noir/noir-repo/tooling/inspector/src/cli/info_cmd.rs index 6a9db2676f2b..34107ebea3a9 100644 --- a/noir/noir-repo/tooling/inspector/src/cli/info_cmd.rs +++ b/noir/noir-repo/tooling/inspector/src/cli/info_cmd.rs @@ -2,8 +2,8 @@ use std::path::PathBuf; use clap::Args; use color_eyre::eyre; -use noirc_artifacts::program::ProgramArtifact; -use noirc_artifacts_info::{count_opcodes_and_gates_in_program, show_info_report, InfoReport}; +use noir_artifact_cli::Artifact; +use noirc_artifacts_info::{InfoReport, count_opcodes_and_gates_in_program, show_info_report}; #[derive(Debug, Clone, Args)] pub(crate) struct InfoCommand { @@ -13,22 +13,42 @@ pub(crate) struct InfoCommand { /// Output a JSON formatted report. Changes to this format are not currently considered breaking. #[clap(long, hide = true)] json: bool, + + /// Name of the function to print, if the artifact is a contract. + #[clap(long)] + contract_fn: Option, } pub(crate) fn run(args: InfoCommand) -> eyre::Result<()> { - let file = std::fs::File::open(args.artifact.clone())?; - let artifact: ProgramArtifact = serde_json::from_reader(file)?; - - let package_name = args - .artifact - .with_extension("") - .file_name() - .map(|s| s.to_string_lossy().to_string()) - .unwrap_or_else(|| "artifact".to_string()); - - let program_info = count_opcodes_and_gates_in_program(artifact, package_name.to_string(), None); - - let info_report = InfoReport { programs: vec![program_info] }; + let artifact = Artifact::read_from_file(&args.artifact)?; + + let programs = match artifact { + Artifact::Program(program) => { + let package_name = args + .artifact + .with_extension("") + .file_name() + .map(|s| s.to_string_lossy().to_string()) + .unwrap_or_else(|| "artifact".to_string()); + + vec![count_opcodes_and_gates_in_program(program, package_name, None)] + } + Artifact::Contract(contract) => contract + .functions + .into_iter() + .filter(|f| args.contract_fn.as_ref().map(|n| *n == f.name).unwrap_or(true)) + .map(|f| { + let package_name = format!("{}::{}", contract.name, f.name); + let program = f.into_compiled_program( + contract.noir_version.clone(), + contract.file_map.clone(), + ); + count_opcodes_and_gates_in_program(program.into(), package_name, None) + }) + .collect::>(), + }; + + let info_report = InfoReport { programs }; show_info_report(info_report, args.json); Ok(()) diff --git a/noir/noir-repo/tooling/inspector/src/cli/mod.rs b/noir/noir-repo/tooling/inspector/src/cli/mod.rs index 8cce6ec3a6f2..411f2076973d 100644 --- a/noir/noir-repo/tooling/inspector/src/cli/mod.rs +++ b/noir/noir-repo/tooling/inspector/src/cli/mod.rs @@ -1,4 +1,4 @@ -use clap::{command, Parser, Subcommand}; +use clap::{Parser, Subcommand, command}; use color_eyre::eyre; use const_format::formatcp; diff --git a/noir/noir-repo/tooling/inspector/src/cli/print_acir_cmd.rs b/noir/noir-repo/tooling/inspector/src/cli/print_acir_cmd.rs index f3dfe528973f..c3580a715d88 100644 --- a/noir/noir-repo/tooling/inspector/src/cli/print_acir_cmd.rs +++ b/noir/noir-repo/tooling/inspector/src/cli/print_acir_cmd.rs @@ -2,20 +2,38 @@ use std::path::PathBuf; use clap::Args; use color_eyre::eyre; -use noirc_artifacts::program::ProgramArtifact; +use noir_artifact_cli::Artifact; #[derive(Debug, Clone, Args)] pub(crate) struct PrintAcirCommand { /// The artifact to print artifact: PathBuf, + + /// Name of the function to print, if the artifact is a contract. + #[clap(long)] + contract_fn: Option, } pub(crate) fn run(args: PrintAcirCommand) -> eyre::Result<()> { - let file = std::fs::File::open(args.artifact.clone())?; - let artifact: ProgramArtifact = serde_json::from_reader(file)?; + let artifact = Artifact::read_from_file(&args.artifact)?; - println!("Compiled ACIR for main:"); - println!("{}", artifact.bytecode); + match artifact { + Artifact::Program(program) => { + println!("Compiled ACIR for main:"); + println!("{}", program.bytecode); + } + Artifact::Contract(contract) => { + println!("Compiled circuits for contract '{}':", contract.name); + for function in contract + .functions + .into_iter() + .filter(|f| args.contract_fn.as_ref().map(|n| *n == f.name).unwrap_or(true)) + { + println!("Compiled ACIR for function '{}':", function.name); + println!("{}", function.bytecode); + } + } + } Ok(()) } diff --git a/noir/noir-repo/tooling/inspector/src/main.rs b/noir/noir-repo/tooling/inspector/src/main.rs index 8270fedbf2c0..4d6513b8394f 100644 --- a/noir/noir-repo/tooling/inspector/src/main.rs +++ b/noir/noir-repo/tooling/inspector/src/main.rs @@ -2,7 +2,7 @@ mod cli; fn main() { if let Err(report) = cli::start_cli() { - eprintln!("{report:?}"); + eprintln!("{report:#}"); std::process::exit(1); } } diff --git a/noir/noir-repo/tooling/lsp/Cargo.toml b/noir/noir-repo/tooling/lsp/Cargo.toml index a055a9a6bcec..d0b67f53c241 100644 --- a/noir/noir-repo/tooling/lsp/Cargo.toml +++ b/noir/noir-repo/tooling/lsp/Cargo.toml @@ -31,6 +31,7 @@ thiserror.workspace = true fm.workspace = true rayon.workspace = true fxhash.workspace = true +iter-extended.workspace = true convert_case = "0.6.0" num-bigint.workspace = true diff --git a/noir/noir-repo/tooling/lsp/src/attribute_reference_finder.rs b/noir/noir-repo/tooling/lsp/src/attribute_reference_finder.rs index e3f31b65b463..384c575f0c65 100644 --- a/noir/noir-repo/tooling/lsp/src/attribute_reference_finder.rs +++ b/noir/noir-repo/tooling/lsp/src/attribute_reference_finder.rs @@ -9,6 +9,7 @@ use std::collections::BTreeMap; use fm::FileId; use noirc_errors::Span; use noirc_frontend::{ + ParsedModule, ast::{AttributeTarget, Visitor}, graph::CrateId, hir::{ @@ -19,7 +20,6 @@ use noirc_frontend::{ parser::ParsedSubModule, token::MetaAttribute, usage_tracker::UsageTracker, - ParsedModule, }; use crate::modules::module_def_id_to_reference_id; @@ -65,7 +65,7 @@ impl<'a> AttributeReferenceFinder<'a> { } } -impl<'a> Visitor for AttributeReferenceFinder<'a> { +impl Visitor for AttributeReferenceFinder<'_> { fn visit_parsed_submodule(&mut self, parsed_sub_module: &ParsedSubModule, _span: Span) -> bool { // Switch `self.module_id` to the submodule let previous_module_id = self.module_id; @@ -90,7 +90,7 @@ impl<'a> Visitor for AttributeReferenceFinder<'a> { attribute: &MetaAttribute, _target: AttributeTarget, ) -> bool { - if !self.includes_span(attribute.span) { + if !self.includes_span(attribute.location.span) { return false; } diff --git a/noir/noir-repo/tooling/lsp/src/lib.rs b/noir/noir-repo/tooling/lsp/src/lib.rs index c722bfdfd3e1..784ac8cf93d6 100644 --- a/noir/noir-repo/tooling/lsp/src/lib.rs +++ b/noir/noir-repo/tooling/lsp/src/lib.rs @@ -15,35 +15,35 @@ use std::{ use acvm::{BlackBoxFunctionSolver, FieldElement}; use async_lsp::{ - router::Router, AnyEvent, AnyNotification, AnyRequest, ClientSocket, Error, LspService, - ResponseError, + AnyEvent, AnyNotification, AnyRequest, ClientSocket, Error, LspService, ResponseError, + router::Router, }; -use fm::{codespan_files as files, FileManager}; +use fm::{FileManager, codespan_files as files}; use fxhash::FxHashSet; use lsp_types::{ + CodeLens, request::{ CodeActionRequest, Completion, DocumentSymbolRequest, HoverRequest, InlayHintRequest, PrepareRenameRequest, References, Rename, SignatureHelpRequest, }, - CodeLens, }; use nargo::{ package::{Package, PackageType}, parse_all, workspace::Workspace, }; -use nargo_toml::{find_file_manifest, resolve_workspace_from_toml, PackageSelection}; -use noirc_driver::{file_manager_with_stdlib, prepare_crate, NOIR_ARTIFACT_VERSION_STRING}; +use nargo_toml::{PackageSelection, find_file_manifest, resolve_workspace_from_toml}; +use noirc_driver::{NOIR_ARTIFACT_VERSION_STRING, file_manager_with_stdlib, prepare_crate}; use noirc_frontend::{ + ParsedModule, graph::{CrateGraph, CrateId, CrateName}, hir::{ - def_map::{parse_file, CrateDefMap}, Context, FunctionNameMatch, ParsedFiles, + def_map::{CrateDefMap, parse_file}, }, node_interner::NodeInterner, parser::ParserError, usage_tracker::UsageTracker, - ParsedModule, }; use rayon::prelude::*; @@ -52,12 +52,11 @@ use notifications::{ on_did_open_text_document, on_did_save_text_document, on_exit, on_initialized, }; use requests::{ - on_code_action_request, on_code_lens_request, on_completion_request, + LspInitializationOptions, on_code_action_request, on_code_lens_request, on_completion_request, on_document_symbol_request, on_formatting, on_goto_declaration_request, on_goto_definition_request, on_goto_type_definition_request, on_hover_request, on_initialize, on_inlay_hint_request, on_prepare_rename_request, on_references_request, on_rename_request, on_shutdown, on_signature_help_request, on_test_run_request, on_tests_request, - LspInitializationOptions, }; use serde_json::Value as JsonValue; use thiserror::Error; @@ -74,12 +73,14 @@ mod types; mod use_segment_positions; mod utils; mod visibility; +mod with_file; #[cfg(test)] mod test_utils; use solver::WrapperSolver; -use types::{notification, request, NargoTest, NargoTestId, Position, Range, Url}; +use types::{NargoTest, NargoTestId, Position, Range, Url, notification, request}; +use with_file::parsed_module_with_file; #[derive(Debug, Error)] pub enum LspError { @@ -237,11 +238,7 @@ fn get_package_tests_in_crate( }) .collect(); - if package_tests.is_empty() { - None - } else { - Some(package_tests) - } + if package_tests.is_empty() { None } else { Some(package_tests) } } fn byte_span_to_range<'a, F: files::Files<'a> + ?Sized>( @@ -388,18 +385,22 @@ fn parse_diff(file_manager: &FileManager, state: &mut LspState) -> ParsedFiles { }) .collect(); - let cache_hits: Vec<_> = noir_file_hashes + let cache_hits = noir_file_hashes .par_iter() .filter_map(|(file_id, file_path, current_hash)| { let cached_version = state.cached_parsed_files.get(file_path); - if let Some((hash, cached_parsing)) = cached_version { + if let Some((hash, (parsed_module, errors))) = cached_version { if hash == current_hash { - return Some((*file_id, cached_parsing.clone())); + // The cached ParsedModule might have FileIDs in it that are different than the file_id we get here, + // so we must replace all of those FileIDs with the one here. + let parsed_module = + parsed_module_with_file(parsed_module.clone(), *file_id); + return Some((*file_id, (parsed_module, errors.clone()))); } } None }) - .collect(); + .collect::>(); let cache_hits_ids: FxHashSet<_> = cache_hits.iter().map(|(file_id, _)| *file_id).collect(); diff --git a/noir/noir-repo/tooling/lsp/src/modules.rs b/noir/noir-repo/tooling/lsp/src/modules.rs index 56529949b3ea..4e7ef64b2c08 100644 --- a/noir/noir-repo/tooling/lsp/src/modules.rs +++ b/noir/noir-repo/tooling/lsp/src/modules.rs @@ -248,15 +248,12 @@ pub(crate) fn module_def_id_relative_path( interner, ) } else { - let Some(module_full_path) = relative_module_full_path( + relative_module_full_path( module_def_id, current_module_id, current_module_parent_id, interner, - ) else { - return None; - }; - module_full_path + )? }; let path = if defining_module.is_some() || !matches!(module_def_id, ModuleDefId::ModuleId(..)) { diff --git a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs index ac8bf7309799..b7ba8cd47615 100644 --- a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs @@ -3,25 +3,25 @@ use std::ops::ControlFlow; use std::path::PathBuf; use crate::{ - insert_all_files_for_workspace_into_file_manager, PackageCacheData, WorkspaceCacheData, + PackageCacheData, WorkspaceCacheData, insert_all_files_for_workspace_into_file_manager, }; use async_lsp::{ErrorCode, LanguageClient, ResponseError}; -use fm::{FileId, FileManager, FileMap}; +use fm::{FileManager, FileMap}; use fxhash::FxHashMap as HashMap; use lsp_types::{DiagnosticRelatedInformation, DiagnosticTag, Url}; use noirc_driver::check_crate; use noirc_errors::reporter::CustomLabel; -use noirc_errors::{DiagnosticKind, FileDiagnostic, Location}; +use noirc_errors::{CustomDiagnostic, DiagnosticKind, Location}; use crate::types::{ - notification, Diagnostic, DiagnosticSeverity, DidChangeConfigurationParams, - DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams, - DidSaveTextDocumentParams, InitializedParams, NargoPackageTests, PublishDiagnosticsParams, + Diagnostic, DiagnosticSeverity, DidChangeConfigurationParams, DidChangeTextDocumentParams, + DidCloseTextDocumentParams, DidOpenTextDocumentParams, DidSaveTextDocumentParams, + InitializedParams, NargoPackageTests, PublishDiagnosticsParams, notification, }; use crate::{ - byte_span_to_range, get_package_tests_in_crate, parse_diff, resolve_workspace_for_source_path, - LspState, + LspState, byte_span_to_range, get_package_tests_in_crate, parse_diff, + resolve_workspace_for_source_path, }; pub(super) fn on_initialized( @@ -191,16 +191,16 @@ fn publish_diagnostics( package_root_dir: &PathBuf, files: &FileMap, fm: &FileManager, - file_diagnostics: Vec, + custom_diagnostics: Vec, ) { let mut diagnostics_per_url: HashMap> = HashMap::default(); - for file_diagnostic in file_diagnostics.into_iter() { - let file_id = file_diagnostic.file_id; - let path = fm.path(file_id).expect("file must exist to have emitted diagnostic"); + for custom_diagnostic in custom_diagnostics.into_iter() { + let file = custom_diagnostic.file; + let path = fm.path(file).expect("file must exist to have emitted diagnostic"); if let Ok(uri) = Url::from_file_path(path) { if let Some(diagnostic) = - file_diagnostic_to_diagnostic(file_diagnostic, files, fm, uri.clone()) + custom_diagnostic_to_diagnostic(custom_diagnostic, files, fm, uri.clone()) { diagnostics_per_url.entry(uri).or_default().push(diagnostic); } @@ -232,21 +232,18 @@ fn publish_diagnostics( state.files_with_errors.insert(package_root_dir.clone(), new_files_with_errors); } -fn file_diagnostic_to_diagnostic( - file_diagnostic: FileDiagnostic, +fn custom_diagnostic_to_diagnostic( + diagnostic: CustomDiagnostic, files: &FileMap, fm: &FileManager, uri: Url, ) -> Option { - let file_id = file_diagnostic.file_id; - let diagnostic = file_diagnostic.diagnostic; - if diagnostic.secondaries.is_empty() { return None; } - let span = diagnostic.secondaries.first().unwrap().span; - let range = byte_span_to_range(files, file_id, span.into())?; + let span = diagnostic.secondaries.first().unwrap().location.span; + let range = byte_span_to_range(files, diagnostic.file, span.into())?; let severity = match diagnostic.kind { DiagnosticKind::Error => DiagnosticSeverity::ERROR, @@ -266,7 +263,7 @@ fn file_diagnostic_to_diagnostic( let secondaries = diagnostic .secondaries .into_iter() - .filter_map(|secondary| secondary_to_related_information(secondary, file_id, files, fm)); + .filter_map(|secondary| secondary_to_related_information(secondary, files, fm)); let notes = diagnostic.notes.into_iter().map(|message| DiagnosticRelatedInformation { location: lsp_types::Location { uri: uri.clone(), range }, message, @@ -293,14 +290,13 @@ fn file_diagnostic_to_diagnostic( fn secondary_to_related_information( secondary: CustomLabel, - file_id: FileId, files: &FileMap, fm: &FileManager, ) -> Option { - let secondary_file = secondary.file.unwrap_or(file_id); + let secondary_file = secondary.location.file; let path = fm.path(secondary_file)?; let uri = Url::from_file_path(path).ok()?; - let range = byte_span_to_range(files, secondary_file, secondary.span.into())?; + let range = byte_span_to_range(files, secondary_file, secondary.location.span.into())?; let message = secondary.message; Some(DiagnosticRelatedInformation { location: lsp_types::Location { uri, range }, message }) } diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action.rs index d5512855b3be..6d74ce2d1f96 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action.rs @@ -11,6 +11,10 @@ use lsp_types::{ TextDocumentPositionParams, TextEdit, Url, WorkspaceEdit, }; use noirc_errors::Span; +use noirc_frontend::{ + ParsedModule, + parser::{Item, ItemKind, ParsedSubModule}, +}; use noirc_frontend::{ ast::{ CallExpression, ConstructorExpression, ItemVisibility, MethodCallExpression, NoirTraitImpl, @@ -21,14 +25,10 @@ use noirc_frontend::{ node_interner::{NodeInterner, Reexport}, usage_tracker::UsageTracker, }; -use noirc_frontend::{ - parser::{Item, ItemKind, ParsedSubModule}, - ParsedModule, -}; use crate::{ - modules::get_ancestor_module_reexport, use_segment_positions::UseSegmentPositions, utils, - visibility::module_def_id_is_visible, LspState, + LspState, modules::get_ancestor_module_reexport, use_segment_positions::UseSegmentPositions, + utils, visibility::module_def_id_is_visible, }; use super::{process_request, to_lsp_location}; @@ -44,7 +44,7 @@ mod tests; pub(crate) fn on_code_action_request( state: &mut LspState, params: CodeActionParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let uri = params.text_document.clone().uri; let position = params.range.start; let text_document_position_params = @@ -56,7 +56,7 @@ pub(crate) fn on_code_action_request( utils::range_to_byte_span(args.files, file_id, ¶ms.range).and_then(|byte_range| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let mut finder = CodeActionFinder::new( uri, @@ -233,16 +233,16 @@ impl<'a> CodeActionFinder<'a> { } } -impl<'a> Visitor for CodeActionFinder<'a> { +impl Visitor for CodeActionFinder<'_> { fn visit_item(&mut self, item: &Item) -> bool { if let ItemKind::Import(use_tree, _) = &item.kind { - if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { + if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.location.span) { self.auto_import_line = (lsp_location.range.end.line + 1) as usize; } self.use_segment_positions.add(use_tree); } - self.includes_span(item.span) + self.includes_span(item.location.span) } fn visit_parsed_submodule(&mut self, parsed_sub_module: &ParsedSubModule, span: Span) -> bool { @@ -306,7 +306,7 @@ impl<'a> Visitor for CodeActionFinder<'a> { } if call.is_macro_call { - self.remove_bang_from_call(call.func.span); + self.remove_bang_from_call(call.func.location.span); } true diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/fill_struct_fields.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/fill_struct_fields.rs index fc8be7c51630..0f9188388ee2 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/fill_struct_fields.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/fill_struct_fields.rs @@ -1,5 +1,5 @@ use lsp_types::TextEdit; -use noirc_errors::{Location, Span}; +use noirc_errors::Span; use noirc_frontend::{ ast::{ConstructorExpression, UnresolvedTypeData}, node_interner::ReferenceId, @@ -9,7 +9,7 @@ use crate::byte_span_to_range; use super::CodeActionFinder; -impl<'a> CodeActionFinder<'a> { +impl CodeActionFinder<'_> { pub(super) fn fill_struct_fields(&mut self, constructor: &ConstructorExpression, span: Span) { if !self.includes_span(span) { return; @@ -19,7 +19,7 @@ impl<'a> CodeActionFinder<'a> { return; }; - let location = Location::new(path.span, self.file); + let location = path.location; let Some(ReferenceId::Type(type_id)) = self.interner.find_referenced(location) else { return; }; diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/implement_missing_members.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/implement_missing_members.rs index c29caf79848a..a39df735b34f 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/implement_missing_members.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/implement_missing_members.rs @@ -11,7 +11,7 @@ use crate::{byte_span_to_range, trait_impl_method_stub_generator::TraitImplMetho use super::CodeActionFinder; -impl<'a> CodeActionFinder<'a> { +impl CodeActionFinder<'_> { pub(super) fn implement_missing_members( &mut self, noir_trait_impl: &NoirTraitImpl, diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/import_or_qualify.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/import_or_qualify.rs index 1141aca23d2d..070dcfcde901 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/import_or_qualify.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/import_or_qualify.rs @@ -6,13 +6,13 @@ use crate::{ byte_span_to_range, modules::module_def_id_relative_path, use_segment_positions::{ - use_completion_item_additional_text_edits, UseCompletionItemAdditionTextEditsRequest, + UseCompletionItemAdditionTextEditsRequest, use_completion_item_additional_text_edits, }, }; use super::CodeActionFinder; -impl<'a> CodeActionFinder<'a> { +impl CodeActionFinder<'_> { pub(super) fn import_or_qualify(&mut self, path: &Path) { if path.segments.len() != 1 { return; diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/import_trait.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/import_trait.rs index 52b3e66033a2..e29558c617b9 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/import_trait.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/import_trait.rs @@ -11,13 +11,13 @@ use crate::{ modules::module_def_id_relative_path, requests::TraitReexport, use_segment_positions::{ - use_completion_item_additional_text_edits, UseCompletionItemAdditionTextEditsRequest, + UseCompletionItemAdditionTextEditsRequest, use_completion_item_additional_text_edits, }, }; use super::CodeActionFinder; -impl<'a> CodeActionFinder<'a> { +impl CodeActionFinder<'_> { pub(super) fn import_trait_in_method_call(&mut self, method_call: &MethodCallExpression) { // First see if the method name already points to a function. let name_location = Location::new(method_call.method_name.span(), self.file); @@ -36,13 +36,13 @@ impl<'a> CodeActionFinder<'a> { } // Find out the type of the object - let object_location = Location::new(method_call.object.span, self.file); + let object_location = method_call.object.location; let Some(typ) = self.interner.type_at_location(object_location) else { return; }; let trait_methods = - self.interner.lookup_trait_methods(&typ, &method_call.method_name.0.contents, true); + self.interner.lookup_trait_methods(typ, &method_call.method_name.0.contents, true); let trait_ids: HashSet<_> = trait_methods.iter().map(|(_, trait_id)| *trait_id).collect(); for trait_id in trait_ids { diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_bang_from_call.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_bang_from_call.rs index 90f4fef0efd2..e2ef2ef84eed 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_bang_from_call.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_bang_from_call.rs @@ -1,12 +1,12 @@ use lsp_types::TextEdit; use noirc_errors::{Location, Span}; -use noirc_frontend::{node_interner::ReferenceId, QuotedType, Type}; +use noirc_frontend::{QuotedType, Type, node_interner::ReferenceId}; use crate::byte_span_to_range; use super::CodeActionFinder; -impl<'a> CodeActionFinder<'a> { +impl CodeActionFinder<'_> { pub(super) fn remove_bang_from_call(&mut self, span: Span) { // If we can't find the referenced function, there's nothing we can do let Some(ReferenceId::Function(func_id)) = diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_unused_import.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_unused_import.rs index 4822a9d61ec8..41d43baa656f 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_unused_import.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/remove_unused_import.rs @@ -1,19 +1,20 @@ use std::collections::HashMap; +use fm::FileId; use lsp_types::TextEdit; -use noirc_errors::Span; +use noirc_errors::{Location, Span}; use noirc_frontend::{ + ParsedModule, ast::{Ident, ItemVisibility, UseTree, UseTreeKind}, parser::{Item, ItemKind}, usage_tracker::UnusedItem, - ParsedModule, }; use crate::byte_span_to_range; use super::CodeActionFinder; -impl<'a> CodeActionFinder<'a> { +impl CodeActionFinder<'_> { pub(super) fn remove_unused_import( &mut self, use_tree: &UseTree, @@ -80,11 +81,7 @@ fn use_tree_without_unused_import( match &use_tree.kind { UseTreeKind::Path(name, alias) => { let ident = alias.as_ref().unwrap_or(name); - if unused_items.contains_key(ident) { - (None, 1) - } else { - (Some(use_tree.clone()), 0) - } + if unused_items.contains_key(ident) { (None, 1) } else { (Some(use_tree.clone()), 0) } } UseTreeKind::List(use_trees) => { let mut new_use_trees: Vec = Vec::new(); @@ -106,12 +103,12 @@ fn use_tree_without_unused_import( let mut prefix = use_tree.prefix.clone(); prefix.segments.extend(new_use_tree.prefix.segments); - Some(UseTree { prefix, kind: new_use_tree.kind, span: use_tree.span }) + Some(UseTree { prefix, kind: new_use_tree.kind, location: use_tree.location }) } else { Some(UseTree { prefix: use_tree.prefix.clone(), kind: UseTreeKind::List(new_use_trees), - span: use_tree.span, + location: use_tree.location, }) }; @@ -130,7 +127,7 @@ fn use_tree_to_string(use_tree: UseTree, visibility: ItemVisibility, nesting: us let parsed_module = ParsedModule { items: vec![Item { kind: ItemKind::Import(use_tree, visibility), - span: Span::from(0..source.len() as u32), + location: Location::new(Span::from(0..source.len() as u32), FileId::dummy()), doc_comments: Vec::new(), }], inner_doc_comments: Vec::new(), diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_action/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/code_action/tests.rs index f5748e29d97b..23026d16d943 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_action/tests.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_action/tests.rs @@ -56,11 +56,7 @@ pub(crate) async fn assert_code_action(title: &str, src: &str, expected: &str) { .iter() .filter_map(|action| { if let CodeActionOrCommand::CodeAction(action) = action { - if action.title == title { - Some(action) - } else { - None - } + if action.title == title { Some(action) } else { None } } else { None } 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 ab98ab8bf100..1870e8e06024 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 @@ -7,9 +7,8 @@ use noirc_driver::check_crate; use noirc_frontend::hir::FunctionNameMatch; use crate::{ - byte_span_to_range, prepare_source, resolve_workspace_for_source_path, + LspState, byte_span_to_range, prepare_source, resolve_workspace_for_source_path, types::{CodeLens, CodeLensParams, CodeLensResult, Command}, - LspState, }; const ARROW: &str = "▶\u{fe0e}"; @@ -40,7 +39,7 @@ fn package_selection_args(workspace: &Workspace, package: &Package) -> Vec impl Future> { +) -> impl Future> + use<> { future::ready(on_code_lens_request_inner(state, params)) } diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion.rs b/noir/noir-repo/tooling/lsp/src/requests/completion.rs index 2f06f6607712..9d11aba95349 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion.rs @@ -15,6 +15,7 @@ use kinds::{FunctionCompletionKind, FunctionKind, RequestedItems}; use lsp_types::{CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse}; use noirc_errors::{Location, Span}; use noirc_frontend::{ + DataType, Kind, ParsedModule, Type, TypeBinding, ast::{ AsTraitPath, AttributeTarget, BlockExpression, CallExpression, ConstructorExpression, Expression, ExpressionKind, ForLoopStatement, GenericTypeArgs, Ident, IfExpression, @@ -35,17 +36,16 @@ use noirc_frontend::{ node_interner::{FuncId, NodeInterner, ReferenceId, TypeId}, parser::{Item, ItemKind, ParsedSubModule}, token::{MetaAttribute, Token, Tokens}, - DataType, Kind, ParsedModule, Type, TypeBinding, }; use sort_text::underscore_sort_text; use crate::{ - requests::to_lsp_location, trait_impl_method_stub_generator::TraitImplMethodStubGenerator, + LspState, requests::to_lsp_location, + trait_impl_method_stub_generator::TraitImplMethodStubGenerator, use_segment_positions::UseSegmentPositions, utils, visibility::module_def_id_is_visible, - LspState, }; -use super::{process_request, TraitReexport}; +use super::{TraitReexport, process_request}; mod auto_import; mod builtins; @@ -57,7 +57,7 @@ mod tests; pub(crate) fn on_completion_request( state: &mut LspState, params: CompletionParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let uri = params.text_document_position.clone().text_document.uri; let result = process_request(state, params.text_document_position.clone(), |args| { @@ -72,7 +72,7 @@ pub(crate) fn on_completion_request( let file = args.files.get_file(file_id).unwrap(); let source = file.source(); let byte = source.as_bytes().get(byte_index - 1).copied(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let mut finder = NodeFinder::new( args.files, @@ -196,7 +196,7 @@ impl<'a> NodeFinder<'a> { let span = if let UnresolvedTypeData::Named(path, _, _) = &constructor_expression.typ.typ { path.last_ident().span() } else { - constructor_expression.typ.span + constructor_expression.typ.location.span }; let location = Location::new(span, self.file); @@ -241,7 +241,7 @@ impl<'a> NodeFinder<'a> { requested_items: RequestedItems, mut in_the_middle: bool, ) { - if !self.includes_span(path.span) { + if !self.includes_span(path.location.span) { return; } @@ -268,7 +268,10 @@ impl<'a> NodeFinder<'a> { let substring = ident.0.contents[0..offset].to_string(); let ident = Ident::new( substring, - Span::from(ident.span().start()..ident.span().start() + offset as u32), + Location::new( + Span::from(ident.span().start()..ident.span().start() + offset as u32), + ident.location().file, + ), ); idents.push(ident); in_the_middle = true; @@ -605,7 +608,7 @@ impl<'a> NodeFinder<'a> { self.complete_tuple_fields(types, self_prefix); } Type::TypeVariable(var) | Type::NamedGeneric(var, _) => { - if let TypeBinding::Bound(ref typ) = &*var.borrow() { + if let TypeBinding::Bound(typ) = &*var.borrow() { return self.complete_type_fields_and_methods( typ, prefix, @@ -1020,7 +1023,7 @@ impl<'a> NodeFinder<'a> { noir_function: &NoirFunction, ) { // First find the trait - let location = Location::new(noir_trait_impl.r#trait.span, self.file); + let location = noir_trait_impl.r#trait.location; let Some(ReferenceId::Trait(trait_id)) = self.interner.find_referenced(location) else { return; }; @@ -1199,16 +1202,16 @@ impl<'a> NodeFinder<'a> { } } -impl<'a> Visitor for NodeFinder<'a> { +impl Visitor for NodeFinder<'_> { fn visit_item(&mut self, item: &Item) -> bool { if let ItemKind::Import(use_tree, _) = &item.kind { - if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { + if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.location.span) { self.auto_import_line = (lsp_location.range.end.line + 1) as usize; } self.use_segment_positions.add(use_tree); } - self.includes_span(item.span) + self.includes_span(item.location.span) } fn visit_import( @@ -1342,11 +1345,11 @@ impl<'a> Visitor for NodeFinder<'a> { self.type_parameters.clear(); self.collect_type_parameters_in_generics(&type_impl.generics); - for (method, span) in &type_impl.methods { - method.item.accept(*span, self); + for (method, location) in &type_impl.methods { + method.item.accept(location.span, self); // Optimization: stop looking in functions past the completion cursor - if span.end() as usize > self.byte_index { + if location.span.end() as usize > self.byte_index { break; } } @@ -1425,7 +1428,7 @@ impl<'a> Visitor for NodeFinder<'a> { // we don't want to insert arguments, because they are already there (even if // they could be wrong) just because inserting them would lead to broken code. if let ExpressionKind::Variable(path) = &call_expression.func.kind { - if self.includes_span(path.span) { + if self.includes_span(path.location.span) { self.find_in_path_impl(path, RequestedItems::AnyItems, true); return false; } @@ -1439,13 +1442,13 @@ impl<'a> Visitor for NodeFinder<'a> { // as "foo(...)" but if we are at a dot right after "foo" it means it's // the above case and we want to suggest methods of foo's type. let after_dot = self.byte == Some(b'.'); - if after_dot && call_expression.func.span.end() as usize == self.byte_index - 1 { - let location = Location::new(call_expression.func.span, self.file); + if after_dot && call_expression.func.location.span.end() as usize == self.byte_index - 1 { + let location = call_expression.func.location; if let Some(typ) = self.interner.type_at_location(location) { let prefix = ""; let self_prefix = false; self.complete_type_fields_and_methods( - &typ, + typ, prefix, FunctionCompletionKind::Name, self_prefix, @@ -1470,7 +1473,7 @@ impl<'a> Visitor for NodeFinder<'a> { // we don't want to insert arguments, because they are already there (even if // they could be wrong) just because inserting them would lead to broken code. if self.includes_span(method_call_expression.method_name.span()) { - let location = Location::new(method_call_expression.object.span, self.file); + let location = method_call_expression.object.location; if let Some(typ) = self.interner.type_at_location(location) { let prefix = method_call_expression.method_name.to_string(); let offset = @@ -1478,7 +1481,7 @@ impl<'a> Visitor for NodeFinder<'a> { let prefix = prefix[0..offset].to_string(); let self_prefix = false; self.complete_type_fields_and_methods( - &typ, + typ, &prefix, FunctionCompletionKind::Name, self_prefix, @@ -1500,7 +1503,7 @@ impl<'a> Visitor for NodeFinder<'a> { statement.accept(self); // Optimization: stop looking in statements past the completion cursor - if statement.span.end() as usize > self.byte_index { + if statement.location.span.end() as usize > self.byte_index { break; } } @@ -1648,14 +1651,14 @@ impl<'a> Visitor for NodeFinder<'a> { // to complete for `bar`, not for `foo & bar`. if self.completion_items.is_empty() && self.byte == Some(b'.') - && expression.span.end() as usize == self.byte_index - 1 + && expression.location.span.end() as usize == self.byte_index - 1 { - let location = Location::new(expression.span, self.file); + let location = expression.location; if let Some(typ) = self.interner.type_at_location(location) { let prefix = ""; let self_prefix = false; self.complete_type_fields_and_methods( - &typ, + typ, prefix, FunctionCompletionKind::NameAndParameters, self_prefix, @@ -1723,12 +1726,12 @@ impl<'a> Visitor for NodeFinder<'a> { if self.byte_index == ident.span().end() as usize { // Assuming member_access_expression is of the form `foo.bar`, we are right after `bar` - let location = Location::new(member_access_expression.lhs.span, self.file); + let location = member_access_expression.lhs.location; if let Some(typ) = self.interner.type_at_location(location) { let prefix = ident.to_string().to_case(Case::Snake); let self_prefix = false; self.complete_type_fields_and_methods( - &typ, + typ, &prefix, FunctionCompletionKind::NameAndParameters, self_prefix, @@ -1780,7 +1783,7 @@ impl<'a> Visitor for NodeFinder<'a> { } fn visit_unresolved_type(&mut self, unresolved_type: &UnresolvedType) -> bool { - self.includes_span(unresolved_type.span) + self.includes_span(unresolved_type.location.span) } fn visit_named_type( @@ -1833,7 +1836,7 @@ impl<'a> Visitor for NodeFinder<'a> { } fn visit_meta_attribute(&mut self, attribute: &MetaAttribute, target: AttributeTarget) -> bool { - if self.byte_index == attribute.name.span.end() as usize { + if self.byte_index == attribute.name.location.span.end() as usize { self.suggest_builtin_attributes(&attribute.name.to_string(), target); } @@ -1846,7 +1849,7 @@ impl<'a> Visitor for NodeFinder<'a> { let mut last_was_dollar = false; for token in &tokens.0 { - let span = token.to_span(); + let span = token.span(); if span.end() as usize > self.byte_index { break; } @@ -1903,13 +1906,10 @@ fn get_field_type(typ: &Type, name: &str) -> Option { } } Type::Alias(alias_type, generics) => Some(alias_type.borrow().get_type(generics)), - Type::TypeVariable(var) | Type::NamedGeneric(var, _) => { - if let TypeBinding::Bound(ref typ) = &*var.borrow() { - get_field_type(typ, name) - } else { - None - } - } + Type::TypeVariable(var) | Type::NamedGeneric(var, _) => match &*var.borrow() { + TypeBinding::Bound(typ) => get_field_type(typ, name), + _ => None, + }, _ => None, } } @@ -1921,13 +1921,10 @@ fn get_array_element_type(typ: Type) -> Option { let typ = alias_type.borrow().get_type(&generics); get_array_element_type(typ) } - Type::TypeVariable(var) | Type::NamedGeneric(var, _) => { - if let TypeBinding::Bound(typ) = &*var.borrow() { - get_array_element_type(typ.clone()) - } else { - None - } - } + Type::TypeVariable(var) | Type::NamedGeneric(var, _) => match &*var.borrow() { + TypeBinding::Bound(typ) => get_array_element_type(typ.clone()), + _ => None, + }, _ => None, } } diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs index 08d155f333c8..cc80c8d8e010 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs @@ -3,18 +3,18 @@ use noirc_frontend::{ast::ItemVisibility, hir::def_map::ModuleDefId, node_intern use crate::{ modules::{get_ancestor_module_reexport, module_def_id_relative_path}, use_segment_positions::{ - use_completion_item_additional_text_edits, UseCompletionItemAdditionTextEditsRequest, + UseCompletionItemAdditionTextEditsRequest, use_completion_item_additional_text_edits, }, }; use super::{ + NodeFinder, kinds::{FunctionCompletionKind, FunctionKind, RequestedItems}, name_matches, sort_text::auto_import_sort_text, - NodeFinder, }; -impl<'a> NodeFinder<'a> { +impl NodeFinder<'_> { pub(super) fn complete_auto_imports( &mut self, prefix: &str, diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs index 10267d4719bc..ce5b5f35f461 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs @@ -3,15 +3,16 @@ use noirc_frontend::{ast::AttributeTarget, token::Keyword}; use strum::IntoEnumIterator; use super::{ + NodeFinder, completion_items::{ completion_item_with_trigger_parameter_hints_command, simple_completion_item, snippet_completion_item, }, kinds::FunctionCompletionKind, - name_matches, NodeFinder, + name_matches, }; -impl<'a> NodeFinder<'a> { +impl NodeFinder<'_> { pub(super) fn builtin_functions_completion( &mut self, prefix: &str, @@ -150,8 +151,8 @@ impl<'a> NodeFinder<'a> { } } -pub(super) fn builtin_integer_types() -> [&'static str; 8] { - ["i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64"] +pub(super) fn builtin_integer_types() -> [&'static str; 9] { + ["i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", "u128"] } /// If a keyword corresponds to a built-in type, returns that type's name. diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs index cfd11bfe1adf..bc266c03f768 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs @@ -3,29 +3,29 @@ use lsp_types::{ InsertTextFormat, MarkupContent, MarkupKind, }; use noirc_frontend::{ + QuotedType, Type, ast::AttributeTarget, hir::def_map::{ModuleDefId, ModuleId}, hir_def::{function::FuncMeta, stmt::HirPattern}, node_interner::{FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId, TypeId}, - QuotedType, Type, }; use crate::{ modules::{relative_module_full_path, relative_module_id_path}, use_segment_positions::{ - use_completion_item_additional_text_edits, UseCompletionItemAdditionTextEditsRequest, + UseCompletionItemAdditionTextEditsRequest, use_completion_item_additional_text_edits, }, }; use super::{ + FunctionCompletionKind, FunctionKind, NodeFinder, RequestedItems, TraitReexport, sort_text::{ crate_or_module_sort_text, default_sort_text, new_sort_text, operator_sort_text, self_mismatch_sort_text, }, - FunctionCompletionKind, FunctionKind, NodeFinder, RequestedItems, TraitReexport, }; -impl<'a> NodeFinder<'a> { +impl NodeFinder<'_> { pub(super) fn module_def_id_completion_items( &self, module_def_id: ModuleDefId, @@ -44,7 +44,7 @@ impl<'a> NodeFinder<'a> { }, RequestedItems::OnlyTraits => match module_def_id { ModuleDefId::FunctionId(_) | ModuleDefId::GlobalId(_) | ModuleDefId::TypeId(_) => { - return Vec::new() + return Vec::new(); } ModuleDefId::ModuleId(_) | ModuleDefId::TypeAliasId(_) diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs index ba6faada6f4f..042b10b9cbab 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs @@ -1,4 +1,4 @@ -use noirc_frontend::{ast::AttributeTarget, Type}; +use noirc_frontend::{Type, ast::AttributeTarget}; /// When suggest a function as a result of completion, whether to autocomplete its name or its name and parameters. #[derive(Clone, Copy, PartialEq, Eq, Debug)] diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs index cbe1a93391a7..98bb2aae6e6f 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs @@ -67,11 +67,7 @@ mod completion_tests { .await .expect("Could not execute on_completion_request"); - if let Some(CompletionResponse::Array(items)) = response { - items - } else { - vec![] - } + if let Some(CompletionResponse::Array(items)) = response { items } else { vec![] } } fn assert_items_match(mut items: Vec, mut expected: Vec) { @@ -799,9 +795,11 @@ mod completion_tests { "#; let items = get_completions(src).await; - assert!(items - .iter() - .any(|item| item.label == "true" && item.kind == Some(CompletionItemKind::KEYWORD))); + assert!( + items + .iter() + .any(|item| item.label == "true" && item.kind == Some(CompletionItemKind::KEYWORD)) + ); } #[test] @@ -2117,17 +2115,17 @@ fn main() { async fn test_auto_import_from_std() { let src = r#" fn main() { - compute_merkle_roo>|< + zeroe>|< } "#; let items = get_completions(src).await; assert_eq!(items.len(), 1); let item = &items[0]; - assert_eq!(item.label, "compute_merkle_root(…)"); + assert_eq!(item.label, "zeroed()"); assert_eq!( item.label_details.as_ref().unwrap().detail, - Some("(use std::merkle::compute_merkle_root)".to_string()), + Some("(use std::mem::zeroed)".to_string()), ); } @@ -2659,8 +2657,8 @@ fn main() { } #[test] - async fn test_suggests_only_macro_call_if_comptime_function_returns_quoted_and_outside_comptime( - ) { + async fn test_suggests_only_macro_call_if_comptime_function_returns_quoted_and_outside_comptime() + { let src = r#" comptime fn foobar() -> Quoted {} 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 b32b2fc7ad7f..4827d8827afb 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/document_symbol.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/document_symbol.rs @@ -8,12 +8,12 @@ use lsp_types::{ }; use noirc_errors::Span; use noirc_frontend::{ + ParsedModule, ast::{ Expression, FunctionReturnType, Ident, LetStatement, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, TypeImpl, UnresolvedType, UnresolvedTypeData, Visitor, }, parser::ParsedSubModule, - ParsedModule, }; use crate::LspState; @@ -23,7 +23,7 @@ use super::process_request; pub(crate) fn on_document_symbol_request( state: &mut LspState, params: DocumentSymbolParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let Ok(file_path) = params.text_document.uri.to_file_path() else { return future::ready(Ok(None)); }; @@ -37,7 +37,7 @@ pub(crate) fn on_document_symbol_request( args.files.get_file_id(&PathString::from_path(file_path)).map(|file_id| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let mut collector = DocumentSymbolCollector::new(file_id, args.files); let symbols = collector.collect(&parsed_module); @@ -75,7 +75,7 @@ impl<'a> DocumentSymbolCollector<'a> { }; let span = if let Some(typ) = typ { - Span::from(name.span().start()..typ.span.end()) + Span::from(name.span().start()..typ.location.span.end()) } else { name.span() }; @@ -114,11 +114,11 @@ impl<'a> DocumentSymbolCollector<'a> { let mut span = name.span(); // If there's a type span, extend the span to include it - span = Span::from(span.start()..typ.span.end()); + span = Span::from(span.start()..typ.location.span.end()); // If there's a default value, extend the span to include it if let Some(default_value) = default_value { - span = Span::from(span.start()..default_value.span.end()); + span = Span::from(span.start()..default_value.location.span.end()); } let Some(location) = self.to_lsp_location(span) else { @@ -143,7 +143,7 @@ impl<'a> DocumentSymbolCollector<'a> { } } -impl<'a> Visitor for DocumentSymbolCollector<'a> { +impl Visitor for DocumentSymbolCollector<'_> { fn visit_noir_function(&mut self, noir_function: &NoirFunction, span: Span) -> bool { if noir_function.def.name.0.contents.is_empty() { return false; @@ -190,7 +190,7 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { for field in &noir_struct.fields { let field_name = &field.item.name; let typ = &field.item.typ; - let span = Span::from(field_name.span().start()..typ.span.end()); + let span = Span::from(field_name.span().start()..typ.location.span.end()); let Some(field_location) = self.to_lsp_location(span) else { continue; @@ -292,18 +292,18 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { // If there's a return type, extend the span to include it match return_type { - FunctionReturnType::Default(return_type_span) => { - span = Span::from(span.start()..return_type_span.end()); + FunctionReturnType::Default(return_type_location) => { + span = Span::from(span.start()..return_type_location.span.end()); } FunctionReturnType::Ty(typ) => { - span = Span::from(span.start()..typ.span.end()); + span = Span::from(span.start()..typ.location.span.end()); } } // If there's a body, extend the span to include it if let Some(body) = body { if let Some(statement) = body.statements.last() { - span = Span::from(span.start()..statement.span.end()); + span = Span::from(span.start()..statement.location.span.end()); } } @@ -349,14 +349,14 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { return false; }; - let name_span = + let name_location = if let UnresolvedTypeData::Named(trait_name, _, _) = &noir_trait_impl.r#trait.typ { - trait_name.span + trait_name.location } else { - noir_trait_impl.r#trait.span + noir_trait_impl.r#trait.location }; - let Some(name_location) = self.to_lsp_location(name_span) else { + let Some(name_location) = self.to_lsp_location(name_location.span) else { return false; }; @@ -418,15 +418,15 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { return false; } - let Some(name_location) = self.to_lsp_location(type_impl.object_type.span) else { + let Some(name_location) = self.to_lsp_location(type_impl.object_type.location.span) else { return false; }; let old_symbols = std::mem::take(&mut self.symbols); self.symbols = Vec::new(); - for (noir_function, noir_function_span) in &type_impl.methods { - noir_function.item.accept(*noir_function_span, self); + for (noir_function, noir_function_location) in &type_impl.methods { + noir_function.item.accept(noir_function_location.span, self); } let children = std::mem::take(&mut self.symbols); @@ -660,7 +660,7 @@ mod document_symbol_tests { deprecated: None, range: Range { start: Position { line: 15, character: 7 }, - end: Position { line: 15, character: 24 }, + end: Position { line: 15, character: 25 }, }, selection_range: Range { start: Position { line: 15, character: 7 }, diff --git a/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs b/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs index bd0f0afb827c..9df6a25f8cf1 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs @@ -1,7 +1,7 @@ use std::future::{self, Future}; -use crate::types::GotoDeclarationResult; use crate::LspState; +use crate::types::GotoDeclarationResult; use async_lsp::ResponseError; use lsp_types::request::{GotoDeclarationParams, GotoDeclarationResponse}; @@ -11,7 +11,7 @@ use super::{process_request, to_lsp_location}; pub(crate) fn on_goto_declaration_request( state: &mut LspState, params: GotoDeclarationParams, -) -> impl Future> { +) -> impl Future> + use<> { let result = on_goto_definition_inner(state, params); future::ready(result) } diff --git a/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs b/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs index a2443ea165dc..4e015e948610 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs @@ -2,7 +2,7 @@ use std::future::{self, Future}; use crate::attribute_reference_finder::AttributeReferenceFinder; use crate::utils; -use crate::{types::GotoDefinitionResult, LspState}; +use crate::{LspState, types::GotoDefinitionResult}; use async_lsp::ResponseError; use fm::PathString; @@ -14,7 +14,7 @@ use super::{process_request, to_lsp_location}; pub(crate) fn on_goto_definition_request( state: &mut LspState, params: GotoDefinitionParams, -) -> impl Future> { +) -> impl Future> + use<> { let result = on_goto_definition_inner(state, params, false); future::ready(result) } @@ -22,7 +22,7 @@ pub(crate) fn on_goto_definition_request( pub(crate) fn on_goto_type_definition_request( state: &mut LspState, params: GotoTypeDefinitionParams, -) -> impl Future> { +) -> impl Future> + use<> { let result = on_goto_definition_inner(state, params, true); future::ready(result) } @@ -40,7 +40,7 @@ fn on_goto_definition_inner( utils::position_to_byte_index(args.files, file_id, &position).and_then(|byte_index| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let mut finder = AttributeReferenceFinder::new( file_id, diff --git a/noir/noir-repo/tooling/lsp/src/requests/hover.rs b/noir/noir-repo/tooling/lsp/src/requests/hover.rs index cbb7dafd3c51..0b53b56eaf91 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/hover.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/hover.rs @@ -16,7 +16,7 @@ mod from_visitor; pub(crate) fn on_hover_request( state: &mut LspState, params: HoverParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let uri = params.text_document_position_params.text_document.uri.clone(); let position = params.text_document_position_params.position; let result = process_request(state, params.text_document_position_params, |args| { diff --git a/noir/noir-repo/tooling/lsp/src/requests/hover/from_reference.rs b/noir/noir-repo/tooling/lsp/src/requests/hover/from_reference.rs index 7f589b9df70a..3bc3b3bded70 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/hover/from_reference.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/hover/from_reference.rs @@ -1,6 +1,8 @@ use fm::{FileId, FileMap}; use lsp_types::{Hover, HoverContents, MarkupContent, MarkupKind, Position}; use noirc_frontend::{ + DataType, EnumVariant, Generics, Shared, StructField, Type, TypeAlias, TypeBinding, + TypeVariable, ast::{ItemVisibility, Visibility}, hir::def_map::ModuleId, hir_def::{ @@ -13,14 +15,12 @@ use noirc_frontend::{ DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, NodeInterner, ReferenceId, TraitId, TraitImplKind, TypeAliasId, TypeId, }, - DataType, EnumVariant, Generics, Shared, StructField, Type, TypeAlias, TypeBinding, - TypeVariable, }; use crate::{ attribute_reference_finder::AttributeReferenceFinder, modules::module_full_path, - requests::{to_lsp_location, ProcessRequestCallbackArgs}, + requests::{ProcessRequestCallbackArgs, to_lsp_location}, utils, }; @@ -34,7 +34,7 @@ pub(super) fn hover_from_reference( utils::position_to_byte_index(args.files, file_id, &position).and_then(|byte_index| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let mut finder = AttributeReferenceFinder::new( file_id, @@ -318,7 +318,7 @@ fn get_global_value(interner: &NodeInterner, expr: ExprId) -> Option { get_global_array_value(interner, hir_array_literal, true) } HirLiteral::Bool(value) => Some(value.to_string()), - HirLiteral::Integer(field_element, _) => Some(field_element.to_string()), + HirLiteral::Integer(value) => Some(value.to_string()), HirLiteral::Str(string) => Some(format!("{:?}", string)), HirLiteral::FmtStr(..) => None, HirLiteral::Unit => Some("()".to_string()), @@ -338,11 +338,7 @@ fn get_global_array_value( match literal { HirArrayLiteral::Standard(values) => { get_exprs_global_value(interner, &values).map(|value| { - if is_slice { - format!("&[{}]", value) - } else { - format!("[{}]", value) - } + if is_slice { format!("&[{}]", value) } else { format!("[{}]", value) } }) } HirArrayLiteral::Repeated { repeated_element, length } => { @@ -360,11 +356,7 @@ fn get_global_array_value( fn get_exprs_global_value(interner: &NodeInterner, exprs: &[ExprId]) -> Option { let strings: Vec = exprs.iter().filter_map(|value| get_global_value(interner, *value)).collect(); - if strings.len() == exprs.len() { - Some(strings.join(", ")) - } else { - None - } + if strings.len() == exprs.len() { Some(strings.join(", ")) } else { None } } fn format_function(id: FuncId, args: &ProcessRequestCallbackArgs) -> String { @@ -403,11 +395,7 @@ fn format_function(id: FuncId, args: &ProcessRequestCallbackArgs) -> String { .trait_generics .iter() .filter_map(|generic| { - if let Type::NamedGeneric(_, name) = generic { - Some(name) - } else { - None - } + if let Type::NamedGeneric(_, name) = generic { Some(name) } else { None } }) .collect(); @@ -781,7 +769,7 @@ struct TypeLinksGatherer<'a> { links: Vec, } -impl<'a> TypeLinksGatherer<'a> { +impl TypeLinksGatherer<'_> { fn gather_type_links(&mut self, typ: &Type) { match typ { Type::Array(typ, _) => self.gather_type_links(typ), diff --git a/noir/noir-repo/tooling/lsp/src/requests/hover/from_visitor.rs b/noir/noir-repo/tooling/lsp/src/requests/hover/from_visitor.rs index 2099d98a93f6..2b58a31e012f 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/hover/from_visitor.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/hover/from_visitor.rs @@ -1,14 +1,15 @@ use std::str::FromStr; -use acvm::FieldElement; use fm::{FileId, FileMap}; use lsp_types::{Hover, HoverContents, MarkupContent, MarkupKind, Position}; use noirc_errors::{Location, Span}; -use noirc_frontend::{ast::Visitor, node_interner::NodeInterner, parse_program, Type}; +use noirc_frontend::{ + Type, ast::Visitor, node_interner::NodeInterner, parse_program, signed_field::SignedField, +}; use num_bigint::BigInt; use crate::{ - requests::{to_lsp_location, ProcessRequestCallbackArgs}, + requests::{ProcessRequestCallbackArgs, to_lsp_location}, utils, }; @@ -20,7 +21,7 @@ pub(super) fn hover_from_visitor( let file_id = file_id?; let file = args.files.get_file(file_id)?; let source = file.source(); - let (parsed_module, _errors) = parse_program(source); + let (parsed_module, _errors) = parse_program(source, file_id); let byte_index = utils::position_to_byte_index(args.files, file_id, &position)?; let mut finder = HoverFinder::new(args.files, file_id, args.interner, byte_index); @@ -50,8 +51,8 @@ impl<'a> HoverFinder<'a> { } } -impl<'a> Visitor for HoverFinder<'a> { - fn visit_literal_integer(&mut self, value: FieldElement, negative: bool, span: Span) { +impl Visitor for HoverFinder<'_> { + fn visit_literal_integer(&mut self, value: SignedField, span: Span) { if !self.intersects_span(span) { return; } @@ -63,28 +64,31 @@ impl<'a> Visitor for HoverFinder<'a> { return; }; - let value = format_integer(typ, value, negative); + let value = format_integer(typ, value); let contents = HoverContents::Markup(MarkupContent { kind: MarkupKind::Markdown, value }); self.hover = Some(Hover { contents, range }); } } -fn format_integer(typ: Type, value: FieldElement, negative: bool) -> String { - let value_base_10 = value.to_string(); +fn format_integer(typ: &Type, value: SignedField) -> String { + let value_base_10 = value.field.to_string(); // For simplicity we parse the value as a BigInt to convert it to hex // because `FieldElement::to_hex` will include many leading zeros. let value_big_int = BigInt::from_str(&value_base_10).unwrap(); - let negative = if negative { "-" } else { "" }; + let negative = if value.is_negative { "-" } else { "" }; - format!(" {typ}\n---\nvalue of literal: `{negative}{value_base_10} ({negative}0x{value_big_int:02x})`") + format!( + " {typ}\n---\nvalue of literal: `{negative}{value_base_10} ({negative}0x{value_big_int:02x})`" + ) } #[cfg(test)] mod tests { use noirc_frontend::{ - ast::{IntegerBitSize, Signedness}, Type, + ast::{IntegerBitSize, Signedness}, + signed_field::SignedField, }; use super::format_integer; @@ -92,27 +96,24 @@ mod tests { #[test] fn format_integer_zero() { let typ = Type::FieldElement; - let value = 0_u128.into(); - let negative = false; + let value = SignedField::positive(0_u128); let expected = " Field\n---\nvalue of literal: `0 (0x00)`"; - assert_eq!(format_integer(typ, value, negative), expected); + assert_eq!(format_integer(&typ, value), expected); } #[test] fn format_positive_integer() { let typ = Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo); - let value = 123456_u128.into(); - let negative = false; + let value = SignedField::positive(123456_u128); let expected = " u32\n---\nvalue of literal: `123456 (0x1e240)`"; - assert_eq!(format_integer(typ, value, negative), expected); + assert_eq!(format_integer(&typ, value), expected); } #[test] fn format_negative_integer() { let typ = Type::Integer(Signedness::Signed, IntegerBitSize::SixtyFour); - let value = 987654_u128.into(); - let negative = true; + let value = SignedField::new(987654_u128.into(), true); let expected = " i64\n---\nvalue of literal: `-987654 (-0xf1206)`"; - assert_eq!(format_integer(typ, value, negative), expected); + assert_eq!(format_integer(&typ, value), expected); } } 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 e2f793f06da8..8c794d288686 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs @@ -8,7 +8,7 @@ use lsp_types::{ }; use noirc_errors::{Location, Span}; use noirc_frontend::{ - self, + self, Kind, Type, TypeBinding, TypeVariable, ast::{ CallExpression, Expression, ExpressionKind, ForLoopStatement, Ident, Lambda, LetStatement, MethodCallExpression, NoirFunction, NoirTraitImpl, Pattern, Statement, TypeImpl, @@ -17,17 +17,16 @@ use noirc_frontend::{ hir_def::stmt::HirPattern, node_interner::{NodeInterner, ReferenceId}, parser::{Item, ParsedSubModule}, - Kind, Type, TypeBinding, TypeVariable, }; -use crate::{utils, LspState}; +use crate::{LspState, utils}; -use super::{process_request, to_lsp_location, InlayHintsOptions}; +use super::{InlayHintsOptions, process_request, to_lsp_location}; pub(crate) fn on_inlay_hint_request( state: &mut LspState, params: InlayHintParams, -) -> impl Future>, ResponseError>> { +) -> impl Future>, ResponseError>> + use<> { let text_document_position_params = TextDocumentPositionParams { text_document: params.text_document.clone(), position: Position { line: 0, character: 0 }, @@ -40,7 +39,7 @@ pub(crate) fn on_inlay_hint_request( args.files.get_file_id(&path).map(|file_id| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let span = utils::range_to_byte_span(args.files, file_id, ¶ms.range) .map(|range| Span::from(range.start as u32..range.end as u32)); @@ -191,7 +190,7 @@ impl<'a> InlayHintCollector<'a> { for (call_argument, (pattern, _, _)) in arguments.iter().zip(parameters) { let Some(lsp_location) = - to_lsp_location(self.files, self.file_id, call_argument.span) + to_lsp_location(self.files, self.file_id, call_argument.location.span) else { continue; }; @@ -234,7 +233,7 @@ impl<'a> InlayHintCollector<'a> { fn collect_method_call_chain_hints(&mut self, method: &MethodCallExpression) { let Some(object_lsp_location) = - to_lsp_location(self.files, self.file_id, method.object.span) + to_lsp_location(self.files, self.file_id, method.object.location.span) else { return; }; @@ -249,14 +248,14 @@ impl<'a> InlayHintCollector<'a> { return; } - let object_location = Location::new(method.object.span, self.file_id); + let object_location = method.object.location; let Some(typ) = self.interner.type_at_location(object_location) else { return; }; self.push_type_hint( object_lsp_location, - &typ, + typ, false, // not editable false, // don't include colon ); @@ -302,7 +301,7 @@ impl<'a> InlayHintCollector<'a> { } fn intersects_span(&self, other_span: Span) -> bool { - self.span.map_or(true, |span| span.intersects(&other_span)) + self.span.is_none_or(|span| span.intersects(&other_span)) } fn show_closing_brace_hint(&mut self, span: Span, f: F) @@ -320,9 +319,9 @@ impl<'a> InlayHintCollector<'a> { } } -impl<'a> Visitor for InlayHintCollector<'a> { +impl Visitor for InlayHintCollector<'_> { fn visit_item(&mut self, item: &Item) -> bool { - self.intersects_span(item.span) + self.intersects_span(item.location.span) } fn visit_noir_trait_impl(&mut self, noir_trait_impl: &NoirTraitImpl, span: Span) -> bool { @@ -358,7 +357,7 @@ impl<'a> Visitor for InlayHintCollector<'a> { } fn visit_statement(&mut self, statement: &Statement) -> bool { - self.intersects_span(statement.span) + self.intersects_span(statement.location.span) } fn visit_let_statement(&mut self, let_statement: &LetStatement) -> bool { @@ -378,13 +377,13 @@ impl<'a> Visitor for InlayHintCollector<'a> { } fn visit_expression(&mut self, expression: &Expression) -> bool { - self.intersects_span(expression.span) + self.intersects_span(expression.location.span) } fn visit_call_expression(&mut self, call_expression: &CallExpression, _: Span) -> bool { self.collect_call_parameter_names( get_expression_name(&call_expression.func), - call_expression.func.span, + call_expression.func.location.span, &call_expression.arguments, ); @@ -516,18 +515,17 @@ fn push_type_parts(typ: &Type, parts: &mut Vec, files: &File parts.push(string_part("&mut ")); push_type_parts(typ, parts, files); } - Type::TypeVariable(binding) => { - if let TypeBinding::Unbound(_, kind) = &*binding.borrow() { - match kind { - Kind::Any | Kind::Normal => push_type_variable_parts(binding, parts, files), - Kind::Integer => push_type_parts(&Type::default_int_type(), parts, files), - Kind::IntegerOrField => parts.push(string_part("Field")), - Kind::Numeric(ref typ) => push_type_parts(typ, parts, files), - } - } else { + Type::TypeVariable(binding) => match &*binding.borrow() { + TypeBinding::Unbound(_, kind) => match kind { + Kind::Any | Kind::Normal => push_type_variable_parts(binding, parts, files), + Kind::Integer => push_type_parts(&Type::default_int_type(), parts, files), + Kind::IntegerOrField => parts.push(string_part("Field")), + Kind::Numeric(typ) => push_type_parts(typ, parts, files), + }, + _ => { push_type_variable_parts(binding, parts, files); } - } + }, Type::CheckedCast { to, .. } => push_type_parts(to, parts, files), Type::FieldElement @@ -919,8 +917,8 @@ mod inlay_hints_tests { } #[test] - async fn test_do_not_show_parameter_inlay_hints_if_single_param_name_is_suffix_of_function_name( - ) { + async fn test_do_not_show_parameter_inlay_hints_if_single_param_name_is_suffix_of_function_name() + { let inlay_hints = get_inlay_hints(64, 67, parameter_hints()).await; assert!(inlay_hints.is_empty()); } diff --git a/noir/noir-repo/tooling/lsp/src/requests/mod.rs b/noir/noir-repo/tooling/lsp/src/requests/mod.rs index 9bfe47bdaa55..9126ab38e103 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/mod.rs @@ -2,13 +2,13 @@ use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use std::{collections::HashMap, future::Future}; -use crate::{insert_all_files_for_workspace_into_file_manager, parse_diff, PackageCacheData}; +use crate::{PackageCacheData, insert_all_files_for_workspace_into_file_manager, parse_diff}; use crate::{ resolve_workspace_for_source_path, types::{CodeLensOptions, InitializeParams}, }; use async_lsp::{ErrorCode, ResponseError}; -use fm::{codespan_files::Error, FileMap, PathString}; +use fm::{FileMap, PathString, codespan_files::Error}; use lsp_types::{ CodeActionKind, DeclarationCapability, Location, Position, TextDocumentPositionParams, TextDocumentSyncCapability, TextDocumentSyncKind, TypeDefinitionProviderCapability, Url, @@ -25,8 +25,8 @@ use noirc_frontend::{graph::Dependency, node_interner::NodeInterner}; use serde::{Deserialize, Serialize}; use crate::{ - types::{InitializeResult, NargoCapability, NargoTestsOptions, ServerCapabilities}, LspState, + types::{InitializeResult, NargoCapability, NargoTestsOptions, ServerCapabilities}, }; // Handlers @@ -191,7 +191,7 @@ impl Default for LspInitializationOptions { pub(crate) fn on_initialize( state: &mut LspState, params: InitializeParams, -) -> impl Future> { +) -> impl Future> + use<> { state.root_path = params.root_uri.and_then(|root_uri| root_uri.to_file_path().ok()); let initialization_options: LspInitializationOptions = params .initialization_options @@ -293,7 +293,7 @@ pub(crate) fn on_initialize( pub(crate) fn on_formatting( state: &mut LspState, params: lsp_types::DocumentFormattingParams, -) -> impl Future>, ResponseError>> { +) -> impl Future>, ResponseError>> + use<> { std::future::ready(on_formatting_inner(state, params)) } @@ -308,7 +308,7 @@ fn on_formatting_inner( let path = params.text_document.uri.to_string(); if let Some(source) = state.input_files.get(&path) { - let (module, errors) = noirc_frontend::parse_program(source); + let (module, errors) = noirc_frontend::parse_program_with_dummy_file(source); let is_all_warnings = errors.iter().all(ParserError::is_warning); if !is_all_warnings { return Ok(None); @@ -441,7 +441,7 @@ where pub(crate) fn on_shutdown( _state: &mut LspState, _params: (), -) -> impl Future> { +) -> impl Future> + use<> { async { Ok(()) } } @@ -628,11 +628,7 @@ pub(crate) fn find_all_references_in_workspace( }); locations.dedup(); - if locations.is_empty() { - None - } else { - Some(locations) - } + if locations.is_empty() { None } else { Some(locations) } } else { None } @@ -671,7 +667,7 @@ mod initialization { }; use tokio::test; - use crate::{requests::on_initialize, types::ServerCapabilities, LspState}; + use crate::{LspState, requests::on_initialize, types::ServerCapabilities}; #[test] async fn test_on_initialize() { diff --git a/noir/noir-repo/tooling/lsp/src/requests/references.rs b/noir/noir-repo/tooling/lsp/src/requests/references.rs index 3b4ef12b5b74..fbe69c99871d 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/references.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/references.rs @@ -10,7 +10,7 @@ use super::{find_all_references_in_workspace, process_request}; pub(crate) fn on_references_request( state: &mut LspState, params: ReferenceParams, -) -> impl Future>, ResponseError>> { +) -> impl Future>, ResponseError>> + use<> { let include_declaration = params.context.include_declaration; let result = process_request(state, params.text_document_position, |args| { find_all_references_in_workspace( diff --git a/noir/noir-repo/tooling/lsp/src/requests/rename.rs b/noir/noir-repo/tooling/lsp/src/requests/rename.rs index 95dd6b506be2..00e39451caea 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/rename.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/rename.rs @@ -16,7 +16,7 @@ use super::{find_all_references_in_workspace, process_request}; pub(crate) fn on_prepare_rename_request( state: &mut LspState, params: TextDocumentPositionParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let result = process_request(state, params, |args| { let reference_id = args.interner.reference_at_location(args.location); let rename_possible = match reference_id { @@ -33,7 +33,7 @@ pub(crate) fn on_prepare_rename_request( pub(crate) fn on_rename_request( state: &mut LspState, params: RenameParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let result = process_request(state, params.text_document_position, |args| { let rename_changes = find_all_references_in_workspace( args.location, @@ -111,7 +111,10 @@ mod rename_tests { changes.iter().filter(|range| !ranges.contains(range)).collect(); let extra_in_ranges: Vec<_> = ranges.iter().filter(|range| !changes.contains(range)).collect(); - panic!("Rename locations did not match.\nThese renames were not found: {:?}\nThese renames should not have been found: {:?}", extra_in_ranges, extra_in_changes); + panic!( + "Rename locations did not match.\nThese renames were not found: {:?}\nThese renames should not have been found: {:?}", + extra_in_ranges, extra_in_changes + ); } assert_eq!(changes, ranges); } diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs index 4a2609d7ae38..7563dc50c987 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs @@ -7,6 +7,7 @@ use lsp_types::{ }; use noirc_errors::{Location, Span}; use noirc_frontend::{ + ParsedModule, Type, ast::{ CallExpression, ConstrainExpression, ConstrainKind, Expression, FunctionReturnType, MethodCallExpression, Statement, Visitor, @@ -14,10 +15,9 @@ use noirc_frontend::{ hir_def::{function::FuncMeta, stmt::HirPattern}, node_interner::{NodeInterner, ReferenceId}, parser::Item, - ParsedModule, Type, }; -use crate::{utils, LspState}; +use crate::{LspState, utils}; use super::process_request; @@ -26,7 +26,7 @@ mod tests; pub(crate) fn on_signature_help_request( state: &mut LspState, params: SignatureHelpParams, -) -> impl Future, ResponseError>> { +) -> impl Future, ResponseError>> + use<> { let uri = params.text_document_position_params.clone().text_document.uri; let result = process_request(state, params.text_document_position_params.clone(), |args| { @@ -40,7 +40,7 @@ pub(crate) fn on_signature_help_request( .and_then(|byte_index| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); let mut finder = SignatureFinder::new(file_id, byte_index, args.interner); finder.find(&parsed_module) @@ -98,8 +98,8 @@ impl<'a> SignatureFinder<'a> { } // Otherwise, the call must be a reference to an fn type - if let Some(mut typ) = self.interner.type_at_location(location) { - typ = typ.follow_bindings(); + if let Some(typ) = self.interner.type_at_location(location) { + let mut typ = typ.follow_bindings(); if let Type::Forall(_, forall_typ) = typ { typ = *forall_typ; } @@ -312,7 +312,9 @@ impl<'a> SignatureFinder<'a> { fn compute_active_parameter(&self, arguments: &[Expression]) -> Option { let mut active_parameter = None; for (index, arg) in arguments.iter().enumerate() { - if self.includes_span(arg.span) || arg.span.start() as usize >= self.byte_index { + if self.includes_span(arg.location.span) + || arg.location.span.start() as usize >= self.byte_index + { active_parameter = Some(index as u32); break; } @@ -330,24 +332,25 @@ impl<'a> SignatureFinder<'a> { } } -impl<'a> Visitor for SignatureFinder<'a> { +impl Visitor for SignatureFinder<'_> { fn visit_item(&mut self, item: &Item) -> bool { - self.includes_span(item.span) + self.includes_span(item.location.span) } fn visit_statement(&mut self, statement: &Statement) -> bool { - self.includes_span(statement.span) + self.includes_span(statement.location.span) } fn visit_expression(&mut self, expression: &Expression) -> bool { - self.includes_span(expression.span) + self.includes_span(expression.location.span) } fn visit_call_expression(&mut self, call_expression: &CallExpression, span: Span) -> bool { call_expression.accept_children(self); - let arguments_span = Span::from(call_expression.func.span.end() + 1..span.end() - 1); - let span = call_expression.func.span; + let arguments_span = + Span::from(call_expression.func.location.span.end() + 1..span.end() - 1); + let span = call_expression.func.location.span; let name_span = Span::from(span.end() - 1..span.end()); let has_self = false; @@ -391,7 +394,7 @@ impl<'a> Visitor for SignatureFinder<'a> { } let kind_len = constrain_statement.kind.to_string().len() as u32; - let span = constrain_statement.span; + let span = constrain_statement.location.span; let arguments_span = Span::from(span.start() + kind_len + 1..span.end() - 1); if !self.includes_span(arguments_span) { diff --git a/noir/noir-repo/tooling/lsp/src/requests/test_run.rs b/noir/noir-repo/tooling/lsp/src/requests/test_run.rs index e2d8edd46c84..6b7a53e4596e 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/test_run.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/test_run.rs @@ -3,24 +3,23 @@ use std::future::{self, Future}; use crate::insert_all_files_for_workspace_into_file_manager; use async_lsp::{ErrorCode, ResponseError}; use nargo::{ - foreign_calls::DefaultForeignCallBuilder, - ops::{run_test, TestStatus}, PrintOutput, + foreign_calls::DefaultForeignCallBuilder, + ops::{TestStatus, run_test}, }; -use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection}; -use noirc_driver::{check_crate, CompileOptions, NOIR_ARTIFACT_VERSION_STRING}; +use nargo_toml::{PackageSelection, find_package_manifest, resolve_workspace_from_toml}; +use noirc_driver::{CompileOptions, NOIR_ARTIFACT_VERSION_STRING, check_crate}; use noirc_frontend::hir::FunctionNameMatch; use crate::{ - parse_diff, + LspState, parse_diff, types::{NargoTestRunParams, NargoTestRunResult}, - LspState, }; pub(crate) fn on_test_run_request( state: &mut LspState, params: NargoTestRunParams, -) -> impl Future> { +) -> impl Future> + use<> { future::ready(on_test_run_request_inner(state, params)) } @@ -120,7 +119,7 @@ fn on_test_run_request_inner( TestStatus::CompileError(diag) => NargoTestRunResult { id: params.id.clone(), result: "error".to_string(), - message: Some(diag.diagnostic.message), + message: Some(diag.message), }, }; Ok(result) diff --git a/noir/noir-repo/tooling/lsp/src/requests/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/tests.rs index 81910bebedba..234bd971eb5d 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/tests.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/tests.rs @@ -3,19 +3,18 @@ use std::future::{self, Future}; use crate::insert_all_files_for_workspace_into_file_manager; use async_lsp::{ErrorCode, LanguageClient, ResponseError}; use lsp_types::{LogMessageParams, MessageType}; -use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection}; -use noirc_driver::{check_crate, NOIR_ARTIFACT_VERSION_STRING}; +use nargo_toml::{PackageSelection, find_package_manifest, resolve_workspace_from_toml}; +use noirc_driver::{NOIR_ARTIFACT_VERSION_STRING, check_crate}; use crate::{ - get_package_tests_in_crate, parse_diff, + LspState, get_package_tests_in_crate, parse_diff, types::{NargoPackageTests, NargoTestsParams, NargoTestsResult}, - LspState, }; pub(crate) fn on_tests_request( state: &mut LspState, params: NargoTestsParams, -) -> impl Future> { +) -> impl Future> + use<> { future::ready(on_tests_request_inner(state, params)) } @@ -73,9 +72,5 @@ fn on_tests_request_inner( }) .collect(); - if package_tests.is_empty() { - Ok(None) - } else { - Ok(Some(package_tests)) - } + if package_tests.is_empty() { Ok(None) } else { Ok(Some(package_tests)) } } diff --git a/noir/noir-repo/tooling/lsp/src/trait_impl_method_stub_generator.rs b/noir/noir-repo/tooling/lsp/src/trait_impl_method_stub_generator.rs index a7444f819935..3a60a882aeab 100644 --- a/noir/noir-repo/tooling/lsp/src/trait_impl_method_stub_generator.rs +++ b/noir/noir-repo/tooling/lsp/src/trait_impl_method_stub_generator.rs @@ -1,6 +1,7 @@ use std::collections::BTreeMap; use noirc_frontend::{ + Kind, ResolvedGeneric, Type, ast::{NoirTraitImpl, UnresolvedTypeData}, graph::CrateId, hir::{ @@ -9,7 +10,6 @@ use noirc_frontend::{ }, hir_def::{function::FuncMeta, stmt::HirPattern, traits::Trait}, node_interner::{FunctionModifiers, NodeInterner, ReferenceId}, - Kind, ResolvedGeneric, Type, }; use crate::modules::relative_module_id_path; @@ -447,7 +447,7 @@ impl<'a> TraitImplMethodStubGenerator<'a> { Kind::Any | Kind::Normal | Kind::Integer | Kind::IntegerOrField => { self.string.push_str(&generic.name); } - Kind::Numeric(ref typ) => { + Kind::Numeric(typ) => { self.string.push_str("let "); self.string.push_str(&generic.name); self.string.push_str(": "); diff --git a/noir/noir-repo/tooling/lsp/src/types.rs b/noir/noir-repo/tooling/lsp/src/types.rs index b49377787e85..21d274a37764 100644 --- a/noir/noir-repo/tooling/lsp/src/types.rs +++ b/noir/noir-repo/tooling/lsp/src/types.rs @@ -15,7 +15,7 @@ pub(crate) use lsp_types::{ }; pub(crate) mod request { - use lsp_types::{request::Request, InitializeParams}; + use lsp_types::{InitializeParams, request::Request}; use super::{ InitializeResult, NargoTestRunParams, NargoTestRunResult, NargoTestsParams, diff --git a/noir/noir-repo/tooling/lsp/src/use_segment_positions.rs b/noir/noir-repo/tooling/lsp/src/use_segment_positions.rs index 2cd406ee7734..7ff83aecdc2a 100644 --- a/noir/noir-repo/tooling/lsp/src/use_segment_positions.rs +++ b/noir/noir-repo/tooling/lsp/src/use_segment_positions.rs @@ -96,7 +96,7 @@ impl UseSegmentPositions { kind_string, UseSegmentPosition::BeforeSegment { segment_span_until_end: Span::from( - segment.ident.span().start()..use_tree.span.end() - 1, + segment.ident.span().start()..use_tree.location.span.end() - 1, ), }, ); @@ -119,7 +119,7 @@ impl UseSegmentPositions { UseSegmentPosition::BeforeSegment { segment_span_until_end: Span::from( use_tree.prefix.segments[index + 1].ident.span().start() - ..use_tree.span.end() - 1, + ..use_tree.location.span.end() - 1, ), }, ); @@ -163,7 +163,7 @@ impl UseSegmentPositions { prefix, UseSegmentPosition::BeforeSegment { segment_span_until_end: Span::from( - ident.span().start()..use_tree.span.end() - 1, + ident.span().start()..use_tree.location.span.end() - 1, ), }, ); @@ -182,7 +182,7 @@ impl UseSegmentPositions { prefix, UseSegmentPosition::BeforeList { first_entry_span: Span::from( - use_tree.span.end() - 1..use_tree.span.end() - 1, + use_tree.location.span.end() - 1..use_tree.location.span.end() - 1, ), list_is_empty: true, }, diff --git a/noir/noir-repo/tooling/lsp/src/utils.rs b/noir/noir-repo/tooling/lsp/src/utils.rs index 96db1f7bfa23..ca607128bf2e 100644 --- a/noir/noir-repo/tooling/lsp/src/utils.rs +++ b/noir/noir-repo/tooling/lsp/src/utils.rs @@ -51,9 +51,5 @@ pub(crate) fn character_to_line_offset(line: &str, character: u32) -> Option ParsedModule { + ParsedModule { + items: parsed_module.items.into_iter().map(|item| item_with_file(item, file)).collect(), + inner_doc_comments: parsed_module.inner_doc_comments, + } +} + +fn item_with_file(item: Item, file: FileId) -> Item { + Item { + kind: item_kind_with_file(item.kind, file), + location: item.location, + doc_comments: item.doc_comments, + } +} + +fn item_kind_with_file(item_kind: ItemKind, file: FileId) -> ItemKind { + match item_kind { + ItemKind::Import(use_tree, item_visibility) => { + ItemKind::Import(use_tree_with_file(use_tree, file), item_visibility) + } + ItemKind::Function(noir_function) => { + ItemKind::Function(noir_function_with_file(noir_function, file)) + } + ItemKind::Struct(noir_struct) => ItemKind::Struct(noir_struct_with_file(noir_struct, file)), + ItemKind::Enum(noir_enumeration) => { + ItemKind::Enum(noir_enumeration_with_file(noir_enumeration, file)) + } + ItemKind::Trait(noir_trait) => ItemKind::Trait(noir_trait_with_file(noir_trait, file)), + ItemKind::TraitImpl(noir_trait_impl) => { + ItemKind::TraitImpl(noir_trait_impl_with_file(noir_trait_impl, file)) + } + ItemKind::Impl(type_impl) => ItemKind::Impl(type_impl_with_file(type_impl, file)), + ItemKind::TypeAlias(noir_type_alias) => { + ItemKind::TypeAlias(noir_type_alias_with_file(noir_type_alias, file)) + } + ItemKind::Global(let_statement, item_visibility) => { + ItemKind::Global(let_statement_with_file(let_statement, file), item_visibility) + } + ItemKind::ModuleDecl(module_declaration) => { + ItemKind::ModuleDecl(module_declaration_with_file(module_declaration, file)) + } + ItemKind::Submodules(parsed_sub_module) => { + ItemKind::Submodules(parsed_sub_module_with_file(parsed_sub_module, file)) + } + ItemKind::InnerAttribute(secondary_attribute) => { + ItemKind::InnerAttribute(secondary_attribute_with_file(secondary_attribute, file)) + } + } +} + +fn parsed_sub_module_with_file(module: ParsedSubModule, file: FileId) -> ParsedSubModule { + ParsedSubModule { + visibility: module.visibility, + name: ident_with_file(module.name, file), + contents: parsed_module_with_file(module.contents, file), + outer_attributes: secondary_attributes_with_file(module.outer_attributes, file), + is_contract: module.is_contract, + } +} + +fn module_declaration_with_file(module: ModuleDeclaration, file: FileId) -> ModuleDeclaration { + ModuleDeclaration { + visibility: module.visibility, + ident: ident_with_file(module.ident, file), + outer_attributes: secondary_attributes_with_file(module.outer_attributes, file), + has_semicolon: module.has_semicolon, + } +} + +fn let_statement_with_file(let_statement: LetStatement, file: FileId) -> LetStatement { + LetStatement { + pattern: pattern_with_file(let_statement.pattern, file), + r#type: unresolved_type_with_file(let_statement.r#type, file), + expression: expression_with_file(let_statement.expression, file), + attributes: secondary_attributes_with_file(let_statement.attributes, file), + comptime: let_statement.comptime, + is_global_let: let_statement.is_global_let, + } +} + +fn patterns_with_file(patterns: Vec, file: FileId) -> Vec { + vecmap(patterns, |pattern| pattern_with_file(pattern, file)) +} + +fn pattern_with_file(pattern: Pattern, file: FileId) -> Pattern { + match pattern { + Pattern::Identifier(ident) => Pattern::Identifier(ident_with_file(ident, file)), + Pattern::Mutable(pattern, location, synthesized) => Pattern::Mutable( + Box::new(pattern_with_file(*pattern, file)), + location_with_file(location, file), + synthesized, + ), + Pattern::Tuple(patterns, location) => { + Pattern::Tuple(patterns_with_file(patterns, file), location_with_file(location, file)) + } + Pattern::Struct(path, items, location) => Pattern::Struct( + path_with_file(path, file), + vecmap(items, |(ident, pattern)| { + (ident_with_file(ident, file), pattern_with_file(pattern, file)) + }), + location_with_file(location, file), + ), + Pattern::Interned(interned_pattern, location) => { + Pattern::Interned(interned_pattern, location_with_file(location, file)) + } + } +} + +fn noir_type_alias_with_file(noir_type_alias: NoirTypeAlias, file: FileId) -> NoirTypeAlias { + NoirTypeAlias { + name: ident_with_file(noir_type_alias.name, file), + generics: unresolved_generics_with_file(noir_type_alias.generics, file), + typ: unresolved_type_with_file(noir_type_alias.typ, file), + visibility: noir_type_alias.visibility, + location: location_with_file(noir_type_alias.location, file), + } +} + +fn type_impl_with_file(type_impl: TypeImpl, file: FileId) -> TypeImpl { + TypeImpl { + object_type: unresolved_type_with_file(type_impl.object_type, file), + type_location: location_with_file(type_impl.type_location, file), + generics: unresolved_generics_with_file(type_impl.generics, file), + where_clause: unresolved_trait_constraints_with_file(type_impl.where_clause, file), + methods: documented_noir_functions_with_file(type_impl.methods, file), + } +} + +fn documented_noir_functions_with_file( + methods: Vec<(Documented, Location)>, + file: FileId, +) -> Vec<(Documented, Location)> { + vecmap(methods, |(method, location)| { + (documented_noir_function_with_file(method, file), location_with_file(location, file)) + }) +} + +fn documented_noir_function_with_file( + function: Documented, + file: FileId, +) -> Documented { + Documented { + item: noir_function_with_file(function.item, file), + doc_comments: function.doc_comments, + } +} + +fn noir_trait_impl_with_file(noir_trait_impl: NoirTraitImpl, file: FileId) -> NoirTraitImpl { + NoirTraitImpl { + impl_generics: unresolved_generics_with_file(noir_trait_impl.impl_generics, file), + r#trait: unresolved_type_with_file(noir_trait_impl.r#trait, file), + object_type: unresolved_type_with_file(noir_trait_impl.object_type, file), + where_clause: unresolved_trait_constraints_with_file(noir_trait_impl.where_clause, file), + items: documented_trait_impl_items_with_file(noir_trait_impl.items, file), + is_synthetic: noir_trait_impl.is_synthetic, + } +} + +fn documented_trait_impl_items_with_file( + items: Vec>, + file: FileId, +) -> Vec> { + vecmap(items, |item| documented_trait_impl_item_with_file(item, file)) +} + +fn documented_trait_impl_item_with_file( + item: Documented, + file: FileId, +) -> Documented { + Documented { item: trait_impl_item_with_file(item.item, file), doc_comments: item.doc_comments } +} + +fn trait_impl_item_with_file(item: TraitImplItem, file: FileId) -> TraitImplItem { + TraitImplItem { + kind: trait_impl_item_kind_with_file(item.kind, file), + location: location_with_file(item.location, file), + } +} + +fn trait_impl_item_kind_with_file(kind: TraitImplItemKind, file: FileId) -> TraitImplItemKind { + match kind { + TraitImplItemKind::Function(noir_function) => { + TraitImplItemKind::Function(noir_function_with_file(noir_function, file)) + } + TraitImplItemKind::Constant(ident, typ, expression) => TraitImplItemKind::Constant( + ident_with_file(ident, file), + unresolved_type_with_file(typ, file), + expression_with_file(expression, file), + ), + TraitImplItemKind::Type { name, alias } => TraitImplItemKind::Type { + name: ident_with_file(name, file), + alias: unresolved_type_with_file(alias, file), + }, + } +} + +fn noir_trait_with_file(noir_trait: NoirTrait, file: FileId) -> NoirTrait { + NoirTrait { + name: ident_with_file(noir_trait.name, file), + generics: unresolved_generics_with_file(noir_trait.generics, file), + bounds: trait_bounds_with_file(noir_trait.bounds, file), + where_clause: unresolved_trait_constraints_with_file(noir_trait.where_clause, file), + location: location_with_file(noir_trait.location, file), + items: documented_trait_items_with_file(noir_trait.items, file), + attributes: secondary_attributes_with_file(noir_trait.attributes, file), + visibility: noir_trait.visibility, + is_alias: noir_trait.is_alias, + } +} + +fn documented_trait_items_with_file( + items: Vec>, + file: FileId, +) -> Vec> { + vecmap(items, |item| documented_trait_item_with_file(item, file)) +} + +fn documented_trait_item_with_file( + item: Documented, + file: FileId, +) -> Documented { + Documented { item: trait_item_with_file(item.item, file), doc_comments: item.doc_comments } +} + +fn trait_item_with_file(item: TraitItem, file: FileId) -> TraitItem { + match item { + TraitItem::Function { + is_unconstrained, + visibility, + is_comptime, + name, + generics, + parameters, + return_type, + where_clause, + body, + } => TraitItem::Function { + is_unconstrained, + visibility, + is_comptime, + name: ident_with_file(name, file), + generics: unresolved_generics_with_file(generics, file), + parameters: vecmap(parameters, |(ident, typ)| { + (ident_with_file(ident, file), unresolved_type_with_file(typ, file)) + }), + return_type: function_return_type_with_file(return_type, file), + where_clause: unresolved_trait_constraints_with_file(where_clause, file), + body: body.map(|body| block_expression_with_file(body, file)), + }, + TraitItem::Constant { name, typ, default_value } => TraitItem::Constant { + name: ident_with_file(name, file), + typ: unresolved_type_with_file(typ, file), + default_value: default_value.map(|value| expression_with_file(value, file)), + }, + TraitItem::Type { name } => TraitItem::Type { name: ident_with_file(name, file) }, + } +} + +fn function_return_type_with_file(typ: FunctionReturnType, file: FileId) -> FunctionReturnType { + match typ { + FunctionReturnType::Default(location) => { + FunctionReturnType::Default(location_with_file(location, file)) + } + FunctionReturnType::Ty(typ) => FunctionReturnType::Ty(unresolved_type_with_file(typ, file)), + } +} + +fn noir_function_with_file(noir_function: NoirFunction, file: FileId) -> NoirFunction { + NoirFunction { + kind: noir_function.kind, + def: function_definition_with_file(noir_function.def, file), + } +} + +fn noir_struct_with_file(noir_struct: NoirStruct, file: FileId) -> NoirStruct { + NoirStruct { + name: ident_with_file(noir_struct.name, file), + attributes: secondary_attributes_with_file(noir_struct.attributes, file), + visibility: noir_struct.visibility, + generics: unresolved_generics_with_file(noir_struct.generics, file), + fields: documented_struct_fields_with_file(noir_struct.fields, file), + location: location_with_file(noir_struct.location, file), + } +} + +fn documented_struct_fields_with_file( + fields: Vec>, + file: FileId, +) -> Vec> { + vecmap(fields, |field| documented_struct_field_with_file(field, file)) +} + +fn documented_struct_field_with_file( + field: Documented, + file: FileId, +) -> Documented { + Documented { item: struct_field_with_file(field.item, file), doc_comments: field.doc_comments } +} + +fn struct_field_with_file(field: StructField, file: FileId) -> StructField { + StructField { + visibility: field.visibility, + name: ident_with_file(field.name, file), + typ: unresolved_type_with_file(field.typ, file), + } +} + +fn noir_enumeration_with_file(noir_enumeration: NoirEnumeration, file: FileId) -> NoirEnumeration { + NoirEnumeration { + name: ident_with_file(noir_enumeration.name, file), + attributes: secondary_attributes_with_file(noir_enumeration.attributes, file), + visibility: noir_enumeration.visibility, + generics: unresolved_generics_with_file(noir_enumeration.generics, file), + variants: documented_enum_variants_with_file(noir_enumeration.variants, file), + location: location_with_file(noir_enumeration.location, file), + } +} + +fn documented_enum_variants_with_file( + variants: Vec>, + file: FileId, +) -> Vec> { + vecmap(variants, |variant| documented_enum_variant_with_file(variant, file)) +} + +fn documented_enum_variant_with_file( + variant: Documented, + file: FileId, +) -> Documented { + Documented { + item: enum_variant_with_file(variant.item, file), + doc_comments: variant.doc_comments, + } +} + +fn enum_variant_with_file(variant: EnumVariant, file: FileId) -> EnumVariant { + EnumVariant { + name: ident_with_file(variant.name, file), + parameters: variant.parameters.map(|params| unresolved_types_with_file(params, file)), + } +} + +fn function_definition_with_file(func: FunctionDefinition, file: FileId) -> FunctionDefinition { + FunctionDefinition { + name: ident_with_file(func.name, file), + attributes: attributes_with_file(func.attributes, file), + is_unconstrained: func.is_unconstrained, + is_comptime: func.is_comptime, + visibility: func.visibility, + generics: unresolved_generics_with_file(func.generics, file), + parameters: params_with_file(func.parameters, file), + body: block_expression_with_file(func.body, file), + location: location_with_file(func.location, file), + where_clause: unresolved_trait_constraints_with_file(func.where_clause, file), + return_type: function_return_type_with_file(func.return_type, file), + return_visibility: func.return_visibility, + } +} + +fn params_with_file(params: Vec, file: FileId) -> Vec { + vecmap(params, |param| param_with_file(param, file)) +} + +fn param_with_file(param: Param, file: FileId) -> Param { + Param { + visibility: param.visibility, + pattern: pattern_with_file(param.pattern, file), + typ: unresolved_type_with_file(param.typ, file), + location: location_with_file(param.location, file), + } +} + +fn attributes_with_file(attributes: Attributes, file: FileId) -> Attributes { + Attributes { + function: attributes.function, + secondary: secondary_attributes_with_file(attributes.secondary, file), + } +} + +fn use_tree_with_file(use_tree: UseTree, file: FileId) -> UseTree { + UseTree { + prefix: path_with_file(use_tree.prefix, file), + kind: use_tree_kind_with_file(use_tree.kind, file), + location: location_with_file(use_tree.location, file), + } +} + +fn use_tree_kind_with_file(kind: UseTreeKind, file: FileId) -> UseTreeKind { + match kind { + UseTreeKind::Path(ident, alias) => UseTreeKind::Path( + ident_with_file(ident, file), + alias.map(|alias| ident_with_file(alias, file)), + ), + UseTreeKind::List(use_trees) => { + UseTreeKind::List(vecmap(use_trees, |use_tree| use_tree_with_file(use_tree, file))) + } + } +} + +fn path_with_file(path: Path, file: FileId) -> Path { + Path { + segments: vecmap(path.segments, |segment| path_segment_with_file(segment, file)), + kind: path.kind, + kind_location: location_with_file(path.kind_location, file), + location: location_with_file(path.location, file), + } +} + +fn path_segment_with_file(segment: PathSegment, file: FileId) -> PathSegment { + PathSegment { + ident: ident_with_file(segment.ident, file), + generics: segment.generics.map(|generics| unresolved_types_with_file(generics, file)), + location: location_with_file(segment.location, file), + } +} + +fn unresolved_types_with_file(types: Vec, file: FileId) -> Vec { + vecmap(types, |typ| unresolved_type_with_file(typ, file)) +} + +fn unresolved_type_with_file(typ: UnresolvedType, file: FileId) -> UnresolvedType { + UnresolvedType { + typ: unresolved_type_data_with_file(typ.typ, file), + location: location_with_file(typ.location, file), + } +} + +fn unresolved_type_data_with_file(typ: UnresolvedTypeData, file: FileId) -> UnresolvedTypeData { + match typ { + UnresolvedTypeData::Array(length, typ) => UnresolvedTypeData::Array( + unresolved_type_expression_with_file(length, file), + Box::new(unresolved_type_with_file(*typ, file)), + ), + UnresolvedTypeData::Slice(typ) => { + UnresolvedTypeData::Slice(Box::new(unresolved_type_with_file(*typ, file))) + } + UnresolvedTypeData::Expression(expr) => { + UnresolvedTypeData::Expression(unresolved_type_expression_with_file(expr, file)) + } + UnresolvedTypeData::String(expr) => { + UnresolvedTypeData::String(unresolved_type_expression_with_file(expr, file)) + } + UnresolvedTypeData::FormatString(expr, typ) => UnresolvedTypeData::FormatString( + unresolved_type_expression_with_file(expr, file), + Box::new(unresolved_type_with_file(*typ, file)), + ), + UnresolvedTypeData::Parenthesized(typ) => { + UnresolvedTypeData::Parenthesized(Box::new(unresolved_type_with_file(*typ, file))) + } + UnresolvedTypeData::Named(path, generic_type_args, synthesized) => { + UnresolvedTypeData::Named( + path_with_file(path, file), + generic_type_args_with_file(generic_type_args, file), + synthesized, + ) + } + UnresolvedTypeData::TraitAsType(path, generic_type_args) => { + UnresolvedTypeData::TraitAsType( + path_with_file(path, file), + generic_type_args_with_file(generic_type_args, file), + ) + } + UnresolvedTypeData::MutableReference(typ) => { + UnresolvedTypeData::MutableReference(Box::new(unresolved_type_with_file(*typ, file))) + } + UnresolvedTypeData::Tuple(types) => { + UnresolvedTypeData::Tuple(unresolved_types_with_file(types, file)) + } + UnresolvedTypeData::Function(args, ret, env, unconstrained) => { + UnresolvedTypeData::Function( + unresolved_types_with_file(args, file), + Box::new(unresolved_type_with_file(*ret, file)), + Box::new(unresolved_type_with_file(*env, file)), + unconstrained, + ) + } + UnresolvedTypeData::AsTraitPath(as_trait_path) => { + UnresolvedTypeData::AsTraitPath(Box::new(as_trait_path_with_file(*as_trait_path, file))) + } + UnresolvedTypeData::Quoted(..) + | UnresolvedTypeData::Resolved(..) + | UnresolvedTypeData::Interned(..) + | UnresolvedTypeData::Unit + | UnresolvedTypeData::Bool + | UnresolvedTypeData::Integer(..) + | UnresolvedTypeData::FieldElement + | UnresolvedTypeData::Unspecified + | UnresolvedTypeData::Error => typ, + } +} + +fn unresolved_type_expression_with_file( + type_expr: UnresolvedTypeExpression, + file: FileId, +) -> UnresolvedTypeExpression { + match type_expr { + UnresolvedTypeExpression::Variable(path) => { + UnresolvedTypeExpression::Variable(path_with_file(path, file)) + } + UnresolvedTypeExpression::Constant(field_element, location) => { + UnresolvedTypeExpression::Constant(field_element, location_with_file(location, file)) + } + UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, location) => { + UnresolvedTypeExpression::BinaryOperation( + Box::new(unresolved_type_expression_with_file(*lhs, file)), + op, + Box::new(unresolved_type_expression_with_file(*rhs, file)), + location_with_file(location, file), + ) + } + UnresolvedTypeExpression::AsTraitPath(as_trait_path) => { + UnresolvedTypeExpression::AsTraitPath(Box::new(as_trait_path_with_file( + *as_trait_path, + file, + ))) + } + } +} + +fn as_trait_path_with_file(as_trait_path: AsTraitPath, file: FileId) -> AsTraitPath { + AsTraitPath { + typ: unresolved_type_with_file(as_trait_path.typ, file), + trait_path: path_with_file(as_trait_path.trait_path, file), + trait_generics: generic_type_args_with_file(as_trait_path.trait_generics, file), + impl_item: ident_with_file(as_trait_path.impl_item, file), + } +} + +fn generic_type_args_with_file(generics: GenericTypeArgs, file: FileId) -> GenericTypeArgs { + GenericTypeArgs { + ordered_args: unresolved_types_with_file(generics.ordered_args, file), + named_args: vecmap(generics.named_args, |(ident, typ)| { + (ident_with_file(ident, file), unresolved_type_with_file(typ, file)) + }), + kinds: generics.kinds, + } +} + +fn ident_with_file(ident: Ident, file: FileId) -> Ident { + let location = location_with_file(ident.location(), file); + Ident::new(ident.0.contents, location) +} + +fn secondary_attributes_with_file( + attributes: Vec, + file: FileId, +) -> Vec { + vecmap(attributes, |attribute| secondary_attribute_with_file(attribute, file)) +} + +fn secondary_attribute_with_file( + secondary_attribute: SecondaryAttribute, + file: FileId, +) -> SecondaryAttribute { + match secondary_attribute { + SecondaryAttribute::Meta(meta_attribute) => { + SecondaryAttribute::Meta(meta_attribute_with_file(meta_attribute, file)) + } + SecondaryAttribute::Deprecated(_) + | SecondaryAttribute::ContractLibraryMethod + | SecondaryAttribute::Export + | SecondaryAttribute::Field(_) + | SecondaryAttribute::Tag(..) + | SecondaryAttribute::Abi(_) + | SecondaryAttribute::Varargs + | SecondaryAttribute::UseCallersScope + | SecondaryAttribute::Allow(_) => secondary_attribute, + } +} + +fn meta_attribute_with_file(meta_attribute: MetaAttribute, file: FileId) -> MetaAttribute { + MetaAttribute { + name: path_with_file(meta_attribute.name, file), + arguments: expressions_with_file(meta_attribute.arguments, file), + location: location_with_file(meta_attribute.location, file), + } +} + +fn expressions_with_file(expressions: Vec, file: FileId) -> Vec { + vecmap(expressions, |expr| expression_with_file(expr, file)) +} + +fn expression_with_file(expr: Expression, file: FileId) -> Expression { + Expression { + kind: expression_kind_with_file(expr.kind, file), + location: location_with_file(expr.location, file), + } +} + +fn expression_kind_with_file(kind: ExpressionKind, file: FileId) -> ExpressionKind { + match kind { + ExpressionKind::Literal(literal) => { + ExpressionKind::Literal(literal_with_file(literal, file)) + } + ExpressionKind::Block(block_expression) => { + ExpressionKind::Block(block_expression_with_file(block_expression, file)) + } + ExpressionKind::Prefix(prefix_expression) => { + ExpressionKind::Prefix(Box::new(prefix_expression_with_file(*prefix_expression, file))) + } + ExpressionKind::Index(index_expression) => { + ExpressionKind::Index(Box::new(index_expression_with_file(*index_expression, file))) + } + ExpressionKind::Call(call_expression) => { + ExpressionKind::Call(Box::new(call_expression_with_file(*call_expression, file))) + } + ExpressionKind::MethodCall(method_call_expression) => ExpressionKind::MethodCall(Box::new( + method_call_expression_with_file(*method_call_expression, file), + )), + ExpressionKind::Constrain(constrain_expression) => { + ExpressionKind::Constrain(constrain_expression_with_file(constrain_expression, file)) + } + ExpressionKind::Constructor(constructor_expression) => ExpressionKind::Constructor( + Box::new(constructor_expression_with_file(*constructor_expression, file)), + ), + ExpressionKind::MemberAccess(member_access_expression) => ExpressionKind::MemberAccess( + Box::new(member_access_expression_with_file(*member_access_expression, file)), + ), + ExpressionKind::Cast(cast_expression) => { + ExpressionKind::Cast(Box::new(cast_expression_with_file(*cast_expression, file))) + } + ExpressionKind::Infix(infix_expression) => { + ExpressionKind::Infix(Box::new(infix_expression_with_file(*infix_expression, file))) + } + ExpressionKind::If(if_expression) => { + ExpressionKind::If(Box::new(if_expression_with_file(*if_expression, file))) + } + ExpressionKind::Match(match_expression) => { + ExpressionKind::Match(Box::new(match_expression_with_file(*match_expression, file))) + } + ExpressionKind::Variable(path) => ExpressionKind::Variable(path_with_file(path, file)), + ExpressionKind::Tuple(expressions) => { + ExpressionKind::Tuple(expressions_with_file(expressions, file)) + } + ExpressionKind::Lambda(lambda) => { + ExpressionKind::Lambda(Box::new(lambda_with_file(*lambda, file))) + } + ExpressionKind::Parenthesized(expression) => { + ExpressionKind::Parenthesized(Box::new(expression_with_file(*expression, file))) + } + ExpressionKind::Quote(tokens) => ExpressionKind::Quote(tokens_with_file(tokens, file)), + ExpressionKind::Unquote(expression) => { + ExpressionKind::Unquote(Box::new(expression_with_file(*expression, file))) + } + ExpressionKind::Comptime(block_expression, location) => ExpressionKind::Comptime( + block_expression_with_file(block_expression, file), + location_with_file(location, file), + ), + ExpressionKind::Unsafe(UnsafeExpression { block, unsafe_keyword_location }) => { + ExpressionKind::Unsafe(UnsafeExpression { + block: block_expression_with_file(block, file), + unsafe_keyword_location: location_with_file(unsafe_keyword_location, file), + }) + } + ExpressionKind::AsTraitPath(as_trait_path) => { + ExpressionKind::AsTraitPath(as_trait_path_with_file(as_trait_path, file)) + } + ExpressionKind::TypePath(type_path) => { + ExpressionKind::TypePath(type_path_with_file(type_path, file)) + } + ExpressionKind::Resolved(..) + | ExpressionKind::Interned(..) + | ExpressionKind::InternedStatement(..) + | ExpressionKind::Error => kind, + } +} + +fn type_path_with_file(type_path: TypePath, file: FileId) -> TypePath { + TypePath { + typ: unresolved_type_with_file(type_path.typ, file), + item: ident_with_file(type_path.item, file), + turbofish: type_path.turbofish.map(|args| generic_type_args_with_file(args, file)), + } +} + +fn tokens_with_file(tokens: Tokens, file: FileId) -> Tokens { + Tokens(vecmap(tokens.0, |token| { + let location = location_with_file(token.location(), file); + LocatedToken::new(token_with_location(token.into_token(), file), location) + })) +} + +fn token_with_location(token: Token, file: FileId) -> Token { + if let Token::FmtStr(fragments, length) = token { + Token::FmtStr( + vecmap(fragments, |fragment| fmt_str_fragment_with_file(fragment, file)), + length, + ) + } else { + token + } +} + +fn fmt_str_fragment_with_file(fragment: FmtStrFragment, file: FileId) -> FmtStrFragment { + match fragment { + FmtStrFragment::Interpolation(string, location) => { + FmtStrFragment::Interpolation(string, location_with_file(location, file)) + } + FmtStrFragment::String(_) => fragment, + } +} + +fn lambda_with_file(lambda: Lambda, file: FileId) -> Lambda { + Lambda { + parameters: vecmap(lambda.parameters, |(pattern, typ)| { + (pattern_with_file(pattern, file), unresolved_type_with_file(typ, file)) + }), + return_type: unresolved_type_with_file(lambda.return_type, file), + body: expression_with_file(lambda.body, file), + } +} + +fn match_expression_with_file(expr: MatchExpression, file: FileId) -> MatchExpression { + MatchExpression { + expression: expression_with_file(expr.expression, file), + rules: vecmap(expr.rules, |(condition, body)| { + (expression_with_file(condition, file), expression_with_file(body, file)) + }), + } +} + +fn if_expression_with_file(expr: IfExpression, file: FileId) -> IfExpression { + IfExpression { + condition: expression_with_file(expr.condition, file), + consequence: expression_with_file(expr.consequence, file), + alternative: expr.alternative.map(|alternative| expression_with_file(alternative, file)), + } +} + +fn infix_expression_with_file(expr: InfixExpression, file: FileId) -> InfixExpression { + InfixExpression { + lhs: expression_with_file(expr.lhs, file), + rhs: expression_with_file(expr.rhs, file), + operator: expr.operator, + } +} + +fn cast_expression_with_file(expr: CastExpression, file: FileId) -> CastExpression { + CastExpression { + lhs: expression_with_file(expr.lhs, file), + r#type: unresolved_type_with_file(expr.r#type, file), + } +} + +fn member_access_expression_with_file( + expr: MemberAccessExpression, + file: FileId, +) -> MemberAccessExpression { + MemberAccessExpression { + lhs: expression_with_file(expr.lhs, file), + rhs: ident_with_file(expr.rhs, file), + } +} + +fn constructor_expression_with_file( + expr: ConstructorExpression, + file: FileId, +) -> ConstructorExpression { + ConstructorExpression { + typ: unresolved_type_with_file(expr.typ, file), + fields: vecmap(expr.fields, |(ident, expression)| { + (ident_with_file(ident, file), expression_with_file(expression, file)) + }), + } +} + +fn constrain_expression_with_file(expr: ConstrainExpression, file: FileId) -> ConstrainExpression { + ConstrainExpression { + kind: expr.kind, + arguments: expressions_with_file(expr.arguments, file), + location: location_with_file(expr.location, file), + } +} + +fn method_call_expression_with_file( + expr: MethodCallExpression, + file: FileId, +) -> MethodCallExpression { + MethodCallExpression { + object: expression_with_file(expr.object, file), + method_name: ident_with_file(expr.method_name, file), + generics: expr.generics.map(|generics| unresolved_types_with_file(generics, file)), + arguments: expressions_with_file(expr.arguments, file), + is_macro_call: expr.is_macro_call, + } +} + +fn call_expression_with_file(expr: CallExpression, file: FileId) -> CallExpression { + CallExpression { + func: Box::new(expression_with_file(*expr.func, file)), + arguments: expressions_with_file(expr.arguments, file), + is_macro_call: expr.is_macro_call, + } +} + +fn index_expression_with_file(expr: IndexExpression, file: FileId) -> IndexExpression { + IndexExpression { + collection: expression_with_file(expr.collection, file), + index: expression_with_file(expr.index, file), + } +} + +fn prefix_expression_with_file(expr: PrefixExpression, file: FileId) -> PrefixExpression { + PrefixExpression { operator: expr.operator, rhs: expression_with_file(expr.rhs, file) } +} + +fn literal_with_file(literal: Literal, file: FileId) -> Literal { + match literal { + Literal::Array(array_literal) => { + Literal::Array(array_literal_with_file(array_literal, file)) + } + Literal::Slice(array_literal) => { + Literal::Slice(array_literal_with_file(array_literal, file)) + } + Literal::FmtStr(fragments, length) => Literal::FmtStr( + vecmap(fragments, |fragment| fmt_str_fragment_with_file(fragment, file)), + length, + ), + Literal::Bool(..) + | Literal::Integer(..) + | Literal::Str(..) + | Literal::RawStr(..) + | Literal::Unit => literal, + } +} + +fn array_literal_with_file(array_literal: ArrayLiteral, file: FileId) -> ArrayLiteral { + match array_literal { + ArrayLiteral::Standard(expressions) => { + ArrayLiteral::Standard(expressions_with_file(expressions, file)) + } + ArrayLiteral::Repeated { repeated_element, length } => ArrayLiteral::Repeated { + repeated_element: Box::new(expression_with_file(*repeated_element, file)), + length: Box::new(expression_with_file(*length, file)), + }, + } +} + +fn block_expression_with_file(block: BlockExpression, file: FileId) -> BlockExpression { + BlockExpression { statements: statements_with_file(block.statements, file) } +} + +fn statements_with_file(statements: Vec, file: FileId) -> Vec { + vecmap(statements, |statement| statement_with_file(statement, file)) +} + +fn statement_with_file(statement: Statement, file: FileId) -> Statement { + Statement { + kind: statement_kind_with_file(statement.kind, file), + location: location_with_file(statement.location, file), + } +} + +fn statement_kind_with_file(kind: StatementKind, file: FileId) -> StatementKind { + match kind { + StatementKind::Let(let_statement) => { + StatementKind::Let(let_statement_with_file(let_statement, file)) + } + StatementKind::Expression(expression) => { + StatementKind::Expression(expression_with_file(expression, file)) + } + StatementKind::Assign(assign_statement) => { + StatementKind::Assign(assign_statement_with_file(assign_statement, file)) + } + StatementKind::For(for_loop_statement) => { + StatementKind::For(for_loop_statement_with_file(for_loop_statement, file)) + } + StatementKind::While(while_) => StatementKind::While(WhileStatement { + condition: expression_with_file(while_.condition, file), + body: expression_with_file(while_.body, file), + while_keyword_location: while_.while_keyword_location, + }), + StatementKind::Loop(expression, location) => StatementKind::Loop( + expression_with_file(expression, file), + location_with_file(location, file), + ), + StatementKind::Comptime(statement) => { + StatementKind::Comptime(Box::new(statement_with_file(*statement, file))) + } + StatementKind::Semi(expression) => { + StatementKind::Semi(expression_with_file(expression, file)) + } + StatementKind::Interned(..) + | StatementKind::Break + | StatementKind::Continue + | StatementKind::Error => kind, + } +} + +fn for_loop_statement_with_file(for_loop: ForLoopStatement, file: FileId) -> ForLoopStatement { + ForLoopStatement { + identifier: ident_with_file(for_loop.identifier, file), + range: for_range_with_file(for_loop.range, file), + block: expression_with_file(for_loop.block, file), + location: location_with_file(for_loop.location, file), + } +} + +fn for_range_with_file(range: ForRange, file: FileId) -> ForRange { + match range { + ForRange::Range(for_bounds) => ForRange::Range(for_bounds_with_file(for_bounds, file)), + ForRange::Array(expression) => ForRange::Array(expression_with_file(expression, file)), + } +} + +fn for_bounds_with_file(for_bounds: ForBounds, file: FileId) -> ForBounds { + ForBounds { + start: expression_with_file(for_bounds.start, file), + end: expression_with_file(for_bounds.end, file), + inclusive: for_bounds.inclusive, + } +} + +fn assign_statement_with_file(assign: AssignStatement, file: FileId) -> AssignStatement { + AssignStatement { + lvalue: lvalue_with_file(assign.lvalue, file), + expression: expression_with_file(assign.expression, file), + } +} + +fn lvalue_with_file(lvalue: LValue, file: FileId) -> LValue { + match lvalue { + LValue::Ident(ident) => LValue::Ident(ident_with_file(ident, file)), + LValue::MemberAccess { object, field_name, location } => LValue::MemberAccess { + object: Box::new(lvalue_with_file(*object, file)), + field_name: ident_with_file(field_name, file), + location: location_with_file(location, file), + }, + LValue::Index { array, index, location } => LValue::Index { + array: Box::new(lvalue_with_file(*array, file)), + index: expression_with_file(index, file), + location: location_with_file(location, file), + }, + LValue::Dereference(lvalue, location) => LValue::Dereference( + Box::new(lvalue_with_file(*lvalue, file)), + location_with_file(location, file), + ), + LValue::Interned(interned_expression_kind, location) => { + LValue::Interned(interned_expression_kind, location_with_file(location, file)) + } + } +} + +fn unresolved_generics_with_file( + generics: Vec, + file: FileId, +) -> Vec { + vecmap(generics, |generic| unresolved_generic_with_file(generic, file)) +} + +fn unresolved_generic_with_file(generic: UnresolvedGeneric, file: FileId) -> UnresolvedGeneric { + match generic { + UnresolvedGeneric::Variable(ident) => { + UnresolvedGeneric::Variable(ident_with_file(ident, file)) + } + UnresolvedGeneric::Numeric { ident, typ } => UnresolvedGeneric::Numeric { + ident: ident_with_file(ident, file), + typ: unresolved_type_with_file(typ, file), + }, + UnresolvedGeneric::Resolved(quoted_type_id, location) => { + UnresolvedGeneric::Resolved(quoted_type_id, location_with_file(location, file)) + } + } +} + +fn unresolved_trait_constraints_with_file( + constraints: Vec, + file: FileId, +) -> Vec { + vecmap(constraints, |constraint| unresolved_trait_constraint_with_file(constraint, file)) +} + +fn unresolved_trait_constraint_with_file( + constraint: UnresolvedTraitConstraint, + file: FileId, +) -> UnresolvedTraitConstraint { + UnresolvedTraitConstraint { + typ: unresolved_type_with_file(constraint.typ, file), + trait_bound: trait_bound_with_file(constraint.trait_bound, file), + } +} + +fn trait_bounds_with_file(trait_bounds: Vec, file: FileId) -> Vec { + vecmap(trait_bounds, |bound| trait_bound_with_file(bound, file)) +} + +fn trait_bound_with_file(trait_bound: TraitBound, file: FileId) -> TraitBound { + TraitBound { + trait_path: path_with_file(trait_bound.trait_path, file), + trait_id: trait_bound.trait_id, + trait_generics: generic_type_args_with_file(trait_bound.trait_generics, file), + } +} + +fn location_with_file(location: Location, file: FileId) -> Location { + Location { file, ..location } +} diff --git a/noir/noir-repo/tooling/nargo/src/errors.rs b/noir/noir-repo/tooling/nargo/src/errors.rs index 00c411bf7e43..f1743af79ca6 100644 --- a/noir/noir-repo/tooling/nargo/src/errors.rs +++ b/noir/noir-repo/tooling/nargo/src/errors.rs @@ -1,17 +1,15 @@ use std::collections::BTreeMap; use acvm::{ + AcirField, FieldElement, acir::circuit::{ - brillig::BrilligFunctionId, ErrorSelector, OpcodeLocation, RawAssertionPayload, - ResolvedAssertionPayload, ResolvedOpcodeLocation, + ErrorSelector, OpcodeLocation, RawAssertionPayload, ResolvedAssertionPayload, + ResolvedOpcodeLocation, brillig::BrilligFunctionId, }, pwg::{ErrorLocation, OpcodeResolutionError}, - AcirField, FieldElement, -}; -use noirc_abi::{display_abi_error, Abi, AbiErrorType}; -use noirc_errors::{ - debug_info::DebugInfo, reporter::ReportedErrors, CustomDiagnostic, FileDiagnostic, }; +use noirc_abi::{Abi, AbiErrorType, display_abi_error}; +use noirc_errors::{CustomDiagnostic, debug_info::DebugInfo, reporter::ReportedErrors}; pub use noirc_errors::Location; @@ -230,7 +228,7 @@ pub fn try_to_diagnose_runtime_error( nargo_err: &NargoError, abi: &Abi, debug: &[DebugInfo], -) -> Option { +) -> Option { let source_locations = match nargo_err { NargoError::ExecutionError(execution_error) => { extract_locations_from_error(execution_error, debug)? @@ -241,6 +239,6 @@ pub fn try_to_diagnose_runtime_error( // of the call stack (the last item in the Vec). let location = *source_locations.last()?; let message = extract_message_from_error(&abi.error_types, nargo_err); - let error = CustomDiagnostic::simple_error(message, String::new(), location.span); - Some(error.with_call_stack(source_locations).in_file(location.file)) + let error = CustomDiagnostic::simple_error(message, String::new(), location); + Some(error.with_call_stack(source_locations)) } diff --git a/noir/noir-repo/tooling/nargo/src/foreign_calls/default.rs b/noir/noir-repo/tooling/nargo/src/foreign_calls/default.rs index 19928e895630..c6053a1175e1 100644 --- a/noir/noir-repo/tooling/nargo/src/foreign_calls/default.rs +++ b/noir/noir-repo/tooling/nargo/src/foreign_calls/default.rs @@ -4,10 +4,10 @@ use serde::{Deserialize, Serialize}; use crate::PrintOutput; use super::{ + ForeignCallExecutor, layers::{self, Either, Layer, Layering}, mocker::{DisabledMockForeignCallExecutor, MockForeignCallExecutor}, print::PrintForeignCallExecutor, - ForeignCallExecutor, }; #[cfg(feature = "rpc")] @@ -29,7 +29,7 @@ pub struct DefaultForeignCallBuilder<'a> { pub package_name: Option, } -impl<'a> Default for DefaultForeignCallBuilder<'a> { +impl Default for DefaultForeignCallBuilder<'_> { fn default() -> Self { Self { output: PrintOutput::default(), @@ -80,7 +80,7 @@ impl<'a> DefaultForeignCallBuilder<'a> { use rand::Rng; base.add_layer(self.resolver_url.map(|resolver_url| { - let id = rand::thread_rng().gen(); + let id = rand::thread_rng().r#gen(); RPCForeignCallExecutor::new( &resolver_url, id, @@ -136,7 +136,7 @@ impl DefaultForeignCallExecutor { resolver_url: Option<&str>, root_path: Option, package_name: Option, - ) -> impl ForeignCallExecutor + 'a + ) -> impl ForeignCallExecutor + 'a + use<'a, F> where F: AcirField + Serialize + for<'de> Deserialize<'de> + 'a, { diff --git a/noir/noir-repo/tooling/nargo/src/foreign_calls/layers.rs b/noir/noir-repo/tooling/nargo/src/foreign_calls/layers.rs index 83145cacb441..4b1d17bdf65c 100644 --- a/noir/noir-repo/tooling/nargo/src/foreign_calls/layers.rs +++ b/noir/noir-repo/tooling/nargo/src/foreign_calls/layers.rs @@ -1,4 +1,4 @@ -use acvm::{acir::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo, AcirField}; +use acvm::{AcirField, acir::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo}; use super::{ForeignCallError, ForeignCallExecutor}; diff --git a/noir/noir-repo/tooling/nargo/src/foreign_calls/mocker.rs b/noir/noir-repo/tooling/nargo/src/foreign_calls/mocker.rs index 41fac610052b..3d432b18ff9e 100644 --- a/noir/noir-repo/tooling/nargo/src/foreign_calls/mocker.rs +++ b/noir/noir-repo/tooling/nargo/src/foreign_calls/mocker.rs @@ -1,7 +1,7 @@ use acvm::{ + AcirField, acir::brillig::{ForeignCallParam, ForeignCallResult}, pwg::ForeignCallWaitInfo, - AcirField, }; use noirc_abi::decode_string_value; diff --git a/noir/noir-repo/tooling/nargo/src/foreign_calls/mod.rs b/noir/noir-repo/tooling/nargo/src/foreign_calls/mod.rs index f17a97cecd49..8d33b07dd089 100644 --- a/noir/noir-repo/tooling/nargo/src/foreign_calls/mod.rs +++ b/noir/noir-repo/tooling/nargo/src/foreign_calls/mod.rs @@ -4,6 +4,7 @@ use thiserror::Error; pub mod layers; pub mod mocker; pub mod print; +pub mod transcript; pub mod default; #[cfg(feature = "rpc")] @@ -83,4 +84,7 @@ pub enum ForeignCallError { #[error("Assert message resolved after an unsatisfied constrain. {0}")] ResolvedAssertMessage(String), + + #[error("Failed to replay oracle transcript: {0}")] + TranscriptError(String), } diff --git a/noir/noir-repo/tooling/nargo/src/foreign_calls/print.rs b/noir/noir-repo/tooling/nargo/src/foreign_calls/print.rs index fb5621da9424..96db9232d0d4 100644 --- a/noir/noir-repo/tooling/nargo/src/foreign_calls/print.rs +++ b/noir/noir-repo/tooling/nargo/src/foreign_calls/print.rs @@ -1,7 +1,7 @@ use acvm::{ + AcirField, acir::brillig::{ForeignCallParam, ForeignCallResult}, pwg::ForeignCallWaitInfo, - AcirField, }; use noirc_abi::{decode_printable_value, decode_string_value}; use noirc_printable_type::{PrintableType, PrintableValueDisplay}; @@ -16,6 +16,7 @@ pub enum PrintOutput<'a> { String(&'a mut String), } +/// Handle `println` calls. #[derive(Debug, Default)] pub struct PrintForeignCallExecutor<'a> { output: PrintOutput<'a>, diff --git a/noir/noir-repo/tooling/nargo/src/foreign_calls/rpc.rs b/noir/noir-repo/tooling/nargo/src/foreign_calls/rpc.rs index 89a748b6c455..6e4858128856 100644 --- a/noir/noir-repo/tooling/nargo/src/foreign_calls/rpc.rs +++ b/noir/noir-repo/tooling/nargo/src/foreign_calls/rpc.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use acvm::{acir::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo, AcirField}; +use acvm::{AcirField, acir::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo}; use jsonrpsee::{ core::client::ClientT, http_client::{HttpClient, HttpClientBuilder}, @@ -115,8 +115,8 @@ where #[cfg(test)] mod tests { use acvm::{ - acir::brillig::ForeignCallParam, brillig_vm::brillig::ForeignCallResult, - pwg::ForeignCallWaitInfo, FieldElement, + FieldElement, acir::brillig::ForeignCallParam, brillig_vm::brillig::ForeignCallResult, + pwg::ForeignCallWaitInfo, }; use jsonrpsee::proc_macros::rpc; use jsonrpsee::server::Server; diff --git a/noir/noir-repo/tooling/nargo/src/foreign_calls/transcript.rs b/noir/noir-repo/tooling/nargo/src/foreign_calls/transcript.rs new file mode 100644 index 000000000000..155835c3fc17 --- /dev/null +++ b/noir/noir-repo/tooling/nargo/src/foreign_calls/transcript.rs @@ -0,0 +1,156 @@ +use std::{ + collections::VecDeque, + path::{Path, PathBuf}, +}; + +use acvm::{AcirField, acir::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo}; +use serde::{Deserialize, Serialize}; +use serde_json::json; + +use crate::PrintOutput; + +use super::{ForeignCallError, ForeignCallExecutor}; + +#[derive(Debug, thiserror::Error)] +pub enum TranscriptError { + #[error(transparent)] + IoError(#[from] std::io::Error), + + #[error(transparent)] + DeserializationError(#[from] serde_json::Error), +} + +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +struct LogItem { + call: ForeignCallWaitInfo, + result: ForeignCallResult, +} + +/// Log foreign calls during the execution, for testing purposes. +pub struct LoggingForeignCallExecutor<'a, E> { + pub executor: E, + pub output: PrintOutput<'a>, +} + +impl<'a, E> LoggingForeignCallExecutor<'a, E> { + pub fn new(executor: E, output: PrintOutput<'a>) -> Self { + Self { executor, output } + } +} + +impl ForeignCallExecutor for LoggingForeignCallExecutor<'_, E> +where + F: AcirField + Serialize, + E: ForeignCallExecutor, +{ + fn execute( + &mut self, + foreign_call: &ForeignCallWaitInfo, + ) -> Result, ForeignCallError> { + let result = self.executor.execute(foreign_call); + if let Ok(ref result) = result { + let log_item = || { + // Match the JSON structure of `LogItem` without having to clone. + let json = json!({"call": foreign_call, "result": result}); + serde_json::to_string(&json).expect("failed to serialize foreign call") + }; + match &mut self.output { + PrintOutput::None => (), + PrintOutput::Stdout => println!("{}", log_item()), + PrintOutput::String(s) => { + s.push_str(&log_item()); + s.push('\n'); + } + } + } + result + } +} + +/// Log foreign calls to stdout as soon as they are made, or buffer them and write to a file at the end. +pub enum ForeignCallLog { + None, + Stdout, + File(PathBuf, String), +} + +impl ForeignCallLog { + /// Instantiate based on an env var. + pub fn from_env(key: &str) -> Self { + match std::env::var(key) { + Err(_) => Self::None, + Ok(s) if s == "stdout" => Self::Stdout, + Ok(s) => Self::File(PathBuf::from(s), String::new()), + } + } + + /// Create a [PrintOutput] based on the log setting. + pub fn print_output(&mut self) -> PrintOutput { + match self { + ForeignCallLog::None => PrintOutput::None, + ForeignCallLog::Stdout => PrintOutput::Stdout, + ForeignCallLog::File(_, s) => PrintOutput::String(s), + } + } + + /// Any final logging. + pub fn write_log(self) -> std::io::Result<()> { + if let ForeignCallLog::File(path, contents) = self { + std::fs::write(path, contents)?; + } + Ok(()) + } +} + +/// Replay an oracle transcript which was logged with [LoggingForeignCallExecutor]. +/// +/// This is expected to be the last executor in the stack, e.g. prints can be handled above it. +pub struct ReplayForeignCallExecutor { + transcript: VecDeque>, +} + +impl Deserialize<'a>> ReplayForeignCallExecutor { + pub fn from_file(path: &Path) -> Result { + let contents = std::fs::read_to_string(path)?; + + let transcript = + contents.lines().map(serde_json::from_str).collect::, _>>()?; + + Ok(Self { transcript }) + } +} + +impl ForeignCallExecutor for ReplayForeignCallExecutor +where + F: AcirField, +{ + fn execute( + &mut self, + foreign_call: &ForeignCallWaitInfo, + ) -> Result, ForeignCallError> { + let error = |msg| Err(ForeignCallError::TranscriptError(msg)); + // Verify without popping. + if let Some(next) = self.transcript.front() { + if next.call.function != foreign_call.function { + let msg = format!( + "unexpected foreign call; expected '{}', got '{}'", + next.call.function, foreign_call.function + ); + return error(msg); + } + if next.call.inputs != foreign_call.inputs { + let msg = format!( + "unexpected foreign call inputs to '{}'; expected {:?}, got {:?}", + next.call.function, next.call.inputs, foreign_call.inputs + ); + return error(msg); + } + } + // Consume the next call. + if let Some(next) = self.transcript.pop_front() { + Ok(next.result) + } else { + error("unexpected foreign call; no more calls in transcript".to_string()) + } + } +} diff --git a/noir/noir-repo/tooling/nargo/src/lib.rs b/noir/noir-repo/tooling/nargo/src/lib.rs index 30f25356e413..11615b3de729 100644 --- a/noir/noir-repo/tooling/nargo/src/lib.rs +++ b/noir/noir-repo/tooling/nargo/src/lib.rs @@ -22,11 +22,11 @@ use std::{ path::PathBuf, }; -use fm::{FileManager, FILE_EXTENSION}; +use fm::{FILE_EXTENSION, FileManager}; use noirc_driver::{add_dep, prepare_crate, prepare_dependency}; use noirc_frontend::{ graph::{CrateId, CrateName}, - hir::{def_map::parse_file, Context, ParsedFiles}, + hir::{Context, ParsedFiles, def_map::parse_file}, }; use package::{Dependency, Package}; use rayon::prelude::*; @@ -106,7 +106,7 @@ fn insert_all_files_for_package_into_file_manager( continue; } - if !entry.path().extension().map_or(false, |ext| ext == FILE_EXTENSION) { + if entry.path().extension().is_none_or(|ext| ext != FILE_EXTENSION) { continue; }; diff --git a/noir/noir-repo/tooling/nargo/src/ops/check.rs b/noir/noir-repo/tooling/nargo/src/ops/check.rs index f22def8bd91b..129aa1bd7885 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/check.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/check.rs @@ -1,6 +1,6 @@ use acvm::compiler::CircuitSimulator; use noirc_driver::{CompiledProgram, ErrorsAndWarnings}; -use noirc_errors::{CustomDiagnostic, FileDiagnostic}; +use noirc_errors::CustomDiagnostic; /// Run each function through a circuit simulator to check that they are solvable. #[tracing::instrument(level = "trace", skip_all)] @@ -8,13 +8,10 @@ pub fn check_program(compiled_program: &CompiledProgram) -> Result<(), ErrorsAnd for (i, circuit) in compiled_program.program.functions.iter().enumerate() { let mut simulator = CircuitSimulator::default(); if !simulator.check_circuit(circuit) { - let diag = FileDiagnostic { - file_id: fm::FileId::dummy(), - diagnostic: CustomDiagnostic::from_message(&format!( - "Circuit \"{}\" is not solvable", - compiled_program.names[i] - )), - }; + let diag = CustomDiagnostic::from_message( + &format!("Circuit \"{}\" is not solvable", compiled_program.names[i]), + fm::FileId::dummy(), + ); return Err(vec![diag]); } } diff --git a/noir/noir-repo/tooling/nargo/src/ops/compile.rs b/noir/noir-repo/tooling/nargo/src/ops/compile.rs index 8c44bf352435..7a3ddbfe3bc6 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/compile.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/compile.rs @@ -1,6 +1,6 @@ use fm::FileManager; use noirc_driver::{ - link_to_debug_crate, CompilationResult, CompileOptions, CompiledContract, CompiledProgram, + CompilationResult, CompileOptions, CompiledContract, CompiledProgram, link_to_debug_crate, }; use noirc_frontend::debug::DebugInstrumenter; use noirc_frontend::hir::ParsedFiles; @@ -120,11 +120,7 @@ pub fn collect_errors(results: Vec>) -> CompilationResul } } - if errors.is_empty() { - Ok((artifacts, warnings)) - } else { - Err(errors) - } + if errors.is_empty() { Ok((artifacts, warnings)) } else { Err(errors) } } pub fn report_errors( diff --git a/noir/noir-repo/tooling/nargo/src/ops/execute.rs b/noir/noir-repo/tooling/nargo/src/ops/execute.rs index 57116ec2efd9..699c54e3f528 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/execute.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/execute.rs @@ -4,14 +4,14 @@ use acvm::acir::circuit::{ }; use acvm::acir::native_types::WitnessStack; use acvm::pwg::{ - ACVMStatus, ErrorLocation, OpcodeNotSolvable, OpcodeResolutionError, ProfilingSamples, ACVM, + ACVM, ACVMStatus, ErrorLocation, OpcodeNotSolvable, OpcodeResolutionError, ProfilingSamples, }; -use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; use acvm::{AcirField, BlackBoxFunctionSolver}; +use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; +use crate::NargoError; use crate::errors::ExecutionError; use crate::foreign_calls::ForeignCallExecutor; -use crate::NargoError; struct ProgramExecutor<'a, F, B: BlackBoxFunctionSolver, E: ForeignCallExecutor> { functions: &'a [Circuit], diff --git a/noir/noir-repo/tooling/nargo/src/ops/mod.rs b/noir/noir-repo/tooling/nargo/src/ops/mod.rs index 7a52a829be36..7ce34b1acd25 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/mod.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/mod.rs @@ -7,7 +7,7 @@ pub use self::optimize::{optimize_contract, optimize_program}; pub use self::transform::{transform_contract, transform_program}; pub use self::execute::{execute_program, execute_program_with_profiling}; -pub use self::test::{run_test, TestStatus}; +pub use self::test::{TestStatus, run_test}; mod check; mod compile; diff --git a/noir/noir-repo/tooling/nargo/src/ops/optimize.rs b/noir/noir-repo/tooling/nargo/src/ops/optimize.rs index 07adfb57df4f..906309e36f8a 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/optimize.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/optimize.rs @@ -1,4 +1,4 @@ -use acvm::{acir::circuit::Program, FieldElement}; +use acvm::{FieldElement, acir::circuit::Program}; use iter_extended::vecmap; use noirc_driver::{CompiledContract, CompiledProgram}; use noirc_errors::debug_info::DebugInfo; diff --git a/noir/noir-repo/tooling/nargo/src/ops/test.rs b/noir/noir-repo/tooling/nargo/src/ops/test.rs index a2f94cd61eba..c4adaa5cfaab 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/test.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/test.rs @@ -1,20 +1,24 @@ use acvm::{ + AcirField, BlackBoxFunctionSolver, FieldElement, acir::{ brillig::ForeignCallResult, native_types::{WitnessMap, WitnessStack}, }, pwg::ForeignCallWaitInfo, - AcirField, BlackBoxFunctionSolver, FieldElement, }; use noirc_abi::Abi; -use noirc_driver::{compile_no_check, CompileError, CompileOptions, DEFAULT_EXPRESSION_WIDTH}; -use noirc_errors::{debug_info::DebugInfo, FileDiagnostic}; -use noirc_frontend::hir::{def_map::TestFunction, Context}; +use noirc_driver::{CompileError, CompileOptions, DEFAULT_EXPRESSION_WIDTH, compile_no_check}; +use noirc_errors::{CustomDiagnostic, debug_info::DebugInfo}; +use noirc_frontend::hir::{Context, def_map::TestFunction}; use crate::{ - errors::try_to_diagnose_runtime_error, - foreign_calls::{layers, print::PrintOutput, ForeignCallError, ForeignCallExecutor}, NargoError, + errors::try_to_diagnose_runtime_error, + foreign_calls::{ + ForeignCallError, ForeignCallExecutor, layers, + print::PrintOutput, + transcript::{ForeignCallLog, LoggingForeignCallExecutor}, + }, }; use super::execute_program; @@ -22,9 +26,9 @@ use super::execute_program; #[derive(Debug)] pub enum TestStatus { Pass, - Fail { message: String, error_diagnostic: Option }, + Fail { message: String, error_diagnostic: Option }, Skipped, - CompileError(FileDiagnostic), + CompileError(CustomDiagnostic), } impl TestStatus { @@ -60,11 +64,21 @@ where let compiled_program = crate::ops::transform_program(compiled_program, target_width); if test_function_has_no_arguments { + let ignore_foreign_call_failures = + std::env::var("NARGO_IGNORE_TEST_FAILURES_FROM_FOREIGN_CALLS") + .is_ok_and(|var| &var == "true"); + + let mut foreign_call_log = ForeignCallLog::from_env("NARGO_TEST_FOREIGN_CALL_LOG"); + // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, // otherwise constraints involving these expressions will not error. // Use a base layer that doesn't handle anything, which we handle in the `execute` below. - let inner_executor = build_foreign_call_executor(output, layers::Unhandled); - let mut foreign_call_executor = TestForeignCallExecutor::new(inner_executor); + let foreign_call_executor = build_foreign_call_executor(output, layers::Unhandled); + let foreign_call_executor = TestForeignCallExecutor::new(foreign_call_executor); + let mut foreign_call_executor = LoggingForeignCallExecutor::new( + foreign_call_executor, + foreign_call_log.print_output(), + ); let circuit_execution = execute_program( &compiled_program.program, @@ -80,9 +94,8 @@ where &circuit_execution, ); - let ignore_foreign_call_failures = - std::env::var("NARGO_IGNORE_TEST_FAILURES_FROM_FOREIGN_CALLS") - .is_ok_and(|var| &var == "true"); + let foreign_call_executor = foreign_call_executor.executor; + foreign_call_log.write_log().expect("failed to write foreign call log"); if let TestStatus::Fail { .. } = status { if ignore_foreign_call_failures @@ -218,7 +231,7 @@ fn test_status_program_compile_pass( fn check_expected_failure_message( test_function: &TestFunction, failed_assertion: Option, - error_diagnostic: Option, + error_diagnostic: Option, ) -> TestStatus { // Extract the expected failure message, if there was one // @@ -235,9 +248,7 @@ fn check_expected_failure_message( // expected_failure_message let expected_failure_message_matches = failed_assertion .as_ref() - .or_else(|| { - error_diagnostic.as_ref().map(|file_diagnostic| &file_diagnostic.diagnostic.message) - }) + .or_else(|| error_diagnostic.as_ref().map(|file_diagnostic| &file_diagnostic.message)) .map(|message| message.contains(expected_failure_message)) .unwrap_or(false); if expected_failure_message_matches { diff --git a/noir/noir-repo/tooling/nargo/src/ops/transform.rs b/noir/noir-repo/tooling/nargo/src/ops/transform.rs index fdda368d150c..14aa74591174 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/transform.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/transform.rs @@ -1,6 +1,6 @@ use acvm::{ - acir::circuit::{ExpressionWidth, Program}, FieldElement, + acir::circuit::{ExpressionWidth, Program}, }; use iter_extended::vecmap; use noirc_driver::{CompiledContract, CompiledProgram}; diff --git a/noir/noir-repo/tooling/nargo/src/workspace.rs b/noir/noir-repo/tooling/nargo/src/workspace.rs index 2a6669aabdca..f7edc5057f6d 100644 --- a/noir/noir-repo/tooling/nargo/src/workspace.rs +++ b/noir/noir-repo/tooling/nargo/src/workspace.rs @@ -4,7 +4,7 @@ // - library will be default use std::{ - iter::{once, Once}, + iter::{Once, once}, path::PathBuf, slice, }; diff --git a/noir/noir-repo/tooling/nargo_cli/Cargo.toml b/noir/noir-repo/tooling/nargo_cli/Cargo.toml index 92eeed1b3911..a80828356ffe 100644 --- a/noir/noir-repo/tooling/nargo_cli/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_cli/Cargo.toml @@ -37,6 +37,7 @@ nargo = { workspace = true, features = ["rpc"] } nargo_fmt.workspace = true nargo_toml.workspace = true noir_lsp.workspace = true +noir_artifact_cli.workspace = true noir_debugger.workspace = true noirc_driver = { workspace = true, features = ["bn254"] } noirc_frontend = { workspace = true, features = ["bn254"] } @@ -98,12 +99,7 @@ sha3.workspace = true iai = "0.1.1" test-case.workspace = true lazy_static.workspace = true -light-poseidon = "0.2.0" - -ark-bn254-v04 = { package = "ark-bn254", version = "^0.4.0", default-features = false, features = [ - "curve", -] } -ark-ff-v04 = { package = "ark-ff", version = "^0.4.0", default-features = false } +light-poseidon = "0.3.0" [[bench]] name = "criterion" diff --git a/noir/noir-repo/tooling/nargo_cli/benches/criterion.rs b/noir/noir-repo/tooling/nargo_cli/benches/criterion.rs index 0722e957833e..22c1107c71ca 100644 --- a/noir/noir-repo/tooling/nargo_cli/benches/criterion.rs +++ b/noir/noir-repo/tooling/nargo_cli/benches/criterion.rs @@ -1,18 +1,14 @@ //! Select representative tests to bench with criterion -use acvm::{acir::native_types::WitnessMap, FieldElement}; +use acvm::{FieldElement, acir::native_types::WitnessMap}; use assert_cmd::prelude::{CommandCargoExt, OutputAssertExt}; -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{Criterion, criterion_group, criterion_main}; -use noirc_abi::{ - input_parser::{Format, InputValue}, - Abi, InputMap, -}; -use noirc_artifacts::program::ProgramArtifact; +use noir_artifact_cli::fs::{artifact::read_program_from_file, inputs::read_inputs_from_file}; use noirc_driver::CompiledProgram; use pprof::criterion::{Output, PProfProfiler}; +use std::cell::RefCell; use std::hint::black_box; use std::path::Path; -use std::{cell::RefCell, collections::BTreeMap}; use std::{process::Command, time::Duration}; include!("./utils.rs"); @@ -50,14 +46,14 @@ fn read_compiled_programs_and_inputs( for package in binary_packages { let program_artifact_path = workspace.package_build_path(package); - let program: CompiledProgram = read_program_from_file(&program_artifact_path).into(); + let program: CompiledProgram = + read_program_from_file(&program_artifact_path).unwrap().into(); let (inputs, _) = read_inputs_from_file( - &package.root_dir, - nargo::constants::PROVER_INPUT_FILE, - Format::Toml, + &package.root_dir.join(nargo::constants::PROVER_INPUT_FILE).with_extension("toml"), &program.abi, - ); + ) + .expect("failed to read input"); let initial_witness = program.abi.encode(&inputs, None).expect("failed to encode input witness"); @@ -67,40 +63,6 @@ fn read_compiled_programs_and_inputs( programs } -/// Read the bytecode and ABI from the compilation output -fn read_program_from_file(circuit_path: &Path) -> ProgramArtifact { - let file_path = circuit_path.with_extension("json"); - let input_string = std::fs::read(file_path).expect("failed to read artifact file"); - serde_json::from_slice(&input_string).expect("failed to deserialize artifact") -} - -/// Read the inputs from Prover.toml -fn read_inputs_from_file( - path: &Path, - file_name: &str, - format: Format, - abi: &Abi, -) -> (InputMap, Option) { - if abi.is_empty() { - return (BTreeMap::new(), None); - } - - let file_path = path.join(file_name).with_extension(format.ext()); - if !file_path.exists() { - if abi.parameters.is_empty() { - return (BTreeMap::new(), None); - } else { - panic!("input file doesn't exist: {}", file_path.display()); - } - } - - let input_string = std::fs::read_to_string(file_path).expect("failed to read input file"); - let mut input_map = format.parse(&input_string, abi).expect("failed to parse input"); - let return_value = input_map.remove(noirc_abi::MAIN_RETURN_NAME); - - (input_map, return_value) -} - /// Use the nargo CLI to compile a test program, then benchmark its execution /// by executing the command directly from the benchmark, so that we can have /// meaningful flamegraphs about the ACVM. diff --git a/noir/noir-repo/tooling/nargo_cli/benches/iai.rs b/noir/noir-repo/tooling/nargo_cli/benches/iai.rs index bcd60111ccf6..c0526e9a7e2f 100644 --- a/noir/noir-repo/tooling/nargo_cli/benches/iai.rs +++ b/noir/noir-repo/tooling/nargo_cli/benches/iai.rs @@ -6,7 +6,7 @@ use std::process::Command; include!("./utils.rs"); macro_rules! iai_command { - ($command_name:tt, $command_string:expr) => { + ($command_name:tt, $command_string:expr_2021) => { paste! { fn []() { let test_program_dirs = get_selected_tests(); diff --git a/noir/noir-repo/tooling/nargo_cli/build.rs b/noir/noir-repo/tooling/nargo_cli/build.rs index afc6994823bd..b9faf018dfda 100644 --- a/noir/noir-repo/tooling/nargo_cli/build.rs +++ b/noir/noir-repo/tooling/nargo_cli/build.rs @@ -198,6 +198,9 @@ fn test_{test_name}(force_brillig: ForceBrillig, inliner_aggressiveness: Inliner // Allow more bytecode in exchange to catch illegal states. nargo.arg("--enable-brillig-debug-assertions"); + // Enable enums as an unstable feature + nargo.arg("-Zenums"); + if force_brillig.0 {{ nargo.arg("--force-brillig"); @@ -435,7 +438,6 @@ fn generate_compile_success_no_bug_tests(test_file: &mut File, test_data_dir: &P &test_dir, "compile", r#" - nargo.arg("--enable-brillig-constraints-check"); nargo.assert().success().stderr(predicate::str::contains("bug:").not()); "#, &MatrixConfig::default(), @@ -465,7 +467,6 @@ fn generate_compile_success_with_bug_tests(test_file: &mut File, test_data_dir: &test_dir, "compile", r#" - nargo.arg("--enable-brillig-constraints-check"); nargo.assert().success().stderr(predicate::str::contains("bug:")); "#, &MatrixConfig::default(), diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs index bb7f06692a66..2247f40f1817 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs @@ -8,15 +8,15 @@ use nargo::{ package::Package, parse_all, prepare_package, workspace::Workspace, }; use nargo_toml::PackageSelection; +use noir_artifact_cli::fs::artifact::write_to_file; use noirc_abi::{AbiParameter, AbiType, MAIN_RETURN_NAME}; -use noirc_driver::{check_crate, compute_function_abi, CompileOptions, CrateId}; +use noirc_driver::{CompileOptions, CrateId, check_crate, compute_function_abi}; use noirc_frontend::{ hir::{Context, ParsedFiles}, monomorphization::monomorphize, }; -use super::{fs::write_to_file, PackageOptions}; -use super::{LockType, WorkspaceCommand}; +use super::{LockType, PackageOptions, WorkspaceCommand}; /// Check a local package and all of its dependencies for errors #[derive(Debug, Clone, Args)] @@ -103,7 +103,8 @@ fn check_package( if should_write_prover { let prover_toml = create_input_toml_template(parameters.clone(), None); - write_to_file(prover_toml.as_bytes(), &path_to_prover_input); + write_to_file(prover_toml.as_bytes(), &path_to_prover_input) + .expect("failed to write template"); } else { eprintln!("Note: Prover.toml already exists. Use --overwrite to force overwrite."); } 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 bb08d2675cb8..2ee19f5edc01 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,6 +9,9 @@ use nargo::package::Package; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::PackageSelection; +use noir_artifact_cli::fs::artifact::{ + read_program_from_file, save_contract_to_file, save_program_to_file, +}; use noirc_driver::DEFAULT_EXPRESSION_WIDTH; use noirc_driver::NOIR_ARTIFACT_VERSION_STRING; use noirc_driver::{CompilationResult, CompileOptions, CompiledContract}; @@ -20,7 +23,6 @@ use notify_debouncer_full::new_debouncer; use crate::errors::CliError; -use super::fs::program::{read_program_from_file, save_contract_to_file, save_program_to_file}; use super::{LockType, PackageOptions, WorkspaceCommand}; use rayon::prelude::*; @@ -80,7 +82,7 @@ fn watch_workspace(workspace: &Workspace, compile_options: &CompileOptions) -> n let noir_files_modified = debounced_events.iter().any(|event| { let mut event_paths = event.event.paths.iter(); let event_affects_noir_file = - event_paths.any(|path| path.extension().map_or(false, |ext| ext == "nr")); + event_paths.any(|path| path.extension().is_some_and(|ext| ext == "nr")); let is_relevant_event_kind = matches!( event.kind, @@ -181,7 +183,7 @@ fn compile_programs( // The loaded circuit includes backend specific transformations, which might be different from the current target. let load_cached_program = |package| { let program_artifact_path = workspace.package_build_path(package); - read_program_from_file(program_artifact_path) + read_program_from_file(&program_artifact_path) .ok() .filter(|p| p.noir_version == NOIR_ARTIFACT_VERSION_STRING) .map(|p| p.into()) @@ -241,7 +243,8 @@ fn compile_programs( // Check solvability. nargo::ops::check_program(&program)?; // Overwrite the build artifacts with the final circuit, which includes the backend specific transformations. - save_program_to_file(&program.into(), &package.name, workspace.target_directory_path()); + save_program_to_file(&program.into(), &package.name, &workspace.target_directory_path()) + .expect("failed to save program"); Ok(((), warnings)) }; @@ -292,7 +295,8 @@ fn save_contract( &contract.into(), &format!("{}-{}", package.name, contract_name), target_dir, - ); + ) + .expect("failed to save contract"); if show_artifact_paths { println!("Saved contract artifact to: {}", artifact_path.display()); } @@ -321,6 +325,7 @@ mod tests { use nargo::ops::compile_program; use nargo_toml::PackageSelection; use noirc_driver::{CompileOptions, CrateName}; + use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use crate::cli::test_cmd::formatters::diagnostic_to_string; use crate::cli::{ @@ -343,7 +348,7 @@ mod tests { fn read_test_program_dirs( test_programs_dir: &Path, test_sub_dir: &str, - ) -> impl Iterator { + ) -> impl Iterator + use<> { let test_case_dir = test_programs_dir.join(test_sub_dir); std::fs::read_dir(test_case_dir) .unwrap() @@ -393,7 +398,7 @@ mod tests { assert!(!test_workspaces.is_empty(), "should find some test workspaces"); - test_workspaces.iter().for_each(|workspace| { + test_workspaces.par_iter().for_each(|workspace| { let (file_manager, parsed_files) = parse_workspace(workspace); let binary_packages = workspace.into_iter().filter(|package| package.is_binary()); diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/dap_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/dap_cmd.rs index 91db844ead34..8987ed80d3e6 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/dap_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/dap_cmd.rs @@ -1,12 +1,12 @@ +use acvm::FieldElement; use acvm::acir::circuit::ExpressionWidth; use acvm::acir::native_types::WitnessMap; -use acvm::FieldElement; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; use nargo::constants::PROVER_INPUT_FILE; use nargo::workspace::Workspace; -use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; -use noirc_abi::input_parser::Format; +use nargo_toml::{PackageSelection, get_package_manifest, resolve_workspace_from_toml}; +use noir_artifact_cli::fs::inputs::read_inputs_from_file; use noirc_driver::{CompileOptions, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING}; use noirc_frontend::graph::CrateName; @@ -20,7 +20,6 @@ use dap::types::Capabilities; use serde_json::Value; use super::debug_cmd::compile_bin_package_for_debugging; -use super::fs::inputs::read_inputs_from_file; use crate::errors::CliError; use noir_debugger::errors::{DapError, LoadError}; @@ -125,11 +124,13 @@ fn load_and_compile_project( let compiled_program = nargo::ops::transform_program(compiled_program, expression_width); - let (inputs_map, _) = - read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &compiled_program.abi) - .map_err(|_| { - LoadError::Generic(format!("Failed to read program inputs from {}", prover_name)) - })?; + let (inputs_map, _) = read_inputs_from_file( + &package.root_dir.join(prover_name).with_extension("toml"), + &compiled_program.abi, + ) + .map_err(|e| { + LoadError::Generic(format!("Failed to read program inputs from {prover_name}: {e}")) + })?; let initial_witness = compiled_program .abi .encode(&inputs_map, None) @@ -143,12 +144,7 @@ fn loop_uninitialized_dap( expression_width: ExpressionWidth, pedantic_solving: bool, ) -> Result<(), DapError> { - loop { - let req = match server.poll_request()? { - Some(req) => req, - None => break, - }; - + while let Some(req) = server.poll_request()? { match req.command { Command::Initialize(_) => { let rsp = req.success(ResponseBody::Initialize(Capabilities { @@ -165,7 +161,7 @@ fn loop_uninitialized_dap( server.respond(req.error("Missing launch arguments"))?; continue; }; - let Some(Value::String(ref project_folder)) = additional_data.get("projectFolder") + let Some(Value::String(project_folder)) = additional_data.get("projectFolder") else { server.respond(req.error("Missing project folder argument"))?; continue; diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs index 259abbbbfd03..f9303180fc0d 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -1,7 +1,7 @@ -use std::path::PathBuf; +use std::path::Path; -use acvm::acir::native_types::WitnessStack; use acvm::FieldElement; +use acvm::acir::native_types::WitnessStack; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; @@ -13,14 +13,15 @@ use nargo::package::{CrateName, Package}; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::PackageSelection; -use noirc_abi::input_parser::{Format, InputValue}; +use noir_artifact_cli::fs::inputs::read_inputs_from_file; +use noir_artifact_cli::fs::witness::save_witness_to_dir; use noirc_abi::InputMap; -use noirc_driver::{file_manager_with_stdlib, CompileOptions, CompiledProgram}; +use noirc_abi::input_parser::InputValue; +use noirc_driver::{CompileOptions, CompiledProgram, file_manager_with_stdlib}; use noirc_frontend::debug::DebugInstrumenter; use noirc_frontend::hir::ParsedFiles; use super::compile_cmd::get_target_width; -use super::fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}; use super::{LockType, WorkspaceCommand}; use crate::errors::CliError; @@ -48,6 +49,10 @@ pub(crate) struct DebugCommand { /// Disable vars debug instrumentation (enabled by default) #[clap(long)] skip_instrumentation: Option, + + /// Raw string printing of source for testing + #[clap(long, hide = true)] + raw_source_printing: Option, } impl WorkspaceCommand for DebugCommand { @@ -97,6 +102,7 @@ pub(crate) fn run(args: DebugCommand, workspace: Workspace) -> Result<(), CliErr &args.witness_name, target_dir, args.compile_options.pedantic_solving, + args.raw_source_printing.unwrap_or(false), ) } @@ -107,7 +113,7 @@ pub(crate) fn compile_bin_package_for_debugging( skip_instrumentation: bool, compile_options: CompileOptions, ) -> Result { - let mut workspace_file_manager = file_manager_with_stdlib(std::path::Path::new("")); + let mut workspace_file_manager = file_manager_with_stdlib(Path::new("")); insert_all_files_for_workspace_into_file_manager(workspace, &mut workspace_file_manager); let mut parsed_files = parse_all(&workspace_file_manager); @@ -170,7 +176,7 @@ fn instrument_package_files( for ancestor in file_path.ancestors() { if ancestor == entry_path_parent { // file is in package - debug_instrumenter.instrument_module(&mut parsed_file.0); + debug_instrumenter.instrument_module(&mut parsed_file.0, *file_id); } } } @@ -183,16 +189,22 @@ fn run_async( program: CompiledProgram, prover_name: &str, witness_name: &Option, - target_dir: &PathBuf, + target_dir: &Path, pedantic_solving: bool, + raw_source_printing: bool, ) -> Result<(), CliError> { use tokio::runtime::Builder; let runtime = Builder::new_current_thread().enable_all().build().unwrap(); runtime.block_on(async { println!("[{}] Starting debugger", package.name); - let (return_value, witness_stack) = - debug_program_and_decode(program, package, prover_name, pedantic_solving)?; + let (return_value, witness_stack) = debug_program_and_decode( + program, + package, + prover_name, + pedantic_solving, + raw_source_printing, + )?; if let Some(solved_witness_stack) = witness_stack { println!("[{}] Circuit witness successfully solved", package.name); @@ -203,7 +215,7 @@ fn run_async( if let Some(witness_name) = witness_name { let witness_path = - save_witness_to_dir(solved_witness_stack, witness_name, target_dir)?; + save_witness_to_dir(&solved_witness_stack, witness_name, target_dir)?; println!("[{}] Witness saved to {}", package.name, witness_path.display()); } @@ -220,12 +232,15 @@ fn debug_program_and_decode( package: &Package, prover_name: &str, pedantic_solving: bool, + raw_source_printing: bool, ) -> Result<(Option, Option>), CliError> { // Parse the initial witness values from Prover.toml - let (inputs_map, _) = - read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &program.abi)?; + let (inputs_map, _) = read_inputs_from_file( + &package.root_dir.join(prover_name).with_extension("toml"), + &program.abi, + )?; let program_abi = program.abi.clone(); - let witness_stack = debug_program(program, &inputs_map, pedantic_solving)?; + let witness_stack = debug_program(program, &inputs_map, pedantic_solving, raw_source_printing)?; match witness_stack { Some(witness_stack) => { @@ -244,6 +259,7 @@ pub(crate) fn debug_program( compiled_program: CompiledProgram, inputs_map: &InputMap, pedantic_solving: bool, + raw_source_printing: bool, ) -> Result>, CliError> { let initial_witness = compiled_program.abi.encode(inputs_map, None)?; @@ -251,6 +267,7 @@ pub(crate) fn debug_program( &Bn254BlackBoxSolver(pedantic_solving), compiled_program, initial_witness, + raw_source_printing, ) .map_err(CliError::from) } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs index 884ba06cdcbf..7ba95a16eebd 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -1,26 +1,14 @@ use std::path::PathBuf; -use acvm::acir::native_types::WitnessStack; -use acvm::FieldElement; -use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; use nargo::constants::PROVER_INPUT_FILE; -use nargo::errors::try_to_diagnose_runtime_error; -use nargo::foreign_calls::DefaultForeignCallBuilder; -use nargo::package::Package; use nargo::workspace::Workspace; -use nargo::PrintOutput; use nargo_toml::PackageSelection; -use noirc_abi::input_parser::{Format, InputValue}; -use noirc_abi::InputMap; -use noirc_artifacts::debug::DebugArtifact; -use noirc_driver::{CompileOptions, CompiledProgram}; +use noirc_driver::CompileOptions; use super::compile_cmd::compile_workspace_full; -use super::fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}; use super::{LockType, PackageOptions, WorkspaceCommand}; -use crate::cli::fs::program::read_program_from_file; use crate::errors::CliError; /// Executes a circuit to calculate its return value @@ -43,8 +31,12 @@ pub(crate) struct ExecuteCommand { compile_options: CompileOptions, /// JSON RPC url to solve oracle calls - #[clap(long)] + #[clap(long, conflicts_with = "oracle_file")] oracle_resolver: Option, + + /// Path to the oracle transcript. + #[clap(long, conflicts_with = "oracle_resolver")] + oracle_file: Option, } impl WorkspaceCommand for ExecuteCommand { @@ -59,126 +51,30 @@ impl WorkspaceCommand for ExecuteCommand { } pub(crate) fn run(args: ExecuteCommand, workspace: Workspace) -> Result<(), CliError> { - let target_dir = &workspace.target_directory_path(); - // Compile the full workspace in order to generate any build artifacts. compile_workspace_full(&workspace, &args.compile_options)?; let binary_packages = workspace.into_iter().filter(|package| package.is_binary()); for package in binary_packages { let program_artifact_path = workspace.package_build_path(package); - let program: CompiledProgram = - read_program_from_file(program_artifact_path.clone())?.into(); - let abi = program.abi.clone(); - - let results = execute_program_and_decode( - program, - package, - &args.prover_name, - args.oracle_resolver.as_deref(), - Some(workspace.root_dir.clone()), - Some(package.name.to_string()), - args.compile_options.pedantic_solving, - )?; - - println!("[{}] Circuit witness successfully solved", package.name); - if let Some(ref return_value) = results.actual_return { - println!("[{}] Circuit output: {return_value:?}", package.name); - } - - let package_name = package.name.clone().into(); - let witness_name = args.witness_name.as_ref().unwrap_or(&package_name); - let witness_path = save_witness_to_dir(results.witness_stack, witness_name, target_dir)?; - println!("[{}] Witness saved to {}", package.name, witness_path.display()); - - // Sanity checks on the return value after the witness has been saved, so it can be inspected if necessary. - if let Some(expected) = results.expected_return { - if results.actual_return.as_ref() != Some(&expected) { - return Err(CliError::UnexpectedReturn { expected, actual: results.actual_return }); - } - } - // We can expect that if the circuit returns something, it should be non-empty after execution. - if let Some(ref expected) = abi.return_type { - if results.actual_return.is_none() { - return Err(CliError::MissingReturn { expected: expected.clone() }); - } - } + let prover_file = package.root_dir.join(&args.prover_name).with_extension("toml"); + + let cmd = noir_artifact_cli::commands::execute_cmd::ExecuteCommand { + artifact_path: program_artifact_path, + prover_file, + output_dir: Some(workspace.target_directory_path()), + witness_name: Some( + args.witness_name.clone().unwrap_or_else(|| package.name.to_string()), + ), + contract_fn: None, + oracle_file: args.oracle_file.clone(), + oracle_resolver: args.oracle_resolver.clone(), + oracle_root_dir: Some(workspace.root_dir.clone()), + oracle_package_name: Some(package.name.to_string()), + pedantic_solving: args.compile_options.pedantic_solving, + }; + + noir_artifact_cli::commands::execute_cmd::run(cmd)?; } Ok(()) } - -fn execute_program_and_decode( - program: CompiledProgram, - package: &Package, - prover_name: &str, - foreign_call_resolver_url: Option<&str>, - root_path: Option, - package_name: Option, - pedantic_solving: bool, -) -> Result { - // Parse the initial witness values from Prover.toml - let (inputs_map, expected_return) = - read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &program.abi)?; - let witness_stack = execute_program( - &program, - &inputs_map, - foreign_call_resolver_url, - root_path, - package_name, - pedantic_solving, - )?; - // Get the entry point witness for the ABI - let main_witness = - &witness_stack.peek().expect("Should have at least one witness on the stack").witness; - let (_, actual_return) = program.abi.decode(main_witness)?; - - Ok(ExecutionResults { expected_return, actual_return, witness_stack }) -} - -struct ExecutionResults { - expected_return: Option, - actual_return: Option, - witness_stack: WitnessStack, -} - -pub(crate) fn execute_program( - compiled_program: &CompiledProgram, - inputs_map: &InputMap, - foreign_call_resolver_url: Option<&str>, - root_path: Option, - package_name: Option, - pedantic_solving: bool, -) -> Result, CliError> { - let initial_witness = compiled_program.abi.encode(inputs_map, None)?; - - let solved_witness_stack_err = nargo::ops::execute_program( - &compiled_program.program, - initial_witness, - &Bn254BlackBoxSolver(pedantic_solving), - &mut DefaultForeignCallBuilder { - output: PrintOutput::Stdout, - enable_mocks: false, - resolver_url: foreign_call_resolver_url.map(|s| s.to_string()), - root_path, - package_name, - } - .build(), - ); - match solved_witness_stack_err { - Ok(solved_witness_stack) => Ok(solved_witness_stack), - Err(err) => { - let debug_artifact = DebugArtifact { - debug_symbols: compiled_program.debug.clone(), - file_map: compiled_program.file_map.clone(), - }; - - if let Some(diagnostic) = - try_to_diagnose_runtime_error(&err, &compiled_program.abi, &compiled_program.debug) - { - diagnostic.report(&debug_artifact, false); - } - - Err(CliError::NargoError(err)) - } - } -} diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs index 09fe11fdb749..373dfce86a91 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs @@ -1,6 +1,7 @@ use nargo::errors::CompileError; use nargo::ops::report_errors; -use noirc_errors::FileDiagnostic; +use noir_artifact_cli::fs::artifact::save_program_to_file; +use noirc_errors::CustomDiagnostic; use noirc_frontend::hir::ParsedFiles; use rayon::prelude::*; @@ -11,7 +12,7 @@ use nargo::prepare_package; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::PackageSelection; -use noirc_driver::{compile_no_check, CompileOptions, CompiledProgram}; +use noirc_driver::{CompileOptions, CompiledProgram, compile_no_check}; use clap::Args; @@ -19,7 +20,6 @@ use crate::errors::CliError; use super::check_cmd::check_crate_and_report_errors; -use super::fs::program::save_program_to_file; use super::{LockType, PackageOptions, WorkspaceCommand}; /// Exports functions marked with #[export] attribute @@ -82,7 +82,7 @@ fn compile_exported_functions( |(function_name, function_id)| -> Result<(String, CompiledProgram), CompileError> { // TODO: We should to refactor how to deal with compilation errors to avoid this. let program = compile_no_check(&mut context, compile_options, function_id, None, false) - .map_err(|error| vec![FileDiagnostic::from(error)]); + .map_err(|error| vec![CustomDiagnostic::from(error)]); let program = report_errors( program.map(|program| (program, Vec::new())), @@ -97,7 +97,7 @@ fn compile_exported_functions( let export_dir = workspace.export_directory_path(); for (function_name, program) in exported_programs { - save_program_to_file(&program.into(), &function_name.parse().unwrap(), &export_dir); + save_program_to_file(&program.into(), &function_name.parse().unwrap(), &export_dir)?; } Ok(()) } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs index 1cdfb1e0c4fb..53551c139508 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs @@ -56,11 +56,8 @@ pub(crate) fn run(args: FormatCommand, workspace: Workspace) -> Result<(), CliEr let is_all_warnings = errors.iter().all(ParserError::is_warning); if !is_all_warnings { let errors = errors - .into_iter() - .map(|error| { - let error = CustomDiagnostic::from(&error); - error.in_file(file_id) - }) + .iter() + .map(CustomDiagnostic::from) .collect(); let _ = report_errors::<()>( @@ -119,7 +116,7 @@ fn visit_noir_files( let path = entry.path(); if path.is_dir() { visit_noir_files(&path, cb)?; - } else if entry.path().extension().map_or(false, |extension| extension == "nr") { + } else if entry.path().extension().is_some_and(|extension| extension == "nr") { cb(&entry)?; } } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/inputs.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/fs/inputs.rs deleted file mode 100644 index 4a7a81431bbc..000000000000 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/inputs.rs +++ /dev/null @@ -1,42 +0,0 @@ -use noirc_abi::{ - input_parser::{Format, InputValue}, - Abi, InputMap, MAIN_RETURN_NAME, -}; -use std::{collections::BTreeMap, path::Path}; - -use crate::errors::FilesystemError; - -/// Returns the circuit's parameters and its return value, if one exists. -/// # Examples -/// -/// ```ignore -/// let (input_map, return_value): (InputMap, Option) = -/// read_inputs_from_file(path, "Verifier", Format::Toml, &abi)?; -/// ``` -pub(crate) fn read_inputs_from_file>( - path: P, - file_name: &str, - format: Format, - abi: &Abi, -) -> Result<(InputMap, Option), FilesystemError> { - if abi.is_empty() { - return Ok((BTreeMap::new(), None)); - } - - let file_path = path.as_ref().join(file_name).with_extension(format.ext()); - if !file_path.exists() { - if abi.parameters.is_empty() { - // Reading a return value from the `Prover.toml` is optional, - // so if the ABI has no parameters we can skip reading the file if it doesn't exist. - return Ok((BTreeMap::new(), None)); - } else { - return Err(FilesystemError::MissingTomlFile(file_name.to_owned(), file_path)); - } - } - - let input_string = std::fs::read_to_string(file_path).unwrap(); - let mut input_map = format.parse(&input_string, abi)?; - let return_value = input_map.remove(MAIN_RETURN_NAME); - - Ok((input_map, return_value)) -} diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/mod.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/fs/mod.rs deleted file mode 100644 index 8658bd5b2482..000000000000 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/mod.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::{ - fs::File, - io::Write, - path::{Path, PathBuf}, -}; - -pub(super) mod inputs; -pub(super) mod program; -pub(super) mod witness; - -pub(super) fn create_named_dir(named_dir: &Path, name: &str) -> PathBuf { - std::fs::create_dir_all(named_dir) - .unwrap_or_else(|_| panic!("could not create the `{name}` directory")); - - PathBuf::from(named_dir) -} - -pub(super) fn write_to_file(bytes: &[u8], path: &Path) -> String { - let display = path.display(); - - let mut file = match File::create(path) { - Err(why) => panic!("couldn't create {display}: {why}"), - Ok(file) => file, - }; - - match file.write_all(bytes) { - Err(why) => panic!("couldn't write to {display}: {why}"), - Ok(_) => display.to_string(), - } -} diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/program.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/fs/program.rs deleted file mode 100644 index 87783e4573a9..000000000000 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/program.rs +++ /dev/null @@ -1,51 +0,0 @@ -use std::path::{Path, PathBuf}; - -use nargo::package::CrateName; -use noirc_artifacts::{contract::ContractArtifact, program::ProgramArtifact}; - -use crate::errors::FilesystemError; - -use super::{create_named_dir, write_to_file}; - -pub(crate) fn save_program_to_file>( - program_artifact: &ProgramArtifact, - crate_name: &CrateName, - circuit_dir: P, -) -> PathBuf { - let circuit_name: String = crate_name.into(); - save_build_artifact_to_file(program_artifact, &circuit_name, circuit_dir) -} - -pub(crate) fn save_contract_to_file>( - compiled_contract: &ContractArtifact, - circuit_name: &str, - circuit_dir: P, -) -> PathBuf { - save_build_artifact_to_file(compiled_contract, circuit_name, circuit_dir) -} - -fn save_build_artifact_to_file, T: ?Sized + serde::Serialize>( - build_artifact: &T, - artifact_name: &str, - circuit_dir: P, -) -> PathBuf { - create_named_dir(circuit_dir.as_ref(), "target"); - let circuit_path = circuit_dir.as_ref().join(artifact_name).with_extension("json"); - write_to_file(&serde_json::to_vec(build_artifact).unwrap(), &circuit_path); - - circuit_path -} - -#[tracing::instrument(level = "trace", skip_all)] -pub(crate) fn read_program_from_file>( - circuit_path: P, -) -> Result { - let file_path = circuit_path.as_ref().with_extension("json"); - - let input_string = - std::fs::read(&file_path).map_err(|_| FilesystemError::PathNotValid(file_path))?; - let program = serde_json::from_slice(&input_string) - .map_err(|err| FilesystemError::ProgramSerializationError(err.to_string()))?; - - Ok(program) -} diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/witness.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/fs/witness.rs deleted file mode 100644 index f95eb3d7a4c0..000000000000 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/witness.rs +++ /dev/null @@ -1,22 +0,0 @@ -use std::path::{Path, PathBuf}; - -use acvm::{acir::native_types::WitnessStack, FieldElement}; -use nargo::constants::WITNESS_EXT; - -use super::{create_named_dir, write_to_file}; -use crate::errors::FilesystemError; - -pub(crate) fn save_witness_to_dir>( - witness_stack: WitnessStack, - witness_name: &str, - witness_dir: P, -) -> Result { - create_named_dir(witness_dir.as_ref(), "witness"); - let witness_path = witness_dir.as_ref().join(witness_name).with_extension(WITNESS_EXT); - - let buf: Vec = witness_stack.try_into()?; - - write_to_file(buf.as_slice(), &witness_path); - - Ok(witness_path) -} diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/generate_completion_script_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/generate_completion_script_cmd.rs index 0b8fa1ee3e70..5f7329e14ba2 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/generate_completion_script_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/generate_completion_script_cmd.rs @@ -23,7 +23,7 @@ pub(crate) fn run(command: GenerateCompletionScriptCommand) -> Result<(), CliErr return Err(CliError::Generic( "Invalid shell. Supported shells are: bash, elvish, fish, powershell, zsh" .to_string(), - )) + )); } }; diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs index 4561a1221f66..c8a9c289fc0e 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs @@ -7,22 +7,21 @@ use nargo::{ workspace::Workspace, }; use nargo_toml::PackageSelection; -use noirc_abi::input_parser::Format; +use noir_artifact_cli::fs::{artifact::read_program_from_file, inputs::read_inputs_from_file}; use noirc_artifacts::program::ProgramArtifact; use noirc_artifacts_info::{ - count_opcodes_and_gates_in_program, show_info_report, FunctionInfo, InfoReport, ProgramInfo, + FunctionInfo, InfoReport, ProgramInfo, count_opcodes_and_gates_in_program, show_info_report, }; use noirc_driver::CompileOptions; -use prettytable::{row, Row}; +use prettytable::{Row, row}; use rayon::prelude::*; use serde::Serialize; use crate::errors::CliError; use super::{ - compile_cmd::{compile_workspace_full, get_target_width}, - fs::{inputs::read_inputs_from_file, program::read_program_from_file}, LockType, PackageOptions, WorkspaceCommand, + compile_cmd::{compile_workspace_full, get_target_width}, }; /// Provides detailed information on each of a program's function (represented by a single circuit) @@ -75,7 +74,7 @@ pub(crate) fn run(mut args: InfoCommand, workspace: Workspace) -> Result<(), Cli .filter(|package| package.is_binary()) .map(|package| -> Result<(Package, ProgramArtifact), CliError> { let program_artifact_path = workspace.package_build_path(package); - let program = read_program_from_file(program_artifact_path)?; + let program = read_program_from_file(&program_artifact_path)?; Ok((package.clone(), program)) }) .collect::>()?; @@ -144,9 +143,7 @@ fn profile_brillig_execution( for (package, program_artifact) in binary_packages.iter() { // Parse the initial witness values from Prover.toml or Prover.json let (inputs_map, _) = read_inputs_from_file( - &package.root_dir, - prover_name, - Format::Toml, + &package.root_dir.join(prover_name).with_extension("toml"), &program_artifact.abi, )?; let initial_witness = program_artifact.abi.encode(&inputs_map, None)?; diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/init_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/init_cmd.rs index ffeb5d9ba74b..bc67a9ba3a87 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/init_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/init_cmd.rs @@ -1,10 +1,10 @@ use crate::errors::CliError; -use super::fs::{create_named_dir, write_to_file}; use super::NargoConfig; use clap::Args; use nargo::constants::{PKG_FILE, SRC_DIR}; use nargo::package::{CrateName, PackageType}; +use noir_artifact_cli::fs::artifact::write_to_file; use std::path::PathBuf; /// Create a Noir project in the current directory. @@ -58,7 +58,6 @@ pub(crate) fn initialize_project( package_type: PackageType, ) { let src_dir = package_dir.join(SRC_DIR); - create_named_dir(&src_dir, "src"); let toml_contents = format!( r#"[package] @@ -69,14 +68,18 @@ authors = [""] [dependencies]"# ); - write_to_file(toml_contents.as_bytes(), &package_dir.join(PKG_FILE)); + write_to_file(toml_contents.as_bytes(), &package_dir.join(PKG_FILE)).unwrap(); // This uses the `match` syntax instead of `if` so we get a compile error when we add new package types (which likely need new template files) match package_type { - PackageType::Binary => write_to_file(BIN_EXAMPLE.as_bytes(), &src_dir.join("main.nr")), + PackageType::Binary => { + write_to_file(BIN_EXAMPLE.as_bytes(), &src_dir.join("main.nr")).unwrap(); + } PackageType::Contract => { - write_to_file(CONTRACT_EXAMPLE.as_bytes(), &src_dir.join("main.nr")) + write_to_file(CONTRACT_EXAMPLE.as_bytes(), &src_dir.join("main.nr")).unwrap(); + } + PackageType::Library => { + write_to_file(LIB_EXAMPLE.as_bytes(), &src_dir.join("lib.nr")).unwrap(); } - PackageType::Library => write_to_file(LIB_EXAMPLE.as_bytes(), &src_dir.join("lib.nr")), }; println!("Project successfully created! It is located at {}", package_dir.display()); } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/mod.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/mod.rs index 1d59fdb806fd..0c644ae554c8 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/mod.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/mod.rs @@ -2,7 +2,7 @@ use clap::{Args, Parser, Subcommand}; use const_format::formatcp; use nargo::workspace::Workspace; use nargo_toml::{ - get_package_manifest, resolve_workspace_from_toml, ManifestError, PackageSelection, + ManifestError, PackageSelection, get_package_manifest, resolve_workspace_from_toml, }; use noirc_driver::{CrateName, NOIR_ARTIFACT_VERSION_STRING}; use std::{ @@ -14,8 +14,6 @@ use color_eyre::eyre; use crate::errors::CliError; -mod fs; - mod check_cmd; mod compile_cmd; mod dap_cmd; @@ -213,14 +211,15 @@ where /// Lock the (selected) packages in the workspace. /// The lock taken can be shared for commands that only read the artifacts, /// or exclusive for the ones that (might) write artifacts as well. -fn lock_workspace(workspace: &Workspace, exclusive: bool) -> Result, CliError> { - use fs2::FileExt as _; - +fn lock_workspace( + workspace: &Workspace, + exclusive: bool, +) -> Result>, CliError> { struct LockedFile(File); impl Drop for LockedFile { fn drop(&mut self) { - let _ = self.0.unlock(); + let _ = fs2::FileExt::unlock(&self.0); } } @@ -233,15 +232,17 @@ fn lock_workspace(workspace: &Workspace, exclusive: bool) -> Result TestRunner<'a> { }; } - if all_passed { - Ok(()) - } else { - Err(CliError::Generic(String::new())) - } + if all_passed { Ok(()) } else { Err(CliError::Generic(String::new())) } } /// Runs all tests. Returns `true` if all tests passed, `false` otherwise. @@ -261,21 +257,22 @@ impl<'a> TestRunner<'a> { // Specify a larger-than-default stack size to prevent overflowing stack in large programs. // (the default is 2MB) .stack_size(STACK_SIZE) - .spawn_scoped(scope, move || loop { - // Get next test to process from the iterator. - let Some(test) = iter.lock().unwrap().next() else { - break; - }; - - self.formatter - .test_start_async(&test.name, &test.package_name) - .expect("Could not display test start"); - - let time_before_test = std::time::Instant::now(); - let (status, output) = match catch_unwind(test.runner) { - Ok((status, output)) => (status, output), - Err(err) => ( - TestStatus::Fail { + .spawn_scoped(scope, move || { + loop { + // Get next test to process from the iterator. + let Some(test) = iter.lock().unwrap().next() else { + break; + }; + + self.formatter + .test_start_async(&test.name, &test.package_name) + .expect("Could not display test start"); + + let time_before_test = std::time::Instant::now(); + let (status, output) = match catch_unwind(test.runner) { + Ok((status, output)) => (status, output), + Err(err) => ( + TestStatus::Fail { message: // It seems `panic!("...")` makes the error be `&str`, so we handle this common case if let Some(message) = err.downcast_ref::<&str>() { @@ -285,31 +282,32 @@ impl<'a> TestRunner<'a> { }, error_diagnostic: None, }, - String::new(), - ), - }; - let time_to_run = time_before_test.elapsed(); - - let test_result = TestResult { - name: test.name, - package_name: test.package_name, - status, - output, - time_to_run, - }; - - self.formatter - .test_end_async( - &test_result, - self.file_manager, - self.args.show_output, - self.args.compile_options.deny_warnings, - self.args.compile_options.silence_warnings, - ) - .expect("Could not display test start"); - - if thread_sender.send(test_result).is_err() { - break; + String::new(), + ), + }; + let time_to_run = time_before_test.elapsed(); + + let test_result = TestResult { + name: test.name, + package_name: test.package_name, + status, + output, + time_to_run, + }; + + self.formatter + .test_end_async( + &test_result, + self.file_manager, + self.args.show_output, + self.args.compile_options.deny_warnings, + self.args.compile_options.silence_warnings, + ) + .expect("Could not display test start"); + + if thread_sender.send(test_result).is_err() { + break; + } } }) .unwrap(); @@ -407,19 +405,21 @@ impl<'a> TestRunner<'a> { // Specify a larger-than-default stack size to prevent overflowing stack in large programs. // (the default is 2MB) .stack_size(STACK_SIZE) - .spawn_scoped(scope, move || loop { - // Get next package to process from the iterator. - let Some(package) = iter.lock().unwrap().next() else { - break; - }; - let tests = self.collect_package_tests::( - package, - self.args.oracle_resolver.as_deref(), - Some(self.workspace.root_dir.clone()), - package.name.to_string(), - ); - if thread_sender.send((package, tests)).is_err() { - break; + .spawn_scoped(scope, move || { + loop { + // Get next package to process from the iterator. + let Some(package) = iter.lock().unwrap().next() else { + break; + }; + let tests = self.collect_package_tests::( + package, + self.args.oracle_resolver.as_deref(), + Some(self.workspace.root_dir.clone()), + package.name.to_string(), + ); + if thread_sender.send((package, tests)).is_err() { + break; + } } }) .unwrap(); @@ -440,11 +440,7 @@ impl<'a> TestRunner<'a> { } }); - if let Some(error) = error { - Err(error) - } else { - Ok(package_tests) - } + if let Some(error) = error { Err(error) } else { Ok(package_tests) } } /// Compiles a single package and returns all of its tests diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd/formatters.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd/formatters.rs index 91d6f0066b1c..686281292458 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd/formatters.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd/formatters.rs @@ -2,8 +2,8 @@ use std::{io::Write, panic::RefUnwindSafe, time::Duration}; use fm::FileManager; use nargo::ops::TestStatus; -use noirc_errors::{reporter::stack_trace, FileDiagnostic}; -use serde_json::{json, Map}; +use noirc_errors::{CustomDiagnostic, reporter::stack_trace}; +use serde_json::{Map, json}; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, StandardStreamLock, WriteColor}; use super::TestResult; @@ -438,7 +438,7 @@ impl Formatter for JsonFormatter { stdout.push_str(message.trim()); if let Some(diagnostic) = error_diagnostic { - if !(diagnostic.diagnostic.is_warning() && silence_warnings) { + if !(diagnostic.is_warning() && silence_warnings) { stdout.push('\n'); stdout.push_str(&diagnostic_to_string(diagnostic, file_manager)); } @@ -450,7 +450,7 @@ impl Formatter for JsonFormatter { TestStatus::CompileError(diagnostic) => { json.insert("event".to_string(), json!("failed")); - if !(diagnostic.diagnostic.is_warning() && silence_warnings) { + if !(diagnostic.is_warning() && silence_warnings) { if !stdout.is_empty() { stdout.push('\n'); } @@ -515,12 +515,11 @@ fn package_start(package_name: &str, test_count: usize) -> std::io::Result<()> { } pub(crate) fn diagnostic_to_string( - file_diagnostic: &FileDiagnostic, + custom_diagnostic: &CustomDiagnostic, file_manager: &FileManager, ) -> String { let file_map = file_manager.as_file_map(); - let custom_diagnostic = &file_diagnostic.diagnostic; let mut message = String::new(); message.push_str(custom_diagnostic.message.trim()); @@ -529,7 +528,7 @@ pub(crate) fn diagnostic_to_string( message.push_str(note.trim()); } - if let Ok(name) = file_map.get_name(file_diagnostic.file_id) { + if let Ok(name) = file_map.get_name(custom_diagnostic.file) { message.push('\n'); message.push_str(&format!("at {name}")); } diff --git a/noir/noir-repo/tooling/nargo_cli/src/errors.rs b/noir/noir-repo/tooling/nargo_cli/src/errors.rs index 9255d6fc6a69..fb241bcbd293 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/errors.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/errors.rs @@ -1,37 +1,11 @@ -use acvm::{acir::native_types::WitnessStackError, FieldElement}; -use nargo::{errors::CompileError, NargoError}; +use acvm::FieldElement; +use nargo::{NargoError, errors::CompileError}; use nargo_toml::ManifestError; use noir_debugger::errors::DapError; -use noirc_abi::{ - errors::{AbiError, InputParserError}, - input_parser::InputValue, - AbiReturnType, -}; +use noirc_abi::errors::AbiError; use std::path::PathBuf; use thiserror::Error; -#[derive(Debug, Error)] -pub(crate) enum FilesystemError { - #[error("Error: {} is not a valid path\nRun either `nargo compile` to generate missing build artifacts or `nargo prove` to construct a proof", .0.display())] - PathNotValid(PathBuf), - - #[error( - " Error: cannot find {0}.toml file.\n Expected location: {1:?} \n Please generate this file at the expected location." - )] - MissingTomlFile(String, PathBuf), - - /// Input parsing error - #[error(transparent)] - InputParserError(#[from] InputParserError), - - /// WitnessStack serialization error - #[error(transparent)] - WitnessStackSerialization(#[from] WitnessStackError), - - #[error("Error: could not deserialize build program: {0}")] - ProgramSerializationError(String), -} - #[derive(Debug, Error)] pub(crate) enum CliError { #[error("{0}")] @@ -43,13 +17,13 @@ pub(crate) enum CliError { #[error("Invalid package name {0}. Did you mean to use `--name`?")] InvalidPackageName(String), - /// ABI encoding/decoding error + /// Artifact CLI error #[error(transparent)] - AbiError(#[from] AbiError), + ArtifactError(#[from] noir_artifact_cli::errors::CliError), - /// Filesystem errors + /// ABI encoding/decoding error #[error(transparent)] - FilesystemError(#[from] FilesystemError), + AbiError(#[from] AbiError), #[error(transparent)] LspError(#[from] async_lsp::Error), @@ -68,10 +42,4 @@ pub(crate) enum CliError { /// Error from the compilation pipeline #[error(transparent)] CompileError(#[from] CompileError), - - #[error("Unexpected return value: expected {expected:?}; got {actual:?}")] - UnexpectedReturn { expected: InputValue, actual: Option }, - - #[error("Missing return witnesses; expected {expected:?}")] - MissingReturn { expected: AbiReturnType }, } diff --git a/noir/noir-repo/tooling/nargo_cli/src/main.rs b/noir/noir-repo/tooling/nargo_cli/src/main.rs index 3ea167b7ffad..33e18ce6f941 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/main.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/main.rs @@ -15,7 +15,10 @@ use std::env; use color_eyre::config::HookBuilder; use tracing_appender::rolling; -use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter}; +use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan}; + +// TODO: Currently only used by benches. +use noir_artifact_cli as _; const PANIC_MESSAGE: &str = "This is a bug. We may have already fixed this in newer versions of Nargo so try searching for similar issues at https://github.com/noir-lang/noir/issues/.\nIf there isn't an open issue for this bug, consider opening one at https://github.com/noir-lang/noir/issues/new?labels=bug&template=bug_report.yml"; @@ -28,7 +31,7 @@ fn main() { panic_hook.install(); if let Err(report) = cli::start_cli() { - eprintln!("{report}"); + eprintln!("{report:#}"); std::process::exit(1); } } @@ -42,6 +45,6 @@ fn setup_tracing() { let debug_file = rolling::daily(log_dir, "nargo-log"); subscriber.with_writer(debug_file).with_ansi(false).json().init(); } else { - subscriber.with_ansi(true).init(); + subscriber.with_writer(std::io::stderr).with_ansi(true).init(); } } diff --git a/noir/noir-repo/tooling/nargo_cli/tests/stdlib-props.rs b/noir/noir-repo/tooling/nargo_cli/tests/stdlib-props.rs index 780b34bf0c34..7c3794d03ab0 100644 --- a/noir/noir-repo/tooling/nargo_cli/tests/stdlib-props.rs +++ b/noir/noir-repo/tooling/nargo_cli/tests/stdlib-props.rs @@ -1,16 +1,15 @@ use std::{cell::RefCell, collections::BTreeMap, path::Path}; -use acvm::{acir::native_types::WitnessStack, AcirField, FieldElement}; +use acvm::{AcirField, FieldElement, acir::native_types::WitnessStack}; use iter_extended::vecmap; use nargo::{foreign_calls::DefaultForeignCallBuilder, ops::execute_program, parse_all}; use noirc_abi::input_parser::InputValue; use noirc_driver::{ - compile_main, file_manager_with_stdlib, prepare_crate, CompilationResult, CompileOptions, - CompiledProgram, CrateId, + CompilationResult, CompileOptions, CompiledProgram, CrateId, compile_main, + file_manager_with_stdlib, prepare_crate, }; use noirc_frontend::hir::Context; use proptest::prelude::*; -use sha3::Digest; /// Inputs and expected output of a snippet encoded in ABI format. #[derive(Debug)] @@ -78,8 +77,8 @@ fn run_snippet_proptest( Err(e) => panic!("failed to compile program; brillig = {force_brillig}:\n{source}\n{e:?}"), }; - let pedandic_solving = true; - let blackbox_solver = bn254_blackbox_solver::Bn254BlackBoxSolver(pedandic_solving); + let pedantic_solving = true; + let blackbox_solver = bn254_blackbox_solver::Bn254BlackBoxSolver(pedantic_solving); let foreign_call_executor = RefCell::new(DefaultForeignCallBuilder::default().build()); // Generate multiple input/output @@ -105,64 +104,6 @@ fn run_snippet_proptest( }); } -/// Run property tests on a code snippet which is assumed to execute a hashing function with the following signature: -/// -/// ```ignore -/// fn main(input: [u8; {max_len}], message_size: u32) -> pub [u8; 32] -/// ``` -/// -/// The calls are executed with and without forcing brillig, because it seems common for hash functions to run different -/// code paths based on `runtime::is_unconstrained()`. -fn run_hash_proptest( - // Different generic maximum input sizes to try. - max_lengths: &[usize], - // Some hash functions allow inputs which are less than the generic parameters, others don't. - variable_length: bool, - // Make the source code specialized for a given expected input size. - source: impl Fn(usize) -> String, - // Rust implementation of the hash function. - hash: fn(&[u8]) -> [u8; N], -) { - for max_len in max_lengths { - let max_len = *max_len; - // The maximum length is used to pick the generic version of the method. - let source = source(max_len); - // Hash functions runs differently depending on whether the code is unconstrained or not. - for force_brillig in [false, true] { - let length_strategy = - if variable_length { (0..=max_len).boxed() } else { Just(max_len).boxed() }; - // The actual input length can be up to the maximum. - let strategy = length_strategy - .prop_flat_map(|len| prop::collection::vec(any::(), len)) - .prop_map(move |mut msg| { - // The output is the hash of the data as it is. - let output = hash(&msg); - - // The input has to be padded to the maximum length. - let msg_size = msg.len(); - msg.resize(max_len, 0u8); - - let mut inputs = vec![("input", bytes_input(&msg))]; - - // Omit the `message_size` if the hash function doesn't support it. - if variable_length { - inputs.push(( - "message_size", - InputValue::Field(FieldElement::from(msg_size)), - )); - } - - SnippetInputOutput::new(inputs, bytes_input(&output)).with_description(format!( - "force_brillig = {force_brillig}, max_len = {max_len}" - )) - }) - .boxed(); - - run_snippet_proptest(source.clone(), force_brillig, strategy); - } - } -} - /// This is just a simple test to check that property testing works. #[test] fn fuzz_basic() { @@ -187,72 +128,6 @@ fn fuzz_basic() { run_snippet_proptest(program.to_string(), false, strategy); } -#[test] -fn fuzz_keccak256_equivalence() { - run_hash_proptest( - // XXX: Currently it fails with inputs >= 135 bytes - &[0, 1, 100, 134], - true, - |max_len| { - format!( - "fn main(input: [u8; {max_len}], message_size: u32) -> pub [u8; 32] {{ - std::hash::keccak256(input, message_size) - }}" - ) - }, - |data| sha3::Keccak256::digest(data).into(), - ); -} - -#[test] -#[should_panic] // Remove once fixed -fn fuzz_keccak256_equivalence_over_135() { - run_hash_proptest( - &[135, 150], - true, - |max_len| { - format!( - "fn main(input: [u8; {max_len}], message_size: u32) -> pub [u8; 32] {{ - std::hash::keccak256(input, message_size) - }}" - ) - }, - |data| sha3::Keccak256::digest(data).into(), - ); -} - -#[test] -fn fuzz_sha256_equivalence() { - run_hash_proptest( - &[0, 1, 200, 511, 512], - true, - |max_len| { - format!( - "fn main(input: [u8; {max_len}], message_size: u64) -> pub [u8; 32] {{ - std::hash::sha256_var(input, message_size) - }}" - ) - }, - |data| sha2::Sha256::digest(data).into(), - ); -} - -#[test] -fn fuzz_sha512_equivalence() { - run_hash_proptest( - &[0, 1, 200], - false, - |max_len| { - format!( - "fn main(input: [u8; {max_len}]) -> pub [u8; 64] {{ - std::hash::sha512::digest(input) - }}" - ) - }, - |data| sha2::Sha512::digest(data).into(), - ); -} - #[test] fn fuzz_poseidon2_equivalence() { use bn254_blackbox_solver::poseidon_hash; @@ -290,17 +165,13 @@ fn fuzz_poseidon2_equivalence() { #[test] fn fuzz_poseidon_equivalence() { - use ark_ff_v04::{BigInteger, PrimeField}; use light_poseidon::{Poseidon, PoseidonHasher}; let poseidon_hash = |inputs: &[FieldElement]| { - let mut poseidon = Poseidon::::new_circom(inputs.len()).unwrap(); - let frs: Vec = inputs - .iter() - .map(|f| ark_bn254_v04::Fr::from_be_bytes_mod_order(&f.to_be_bytes())) - .collect::>(); - let hash = poseidon.hash(&frs).expect("failed to hash"); - FieldElement::from_be_bytes_reduce(&hash.into_bigint().to_bytes_be()) + let mut poseidon = Poseidon::::new_circom(inputs.len()).unwrap(); + let frs: Vec = inputs.iter().map(|f| f.into_repr()).collect::>(); + let hash: ark_bn254::Fr = poseidon.hash(&frs).expect("failed to hash"); + FieldElement::from_repr(hash) }; // Noir has hashes up to length 16, but the reference library won't work with more than 12. @@ -332,12 +203,6 @@ fn fuzz_poseidon_equivalence() { } } -fn bytes_input(bytes: &[u8]) -> InputValue { - InputValue::Vec( - bytes.iter().map(|b| InputValue::Field(FieldElement::from(*b as u32))).collect(), - ) -} - fn field_vec_strategy(len: usize) -> impl Strategy> { // Generate Field elements from random 32 byte vectors. let field = prop::collection::vec(any::(), 32) diff --git a/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs b/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs index 2e3e3e2ae5af..6096b619e576 100644 --- a/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs +++ b/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs @@ -2,15 +2,15 @@ #![allow(clippy::items_after_test_module)] use clap::Parser; use fm::FileManager; -use nargo::foreign_calls::DefaultForeignCallBuilder; use nargo::PrintOutput; -use noirc_driver::{check_crate, file_manager_with_stdlib, CompileOptions}; +use nargo::foreign_calls::DefaultForeignCallBuilder; +use noirc_driver::{CompileOptions, check_crate, file_manager_with_stdlib}; use noirc_frontend::hir::FunctionNameMatch; use std::io::Write; use std::{collections::BTreeMap, path::PathBuf}; use nargo::{ - ops::{report_errors, run_test, TestStatus}, + ops::{TestStatus, report_errors, run_test}, package::{Package, PackageType}, parse_all, prepare_package, }; diff --git a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml index 710519712a2c..46dc61d30157 100644 --- a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml @@ -11,6 +11,7 @@ workspace = true [dependencies] noirc_frontend.workspace = true +noirc_errors.workspace = true serde.workspace = true toml.workspace = true thiserror.workspace = true diff --git a/noir/noir-repo/tooling/nargo_fmt/build.rs b/noir/noir-repo/tooling/nargo_fmt/build.rs index 988a7dcc94d5..f0dde56b0ca0 100644 --- a/noir/noir-repo/tooling/nargo_fmt/build.rs +++ b/noir/noir-repo/tooling/nargo_fmt/build.rs @@ -60,7 +60,7 @@ fn generate_formatter_tests(test_file: &mut File, test_data_dir: &Path) { let expected_output = r#"{output_source}"#; - let (parsed_module, _errors) = noirc_frontend::parse_program(input); + let (parsed_module, _errors) = noirc_frontend::parse_program_with_dummy_file(input); let config = nargo_fmt::Config::of("{config}", &std::path::PathBuf::new()).unwrap(); let fmt_text = nargo_fmt::format(input, parsed_module, &config); @@ -82,7 +82,7 @@ fn generate_formatter_tests(test_file: &mut File, test_data_dir: &Path) { fn format_idempotent_{test_name}() {{ let expected_output = r#"{output_source}"#; - let (parsed_module, _errors) = noirc_frontend::parse_program(expected_output); + let (parsed_module, _errors) = noirc_frontend::parse_program_with_dummy_file(expected_output); let config = nargo_fmt::Config::of("{config}", &std::path::PathBuf::new()).unwrap(); let fmt_text = nargo_fmt::format(expected_output, parsed_module, &config); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/chunks.rs b/noir/noir-repo/tooling/nargo_fmt/src/chunks.rs index fcef261284df..28e6d22f2c8e 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/chunks.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/chunks.rs @@ -113,11 +113,7 @@ impl Chunk { /// Returns the current chunk as a Group, if it is one. Otherwise returns None. pub(crate) fn group(self) -> Option { - if let Chunk::Group(group) = self { - Some(group) - } else { - None - } + if let Chunk::Group(group) = self { Some(group) } else { None } } } @@ -546,7 +542,7 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { /// Treating a `ChunkFormatter` as a `Formatter` in read-only mode is always fine, /// and reduces some boilerplate. -impl<'a, 'b> Deref for ChunkFormatter<'a, 'b> { +impl<'b> Deref for ChunkFormatter<'_, 'b> { type Target = Formatter<'b>; fn deref(&self) -> &Self::Target { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/config.rs b/noir/noir-repo/tooling/nargo_fmt/src/config.rs index f01afc87af2e..3950d267c5ea 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/config.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/config.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use crate::errors::ConfigError; macro_rules! config { - ($($field_name:ident: $field_ty:ty, $default_value:expr, $description:expr );+ $(;)*) => ( + ($($field_name:ident: $field_ty:ty, $default_value:expr_2021, $description:expr_2021 );+ $(;)*) => ( pub struct Config { $( #[doc = $description] diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter.rs index 2a8adb3fb28a..cff0b2c0619b 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter.rs @@ -1,10 +1,10 @@ use buffer::Buffer; use noirc_frontend::{ + ParsedModule, ast::Ident, hir::resolution::errors::Span, lexer::Lexer, token::{Keyword, SpannedToken, Token}, - ParsedModule, }; use crate::Config; @@ -80,7 +80,7 @@ pub(crate) struct Formatter<'a> { impl<'a> Formatter<'a> { pub(crate) fn new(source: &'a str, config: &'a Config) -> Self { - let lexer = Lexer::new(source).skip_comments(false).skip_whitespaces(false); + let lexer = Lexer::new_with_dummy_file(source).skip_comments(false).skip_whitespaces(false); let mut formatter = Self { config, source, @@ -107,6 +107,7 @@ impl<'a> Formatter<'a> { ); self.format_parsed_module(parsed_module, self.ignore_next); + self.buffer.trim_multiple_newlines(); } pub(crate) fn format_parsed_module(&mut self, parsed_module: ParsedModule, ignore_next: bool) { @@ -295,7 +296,7 @@ impl<'a> Formatter<'a> { self.ignore_next = false; let next_token = self.read_token_internal(); - self.token_span = next_token.to_span(); + self.token_span = next_token.span(); std::mem::replace(&mut self.token, next_token.into_token()) } @@ -303,7 +304,7 @@ impl<'a> Formatter<'a> { let token = self.lexer.next(); if let Some(token) = token { match token { - Ok(token) => token, + Ok(token) => token.into_spanned_token(), Err(err) => panic!("Expected lexer not to error, but got: {:?}", err), } } else { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/alias.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/alias.rs index d4c63ebdd9e1..96eedfd3d885 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/alias.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/alias.rs @@ -5,7 +5,7 @@ use noirc_frontend::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_type_alias(&mut self, type_alias: NoirTypeAlias) { self.write_indentation(); self.format_item_visibility(type_alias.visibility); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/attribute.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/attribute.rs index 19d5730a546c..d23a788eae4c 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/attribute.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/attribute.rs @@ -6,7 +6,7 @@ use crate::chunks::ChunkGroup; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_attributes(&mut self, attributes: Attributes) { let mut all_attributes = Vec::new(); for attribute in attributes.secondary { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/buffer.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/buffer.rs index 3e4bebef6081..0440f30eb586 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/buffer.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/buffer.rs @@ -56,6 +56,13 @@ impl Buffer { } } + /// Trim multiple newlines from the end of the buffer, keeping at most one. + pub(crate) fn trim_multiple_newlines(&mut self) { + while self.buffer.ends_with("\n\n") { + self.buffer.truncate(self.buffer.len() - 1); + } + } + pub(crate) fn contents(self) -> String { self.buffer } diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/comments_and_whitespace.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/comments_and_whitespace.rs index e20eb4291d18..da988e7039ec 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/comments_and_whitespace.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/comments_and_whitespace.rs @@ -7,7 +7,7 @@ const NEWLINE: &str = "\r\n"; #[cfg(not(windows))] const NEWLINE: &str = "\n"; -impl<'a> Formatter<'a> { +impl Formatter<'_> { /// Writes a single space, skipping any whitespace and comments. /// That is, suppose the next token is a big whitespace, possibly with multiple lines. /// Those are skipped but only one space is written. In this way if we have @@ -858,4 +858,11 @@ global x = 1; "; assert_format(src, expected); } + + #[test] + fn trims_newlines_from_the_end_of_the_file() { + let src = "global x: Field = 1;\n\n\n"; + let expected = "global x: Field = 1;\n"; + assert_format(src, expected); + } } diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/doc_comments.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/doc_comments.rs index f591f09e7299..6d25d7688d0b 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/doc_comments.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/doc_comments.rs @@ -2,7 +2,7 @@ use noirc_frontend::token::{DocStyle, Token}; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_inner_doc_comments(&mut self) { loop { self.skip_comments_and_whitespace(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/enums.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/enums.rs index 2d1182a941ce..beabf11fa46b 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/enums.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/enums.rs @@ -6,7 +6,7 @@ use noirc_frontend::{ use super::Formatter; use crate::chunks::ChunkGroup; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_enum(&mut self, noir_enum: NoirEnumeration) { self.format_secondary_attributes(noir_enum.attributes); self.write_indentation(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/expression.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/expression.rs index 1a6610364f28..6b46a4557a2b 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/expression.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/expression.rs @@ -6,7 +6,7 @@ use noirc_frontend::{ MemberAccessExpression, MethodCallExpression, PrefixExpression, TypePath, UnaryOp, UnresolvedTypeData, }, - token::{Keyword, Token}, + token::{Keyword, Token, TokenKind}, }; use crate::chunks::{Chunk, ChunkFormatter, ChunkGroup, GroupKind, GroupTag, TextChunk}; @@ -19,9 +19,21 @@ struct FormattedLambda { first_line_width: usize, } -impl<'a, 'b> ChunkFormatter<'a, 'b> { +impl ChunkFormatter<'_, '_> { pub(super) fn format_expression(&mut self, expression: Expression, group: &mut ChunkGroup) { - group.leading_comment(self.skip_comments_and_whitespace_chunk()); + group.leading_comment(self.chunk(|formatter| { + // Doc comments for an expression could come before a potential non-doc comment + if formatter.token.kind() == TokenKind::OuterDocComment { + formatter.format_outer_doc_comments_checking_safety(); + } + + formatter.skip_comments_and_whitespace(); + + // Or doc comments could come after a potential non-doc comment + if formatter.token.kind() == TokenKind::OuterDocComment { + formatter.format_outer_doc_comments_checking_safety(); + } + })); match expression.kind { ExpressionKind::Literal(literal) => self.format_literal(literal, group), @@ -86,9 +98,9 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { false, // force multiple lines )); } - ExpressionKind::Unsafe(block_expression, _span) => { + ExpressionKind::Unsafe(unsafe_xpression) => { group.group(self.format_unsafe_expression( - block_expression, + unsafe_xpression.block, false, // force multiple lines )); } @@ -349,7 +361,7 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { formatter.write_space(); formatter.write(&delimiter_start.to_string()); for token in tokens.0 { - formatter.write_source_span(token.to_span()); + formatter.write_source_span(token.span()); } formatter.write(&delimiter_end.to_string()); })); @@ -377,7 +389,6 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { ) -> ChunkGroup { let mut group = ChunkGroup::new(); group.text(self.chunk(|formatter| { - formatter.format_outer_doc_comments_checking_safety(); formatter.write_keyword(Keyword::Unsafe); formatter.write_space(); })); @@ -1156,7 +1167,9 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { ConstrainKind::Assert => Keyword::Assert, ConstrainKind::AssertEq => Keyword::AssertEq, ConstrainKind::Constrain => { - unreachable!("constrain always produces an error, and the formatter doesn't run when there are errors") + unreachable!( + "constrain always produces an error, and the formatter doesn't run when there are errors" + ) } }; @@ -1309,7 +1322,7 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { } } -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_empty_block_contents(&mut self) { if let Some(chunks) = self.chunk_formatter().empty_block_contents_chunk() { self.format_chunk_group(chunks); @@ -1331,7 +1344,7 @@ fn force_if_chunks_to_multiple_lines(group: &mut ChunkGroup, group_tag: GroupTag #[cfg(test)] mod tests { - use crate::{assert_format, assert_format_with_config, assert_format_with_max_width, Config}; + use crate::{Config, assert_format, assert_format_with_config, assert_format_with_max_width}; #[test] fn format_unit() { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/function.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/function.rs index ca905f3dcf88..f37683286d36 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/function.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/function.rs @@ -22,7 +22,7 @@ pub(super) struct FunctionToFormat { pub(super) skip_visibility: bool, } -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_function(&mut self, func: NoirFunction, skip_visibility: bool) { self.format_function_impl(FunctionToFormat { attributes: func.def.attributes, @@ -542,7 +542,6 @@ fn bar() { // noir-fmt:ignore fn baz() { let z = 3 ; } - "; assert_format(src, expected); } diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/generics.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/generics.rs index 4ee5a7439421..c457f4976d5e 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/generics.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/generics.rs @@ -5,7 +5,7 @@ use noirc_frontend::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_generics(&mut self, generics: Vec) { self.skip_comments_and_whitespace(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/global.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/global.rs index c351e15e3b66..4d6a43d0674f 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/global.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/global.rs @@ -6,7 +6,7 @@ use noirc_frontend::{ use super::Formatter; use crate::chunks::{ChunkFormatter, ChunkGroup}; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_global( &mut self, let_statement: LetStatement, @@ -20,7 +20,7 @@ impl<'a> Formatter<'a> { } } -impl<'a, 'b> ChunkFormatter<'a, 'b> { +impl ChunkFormatter<'_, '_> { pub(super) fn format_global( &mut self, let_statement: LetStatement, diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/impls.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/impls.rs index 71548dd5efac..b58b9381d172 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/impls.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/impls.rs @@ -2,7 +2,7 @@ use noirc_frontend::{ast::TypeImpl, token::Keyword}; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_impl(&mut self, type_impl: TypeImpl) { let has_where_clause = !type_impl.where_clause.is_empty(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/item.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/item.rs index 499acb8415c8..fa07478cee12 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/item.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/item.rs @@ -8,7 +8,7 @@ use crate::config::ImportsGranularity; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_items(&mut self, mut items: Vec, mut ignore_next: bool) { // Reverse the items because we'll be processing them one by one, and it's a bit // more efficient to pop than to shift. @@ -50,7 +50,7 @@ impl<'a> Formatter<'a> { ignore_next |= self.ignore_next; if ignore_next { - self.write_and_skip_span_without_formatting(item.span); + self.write_and_skip_span_without_formatting(item.location.span); return; } @@ -98,7 +98,7 @@ impl<'a> Formatter<'a> { let mut imports = Vec::new(); let item = items.last()?; - if self.span_has_comments(item.span) { + if self.span_has_comments(item.location.span) { return None; } @@ -112,16 +112,17 @@ impl<'a> Formatter<'a> { }; imports.push(use_tree); - let mut span_end = item.span.end(); + let mut span_end = item.location.span.end(); while let Some(item) = items.last() { - if self.span_is_import_group_separator(Span::from(span_end..item.span.start())) { + if self.span_is_import_group_separator(Span::from(span_end..item.location.span.start())) + { break; } let next_item_start = if items.len() > 1 { if let Some(next_item) = items.get(items.len() - 2) { - next_item.span.start() + next_item.location.span.start() } else { self.source.len() as u32 } @@ -129,8 +130,9 @@ impl<'a> Formatter<'a> { self.source.len() as u32 }; - if self.span_starts_with_trailing_comment(Span::from(item.span.end()..next_item_start)) - { + if self.span_starts_with_trailing_comment(Span::from( + item.location.span.end()..next_item_start, + )) { break; } @@ -147,7 +149,7 @@ impl<'a> Formatter<'a> { panic!("Expected import, got {:?}", item.kind); }; imports.push(use_tree); - span_end = item.span.end(); + span_end = item.location.span.end(); } Some(ImportGroup { imports, visibility, span_end }) diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/lvalue.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/lvalue.rs index 15fb7f5fa265..8dd1c76ab938 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/lvalue.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/lvalue.rs @@ -3,7 +3,7 @@ use noirc_frontend::{ast::LValue, token::Token}; use super::Formatter; use crate::chunks::ChunkGroup; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_lvalue(&mut self, lvalue: LValue) { // Parenthesized l-values exist but are not represented in the AST while let Token::LeftParen = self.token { @@ -12,12 +12,12 @@ impl<'a> Formatter<'a> { match lvalue { LValue::Ident(ident) => self.write_identifier(ident), - LValue::MemberAccess { object, field_name, span: _ } => { + LValue::MemberAccess { object, field_name, location: _ } => { self.format_lvalue(*object); self.write_token(Token::Dot); self.write_identifier_or_integer(field_name); } - LValue::Index { array, index, span: _ } => { + LValue::Index { array, index, location: _ } => { self.format_lvalue(*array); self.write_left_bracket(); let mut group = ChunkGroup::new(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/module.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/module.rs index e07d22c75863..b2cb250a90a6 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/module.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/module.rs @@ -1,10 +1,10 @@ use noirc_frontend::{ - ast::ModuleDeclaration, parser::ParsedSubModule, token::Keyword, ParsedModule, + ParsedModule, ast::ModuleDeclaration, parser::ParsedSubModule, token::Keyword, }; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_module_declaration(&mut self, module_declaration: ModuleDeclaration) { self.format_secondary_attributes(module_declaration.outer_attributes); self.write_indentation(); @@ -47,7 +47,7 @@ fn parsed_module_is_empty(parsed_module: &ParsedModule) -> bool { #[cfg(test)] mod tests { - use crate::{assert_format, assert_format_with_config, Config}; + use crate::{Config, assert_format, assert_format_with_config}; #[test] fn format_module_declaration() { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/path.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/path.rs index 2a46467bf72f..f79bb72eff16 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/path.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/path.rs @@ -5,7 +5,7 @@ use noirc_frontend::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_path(&mut self, path: Path) { self.skip_comments_and_whitespace(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/pattern.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/pattern.rs index 9a76612109bd..f0f94dfa7678 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/pattern.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/pattern.rs @@ -6,7 +6,7 @@ use noirc_frontend::{ use super::Formatter; use crate::chunks::ChunkGroup; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_pattern(&mut self, pattern: Pattern) { self.skip_comments_and_whitespace(); @@ -93,11 +93,7 @@ impl<'a> Formatter<'a> { } fn is_identifier_pattern(pattern: &Pattern, ident: &Ident) -> bool { - if let Pattern::Identifier(pattern_ident) = pattern { - pattern_ident == ident - } else { - false - } + if let Pattern::Identifier(pattern_ident) = pattern { pattern_ident == ident } else { false } } #[cfg(test)] diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/statement.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/statement.rs index f31cb80b9fdc..d70778ae5d11 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/statement.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/statement.rs @@ -8,7 +8,7 @@ use noirc_frontend::{ use crate::chunks::{ChunkFormatter, ChunkGroup, GroupKind}; -impl<'a, 'b> ChunkFormatter<'a, 'b> { +impl ChunkFormatter<'_, '_> { pub(super) fn format_statement( &mut self, statement: Statement, @@ -39,7 +39,7 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { if ignore_next { group.text(self.chunk(|formatter| { - formatter.write_and_skip_span_without_formatting(statement.span); + formatter.write_and_skip_span_without_formatting(statement.location.span); })); return; } @@ -52,9 +52,10 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { ExpressionKind::Block(block) => group.group(self.format_block_expression( block, true, // force multiple lines )), - ExpressionKind::Unsafe(block, _) => { + ExpressionKind::Unsafe(unsafe_expression) => { group.group(self.format_unsafe_expression( - block, true, // force multiple lines + unsafe_expression.block, + true, // force multiple lines )); } ExpressionKind::If(if_expression) => { @@ -408,7 +409,7 @@ mod tests { #[test] fn format_let_statement_with_unsafe_comment() { - let src = " fn foo() { + let src = " fn foo() { // Safety: some comment let x = unsafe { 1 } ; } "; let expected = "fn foo() { @@ -421,7 +422,7 @@ mod tests { #[test] fn format_let_statement_with_unsafe_doc_comment() { - let src = " fn foo() { + let src = " fn foo() { /// Safety: some comment let x = unsafe { 1 } ; } "; let expected = "fn foo() { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/structs.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/structs.rs index c26ab552f30c..9af10ff505c3 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/structs.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/structs.rs @@ -6,7 +6,7 @@ use noirc_frontend::{ use super::Formatter; use crate::chunks::ChunkGroup; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_struct(&mut self, noir_struct: NoirStruct) { self.format_secondary_attributes(noir_struct.attributes); self.write_indentation(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/trait_impl.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/trait_impl.rs index 896620c3bf80..9252082b26dd 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/trait_impl.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/trait_impl.rs @@ -5,7 +5,7 @@ use noirc_frontend::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_trait_impl(&mut self, trait_impl: NoirTraitImpl) { // skip synthetic trait impl's, e.g. generated from trait aliases if trait_impl.is_synthetic { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/traits.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/traits.rs index 175dcad61701..8195a30c2962 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/traits.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/traits.rs @@ -1,11 +1,12 @@ +use noirc_errors::Location; use noirc_frontend::{ ast::{NoirTrait, Param, Pattern, TraitItem, Visibility}, token::{Attributes, Keyword, Token}, }; -use super::{function::FunctionToFormat, Formatter}; +use super::{Formatter, function::FunctionToFormat}; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_trait(&mut self, noir_trait: NoirTrait) { self.format_secondary_attributes(noir_trait.attributes); self.write_indentation(); @@ -99,7 +100,7 @@ impl<'a> Formatter<'a> { visibility: Visibility::Private, pattern: Pattern::Identifier(name), typ, - span: Default::default(), // Doesn't matter + location: Location::dummy(), // Doesn't matter }) .collect(); @@ -299,6 +300,23 @@ mod tests { assert_format(src, expected); } + #[test] + fn format_trait_with_function_with_multiple_where_clauses() { + let src = " mod moo { trait Foo { + fn foo () where A: B, C: D; + } }"; + let expected = "mod moo { + trait Foo { + fn foo() + where + A: B, + C: D; + } +} +"; + assert_format(src, expected); + } + #[test] fn format_trait_with_function_with_visibility() { let src = " mod moo { trait Foo { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/type_expression.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/type_expression.rs index 95b0c0451566..8bebfd42f0cf 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/type_expression.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/type_expression.rs @@ -2,7 +2,7 @@ use noirc_frontend::{ast::UnresolvedTypeExpression, token::Token}; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_type_expression(&mut self, type_expr: UnresolvedTypeExpression) { self.skip_comments_and_whitespace(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/types.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/types.rs index e52704ddaa74..6a0e66bc1f98 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/types.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/types.rs @@ -5,7 +5,7 @@ use noirc_frontend::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_type(&mut self, typ: UnresolvedType) { self.skip_comments_and_whitespace(); @@ -178,7 +178,7 @@ mod tests { fn assert_format_type(src: &str, expected: &str) { let module_src = format!("type X = {};", src); - let (parsed_module, errors) = parser::parse_program(&module_src); + let (parsed_module, errors) = parser::parse_program_with_dummy_file(&module_src); if !errors.is_empty() { panic!("Expected no errors, got: {:?}", errors); } @@ -187,7 +187,7 @@ mod tests { let type_result = &type_result[..type_result.len() - 2]; similar_asserts::assert_eq!(type_result, expected); - let (parsed_module, errors) = parser::parse_program(&result); + let (parsed_module, errors) = parser::parse_program_with_dummy_file(&result); if !errors.is_empty() { panic!("Expected no errors in idempotent check, got: {:?}", errors); } diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree.rs index 98d63ef6611b..471dd00ca301 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree.rs @@ -7,7 +7,7 @@ use crate::chunks::{Chunk, ChunkFormatter, ChunkGroup}; use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_import(&mut self, use_tree: UseTree, visibility: ItemVisibility) { let group = self.chunk_formatter().format_import(use_tree, visibility); @@ -16,7 +16,7 @@ impl<'a> Formatter<'a> { } } -impl<'a, 'b> ChunkFormatter<'a, 'b> { +impl ChunkFormatter<'_, '_> { pub(super) fn format_import( &mut self, use_tree: UseTree, @@ -121,7 +121,7 @@ impl<'a, 'b> ChunkFormatter<'a, 'b> { #[cfg(test)] mod tests { - use crate::{assert_format_with_config, config::ImportsGranularity, Config}; + use crate::{Config, assert_format_with_config, config::ImportsGranularity}; fn assert_format(src: &str, expected: &str) { let config = Config { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree_merge.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree_merge.rs index a679e026435b..f64b0cb128ee 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree_merge.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/use_tree_merge.rs @@ -9,7 +9,7 @@ use crate::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn merge_and_format_imports( &mut self, imports: Vec, @@ -180,11 +180,7 @@ impl Ord for Segment { if let (Segment::Plain(self_string), Segment::Plain(other_string)) = (self, other) { // Case-insensitive comparison for plain segments let ordering = self_string.to_lowercase().cmp(&other_string.to_lowercase()); - if ordering == Ordering::Equal { - self_string.cmp(other_string) - } else { - ordering - } + if ordering == Ordering::Equal { self_string.cmp(other_string) } else { ordering } } else { order_number_ordering } @@ -299,7 +295,7 @@ fn merge_imports_in_tree(imports: Vec, mut tree: &mut ImportTree) { #[cfg(test)] mod tests { - use crate::{assert_format_with_config, config::ImportsGranularity, Config}; + use crate::{Config, assert_format_with_config, config::ImportsGranularity}; fn assert_format(src: &str, expected: &str) { let config = Config { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/visibility.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/visibility.rs index 27441b977bb2..2c2279ecb48f 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/visibility.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/visibility.rs @@ -4,7 +4,7 @@ use noirc_frontend::{ token::Keyword, }; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_item_visibility(&mut self, visibility: ItemVisibility) { self.skip_comments_and_whitespace(); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/formatter/where_clause.rs b/noir/noir-repo/tooling/nargo_fmt/src/formatter/where_clause.rs index 538d2ba8c011..c5ecb178bbb1 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/formatter/where_clause.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/formatter/where_clause.rs @@ -5,7 +5,7 @@ use noirc_frontend::{ use super::Formatter; -impl<'a> Formatter<'a> { +impl Formatter<'_> { pub(super) fn format_where_clause( &mut self, constraints: Vec, @@ -23,7 +23,8 @@ impl<'a> Formatter<'a> { // To format it we'll have to skip the second type `F` if we find a `+` token. let mut write_type = true; - for constraint in constraints { + let constrains_len = constraints.len(); + for (index, constraint) in constraints.into_iter().enumerate() { if write_type { self.write_line(); self.write_indentation(); @@ -45,7 +46,9 @@ impl<'a> Formatter<'a> { write_type = true; - if self.is_at(Token::Comma) { + if index < constrains_len - 1 { + self.write_token(Token::Comma); + } else if self.is_at(Token::Comma) { if write_trailing_comma_and_new_line { self.write_token(Token::Comma); } else { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/lib.rs b/noir/noir-repo/tooling/nargo_fmt/src/lib.rs index 05bfb0e7d570..bf3cf48184df 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/lib.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/lib.rs @@ -65,7 +65,7 @@ pub(crate) fn assert_format_with_max_width(src: &str, expected: &str, max_width: pub(crate) fn assert_format_with_config(src: &str, expected: &str, config: Config) { use noirc_frontend::parser; - let (parsed_module, errors) = parser::parse_program(src); + let (parsed_module, errors) = parser::parse_program_with_dummy_file(src); let errors: Vec<_> = errors.into_iter().filter(|error| !error.is_warning()).collect(); if !errors.is_empty() { panic!("Expected no errors, got: {:?}", errors); @@ -78,7 +78,7 @@ pub(crate) fn assert_format_with_config(src: &str, expected: &str, config: Confi similar_asserts::assert_eq!(result, expected); let src = &result; - let (parsed_module, errors) = parser::parse_program(src); + let (parsed_module, errors) = parser::parse_program_with_dummy_file(src); let errors: Vec<_> = errors.into_iter().filter(|error| !error.is_warning()).collect(); if !errors.is_empty() { panic!("Expected no errors in idempotent check, got: {:?}", errors); diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/databus.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/databus.nr index 0e9761ed52dc..15ba3fc12729 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/databus.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/databus.nr @@ -1,2 +1 @@ fn main(x: pub u8, y: call_data(0) u8) -> return_data u32 {} - diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr index afdb8883e158..571ec33f5d36 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr @@ -95,4 +95,3 @@ fn four() {} #[test(should_fail_with = "oops")] fn five() {} - diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/ignore.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/ignore.nr index 6e957539405b..1c84e178f34b 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/ignore.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/ignore.nr @@ -24,4 +24,3 @@ fn mk_array() { ]; let array = [1]; } - diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait.nr index 0467585fac39..8b64273a1a21 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait.nr @@ -1,2 +1 @@ trait Foo: Bar + Baz {} - diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait_alias.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait_alias.nr index 926f31602790..5b2eb0ce5523 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait_alias.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/trait_alias.nr @@ -83,4 +83,3 @@ fn main() { assert(0.foo_3().bar_3() == baz_3(0)); } - diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr index 265cba9604f6..ca32b3954e0f 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr @@ -12,4 +12,3 @@ fn main(x: pub u8, y: u8) { assert_eq(x, y); } } - diff --git a/noir/noir-repo/tooling/nargo_toml/src/errors.rs b/noir/noir-repo/tooling/nargo_toml/src/errors.rs index 7e1003d04f76..5aeb6a135f1b 100644 --- a/noir/noir-repo/tooling/nargo_toml/src/errors.rs +++ b/noir/noir-repo/tooling/nargo_toml/src/errors.rs @@ -28,7 +28,9 @@ pub enum ManifestError { #[error("Nargo.toml is badly formed, could not parse.\n\n {0}")] MalformedFile(#[from] toml::de::Error), - #[error("Unexpected workspace definition found in {0}. If you're attempting to load this as a dependency, you may need to add a `directory` field to your `Nargo.toml` to show which package within the workspace to use")] + #[error( + "Unexpected workspace definition found in {0}. If you're attempting to load this as a dependency, you may need to add a `directory` field to your `Nargo.toml` to show which package within the workspace to use" + )] UnexpectedWorkspace(PathBuf), #[error("Cannot find file {entry} which was specified as the `entry` field in {toml}")] @@ -80,16 +82,24 @@ pub enum ManifestError { #[allow(clippy::enum_variant_names)] #[derive(Error, Debug, PartialEq, Eq, Clone)] pub enum SemverError { - #[error("Invalid value for `compiler_version` in package {package_name}. Requirements may only refer to full releases")] + #[error( + "Invalid value for `compiler_version` in package {package_name}. Requirements may only refer to full releases" + )] InvalidCompilerVersionRequirement { package_name: CrateName, required_compiler_version: String }, - #[error("Incompatible compiler version in package {package_name}. Required compiler version is {required_compiler_version} but the compiler version is {compiler_version_found}.\n Update the compiler_version field in Nargo.toml to >={required_compiler_version} or compile this project with version {required_compiler_version}")] + #[error( + "Incompatible compiler version in package {package_name}. Required compiler version is {required_compiler_version} but the compiler version is {compiler_version_found}.\n Update the compiler_version field in Nargo.toml to >={required_compiler_version} or compile this project with version {required_compiler_version}" + )] IncompatibleVersion { package_name: CrateName, required_compiler_version: String, compiler_version_found: String, }, - #[error("Could not parse the required compiler version for package {package_name} in Nargo.toml. Error: {error}")] + #[error( + "Could not parse the required compiler version for package {package_name} in Nargo.toml. Error: {error}" + )] CouldNotParseRequiredVersion { package_name: String, error: String }, - #[error("Could not parse the package version for package {package_name} in Nargo.toml. Error: {error}")] + #[error( + "Could not parse the package version for package {package_name} in Nargo.toml. Error: {error}" + )] CouldNotParsePackageVersion { package_name: String, error: String }, } diff --git a/noir/noir-repo/tooling/nargo_toml/src/flock.rs b/noir/noir-repo/tooling/nargo_toml/src/flock.rs index 031dbcff647e..1433827c3fa9 100644 --- a/noir/noir-repo/tooling/nargo_toml/src/flock.rs +++ b/noir/noir-repo/tooling/nargo_toml/src/flock.rs @@ -1,4 +1,3 @@ -use fs2::FileExt; use std::{ fs::{File, OpenOptions}, path::Path, @@ -14,11 +13,11 @@ impl FileLock { pub(crate) fn new(file_path: &Path, lock_name: &str) -> std::io::Result { std::fs::create_dir_all(file_path.parent().expect("can't create lock on filesystem root"))?; let file = OpenOptions::new().create(true).truncate(false).write(true).open(file_path)?; - if file.try_lock_exclusive().is_err() { + if fs2::FileExt::try_lock_exclusive(&file).is_err() { eprintln!("Waiting for lock on {lock_name}..."); } - file.lock_exclusive()?; + fs2::FileExt::lock_exclusive(&file)?; Ok(Self { file }) } @@ -26,7 +25,7 @@ impl FileLock { impl Drop for FileLock { fn drop(&mut self) { - if let Err(e) = self.file.unlock() { + if let Err(e) = fs2::FileExt::unlock(&self.file) { tracing::warn!("failed to release lock: {e:?}"); } } diff --git a/noir/noir-repo/tooling/nargo_toml/src/lib.rs b/noir/noir-repo/tooling/nargo_toml/src/lib.rs index edf26411cf5a..b62884f28c4c 100644 --- a/noir/noir-repo/tooling/nargo_toml/src/lib.rs +++ b/noir/noir-repo/tooling/nargo_toml/src/lib.rs @@ -9,7 +9,7 @@ use std::{ }; use errors::SemverError; -use fm::{NormalizePath, FILE_EXTENSION}; +use fm::{FILE_EXTENSION, NormalizePath}; use nargo::{ package::{Dependency, Package, PackageType}, workspace::Workspace, @@ -52,11 +52,7 @@ pub fn find_file_manifest(current_path: &Path) -> Option { /// /// Returns a [ManifestError] if no parent directories of `current_path` contain a manifest file. pub fn find_root(current_path: &Path, workspace: bool) -> Result { - if workspace { - find_package_root(current_path) - } else { - find_file_root(current_path) - } + if workspace { find_package_root(current_path) } else { find_file_root(current_path) } } /// Returns the [PathBuf] of the directory containing the `Nargo.toml` by searching from `current_path` to the root of its [Path], @@ -190,7 +186,7 @@ impl PackageConfig { return Err(ManifestError::InvalidPackageType( root_dir.join("Nargo.toml"), invalid.to_string(), - )) + )); } None => return Err(ManifestError::MissingPackageType(root_dir.join("Nargo.toml"))), }; @@ -389,7 +385,7 @@ fn toml_to_workspace( let member = package_config.resolve_to_package(&nargo_toml.root_dir, &mut resolved)?; match &package_selection { PackageSelection::Selected(selected_name) if selected_name != &member.name => { - return Err(ManifestError::MissingSelectedPackage(member.name)) + return Err(ManifestError::MissingSelectedPackage(member.name)); } _ => Workspace { root_dir: nargo_toml.root_dir, @@ -540,7 +536,7 @@ mod tests { use test_case::test_matrix; - use crate::{find_root, Config, ManifestError}; + use crate::{Config, ManifestError, find_root}; #[test] fn parse_standard_toml() { @@ -649,7 +645,8 @@ mod tests { assert!( indent <= current_indent + 1, - "cannot increase indent by more than {INDENT_SIZE}; item = {item}, current_dir={}", current_dir.display() + "cannot increase indent by more than {INDENT_SIZE}; item = {item}, current_dir={}", + current_dir.display() ); // Go into the last created directory diff --git a/noir/noir-repo/tooling/nargo_toml/src/semver.rs b/noir/noir-repo/tooling/nargo_toml/src/semver.rs index ececa1b30dd0..83f31c71a5e2 100644 --- a/noir/noir-repo/tooling/nargo_toml/src/semver.rs +++ b/noir/noir-repo/tooling/nargo_toml/src/semver.rs @@ -1,4 +1,4 @@ -use crate::{errors::SemverError, ManifestError}; +use crate::{ManifestError, errors::SemverError}; use nargo::{ package::{Dependency, Package}, workspace::Workspace, @@ -37,7 +37,7 @@ fn semver_check_package(package: &Package, compiler_version: &Version) -> Result return Err(SemverError::CouldNotParseRequiredVersion { package_name: package.name.clone().into(), error: err.to_string(), - }) + }); } }; @@ -109,12 +109,16 @@ mod tests { expression_width: None, }; if let Err(err) = semver_check_package(&package, &compiler_version) { - panic!("semver check should have passed. compiler version is 0.1.0 and required version from the package is 0.1.0\n error: {err:?}") + panic!( + "semver check should have passed. compiler version is 0.1.0 and required version from the package is 0.1.0\n error: {err:?}" + ) }; package.compiler_required_version = Some("0.2.0".to_string()); let got_err = match semver_check_package(&package, &compiler_version) { - Ok(_) => panic!("semver check should have failed. compiler version is 0.1.0 and required version from the package is 0.2.0"), + Ok(_) => panic!( + "semver check should have failed. compiler version is 0.1.0 and required version from the package is 0.2.0" + ), Err(err) => err, }; @@ -168,15 +172,19 @@ mod tests { ); if let Err(err) = semver_check_package(&package, &compiler_version) { - panic!("semver check should have passed. compiler version is 0.1.0 and required version from the package is 0.1.0\n error: {err:?}") + panic!( + "semver check should have passed. compiler version is 0.1.0 and required version from the package is 0.1.0\n error: {err:?}" + ) }; package.dependencies.insert( CrateName::from_str("test_dep_invalid").unwrap(), Dependency::Local { package: invalid_dependency.clone() }, ); - let got_err = match semver_check_package(&package,&compiler_version) { - Ok(_) => panic!("semver check should have failed. compiler version is 0.1.0 and required version from the package is 0.2.0"), + let got_err = match semver_check_package(&package, &compiler_version) { + Ok(_) => panic!( + "semver check should have failed. compiler version is 0.1.0 and required version from the package is 0.2.0" + ), Err(err) => err, }; @@ -204,7 +212,9 @@ mod tests { }; if let Err(err) = semver_check_package(&package, &compiler_version) { - panic!("semver check should have passed. compiler version is 0.2.0 and required version from the package is >=0.1.0\n error: {err:?}") + panic!( + "semver check should have passed. compiler version is 0.2.0 and required version from the package is >=0.1.0\n error: {err:?}" + ) }; } @@ -244,7 +254,9 @@ mod tests { }; if let Err(err) = semver_check_package(&package, &compiler_version) { - panic!("semver check should have passed. compiler version is 0.1.0+build_data and required version from the package is 0.1.0\n The build data should be ignored\n error: {err:?}") + panic!( + "semver check should have passed. compiler version is 0.1.0+build_data and required version from the package is 0.1.0\n The build data should be ignored\n error: {err:?}" + ) }; } } diff --git a/noir/noir-repo/tooling/noir_codegen/package.json b/noir/noir-repo/tooling/noir_codegen/package.json index 1297658b14eb..5f818f694d18 100644 --- a/noir/noir-repo/tooling/noir_codegen/package.json +++ b/noir/noir-repo/tooling/noir_codegen/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "1.0.0-beta.2", + "version": "1.0.0-beta.3", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/noir/noir-repo/tooling/noir_js/package.json b/noir/noir-repo/tooling/noir_js/package.json index fee79c680de2..5269867708f0 100644 --- a/noir/noir-repo/tooling/noir_js/package.json +++ b/noir/noir-repo/tooling/noir_js/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "1.0.0-beta.2", + "version": "1.0.0-beta.3", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/noir/noir-repo/tooling/noir_js_types/package.json b/noir/noir-repo/tooling/noir_js_types/package.json index 4cf637db0db8..31a1e6fdb79a 100644 --- a/noir/noir-repo/tooling/noir_js_types/package.json +++ b/noir/noir-repo/tooling/noir_js_types/package.json @@ -4,7 +4,7 @@ "The Noir Team " ], "packageManager": "yarn@3.5.1", - "version": "1.0.0-beta.2", + "version": "1.0.0-beta.3", "license": "(MIT OR Apache-2.0)", "homepage": "https://noir-lang.org/", "repository": { diff --git a/noir/noir-repo/tooling/noirc_abi/proptest-regressions/input_parser/json.txt b/noir/noir-repo/tooling/noirc_abi/proptest-regressions/input_parser/json.txt index 19de8eeaf489..66aeed8f1202 100644 --- a/noir/noir-repo/tooling/noirc_abi/proptest-regressions/input_parser/json.txt +++ b/noir/noir-repo/tooling/noirc_abi/proptest-regressions/input_parser/json.txt @@ -5,3 +5,4 @@ # It is recommended to check this file in to source control so that # everyone who runs the test benefits from these saved cases. cc b3f9ae88d54944ca274764f4d99a2023d4b0ac09beb89bc599cbba1e45dd3620 # shrinks to (typ, value) = (Integer { sign: Signed, width: 1 }, -1) +cc afc4c7b26dcb04c8bb68af61dd8422745802d5a722b2200a9cb69a8425154b0a # shrinks to (abi, input_map) = (Abi { parameters: [AbiParameter { name: "A", typ: Struct { path: "\u{8}\u{6d174}\0:.Ѩ**¥\u{feff}`\u{4}o\u{55a3b}2=\u{6}:<", fields: [("\u{10bba4}Y&\u{83c23}?${\u{dc4b5}Z𬰫'º\u{1b}\u{37592}\u{feff}Ѩ.\u{4b753}c\u{736c6}", Integer { sign: Signed, width: 128 })] }, visibility: Public }], return_type: Some(AbiReturnType { abi_type: Tuple { fields: [Array { length: 1, typ: Field }, Tuple { fields: [Boolean] }, Array { length: 5, typ: Struct { path: "|\u{99332}?\u{7}\u{5131a}{q", fields: [("*🕴\u{d41d7}?\r*&O\\\u{c01a6}5{\t\u{9a8f1}\"\u{464e9}hM\\", Integer { sign: Signed, width: 83 }), ("ä:$\u{d4b96}\u{911ce}\u{1cc1c}EL{.¥🕴\u{36eb8}\u{feff}\u{a0}\u{40267}.🕴🕴{\u{97de2}\u{df645}\u{b}B}\u{ba0d1}(iter: impl Iterator) { proptest::prop_compose! { pub(super) fn arb_field_from_integer(bit_size: u32)(value: u128)-> FieldElement { - let width = (bit_size % 128).clamp(1, 127); - let max_value = 2u128.pow(width) - 1; + let width = (bit_size % 129).clamp(1, 128); + let max_value = if bit_size == 128 { u128::MAX } else { (1u128 << width) - 1 }; FieldElement::from(value.clamp(0, max_value)) } } @@ -86,7 +86,7 @@ fn arb_primitive_abi_type() -> SBoxedStrategy { Just(AbiType::Field), Just(AbiType::Boolean), any::<(Sign, u32)>().prop_map(|(sign, width)| { - let width = (width % 128).clamp(1, 127); + let width = (width % 129).clamp(1, 128); AbiType::Integer { sign, width } }), // restrict length of strings to avoid running out of memory diff --git a/noir/noir-repo/tooling/noirc_abi/src/errors.rs b/noir/noir-repo/tooling/noirc_abi/src/errors.rs index c46945d8ff22..f08f7b217219 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/errors.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/errors.rs @@ -1,8 +1,8 @@ use crate::{ - input_parser::{InputTypecheckingError, InputValue}, AbiType, + input_parser::{InputTypecheckingError, InputValue}, }; -use acvm::{acir::native_types::Witness, AcirField, FieldElement}; +use acvm::{AcirField, FieldElement, acir::native_types::Witness}; use thiserror::Error; #[derive(Debug, Error)] @@ -13,9 +13,13 @@ pub enum InputParserError { "The value passed for parameter `{arg_name}` is invalid:\nExpected witness values to be integers, but `{value}` failed with `{error}`" )] ParseStr { arg_name: String, value: String, error: String }, - #[error("The value passed for parameter `{arg_name}` is invalid:\nValue {value} is less than minimum allowed value of {min}")] + #[error( + "The value passed for parameter `{arg_name}` is invalid:\nValue {value} is less than minimum allowed value of {min}" + )] InputUnderflowsMinimum { arg_name: String, value: String, min: String }, - #[error("The value passed for parameter `{arg_name}` is invalid:\nValue {value} exceeds maximum allowed value of {max}")] + #[error( + "The value passed for parameter `{arg_name}` is invalid:\nValue {value} exceeds maximum allowed value of {max}" + )] InputOverflowsMaximum { arg_name: String, value: String, max: String }, #[error( "The value passed for parameter `{arg_name}` is invalid:\nValue {value} exceeds field modulus. Values must fall within [0, {})", @@ -58,9 +62,13 @@ pub enum AbiError { "Could not read witness value at index {witness_index:?} (required for parameter \"{name}\")" )] MissingParamWitnessValue { name: String, witness_index: Witness }, - #[error("Attempted to write to witness index {0:?} but it is already initialized to a different value")] + #[error( + "Attempted to write to witness index {0:?} but it is already initialized to a different value" + )] InconsistentWitnessAssignment(Witness), - #[error("The return value is expected to be a {return_type:?} but found incompatible value {value:?}")] + #[error( + "The return value is expected to be a {return_type:?} but found incompatible value {value:?}" + )] ReturnTypeMismatch { return_type: AbiType, value: InputValue }, #[error("No return value is expected but received {0:?}")] UnexpectedReturnValue(InputValue), diff --git a/noir/noir-repo/tooling/noirc_abi/src/input_parser/json.rs b/noir/noir-repo/tooling/noirc_abi/src/input_parser/json.rs index 7b2e8be454bf..f8b73ad05a5c 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/input_parser/json.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/input_parser/json.rs @@ -1,8 +1,8 @@ use super::{ - field_to_signed_hex, parse_integer_to_signed, parse_str_to_field, parse_str_to_signed, - InputValue, + InputValue, field_to_signed_hex, parse_integer_to_signed, parse_str_to_field, + parse_str_to_signed, }; -use crate::{errors::InputParserError, Abi, AbiType, MAIN_RETURN_NAME}; +use crate::{Abi, AbiType, MAIN_RETURN_NAME, errors::InputParserError}; use acvm::{AcirField, FieldElement}; use iter_extended::{try_btree_map, try_vecmap}; use serde::{Deserialize, Serialize}; @@ -225,9 +225,9 @@ mod test { use proptest::prelude::*; use crate::{ - arbitrary::arb_abi_and_input_map, - input_parser::{arbitrary::arb_signed_integer_type_and_value, json::JsonTypes, InputValue}, AbiType, + arbitrary::arb_abi_and_input_map, + input_parser::{InputValue, arbitrary::arb_signed_integer_type_and_value, json::JsonTypes}, }; use super::{parse_json, serialize_to_json}; diff --git a/noir/noir-repo/tooling/noirc_abi/src/input_parser/mod.rs b/noir/noir-repo/tooling/noirc_abi/src/input_parser/mod.rs index fce627c8d313..72aef1f48bb3 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/input_parser/mod.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/input_parser/mod.rs @@ -27,7 +27,9 @@ pub enum InputValue { pub enum InputTypecheckingError { #[error("Value {value:?} does not fall within range of allowable values for a {typ:?}")] OutsideOfValidRange { path: String, typ: AbiType, value: InputValue }, - #[error("Type {typ:?} is expected to have length {expected_length} but value {value:?} has length {actual_length}")] + #[error( + "Type {typ:?} is expected to have length {expected_length} but value {value:?} has length {actual_length}" + )] LengthMismatch { path: String, typ: AbiType, @@ -35,9 +37,13 @@ pub enum InputTypecheckingError { expected_length: usize, actual_length: usize, }, - #[error("Could not find value for required field `{expected_field}`. Found values for fields {found_fields:?}")] + #[error( + "Could not find value for required field `{expected_field}`. Found values for fields {found_fields:?}" + )] MissingField { path: String, expected_field: String, found_fields: Vec }, - #[error("Additional unexpected field was provided for type {typ:?}. Found field named `{extra_field}`")] + #[error( + "Additional unexpected field was provided for type {typ:?}. Found field named `{extra_field}`" + )] UnexpectedField { path: String, typ: AbiType, extra_field: String }, #[error("Type {typ:?} and value {value:?} do not match")] IncompatibleTypes { path: String, typ: AbiType, value: InputValue }, @@ -242,8 +248,8 @@ mod serialization_tests { use strum::IntoEnumIterator; use crate::{ - input_parser::InputValue, Abi, AbiParameter, AbiReturnType, AbiType, AbiVisibility, Sign, - MAIN_RETURN_NAME, + Abi, AbiParameter, AbiReturnType, AbiType, AbiVisibility, MAIN_RETURN_NAME, Sign, + input_parser::InputValue, }; use super::Format; @@ -357,8 +363,11 @@ fn parse_str_to_signed( error: err_msg.to_string(), }) .and_then(|bigint| { - let max = BigInt::from(2_u128.pow(width - 1) - 1); - let min = BigInt::from(-(2_i128.pow(width - 1))); + let min = if width == 128 { i128::MIN } else { -(1 << (width - 1)) }; + let max = if width == 128 { i128::MAX } else { (1 << (width - 1)) - 1 }; + + let max = BigInt::from(max); + let min = BigInt::from(min); if bigint < min { return Err(InputParserError::InputUnderflowsMinimum { @@ -398,8 +407,8 @@ fn parse_integer_to_signed( width: u32, arg_name: &str, ) -> Result { - let min = -(1 << (width - 1)); - let max = (1 << (width - 1)) - 1; + let min = if width == 128 { i128::MIN } else { -(1 << (width - 1)) }; + let max = if width == 128 { i128::MAX } else { (1 << (width - 1)) - 1 }; if integer < min { return Err(InputParserError::InputUnderflowsMinimum { @@ -417,8 +426,12 @@ fn parse_integer_to_signed( }); } - let integer = if integer < 0 { (1 << width) + integer } else { integer }; - Ok(FieldElement::from(integer as u128)) + let integer = if integer < 0 { + FieldElement::from(2u32).pow(&width.into()) + FieldElement::from(integer) + } else { + FieldElement::from(integer) + }; + Ok(integer) } fn field_from_big_uint(bigint: BigUint) -> FieldElement { @@ -439,9 +452,9 @@ fn field_from_big_int(bigint: BigInt) -> FieldElement { fn field_to_signed_hex(f: FieldElement, bit_size: u32) -> String { let f_u128 = f.to_u128(); - let max = 2_u128.pow(bit_size - 1) - 1; + let max = if bit_size == 128 { i128::MAX as u128 } else { (1 << (bit_size - 1)) - 1 }; if f_u128 > max { - let f = FieldElement::from(2_u128.pow(bit_size) - f_u128); + let f = FieldElement::from(2u32).pow(&bit_size.into()) - f; format!("-0x{}", f.to_hex()) } else { format!("0x{}", f.to_hex()) @@ -454,7 +467,7 @@ mod test { use num_bigint::BigUint; use strum::IntoEnumIterator; - use super::{parse_str_to_field, parse_str_to_signed, Format}; + use super::{Format, parse_str_to_field, parse_str_to_signed}; fn big_uint_from_field(field: FieldElement) -> BigUint { BigUint::from_bytes_be(&field.to_be_bytes()) diff --git a/noir/noir-repo/tooling/noirc_abi/src/input_parser/toml.rs b/noir/noir-repo/tooling/noirc_abi/src/input_parser/toml.rs index aaa3358f4fca..2e2d12f853c5 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/input_parser/toml.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/input_parser/toml.rs @@ -1,8 +1,8 @@ use super::{ - field_to_signed_hex, parse_integer_to_signed, parse_str_to_field, parse_str_to_signed, - InputValue, + InputValue, field_to_signed_hex, parse_integer_to_signed, parse_str_to_field, + parse_str_to_signed, }; -use crate::{errors::InputParserError, Abi, AbiType, MAIN_RETURN_NAME}; +use crate::{Abi, AbiType, MAIN_RETURN_NAME, errors::InputParserError}; use acvm::{AcirField, FieldElement}; use iter_extended::{try_btree_map, try_vecmap}; use serde::{Deserialize, Serialize}; @@ -210,9 +210,9 @@ mod test { use proptest::prelude::*; use crate::{ - arbitrary::arb_abi_and_input_map, - input_parser::{arbitrary::arb_signed_integer_type_and_value, toml::TomlTypes, InputValue}, AbiType, + arbitrary::arb_abi_and_input_map, + input_parser::{InputValue, arbitrary::arb_signed_integer_type_and_value, toml::TomlTypes}, }; use super::{parse_toml, serialize_to_toml}; diff --git a/noir/noir-repo/tooling/noirc_abi/src/lib.rs b/noir/noir-repo/tooling/noirc_abi/src/lib.rs index 5f5f3748bc46..17f9a07c2217 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/lib.rs @@ -4,11 +4,11 @@ #![warn(clippy::semicolon_if_nothing_returned)] use acvm::{ + AcirField, FieldElement, acir::{ circuit::ErrorSelector, native_types::{Witness, WitnessMap}, }, - AcirField, FieldElement, }; use errors::AbiError; use input_parser::InputValue; @@ -202,7 +202,7 @@ impl Abi { let has_public_return = self .return_type .as_ref() - .map_or(false, |typ| matches!(typ.visibility, AbiVisibility::Public)); + .is_some_and(|typ| matches!(typ.visibility, AbiVisibility::Public)); has_public_args || has_public_return } @@ -263,7 +263,7 @@ impl Abi { encoded_inputs.push(encoded_return_fields); } (None, Some(return_value)) => { - return Err(AbiError::UnexpectedReturnValue(return_value)) + return Err(AbiError::UnexpectedReturnValue(return_value)); } // We allow not passing a return value despite the circuit defining one // in order to generate the initial partial witness. diff --git a/noir/noir-repo/tooling/noirc_abi_wasm/Cargo.toml b/noir/noir-repo/tooling/noirc_abi_wasm/Cargo.toml index b00d580515ef..1d9dc02939fc 100644 --- a/noir/noir-repo/tooling/noirc_abi_wasm/Cargo.toml +++ b/noir/noir-repo/tooling/noirc_abi_wasm/Cargo.toml @@ -31,5 +31,5 @@ getrandom = { workspace = true, features = ["js"] } [build-dependencies] build-data.workspace = true -[dev-dependencies] +[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dev-dependencies] wasm-bindgen-test.workspace = true 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/tooling/noirc_abi_wasm/package.json b/noir/noir-repo/tooling/noirc_abi_wasm/package.json index 769efe1d92a1..0a0941be34a8 100644 --- a/noir/noir-repo/tooling/noirc_abi_wasm/package.json +++ b/noir/noir-repo/tooling/noirc_abi_wasm/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "1.0.0-beta.2", + "version": "1.0.0-beta.3", "license": "(MIT OR Apache-2.0)", "homepage": "https://noir-lang.org/", "repository": { diff --git a/noir/noir-repo/tooling/noirc_abi_wasm/src/js_witness_map.rs b/noir/noir-repo/tooling/noirc_abi_wasm/src/js_witness_map.rs index a82621822e46..8b751426ac20 100644 --- a/noir/noir-repo/tooling/noirc_abi_wasm/src/js_witness_map.rs +++ b/noir/noir-repo/tooling/noirc_abi_wasm/src/js_witness_map.rs @@ -1,11 +1,11 @@ //! This can most likely be imported from acvm_js to avoid redefining it here. use acvm::{ - acir::native_types::{Witness, WitnessMap}, AcirField, FieldElement, + acir::native_types::{Witness, WitnessMap}, }; use js_sys::{JsString, Map}; -use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; +use wasm_bindgen::prelude::{JsValue, wasm_bindgen}; // WitnessMap #[wasm_bindgen] @@ -65,21 +65,21 @@ pub(crate) fn field_element_to_js_string(field_element: &FieldElement) -> JsStri format!("0x{}", field_element.to_hex()).into() } -#[cfg(test)] +#[cfg(all(test, any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))] mod test { - use wasm_bindgen_test::wasm_bindgen_test as test; + use wasm_bindgen_test::*; use std::collections::BTreeMap; use acvm::{ - acir::native_types::{Witness, WitnessMap}, AcirField, FieldElement, + acir::native_types::{Witness, WitnessMap}, }; use wasm_bindgen::JsValue; use crate::JsWitnessMap; - #[test] + #[wasm_bindgen_test] fn test_witness_map_to_js() { let witness_map = BTreeMap::from([ (Witness(1), FieldElement::one()), diff --git a/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs b/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs index 79aecafa7426..390f4353bb2d 100644 --- a/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs @@ -6,24 +6,23 @@ use getrandom as _; use acvm::{ + FieldElement, acir::{ circuit::RawAssertionPayload, native_types::{WitnessMap, WitnessStack}, }, - FieldElement, }; use iter_extended::try_btree_map; use noirc_abi::{ - decode_value, display_abi_error, + Abi, AbiErrorType, MAIN_RETURN_NAME, decode_value, display_abi_error, errors::InputParserError, - input_parser::{json::JsonTypes, InputValue}, - Abi, AbiErrorType, MAIN_RETURN_NAME, + input_parser::{InputValue, json::JsonTypes}, }; use serde::Serialize; use std::collections::BTreeMap; use gloo_utils::format::JsValueSerdeExt; -use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; +use wasm_bindgen::{JsValue, prelude::wasm_bindgen}; mod errors; mod js_witness_map; diff --git a/noir/noir-repo/tooling/noirc_artifacts/src/contract.rs b/noir/noir-repo/tooling/noirc_artifacts/src/contract.rs index 0649a34e6255..9f8f7019ff13 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/src/contract.rs +++ b/noir/noir-repo/tooling/noirc_artifacts/src/contract.rs @@ -1,6 +1,6 @@ -use acvm::{acir::circuit::Program, FieldElement}; +use acvm::{FieldElement, acir::circuit::Program}; use noirc_abi::{Abi, AbiType, AbiValue}; -use noirc_driver::{CompiledContract, CompiledContractOutputs, ContractFunction}; +use noirc_driver::{CompiledContract, CompiledContractOutputs, CompiledProgram, ContractFunction}; use serde::{Deserialize, Serialize}; use noirc_driver::DebugFile; @@ -9,6 +9,8 @@ use std::collections::{BTreeMap, HashMap}; use fm::FileId; +use super::{deserialize_hash, serialize_hash}; + #[derive(Clone, Serialize, Deserialize, Debug)] pub struct ContractOutputsArtifact { pub structs: HashMap>, @@ -47,6 +49,14 @@ impl From for ContractArtifact { } } +impl ContractArtifact { + pub fn function_as_compiled_program(&self, function_name: &str) -> Option { + self.functions.iter().find(|f| f.name == function_name).map(|f| { + f.clone().into_compiled_program(self.noir_version.clone(), self.file_map.clone()) + }) + } +} + /// Each function in the contract will be compiled as a separate noir program. /// /// A contract function unlike a regular Noir program however can have additional properties. @@ -55,6 +65,12 @@ impl From for ContractArtifact { pub struct ContractFunctionArtifact { pub name: String, + /// Hash of the [`Program`][noirc_frontend::monomorphization::ast::Program] from which the [`ContractFunction`] + /// was compiled. + #[serde(default)] // For backwards compatibility (it was missing). + #[serde(serialize_with = "serialize_hash", deserialize_with = "deserialize_hash")] + pub hash: u64, + pub is_unconstrained: bool, pub custom_attributes: Vec, @@ -72,18 +88,41 @@ pub struct ContractFunctionArtifact { deserialize_with = "ProgramDebugInfo::deserialize_compressed_base64_json" )] pub debug_symbols: ProgramDebugInfo, - + #[serde(default)] // For backwards compatibility (it was missing). + pub names: Vec, pub brillig_names: Vec, } +impl ContractFunctionArtifact { + pub fn into_compiled_program( + self, + noir_version: String, + file_map: BTreeMap, + ) -> CompiledProgram { + CompiledProgram { + noir_version, + hash: self.hash, + program: self.bytecode, + abi: self.abi, + debug: self.debug_symbols.debug_infos, + file_map, + warnings: Vec::new(), + names: self.names, + brillig_names: self.brillig_names, + } + } +} + impl From for ContractFunctionArtifact { fn from(func: ContractFunction) -> Self { ContractFunctionArtifact { name: func.name, + hash: func.hash, is_unconstrained: func.is_unconstrained, custom_attributes: func.custom_attributes, abi: func.abi, bytecode: func.bytecode, + names: func.names, brillig_names: func.brillig_names, debug_symbols: ProgramDebugInfo { debug_infos: func.debug }, } diff --git a/noir/noir-repo/tooling/noirc_artifacts/src/debug.rs b/noir/noir-repo/tooling/noirc_artifacts/src/debug.rs index 5c47f1f2652c..4924a9f1ca19 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/src/debug.rs +++ b/noir/noir-repo/tooling/noirc_artifacts/src/debug.rs @@ -1,6 +1,6 @@ use codespan_reporting::files::{Error, Files, SimpleFile}; use noirc_driver::{CompiledContract, CompiledProgram, DebugFile}; -use noirc_errors::{debug_info::DebugInfo, Location}; +use noirc_errors::{Location, debug_info::DebugInfo}; use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeMap, BTreeSet}, @@ -204,12 +204,12 @@ mod tests { use crate::debug::DebugArtifact; use acvm::acir::circuit::OpcodeLocation; use fm::FileManager; - use noirc_errors::{debug_info::DebugInfo, Location, Span}; + use noirc_errors::{Location, Span, debug_info::DebugInfo}; use std::collections::BTreeMap; use std::ops::Range; use std::path::Path; use std::path::PathBuf; - use tempfile::{tempdir, TempDir}; + use tempfile::{TempDir, tempdir}; // Returns the absolute path to the file fn create_dummy_file(dir: &TempDir, file_name: &Path) -> PathBuf { diff --git a/noir/noir-repo/tooling/noirc_artifacts/src/lib.rs b/noir/noir-repo/tooling/noirc_artifacts/src/lib.rs index 77873ed94098..337d5d8550da 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_artifacts/src/lib.rs @@ -9,7 +9,48 @@ //! Should any projects require/desire a different artifact format, it's expected that they will write a transformer //! to generate them using these artifacts as a starting point. +use serde::{Deserializer, Serializer, de::Visitor}; + pub mod contract; pub mod debug; mod debug_vars; pub mod program; + +/// Serialize `hash` as `String`, so that it doesn't get truncated in Javascript. +fn serialize_hash(hash: &u64, s: S) -> Result +where + S: Serializer, +{ + s.serialize_str(&hash.to_string()) +} + +/// Deserialize `hash` from `String` in JSON. +fn deserialize_hash<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + use serde::de::Error; + + // Backwards compatible with `hash` serialized as a number. + struct StringOrU64; + + impl Visitor<'_> for StringOrU64 { + type Value = u64; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("String or u64") + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + value.parse().map_err(E::custom) + } + + fn visit_u64(self, value: u64) -> Result { + Ok(value) + } + } + deserializer.deserialize_any(StringOrU64) +} diff --git a/noir/noir-repo/tooling/noirc_artifacts/src/program.rs b/noir/noir-repo/tooling/noirc_artifacts/src/program.rs index ffffc6345d58..fc452fcb2afd 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/src/program.rs +++ b/noir/noir-repo/tooling/noirc_artifacts/src/program.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; -use acvm::acir::circuit::Program; use acvm::FieldElement; +use acvm::acir::circuit::Program; use fm::FileId; use noirc_abi::Abi; use noirc_driver::CompiledProgram; @@ -9,6 +9,8 @@ use noirc_driver::DebugFile; use noirc_errors::debug_info::ProgramDebugInfo; use serde::{Deserialize, Serialize}; +use super::{deserialize_hash, serialize_hash}; + #[derive(Clone, Serialize, Deserialize, Debug)] pub struct ProgramArtifact { pub noir_version: String, @@ -17,6 +19,7 @@ pub struct ProgramArtifact { /// was compiled. /// /// Used to short-circuit compilation in the case of the source code not changing since the last compilation. + #[serde(serialize_with = "serialize_hash", deserialize_with = "deserialize_hash")] pub hash: u64, pub abi: Abi, diff --git a/noir/noir-repo/tooling/noirc_artifacts_info/src/lib.rs b/noir/noir-repo/tooling/noirc_artifacts_info/src/lib.rs index 6f4c80accbdd..b1db98f21579 100644 --- a/noir/noir-repo/tooling/noirc_artifacts_info/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_artifacts_info/src/lib.rs @@ -1,7 +1,7 @@ use acvm::acir::circuit::ExpressionWidth; use iter_extended::vecmap; use noirc_artifacts::program::ProgramArtifact; -use prettytable::{row, table, Row}; +use prettytable::{Row, row, table}; use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; use serde::Serialize; diff --git a/noir/noir-repo/tooling/profiler/Cargo.toml b/noir/noir-repo/tooling/profiler/Cargo.toml index 798a21ea0d6c..b61271e03ef9 100644 --- a/noir/noir-repo/tooling/profiler/Cargo.toml +++ b/noir/noir-repo/tooling/profiler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "noir_profiler" -description = "Profiler for noir circuits" +description = "Profiler for Noir circuits" version.workspace = true authors.workspace = true edition.workspace = true @@ -32,8 +32,9 @@ im.workspace = true acir.workspace = true nargo.workspace = true noirc_errors.workspace = true -noirc_abi.workspace = true noirc_evaluator.workspace = true +noir_artifact_cli.workspace = true +thiserror.workspace = true # Logs tracing-subscriber.workspace = true @@ -43,4 +44,3 @@ tracing-appender = "0.2.3" noirc_abi.workspace = true noirc_driver.workspace = true tempfile.workspace = true - diff --git a/noir/noir-repo/tooling/profiler/src/cli/execution_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/execution_flamegraph_cmd.rs index dbb2a2c38bcd..8aaf1c0ad851 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/execution_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/execution_flamegraph_cmd.rs @@ -1,18 +1,23 @@ use std::path::{Path, PathBuf}; +use acir::circuit::Opcode; use acir::circuit::OpcodeLocation; use clap::Args; use color_eyre::eyre::{self, Context}; -use nargo::foreign_calls::DefaultForeignCallBuilder; use nargo::PrintOutput; +use nargo::errors::try_to_diagnose_runtime_error; +use nargo::foreign_calls::DefaultForeignCallBuilder; +use noir_artifact_cli::fs::artifact::read_program_from_file; +use noir_artifact_cli::fs::inputs::read_inputs_from_file; +use noirc_artifacts::program::ProgramArtifact; +use crate::errors::{CliError, report_error}; use crate::flamegraph::{BrilligExecutionSample, FlamegraphGenerator, InfernoFlamegraphGenerator}; -use crate::fs::{read_inputs_from_file, read_program_from_file}; use crate::opcode_formatter::format_brillig_opcode; use bn254_blackbox_solver::Bn254BlackBoxSolver; -use noirc_abi::input_parser::Format; use noirc_artifacts::debug::DebugArtifact; +/// Generates a flamegraph mapping unconstrained Noir execution to source code. #[derive(Debug, Clone, Args)] pub(crate) struct ExecutionFlamegraphCommand { /// The path to the artifact JSON file @@ -25,13 +30,22 @@ pub(crate) struct ExecutionFlamegraphCommand { /// The output folder for the flamegraph svg files #[clap(long, short)] - output: PathBuf, + output: Option, /// Use pedantic ACVM solving, i.e. double-check some black-box function /// assumptions when solving. /// This is disabled by default. #[clap(long, default_value = "false")] pedantic_solving: bool, + + /// A single number representing the total opcodes executed. + /// Outputs to stdout and skips generating a flamegraph. + #[clap(long, default_value = "false")] + sample_count: bool, + + /// Enables additional logging + #[clap(long, default_value = "false")] + verbose: bool, } pub(crate) fn run(args: ExecutionFlamegraphCommand) -> eyre::Result<()> { @@ -41,6 +55,8 @@ pub(crate) fn run(args: ExecutionFlamegraphCommand) -> eyre::Result<()> { &InfernoFlamegraphGenerator { count_name: "samples".to_string() }, &args.output, args.pedantic_solving, + args.sample_count, + args.verbose, ) } @@ -48,26 +64,71 @@ fn run_with_generator( artifact_path: &Path, prover_toml_path: &Path, flamegraph_generator: &impl FlamegraphGenerator, - output_path: &Path, + output_path: &Option, pedantic_solving: bool, + print_sample_count: bool, + verbose: bool, ) -> eyre::Result<()> { let program = read_program_from_file(artifact_path).context("Error reading program from file")?; - let (inputs_map, _) = read_inputs_from_file(prover_toml_path, Format::Toml, &program.abi)?; + ensure_brillig_entry_point(&program)?; + + if !print_sample_count && output_path.is_none() { + return report_error("Missing --output argument for when building a flamegraph") + .map_err(Into::into); + } + + let (inputs_map, _) = + read_inputs_from_file(&prover_toml_path.with_extension("toml"), &program.abi)?; let initial_witness = program.abi.encode(&inputs_map, None)?; - println!("Executing"); - let (_, mut profiling_samples) = nargo::ops::execute_program_with_profiling( + if verbose { + println!("Executing..."); + } + + let solved_witness_stack_err = nargo::ops::execute_program_with_profiling( &program.bytecode, initial_witness, &Bn254BlackBoxSolver(pedantic_solving), &mut DefaultForeignCallBuilder::default().with_output(PrintOutput::Stdout).build(), - )?; - println!("Executed"); + ); + let mut profiling_samples = match solved_witness_stack_err { + Ok((_, profiling_samples)) => profiling_samples, + Err(err) => { + let debug_artifact = DebugArtifact { + debug_symbols: program.debug_symbols.debug_infos.clone(), + file_map: program.file_map.clone(), + }; + + if let Some(diagnostic) = try_to_diagnose_runtime_error( + &err, + &program.abi, + &program.debug_symbols.debug_infos, + ) { + diagnostic.report(&debug_artifact, false); + } + + return Err(CliError::Generic.into()); + } + }; + + if verbose { + println!("Executed"); + } + + if print_sample_count { + println!("{}", profiling_samples.len()); + return Ok(()); + } - println!("Collecting {} samples", profiling_samples.len()); + // We place this logging output before the transforming and collection of the samples. + // This is done because large traces can take some time, and can make it look + // as if the profiler has stalled. + if verbose { + println!("Generating flamegraph for {} samples...", profiling_samples.len()); + } let profiling_samples: Vec = profiling_samples .iter_mut() @@ -89,18 +150,124 @@ fn run_with_generator( }) .collect(); - let debug_artifact: DebugArtifact = program.into(); - - println!("Generating flamegraph with {} samples", profiling_samples.len()); + let output_path = + output_path.as_ref().expect("Should have already checked for the output path"); + let debug_artifact: DebugArtifact = program.into(); flamegraph_generator.generate_flamegraph( profiling_samples, &debug_artifact.debug_symbols[0], &debug_artifact, artifact_path.to_str().unwrap(), "main", - &Path::new(&output_path).join(Path::new(&format!("{}.svg", "main"))), + &Path::new(output_path).join(Path::new(&format!("{}_brillig_trace.svg", "main"))), )?; + if verbose { + println!("Generated flamegraph"); + } + Ok(()) } + +fn ensure_brillig_entry_point(artifact: &ProgramArtifact) -> Result<(), CliError> { + let err_msg = "Command only supports fully unconstrained Noir programs e.g. `unconstrained fn main() { .. }"; + let program = &artifact.bytecode; + if program.functions.len() != 1 || program.unconstrained_functions.len() != 1 { + return report_error(err_msg); + } + + let main_function = &program.functions[0]; + let Opcode::BrilligCall { id, .. } = main_function.opcodes[0] else { + return report_error(err_msg); + }; + + if id.as_usize() != 0 { + return report_error(err_msg); + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use acir::circuit::{Circuit, Program, brillig::BrilligBytecode}; + use color_eyre::eyre; + use fm::codespan_files::Files; + use noirc_artifacts::program::ProgramArtifact; + use noirc_driver::CrateName; + use noirc_errors::debug_info::{DebugInfo, ProgramDebugInfo}; + use std::{collections::BTreeMap, path::Path, str::FromStr}; + + use crate::flamegraph::Sample; + + #[derive(Default)] + struct TestFlamegraphGenerator {} + + impl super::FlamegraphGenerator for TestFlamegraphGenerator { + fn generate_flamegraph<'files, S: Sample>( + &self, + _samples: Vec, + _debug_symbols: &DebugInfo, + _files: &'files impl Files<'files, FileId = fm::FileId>, + _artifact_name: &str, + _function_name: &str, + output_path: &Path, + ) -> eyre::Result<()> { + let output_file = std::fs::File::create(output_path).unwrap(); + std::io::Write::write_all(&mut std::io::BufWriter::new(output_file), b"success") + .unwrap(); + + Ok(()) + } + } + + #[test] + fn error_reporter_smoke_test() { + // This test purposefully uses an artifact that does not represent a Brillig entry point. + // The goal is to see that our program fails gracefully and does not panic. + let temp_dir = tempfile::tempdir().unwrap(); + + let prover_toml_path = temp_dir.path().join("Prover.toml"); + + let artifact = ProgramArtifact { + noir_version: "0.0.0".to_string(), + hash: 27, + abi: noirc_abi::Abi::default(), + bytecode: Program { + functions: vec![Circuit::default()], + unconstrained_functions: vec![ + BrilligBytecode::default(), + BrilligBytecode::default(), + ], + }, + debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, + file_map: BTreeMap::default(), + names: vec!["main".to_string()], + brillig_names: Vec::new(), + }; + + // Write the artifact to a file + let artifact_path = noir_artifact_cli::fs::artifact::save_program_to_file( + &artifact, + &CrateName::from_str("test").unwrap(), + temp_dir.path(), + ) + .unwrap(); + + let flamegraph_generator = TestFlamegraphGenerator::default(); + + assert!( + super::run_with_generator( + &artifact_path, + &prover_toml_path, + &flamegraph_generator, + &Some(temp_dir.into_path()), + false, + false, + false + ) + .is_err() + ); + } +} diff --git a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs index e68a8cd5bd27..736cddf7cf4a 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs @@ -4,33 +4,35 @@ use acir::circuit::OpcodeLocation; use clap::Args; use color_eyre::eyre::{self, Context}; +use noir_artifact_cli::fs::artifact::read_program_from_file; use noirc_artifacts::debug::DebugArtifact; use crate::flamegraph::{CompilationSample, FlamegraphGenerator, InfernoFlamegraphGenerator}; -use crate::fs::read_program_from_file; use crate::gates_provider::{BackendGatesProvider, GatesProvider}; use crate::opcode_formatter::format_acir_opcode; +/// Generates a flamegraph mapping backend opcodes to their associated locations in the source code. #[derive(Debug, Clone, Args)] pub(crate) struct GatesFlamegraphCommand { /// The path to the artifact JSON file #[clap(long, short)] - artifact_path: String, + artifact_path: PathBuf, - /// Path to the noir backend binary + /// Path to the Noir backend binary #[clap(long, short)] - backend_path: String, + backend_path: PathBuf, /// Command to get a gates report from the backend. Defaults to "gates" #[clap(long, short = 'g', default_value = "gates")] backend_gates_command: String, + /// Optional arguments for the backend gates command #[arg(trailing_var_arg = true, allow_hyphen_values = true)] backend_extra_args: Vec, /// The output folder for the flamegraph svg files #[clap(long, short)] - output: String, + output: PathBuf, /// The output name for the flamegraph svg files #[clap(long, short = 'f')] @@ -39,14 +41,14 @@ pub(crate) struct GatesFlamegraphCommand { pub(crate) fn run(args: GatesFlamegraphCommand) -> eyre::Result<()> { run_with_provider( - &PathBuf::from(args.artifact_path), + &args.artifact_path, &BackendGatesProvider { - backend_path: PathBuf::from(args.backend_path), + backend_path: args.backend_path, gates_command: args.backend_gates_command, extra_args: args.backend_extra_args, }, &InfernoFlamegraphGenerator { count_name: "gates".to_string() }, - &PathBuf::from(args.output), + &args.output, args.output_filename, ) } @@ -64,19 +66,24 @@ fn run_with_provider( let backend_gates_response = gates_provider.get_gates(artifact_path).context("Error querying backend for gates")?; - let function_names = program.names.clone(); + let function_names = std::mem::take(&mut program.names); let bytecode = std::mem::take(&mut program.bytecode); let debug_artifact: DebugArtifact = program.into(); - for (func_idx, ((func_gates, func_name), bytecode)) in backend_gates_response - .functions - .into_iter() - .zip(function_names) - .zip(bytecode.functions) - .enumerate() + let num_functions = bytecode.functions.len(); + for (func_idx, (func_gates, circuit)) in + backend_gates_response.functions.into_iter().zip(bytecode.functions).enumerate() { + // We can have repeated names if there are functions with the same name in different + // modules or functions that use generics. Thus, add the unique function index as a suffix. + let function_name = if num_functions > 1 { + format!("{}_{}", function_names[func_idx].as_str(), func_idx) + } else { + function_names[func_idx].to_owned() + }; + println!( "Opcode count: {}, Total gates by opcodes: {}, Circuit size: {}", func_gates.acir_opcodes, @@ -87,7 +94,7 @@ fn run_with_provider( let samples = func_gates .gates_per_opcode .into_iter() - .zip(bytecode.opcodes) + .zip(circuit.opcodes) .enumerate() .map(|(index, (gates, opcode))| CompilationSample { opcode: Some(format_acir_opcode(&opcode)), @@ -98,16 +105,16 @@ fn run_with_provider( .collect(); let output_filename = if let Some(output_filename) = &output_filename { - format!("{}::{}::gates.svg", output_filename, func_name) + format!("{}_{}_gates.svg", output_filename, function_name) } else { - format!("{}::gates.svg", func_name) + format!("{}_gates.svg", function_name) }; flamegraph_generator.generate_flamegraph( samples, &debug_artifact.debug_symbols[func_idx], &debug_artifact, artifact_path.to_str().unwrap(), - &func_name, + &function_name, &Path::new(&output_path).join(Path::new(&output_filename)), )?; } @@ -118,7 +125,7 @@ fn run_with_provider( #[cfg(test)] mod tests { use acir::circuit::{Circuit, Program}; - use color_eyre::eyre::{self}; + use color_eyre::eyre; use fm::codespan_files::Files; use noirc_artifacts::program::ProgramArtifact; use noirc_errors::debug_info::{DebugInfo, ProgramDebugInfo}; @@ -210,7 +217,7 @@ mod tests { .expect("should run without errors"); // Check that the output file was written to - let output_file = temp_dir.path().join("test_filename::main::gates.svg"); + let output_file = temp_dir.path().join("test_filename_main_gates.svg"); assert!(output_file.exists()); } } diff --git a/noir/noir-repo/tooling/profiler/src/cli/mod.rs b/noir/noir-repo/tooling/profiler/src/cli/mod.rs index 80c6bceb3ce2..0b4e0a92b271 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/mod.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/mod.rs @@ -1,5 +1,5 @@ use clap::{Parser, Subcommand}; -use color_eyre::eyre; +use color_eyre::eyre::{self}; use const_format::formatcp; mod execution_flamegraph_cmd; @@ -32,5 +32,7 @@ pub(crate) fn start_cli() -> eyre::Result<()> { ProfilerCommand::Gates(args) => gates_flamegraph_cmd::run(args), ProfilerCommand::Opcodes(args) => opcodes_flamegraph_cmd::run(args), ProfilerCommand::ExecutionOpcodes(args) => execution_flamegraph_cmd::run(args), - } + }?; + + Ok(()) } diff --git a/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs index 4e271c9f8385..8ce9ba1de398 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs @@ -5,21 +5,22 @@ use acir::circuit::{Circuit, Opcode, OpcodeLocation}; use clap::Args; use color_eyre::eyre::{self, Context}; +use noir_artifact_cli::fs::artifact::read_program_from_file; use noirc_artifacts::debug::DebugArtifact; use crate::flamegraph::{CompilationSample, FlamegraphGenerator, InfernoFlamegraphGenerator}; -use crate::fs::read_program_from_file; use crate::opcode_formatter::{format_acir_opcode, format_brillig_opcode}; +/// Generates a flamegraph mapping ACIR opcodes to their associated locations in the source code. #[derive(Debug, Clone, Args)] pub(crate) struct OpcodesFlamegraphCommand { /// The path to the artifact JSON file #[clap(long, short)] - artifact_path: String, + artifact_path: PathBuf, /// The output folder for the flamegraph svg files #[clap(long, short)] - output: String, + output: PathBuf, /// Whether to skip brillig functions #[clap(long, short, action)] @@ -28,9 +29,9 @@ pub(crate) struct OpcodesFlamegraphCommand { pub(crate) fn run(args: OpcodesFlamegraphCommand) -> eyre::Result<()> { run_with_generator( - &PathBuf::from(args.artifact_path), + &args.artifact_path, &InfernoFlamegraphGenerator { count_name: "opcodes".to_string() }, - &PathBuf::from(args.output), + &args.output, args.skip_brillig, ) } @@ -44,18 +45,25 @@ fn run_with_generator( let mut program = read_program_from_file(artifact_path).context("Error reading program from file")?; - let function_names = program.names.clone(); + let acir_names = std::mem::take(&mut program.names); + let brillig_names = std::mem::take(&mut program.brillig_names); let bytecode = std::mem::take(&mut program.bytecode); let debug_artifact: DebugArtifact = program.into(); - for (func_idx, (func_name, bytecode)) in - function_names.into_iter().zip(bytecode.functions.iter()).enumerate() - { - println!("Opcode count for {}: {}", func_name, bytecode.opcodes.len()); + for (func_idx, circuit) in bytecode.functions.iter().enumerate() { + // We can have repeated names if there are functions with the same name in different + // modules or functions that use generics. Thus, add the unique function index as a suffix. + let function_name = if bytecode.functions.len() > 1 { + format!("{}_{}", acir_names[func_idx].as_str(), func_idx) + } else { + acir_names[func_idx].to_owned() + }; - let samples = bytecode + println!("Opcode count for {}: {}", function_name, circuit.opcodes.len()); + + let samples = circuit .opcodes .iter() .enumerate() @@ -72,51 +80,55 @@ fn run_with_generator( &debug_artifact.debug_symbols[func_idx], &debug_artifact, artifact_path.to_str().unwrap(), - &func_name, - &Path::new(&output_path).join(Path::new(&format!("{}_acir_opcodes.svg", &func_name))), + &function_name, + &Path::new(&output_path) + .join(Path::new(&format!("{}_acir_opcodes.svg", &function_name))), )?; } - if !skip_brillig { - for (brillig_fn_index, brillig_bytecode) in - bytecode.unconstrained_functions.into_iter().enumerate() - { - let acir_location = locate_brillig_call(brillig_fn_index, &bytecode.functions); - let Some((acir_fn_index, acir_opcode_index)) = acir_location else { - continue; - }; - - println!( - "Opcode count for brillig_{}: {}", - brillig_fn_index, - brillig_bytecode.bytecode.len() - ); - - let samples = brillig_bytecode - .bytecode - .into_iter() - .enumerate() - .map(|(brillig_index, opcode)| CompilationSample { - opcode: Some(format_brillig_opcode(&opcode)), - call_stack: vec![OpcodeLocation::Brillig { - acir_index: acir_opcode_index, - brillig_index, - }], - count: 1, - brillig_function_id: Some(BrilligFunctionId(brillig_fn_index as u32)), - }) - .collect(); - - flamegraph_generator.generate_flamegraph( - samples, - &debug_artifact.debug_symbols[acir_fn_index], - &debug_artifact, - artifact_path.to_str().unwrap(), - &format!("brillig_{}", brillig_fn_index), - &Path::new(&output_path) - .join(Path::new(&format!("{}_brillig_opcodes.svg", &brillig_fn_index))), - )?; - } + if skip_brillig { + return Ok(()); + } + + for (brillig_fn_index, brillig_bytecode) in + bytecode.unconstrained_functions.into_iter().enumerate() + { + let acir_location = locate_brillig_call(brillig_fn_index, &bytecode.functions); + let Some((acir_fn_index, acir_opcode_index)) = acir_location else { + continue; + }; + + // We can have repeated names if there are functions with the same name in different + // modules or functions that use generics. Thus, add the unique function index as a suffix. + let function_name = + format!("{}_{}", brillig_names[brillig_fn_index].as_str(), brillig_fn_index); + + println!("Opcode count for {}_brillig: {}", function_name, brillig_bytecode.bytecode.len()); + + let samples = brillig_bytecode + .bytecode + .into_iter() + .enumerate() + .map(|(brillig_index, opcode)| CompilationSample { + opcode: Some(format_brillig_opcode(&opcode)), + call_stack: vec![OpcodeLocation::Brillig { + acir_index: acir_opcode_index, + brillig_index, + }], + count: 1, + brillig_function_id: Some(BrilligFunctionId(brillig_fn_index as u32)), + }) + .collect(); + + flamegraph_generator.generate_flamegraph( + samples, + &debug_artifact.debug_symbols[acir_fn_index], + &debug_artifact, + artifact_path.to_str().unwrap(), + &function_name, + &Path::new(&output_path) + .join(Path::new(&format!("{}_brillig_opcodes.svg", function_name))), + )?; } Ok(()) @@ -130,7 +142,7 @@ fn locate_brillig_call( for (acir_opcode_index, acir_opcode) in acir_fn.opcodes.iter().enumerate() { match acir_opcode { Opcode::BrilligCall { id, .. } if id.as_usize() == brillig_fn_index => { - return Some((acir_fn_index, acir_opcode_index)) + return Some((acir_fn_index, acir_opcode_index)); } _ => {} } @@ -142,13 +154,13 @@ fn locate_brillig_call( #[cfg(test)] mod tests { use acir::{ + FieldElement, circuit::{ - brillig::{BrilligBytecode, BrilligFunctionId}, Circuit, Opcode, Program, + brillig::{BrilligBytecode, BrilligFunctionId}, }, - FieldElement, }; - use color_eyre::eyre::{self}; + use color_eyre::eyre; use fm::codespan_files::Files; use noirc_artifacts::program::ProgramArtifact; use noirc_errors::debug_info::{DebugInfo, ProgramDebugInfo}; @@ -214,12 +226,26 @@ mod tests { let artifact_path = temp_dir.path().join("test.json"); - let acir: Vec> = vec![Opcode::BrilligCall { - id: BrilligFunctionId(0), - inputs: vec![], - outputs: vec![], - predicate: None, - }]; + let acir: Vec> = vec![ + Opcode::BrilligCall { + id: BrilligFunctionId(0), + inputs: vec![], + outputs: vec![], + predicate: None, + }, + Opcode::BrilligCall { + id: BrilligFunctionId(1), + inputs: vec![], + outputs: vec![], + predicate: None, + }, + Opcode::BrilligCall { + id: BrilligFunctionId(2), + inputs: vec![], + outputs: vec![], + predicate: None, + }, + ]; let artifact = ProgramArtifact { noir_version: "0.0.0".to_string(), @@ -227,12 +253,16 @@ mod tests { abi: noirc_abi::Abi::default(), bytecode: Program { functions: vec![Circuit { opcodes: acir, ..Circuit::default() }], - unconstrained_functions: vec![BrilligBytecode::default()], + unconstrained_functions: vec![ + BrilligBytecode::default(), + BrilligBytecode::default(), + BrilligBytecode::default(), + ], }, debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], - brillig_names: vec!["main".to_string()], + brillig_names: vec!["main".to_string(), "main".to_string(), "main_1".to_string()], }; // Write the artifact to a file @@ -244,11 +274,17 @@ mod tests { super::run_with_generator(&artifact_path, &flamegraph_generator, temp_dir.path(), false) .expect("should run without errors"); - // Check that the output files ware written to + // Check that the output files were written let output_file = temp_dir.path().join("main_acir_opcodes.svg"); assert!(output_file.exists()); - let output_file = temp_dir.path().join("0_brillig_opcodes.svg"); + let output_file = temp_dir.path().join("main_0_brillig_opcodes.svg"); + assert!(output_file.exists()); + + let output_file = temp_dir.path().join("main_1_brillig_opcodes.svg"); + assert!(output_file.exists()); + + let output_file = temp_dir.path().join("main_1_2_brillig_opcodes.svg"); assert!(output_file.exists()); } } diff --git a/noir/noir-repo/tooling/profiler/src/errors.rs b/noir/noir-repo/tooling/profiler/src/errors.rs new file mode 100644 index 000000000000..951199436aa3 --- /dev/null +++ b/noir/noir-repo/tooling/profiler/src/errors.rs @@ -0,0 +1,16 @@ +use fm::{FileId, FileMap}; +use noirc_errors::CustomDiagnostic; +use thiserror::Error; + +#[derive(Debug, Error)] +pub(crate) enum CliError { + #[error("Failed to run profiler command")] + Generic, +} + +/// Report an error from the CLI that is not reliant on a stack trace. +pub(crate) fn report_error(message: &str) -> Result<(), CliError> { + let error = CustomDiagnostic::from_message(message, FileId::dummy()); + noirc_errors::reporter::report(&FileMap::default(), &error, false); + Err(CliError::Generic) +} diff --git a/noir/noir-repo/tooling/profiler/src/flamegraph.rs b/noir/noir-repo/tooling/profiler/src/flamegraph.rs index 38966902314e..16857eb2454c 100644 --- a/noir/noir-repo/tooling/profiler/src/flamegraph.rs +++ b/noir/noir-repo/tooling/profiler/src/flamegraph.rs @@ -1,15 +1,15 @@ use std::path::Path; use std::{collections::BTreeMap, io::BufWriter}; -use acir::circuit::brillig::BrilligFunctionId; use acir::circuit::OpcodeLocation; -use color_eyre::eyre::{self}; +use acir::circuit::brillig::BrilligFunctionId; +use color_eyre::eyre; use fm::codespan_files::Files; use fxhash::FxHashMap as HashMap; -use inferno::flamegraph::{from_lines, Options, TextTruncateDirection}; +use inferno::flamegraph::{Options, TextTruncateDirection, from_lines}; +use noirc_errors::Location; use noirc_errors::debug_info::DebugInfo; use noirc_errors::reporter::line_and_column_from_span; -use noirc_errors::Location; use noirc_evaluator::brillig::ProcedureId; pub(crate) trait Sample { @@ -112,7 +112,7 @@ impl FlamegraphGenerator for InfernoFlamegraphGenerator { let mut options = Options::default(); options.hash = true; options.deterministic = true; - options.title = format!("{}-{}", artifact_name, function_name); + options.title = format!("Artifact: {}, Function: {}", artifact_name, function_name); options.frame_height = 24; options.color_diffusion = true; options.min_width = 0.0; @@ -293,12 +293,12 @@ fn to_folded_sorted_lines( #[cfg(test)] mod tests { use acir::{ - circuit::{opcodes::BlockId, Opcode as AcirOpcode, OpcodeLocation}, - native_types::Expression, FieldElement, + circuit::{Opcode as AcirOpcode, OpcodeLocation, opcodes::BlockId}, + native_types::Expression, }; use fm::FileManager; - use noirc_errors::{debug_info::DebugInfo, Location, Span}; + use noirc_errors::{Location, Span, debug_info::DebugInfo}; use std::{collections::BTreeMap, path::Path}; use crate::{flamegraph::CompilationSample, opcode_formatter::format_acir_opcode}; diff --git a/noir/noir-repo/tooling/profiler/src/fs.rs b/noir/noir-repo/tooling/profiler/src/fs.rs deleted file mode 100644 index 43848504a7ff..000000000000 --- a/noir/noir-repo/tooling/profiler/src/fs.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::{collections::BTreeMap, path::Path}; - -use color_eyre::eyre; -use noirc_abi::{ - input_parser::{Format, InputValue}, - Abi, InputMap, MAIN_RETURN_NAME, -}; -use noirc_artifacts::program::ProgramArtifact; - -pub(crate) fn read_program_from_file>( - circuit_path: P, -) -> eyre::Result { - let file_path = circuit_path.as_ref().with_extension("json"); - - let input_string = std::fs::read(file_path)?; - let program = serde_json::from_slice(&input_string)?; - - Ok(program) -} - -/// Returns the circuit's parameters and its return value, if one exists. -/// # Examples -/// -/// ```ignore -/// let (input_map, return_value): (InputMap, Option) = -/// read_inputs_from_file(path, "Verifier", Format::Toml, &abi)?; -/// ``` -pub(crate) fn read_inputs_from_file( - file_path: &Path, - format: Format, - abi: &Abi, -) -> eyre::Result<(InputMap, Option)> { - if abi.is_empty() { - return Ok((BTreeMap::new(), None)); - } - - let input_string = std::fs::read_to_string(file_path)?; - let mut input_map = format.parse(&input_string, abi)?; - let return_value = input_map.remove(MAIN_RETURN_NAME); - - Ok((input_map, return_value)) -} diff --git a/noir/noir-repo/tooling/profiler/src/gates_provider.rs b/noir/noir-repo/tooling/profiler/src/gates_provider.rs index 3f07f3e4be6f..044e2a3642cd 100644 --- a/noir/noir-repo/tooling/profiler/src/gates_provider.rs +++ b/noir/noir-repo/tooling/profiler/src/gates_provider.rs @@ -1,7 +1,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; -use color_eyre::eyre::{self}; +use color_eyre::eyre; use serde::{Deserialize, Serialize}; pub(crate) trait GatesProvider { diff --git a/noir/noir-repo/tooling/profiler/src/main.rs b/noir/noir-repo/tooling/profiler/src/main.rs index b0b42e6ee41a..1ac17617825c 100644 --- a/noir/noir-repo/tooling/profiler/src/main.rs +++ b/noir/noir-repo/tooling/profiler/src/main.rs @@ -4,15 +4,15 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies, unused_extern_crates))] mod cli; +mod errors; mod flamegraph; -mod fs; mod gates_provider; mod opcode_formatter; use std::env; use tracing_appender::rolling; -use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter}; +use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan}; fn main() { // Setup tracing @@ -22,18 +22,19 @@ fn main() { .with_span_events(FmtSpan::ACTIVE) .with_writer(debug_file) .with_ansi(false) - .with_env_filter(EnvFilter::from_default_env()) + .with_env_filter(EnvFilter::from_env("NOIR_LOG")) .init(); } else { tracing_subscriber::fmt() .with_span_events(FmtSpan::ACTIVE) + .with_writer(std::io::stderr) .with_ansi(true) .with_env_filter(EnvFilter::from_env("NOIR_LOG")) .init(); } if let Err(report) = cli::start_cli() { - eprintln!("{report:?}"); + eprintln!("{report:#}"); std::process::exit(1); } } diff --git a/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs b/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs index d1081de6c8f2..2276bcb4403f 100644 --- a/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs +++ b/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs @@ -1,6 +1,6 @@ -use acir::brillig::{BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpcode}; -use acir::circuit::{opcodes::BlackBoxFuncCall, Opcode as AcirOpcode}; use acir::AcirField; +use acir::brillig::{BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpcode}; +use acir::circuit::{Opcode as AcirOpcode, opcodes::BlackBoxFuncCall}; fn format_blackbox_function(call: &BlackBoxFuncCall) -> String { match call { diff --git a/noir/noir-repo/yarn.lock b/noir/noir-repo/yarn.lock index 56088fedd94b..640bfac23060 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,9 +221,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@portal:../../../../barretenberg/ts::locator=integration-tests%40workspace%3Acompiler%2Fintegration-tests": - version: 0.0.0-use.local - resolution: "@aztec/bb.js@portal:../../../../barretenberg/ts::locator=integration-tests%40workspace%3Acompiler%2Fintegration-tests" +"@aztec/bb.js@npm:0.77.1": + version: 0.77.1 + resolution: "@aztec/bb.js@npm:0.77.1" dependencies: comlink: ^4.4.1 commander: ^12.1.0 @@ -233,8 +233,9 @@ __metadata: tslib: ^2.4.0 bin: bb.js: dest/node/main.js + checksum: d2df8b889d3bf6fa01b4aaf031a0f2b37f78b74b80a7afacc113648c9301547e295d485a3f483a60e5b26e9478cf469517f2cdd98110dff0c566099b21f9b39a 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 @@ -16608,7 +16609,7 @@ __metadata: version: 0.0.0-use.local resolution: "integration-tests@workspace:compiler/integration-tests" dependencies: - "@aztec/bb.js": "portal:../../../../barretenberg/ts" + "@aztec/bb.js": 0.77.1 "@noir-lang/noir_js": "workspace:*" "@noir-lang/noir_wasm": "workspace:*" "@nomicfoundation/hardhat-chai-matchers": ^2.0.0 From 54c6e8cabe972a7edad92a44b0de86043b087560 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Thu, 6 Mar 2025 08:09:45 +0000 Subject: [PATCH 10/13] chore: apply sync fixes --- noir/bb-version | 2 +- noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../noir-repo/compiler/integration-tests/package.json | 2 +- noir/noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- noir/noir-repo/yarn.lock | 11 +++++------ 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/noir/bb-version b/noir/bb-version index fac6d18325ad..548a9079fc97 100644 --- a/noir/bb-version +++ b/noir/bb-version @@ -1 +1 @@ -0.72.1 +0.77.1 diff --git a/noir/noir-repo/acvm-repo/acvm_js/build.sh b/noir/noir-repo/acvm-repo/acvm_js/build.sh index 16fb26e55db1..c07d2d8a4c1d 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/compiler/integration-tests/package.json b/noir/noir-repo/compiler/integration-tests/package.json index 87afd208cf7b..e33179f31e7f 100644 --- a/noir/noir-repo/compiler/integration-tests/package.json +++ b/noir/noir-repo/compiler/integration-tests/package.json @@ -13,7 +13,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "0.77.1", + "@aztec/bb.js": "portal:../../../../barretenberg/ts", "@noir-lang/noir_js": "workspace:*", "@noir-lang/noir_wasm": "workspace:*", "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", diff --git a/noir/noir-repo/tooling/noirc_abi_wasm/build.sh b/noir/noir-repo/tooling/noirc_abi_wasm/build.sh index 16fb26e55db1..c07d2d8a4c1d 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 640bfac23060..56088fedd94b 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,9 +221,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.77.1": - version: 0.77.1 - resolution: "@aztec/bb.js@npm:0.77.1" +"@aztec/bb.js@portal:../../../../barretenberg/ts::locator=integration-tests%40workspace%3Acompiler%2Fintegration-tests": + version: 0.0.0-use.local + resolution: "@aztec/bb.js@portal:../../../../barretenberg/ts::locator=integration-tests%40workspace%3Acompiler%2Fintegration-tests" dependencies: comlink: ^4.4.1 commander: ^12.1.0 @@ -233,9 +233,8 @@ __metadata: tslib: ^2.4.0 bin: bb.js: dest/node/main.js - checksum: d2df8b889d3bf6fa01b4aaf031a0f2b37f78b74b80a7afacc113648c9301547e295d485a3f483a60e5b26e9478cf469517f2cdd98110dff0c566099b21f9b39a languageName: node - linkType: hard + linkType: soft "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.11, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5, @babel/code-frame@npm:^7.8.3": version: 7.23.5 @@ -16609,7 +16608,7 @@ __metadata: version: 0.0.0-use.local resolution: "integration-tests@workspace:compiler/integration-tests" dependencies: - "@aztec/bb.js": 0.77.1 + "@aztec/bb.js": "portal:../../../../barretenberg/ts" "@noir-lang/noir_js": "workspace:*" "@noir-lang/noir_wasm": "workspace:*" "@nomicfoundation/hardhat-chai-matchers": ^2.0.0 From 24ebe75209f0ffbc59a0a5e837e326fe7c4caaa9 Mon Sep 17 00:00:00 2001 From: Tom French Date: Thu, 6 Mar 2025 10:31:45 +0000 Subject: [PATCH 11/13] . --- noir/noir-repo/Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index 602052720d14..fff8a72fd922 100644 --- a/noir/noir-repo/Cargo.lock +++ b/noir/noir-repo/Cargo.lock @@ -5324,7 +5324,7 @@ checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap 2.7.1", "toml_datetime", - "winnow 0.7.3", + "winnow 0.6.26", ] [[package]] @@ -5758,7 +5758,7 @@ checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.96", ] [[package]] @@ -5985,9 +5985,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.3" +version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" +checksum = "1e90edd2ac1aa278a5c4599b1d89cf03074b610800f866d4026dc199d7929a28" dependencies = [ "memchr", ] From 8d432104f38733ca8bc693bb904841b85766121b Mon Sep 17 00:00:00 2001 From: Tom French Date: Thu, 6 Mar 2025 10:34:23 +0000 Subject: [PATCH 12/13] . --- .../codegen_verifier/codegen_verifier.sh | 8 +++---- .../prove_and_verify/prove_and_verify.sh | 6 ++--- .../recursion/generate_recursive_proof.sh | 24 +++++++++---------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh b/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh index 30dc06e47127..89df3ced1814 100755 --- a/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh +++ b/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh @@ -6,18 +6,18 @@ BACKEND=${BACKEND:-bb} nargo compile # TODO: backend should automatically generate vk if necessary. -$BACKEND write_vk -b ./target/hello_world.json -$BACKEND contract -o ./src/contract.sol +$BACKEND OLD_API write_vk -b ./target/hello_world.json +$BACKEND OLD_API contract -o ./src/contract.sol # We now generate a proof and check whether the verifier contract will verify it. nargo execute --pedantic-solving witness PROOF_PATH=./target/proof -$BACKEND prove -b ./target/hello_world.json -w ./target/witness.gz -o $PROOF_PATH +$BACKEND OLD_API prove -b ./target/hello_world.json -w ./target/witness.gz -o $PROOF_PATH # Sanity check that proof is valid. -$BACKEND verify -k ./target/vk -p ./target/proof +$BACKEND OLD_API verify -k ./target/vk -p ./target/proof NUM_PUBLIC_INPUTS=2 PUBLIC_INPUT_BYTES=$((32 * $NUM_PUBLIC_INPUTS)) diff --git a/noir/noir-repo/examples/prove_and_verify/prove_and_verify.sh b/noir/noir-repo/examples/prove_and_verify/prove_and_verify.sh index 411f5258caf4..92288bc92e02 100755 --- a/noir/noir-repo/examples/prove_and_verify/prove_and_verify.sh +++ b/noir/noir-repo/examples/prove_and_verify/prove_and_verify.sh @@ -7,8 +7,8 @@ nargo execute --pedantic-solving witness # TODO: `bb` should create `proofs` directory if it doesn't exist. mkdir -p proofs -$BACKEND prove -b ./target/hello_world.json -w ./target/witness.gz +$BACKEND OLD_API prove -b ./target/hello_world.json -w ./target/witness.gz # TODO: backend should automatically generate vk if necessary. -$BACKEND write_vk -b ./target/hello_world.json -$BACKEND verify -k ./target/vk -p ./proofs/proof +$BACKEND OLD_API write_vk -b ./target/hello_world.json +$BACKEND OLD_API verify -k ./target/vk -p ./proofs/proof diff --git a/noir/noir-repo/examples/recursion/generate_recursive_proof.sh b/noir/noir-repo/examples/recursion/generate_recursive_proof.sh index 09b01d547b68..e05dd854d69f 100755 --- a/noir/noir-repo/examples/recursion/generate_recursive_proof.sh +++ b/noir/noir-repo/examples/recursion/generate_recursive_proof.sh @@ -4,16 +4,16 @@ set -eu BACKEND=${BACKEND:-bb} nargo execute sum_witness --package sum -$BACKEND prove -b ./target/sum.json -w ./target/sum_witness.gz -o ./target/sum_proof --recursive +$BACKEND OLD_API prove -b ./target/sum.json -w ./target/sum_witness.gz -o ./target/sum_proof --recursive # Once we have generated our inner proof, we must use this to generate inputs to `recurse_leaf`` -$BACKEND write_vk -b ./target/sum.json -o ./target/sum_key --recursive -$BACKEND vk_as_fields -k ./target/sum_key -o ./target/sum_vk_as_fields +$BACKEND OLD_API write_vk -b ./target/sum.json -o ./target/sum_key --recursive +$BACKEND OLD_API vk_as_fields -k ./target/sum_key -o ./target/sum_vk_as_fields VK_HASH=$(jq -r '.[0]' ./target/sum_vk_as_fields) VK_AS_FIELDS=$(jq -r '.[1:]' ./target/sum_vk_as_fields) -FULL_PROOF_AS_FIELDS="$($BACKEND proof_as_fields -p ./target/sum_proof -k ./target/sum_key -o -)" +FULL_PROOF_AS_FIELDS="$($BACKEND OLD_API proof_as_fields -p ./target/sum_proof -k ./target/sum_key -o -)" # sum has 3 public inputs PUBLIC_INPUTS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[:3]') PROOF_AS_FIELDS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[3:]') @@ -28,19 +28,19 @@ echo "public_inputs = $PUBLIC_INPUTS" >> $RECURSE_LEAF_PROVER_TOML # We can now execute and prove `recurse_leaf` nargo execute recurse_leaf_witness --package recurse_leaf -$BACKEND prove -b ./target/recurse_leaf.json -w ./target/recurse_leaf_witness.gz -o ./target/recurse_leaf_proof --recursive +$BACKEND OLD_API prove -b ./target/recurse_leaf.json -w ./target/recurse_leaf_witness.gz -o ./target/recurse_leaf_proof --recursive # Let's do a sanity check that the proof we've generated so far is valid. -$BACKEND write_vk -b ./target/recurse_leaf.json -o ./target/recurse_leaf_key --recursive -$BACKEND verify -p ./target/recurse_leaf_proof -k ./target/recurse_leaf_key +$BACKEND OLD_API write_vk -b ./target/recurse_leaf.json -o ./target/recurse_leaf_key --recursive +$BACKEND OLD_API verify -p ./target/recurse_leaf_proof -k ./target/recurse_leaf_key # Now we generate the final `recurse_node` proof similarly to how we did for `recurse_leaf`. -$BACKEND vk_as_fields -k ./target/recurse_leaf_key -o ./target/recurse_leaf_vk_as_fields +$BACKEND OLD_API vk_as_fields -k ./target/recurse_leaf_key -o ./target/recurse_leaf_vk_as_fields VK_HASH=$(jq -r '.[0]' ./target/recurse_leaf_vk_as_fields) VK_AS_FIELDS=$(jq -r '.[1:]' ./target/recurse_leaf_vk_as_fields) -FULL_PROOF_AS_FIELDS="$($BACKEND proof_as_fields -p ./target/recurse_leaf_proof -k ./target/recurse_leaf_key -o -)" +FULL_PROOF_AS_FIELDS="$($BACKEND OLD_API proof_as_fields -p ./target/recurse_leaf_proof -k ./target/recurse_leaf_key -o -)" # recurse_leaf has 4 public inputs (excluding aggregation object) PUBLIC_INPUTS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[:4]') PROOF_AS_FIELDS=$(echo $FULL_PROOF_AS_FIELDS | jq -r '.[4:]') @@ -54,8 +54,8 @@ echo "public_inputs = $PUBLIC_INPUTS" >> $RECURSE_NODE_PROVER_TOML # We can now execute and prove `recurse_node` nargo execute recurse_node_witness --package recurse_node -$BACKEND prove -b ./target/recurse_node.json -w ./target/recurse_node_witness.gz -o ./target/recurse_node_proof +$BACKEND OLD_API prove -b ./target/recurse_node.json -w ./target/recurse_node_witness.gz -o ./target/recurse_node_proof # We finally verify that the generated recursive proof is valid. -$BACKEND write_vk -b ./target/recurse_node.json -o ./target/recurse_node_key -$BACKEND verify -p ./target/recurse_node_proof -k ./target/recurse_node_key +$BACKEND OLD_API write_vk -b ./target/recurse_node.json -o ./target/recurse_node_key +$BACKEND OLD_API verify -p ./target/recurse_node_proof -k ./target/recurse_node_key From 096e672607c41602271fa0a664ece96261c3fc81 Mon Sep 17 00:00:00 2001 From: Tom French Date: Thu, 6 Mar 2025 11:15:38 +0000 Subject: [PATCH 13/13] . --- .test_patterns.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.test_patterns.yml b/.test_patterns.yml index 63d4249fb719..3fe5f267f717 100644 --- a/.test_patterns.yml +++ b/.test_patterns.yml @@ -22,6 +22,13 @@ tests: owners: - "U03JYU7AQET" # luke + # Sumcheck is failing for some reason + - regex: "barretenberg/acir_tests/run_test.sh ram_blowup_regression" + skip: true + owners: + - "U04LLT331NK" # Tom + + # noir # Something to do with how I run the tests now. Think these are fine in nextest. - regex: "noir_lsp-.* notifications::notification_tests::test_caches_open_files"

v!XQb6HST}A%e`>a+y z#8LRmtSmP={d`u#Gsh1o>v5k5WYT>xu?NJ@y=?{Fd@j&&+Jf^=O$s(|+gR{IB-nqD zXQ>|ua&emwY}F~xTGTJp=;w=l(QBb7knOhnY{_5Ob_s~gF+a?+`(7hKh3;Y%s~!w& zS@g)Ackn970kD#TC}V-EB>f&zCTeGVlsduuyG=Qa60@K6x}=<7>q<9pu>JlrY}r}Z z^!wPc($9ROD>=PG?&?QSsP@;sE@YKd;J)Ca3B=iH5huxBDZNE~?JFA-qUNX1j*gXj zx)`(p>>GW(6A#vjvlk?qBbl&!gS%gJN6jR3t^-6SQmGjN%%!k=g$h88V7uWjpVXh4 znJ7!5Nk8(yBfBvFA@$8_P(~*4giu{;`Oa6dSUDlY92%S_e($4h9L48+7=1Y2u;=ht zGOVR%d_12QpC0cdPuQihX&|Q+6c;0Tj5@Pv7yGE}XC~cB{GV!x?N$#Pds)oiqi({m zcw08`xr$<2Wu!Hs*)(WGn>hcyCizEI^ZEhbl^6BK%xRNy97gxp=D~Y_J4~C-&a}X6Vl>Lmb z_9N?z{07nrZ5! zVl0~6-WEmiEyY2VL(%F841UjNUl+-wbYm7lCyJ^Q>k)kM2(YeC=lWI9=$4rF^`{lp zOLgL7gzX;|ocg_Y+0n!b$P;yPwp>yWFn92PPK}q6osuqf>|vG(sTAdHrWO_3$KV$g z4sy;IaM>L`U6kw6G*RtRqudg2C4POTItDmQ(F>!Fs;z#5L3D8+%#jFE6|VPBvZ+1Y zP9XK`of^gWi?Iq(lNT6au?q!gBfIUAn&c?7w!c~bBMtkXvxp}UNC~up{;~AB4NI8R zd&V_NJd40b<+)vuubj``r=KZ%B=YwFguSz5cU*hxZvK@aK@s(PXaS0BII4Z^OHf(O z$aY>V*h(4en?eTD8WQI7gLWJl=wHHWNR5qM3FNGXBK)p*K%I?sjgLmn;2p{g?55phEfF`y6 zA}WbG1FOBKhZjWyY8y%Vwjy+v+jR#}HqA}KX{8doLzmpn)k?Se&Qu9mtlX9R+1!!c z{+gnei-=#$6ah<2zrN?7QNFHc@c8$Ncc&jj?IJR-s|IvWQqyXe1lb#U%_vm{UV#4R z2mJ3R%UH+HDX=87&e=f0_#yy(v+pEuX<7sd50A)h-C=Ry^*3nVedcR*b+Q=e`hp* z5r>+RtR3-TGoh|Y#N!AbWv!`RzF#Skysfz=ksG*QRWyIOY*1Q<2-!Y7y^gfj(oLZ6{VkFl|`L8q0rj6ynFL88(d=f!Mmfr8Bo{G zA1}N{$iO)cgh8`?=~XgiUp^vIQWVp*P`-k-2=^T+so*<) z9u_C|!KwTU=kCc7%vrEM`SrDo@a;4kb8$w$JMdZM@j$+)V&{S8i`z? zN**MB9GtFZA$YD{;{kZY?CSKSpns%C4r-FUFO^k_1DK+KawMViY;$9-C%cqI5&%+}(IVZux~4ys66RG4Gs~-V@Vwi(YABA4F(6Y= zKApKc`ufhYG0HpiTQg=jYwdeQ!SgJ4o`?2>)#jQFc_0u(4^3d|d8lQOpZu=bH{YxM zSv(ZaOm24X4ZgCfjc^uQ5Zvf_hbc${Fo)W!NnfdNFQ)lJ>Y8|zWPYSlrd>p60>0zP z=EZq_f;7v?t=LinWAfv>M7-ea4s7}73j&OQE`Zcq6iepYRwMJeu_7|r{rU~+Mu>)f z?P-DxT>VV}b>5F#Ap|}Wqu55F8~*pT0F8_HqTIQ)z_#Mk+Kcbmf@@a1yEZ)H$se4Z zU*!fm@GOu5EV$B7{o(JvX`L85VnitfjwhOL^u2eb<1`8I?-%}W4ZHup*bz=$a)AEl z<7c$5Se`;d6H8SmZ|fh-GPGWdRMDlHCOjOXUvbgDeFT7KOaPIo=}&G;rTPeHTTs0| zJTZFq6La12LKzRO`#}Aj1Q(CIg8M4bjoBb^efEgKXyM3;NOatZ#>8&#@vG)I#RTTa z1pTt7X7e>)AI6@iCnt_Gsr8_ZEFo-wIv)9{OYqGR1z4klDuijmPGneE4 zCV&rW{o?IatLnojMZ;G;a03=j^Q4p_9hSfA4t*B zuv-<^p}|n$9Ya5C8YWDdSe^c*A|}Xv`|w^6H3fVKHJ04+>E>{{Q?@_ktv|)T&$?^@ zx*X9%<-jK*?j+bQ^NvXk^^cOdPptro2YXPPB~_j3$<1K(`6TkD6AwmPS+PIUCiE7{ z#W>~|Ve-6nYhl?m4p=u@;57djpFl}b={7=TpT8}W8XR!ezIP5%Tl8DtaOwC-g={O; z>JwaR6jR6)qoX9pfq4TwR%Y0N%HV9UM^?i37^*4F;CF9bnA7)`W`sq8OSjeynd=^_ z8~v7FS7qSc-hMAZQS%rTqYinsw?t5<3{M@$>#tQb&Mm= zkh0c+ogh04s~m9Aqi%Ljg(=!T*_y(aYQnuFa`Ap35GS|8ISYmk1=rm<6fM*xsmbHO zRL^xKTIT8|sZcCA+OJ58u^2<&tR8Vqx5NfErt+$NiCOznXg11c^ogh(%sH?izdqBJ zKf--UFKaTmS@7t)~x z`)hW2c<@Ec(^t~xlwj-c9gL1E7)U~26}4}32W~F7-jRz8&96XnL{j6!dFoX}mox)7 zalqrRH|n}xUtm5Y2Gvcmodz7iZOp<+fH?7(a+gE&k50TV_r*4SwEyh3_9?I%D%UD3HS z1#3(`VyiDvAEM*U$d+Cp(<0Xiw;4^!M@_<bW(L+*c2#!BizwFronVz^RT)n{He3Pk1&jlLZ`Z1U;*X#4@kST417*o8n%JbQd>@fTBqHNPZdSCt$z>bF-y37=Z z%db+c1OeE>1S!REZXM&nnGc$6#Y%sT7{HW8T9GOAPI3*Ol_P}EXi~J{ZCv~MplEEB zycio;)7G7!;;9IV_0=Y|;nNYo;wF7Sm}IJI*l;s#Vt;OCf$B;Z?_JRsD`rOxhPY9` zN42jaea_fJP4lua%6hHFQ=Udt)AlslTtZ4j$dm$Q-J)_|-xm7M)Fbk`9=Z>f=V5A+ zI%vZb+YPo~=4!Vig!yo7tz5YrxJ#A&jtHNGNl&b7n%qdESWhqyoEcwmvy8lX6o0I= z$K8@-?a23`4a4!y@rOgE^~{*dtBLm}3d371d9wz4^7yd{{`Tb@Wpn`x@Ifhlm5}?l zSnu5u2`5FUghrV(Bt2H=Ix4|xC3?HUfkORHX!Oa6twY7v5ccOvvzdLL>thWUQ6&GRbaSCK#Ljyygv&ZHKh5Ex48|;&(Q?ltumB z7Au@ILIZtL)77C>k*=Dmx3G^_=Jv=loz3NtTZJ3Eattp)Ipqy0Iwi->ICTY)Pay*F zBB>?g4E^rSZ?e@j<~42o%{-@IMaggV`Ijm|*?(9!H~Seg?Bc%QQD`NnYD^Irr`|bsyfS6UT>% z+Lf{G=fiNOzQCF3_%E012OwRiN5OQN!D&a;`m1yWu(hIwqs2m|(Srw5A+I7o4jRUe zaP(zSof={5yyisDK{=(wR0*Dl+>TKqXq)nlVKCYf6uE?Ot=`A=#J;=sfciE%SGmBu{u+a6<&!})QgspD$%img&1#|%sUcS7mWkf%vz+P1Ik?eY z>8DY**%z5+2yx>agq@k>q(?^ET96NMN!7ic`nsPsj+VzS0=~4P@3j7JoT5+l&` zvG=L!Fqn8zCNNRF*$L*$4sPoF+yM!HJEX5M6Uig5I{l%jKAae<0#5`BjN`=tMVo`osc4T6@U7cr9vd-f*3zB z1&%WcGNh+}YvY(Ej<=o*Q`GThiZk>KtD`F{y#OY!wzf^7VKPV~5#-HIX17lv1_I{a zebN0Vv?=qz$|z=T2zU%_16Jlc>irq}WK5HVsBM2xMWmB%RivM|zK-e^;JNA13i-l; zv&D9b9hvTb*dzwy9vZ$isFO#xGwkk(Ws*Q!fy@OPL^PAQW6E~LmfK!x%VOQ6c`OL9HFDax|C-fC zB0Kv>DKnq9&!E^kh44!f2DjK-pS16qV$XWHK9^yPb8%wVW5a7|xJ*+)ZCTNNA{J?3 zfE7+DP`NTOA_W7eL;kj%@tcSs(pleN&-2`;?;i(#DqDi#4}2dGOjfO*zx!hrxj<7! z{%3*Ojv>qW`e#4IK1xh>XqnHvhd1GSnL}#QM3=J6 zf9VMQ1xyeXRHo!pzxT;yS2lG(FTm@GJ2472_`Ti zTYaR0>^y9MLBkXx9JA*O4C*o{Bu9th&p-*Kidw&21_m-ykRSb&WZwn08sSdlnzP{y zL#nC^*#7cUJeYg8W7Mah6MIlZATE(Unl_bd3t8{W(939|!N56^!tZ_GikN7f)=ha4 z996Nu?xc}wzOwCO?lJ!41?Tg4F=$Hbn+2oG?R(i{c9*dYLd0J`4t%6U3isO6@1=fY zCHca;=t#fu!=Kn5xfgkdd9`%#2;}o*{7>wa^T`?U@pwGcC#?lER07n7TcsJf4MNNJ zoi`!u7Lr2YLEoj*h7aAHOsacVh~RS!yodEW@(G6{jYOrV#Z3On$i(tKAcU9vt&}dg zE(uEv0PqKD$wy(pV|tJcA_>NfbEAEi!7h|lChD>7{n_VDwwEY*oSKqK{TmUc-g18C zQLIu&J4@j=Ip^##DC=aw9Sn_C>Up|VJFD9iA+Ho=4E{NTeb z)%ZJvE2KYv0J%cU+ys8Y`|hpC9YPRPrcLQ}>vXf)8xP)l2xhko_9o?b>J zIS7?9LP|vxg(|fgt)-CJ#q{)abrWB{m zg13SExJG)tUyBJxIdD8S+R1Sa`{B636h!Pg~=Ru?b5^ex6ORNh3)kb z1)c^SOrO~#+81A6n^4TEayYn^`@HCb@ZyATQSAA_t*hRPgv$gmot6?eMH*Wdg&n-T zB^-H~f>bi=wvLMr52}^U05cWJ}`%RSvp4 zy7YLgJ0jkyrwi^JTDm@)OwTWq@j1hRceK5H8{v(7uYn~6jjQX#@jPSd-h_|`VVO_u(U zveBbHq((ZVUlsg%DpuCr66V=88qDU@{9?0fl97%4;JZ*K+) z5^H5F$kP0kNu9ziOHVE4R)5C{Q=!21x*_FMv__?0o{yH^Q%?NLgqA>=9~^}5dyiQh=Qws;yvxS zw0W#Mn@jgR97~7wIF&5UYb_6(DH4m!Zrz+MWwx|U64HU@_&~OPkW;4`PFUbUo;R1M5)4R)Oz=y{VZMZT? zydR*ZaNpY*lG7l4Qq(`3`@))j?thA{F;SN|{^pRdo*nUc<`+d64L|sUUHj=xAq1!G z=R?>8F?ETl9Q$~O;O$^3EUprEC4dd%%KWdlvvCva{^&y*)Uu z@)r_BJx{d&PVV8l%M53l@lh#ePF-u&kXY+`Z8_{UQ>P#?W_3N(onR{@RRhT)Tb@1PYv z6=u?qrUCLa4X$QVYd&hsr(=rKlP!e4Fw-0j`iCY6>O~mZhtq=H-CUSPck-g7b_vXq zZdM4_0Gi*i*jj@vCdKeiG0OA*5cbwlZMEOlXbZGhaVQiE?o!+>w73;_m*Vbjg;0u1 zacC*-?(P(5f#Pl{?gR))Zr<~rGsgYScgOvW{K4Kk$q31Q)_T^OYpylX{tZ}kfO|dx zaEAO|{o7~}#FJ#Xd}H3`Z!gQJ{UP~+DWEJ*0e}FAdG>1)v({yV2z{r|yFWPme^0~x zzsBSKb0K^$_*n*bZcf%3asw{*^z=;YOSb841@DECp+NWzvd?1yA|lhx7#Otv{%-Zp z|Da+3LTrL;4 z==TIH^_KRFa&@LE(*}Uc)B4b@*MphJ+}$1Kj@X7zUNf&>3;9hydZzzlX_P1oX>LsT z|Ir;TeZuf-h|zKx7>K2p6!wS>#3u7M#eLX7()4Jgm-k7m6aoj(j{NfD9Pxf6IPH*F z_n6$_?Xz8ipLQPysrzBfA8$d>S}Eoh@HI30$B z@%-zb?h|S1?B{F`XKli=l5sY!oLfy81su-^e?A&rnuR==xOCY28B@Gv74`b4&ZPtK2Y<-m}g?Lg%US`4}KWVM*ur6;5o)ixWFC8??Nn-> zPX!C9v74qbsnPe~ngU^kUBY3iP4V0JokueQnngen#eKWlw$05@=cnUE*CcX*>DZj_ z3MzS`D|aVD5j2uC-^CyQRvuX0?4>3qb>CGGbo7QIV0a!w1e`=UsV}fiQ>c8a@Tc%v z08_8t!POqL)Vo>pMAvPqISp^(w5#;Fel>iC=iU7V*1->^L{Q=*qO#2WHNoMJm3pPJB(8D%snq)b+VdG$Nfe}Etw@XKw6I$#>#^OA(IA)G z8T){)nyhOq9VyX#l!L{RFFDq;*yoe%wL#_?tamGwB{%1rtb%xiM~UPDijq2nvp0v0 zd!+>-IwGVFiyO!!3BjNHT~0(CP5UfiaoCdHk)5aSQ(+5yvami{(0t)S$~@gvwnctJzpz=cv&d zfzm{0z$$)kv!l=$ZH%$)-pF(wcgm{kc~!97bmNet`O{qKOa>;ADFMECAySiM=QXRA zp}XQ`P}rTI%g=3&Qu_|V9p z(YE@}Fm>>DG_x{zn)H~Qeb)`E9MBEOhzXGbU0=8DuB<}l%jcp!Myb9MMF;lqJOmx6 zf8aK2bIVGhI;(Gg4ZE16OZm|5@&1hzTu?Py3gP_an!ERI*De0%`3vlfqbxDSnY9Av zVu=9bB0HDUU$@bBo}TYV68t}G1R^{iwj#5Bdw&uL*zjjosx__FTG(u29mPn?%F1G$ z)cS%4qYYi}xYg|R;XAH0YMuNs!+FXtHr$hq*YnVP4h5OE!$I6&J*VEQk?mePIhX$cy$b0P8@ghr4Wqe#v*r2{vASdZSFD8iuSU-DnGbT&?O=J zhd$*wgH>hi#l8N%^S@j6-8s7%XFm6scxYR@82XvDIWf-c8;|An*Dd@N*)x=d1>_hO zXMBBoGX9T1N*=Ttsg1z%p2oX zI+d-Y&-eA(wfh#7EGGG2t(L1FvipMCt{~VsO@UDF1Cc)SjD(QF(Vtc3dIe0b@75Pv zqp4THa$m^BCev8yy*YKS47yxipe2aam>?AiY}lLhyRyT6G!UtuB%K#p7+8&`=xI$j zRbO-4?9yf#tuRmbPUoSMA!i}jKfoZ6ojByIhm4N#00rA=FY7kX8HwMq^7@I!5mbv3Cq%)VCNvl#-F3BDwEHRVH2>7*}PDCbkTf@#FYL>VF_jl}BQlaI9k_kFO60j?(1LQ){d~wyvYI8+#?%eu? ze^R{+L(g)(Lm*QreS)1}5BqTCkAzT|Hm~m)AEB2`WV6?by`E7h%lXQ!rja8Vw{gQ3 z<(+c^x%cPchk9@PSOXUOl6T<fL#2jmeV6(cZ zh@H^VnS&|Uj72lVSjWftvV7x!Lwn8AY10OeeTP8mh~`vAWm6C?Z5!A3Eb@Nx7Z#L` zV(S;kO=nC7-Na#sCT=Il?mq=&;XBdN4U-gns=Kaqw%GwYLyPlB9rah;4+p3oOBU!A z8dc&B9-_q|?(B)k-d&*LF98`|$>CG@8kJM;Cg2RMURW)bz#u>~SA=FmeS<`MP zQO0$;ax1iC6_pYJ4c!VaEq2|E*P?f1kTo{5@xywGk?-$%fNDTPCA}XEUhrtr9Wl%c9WsQLSmW+WK;?P1fZe zn;!gXa_%NSuer9gfVyz6hMo?`8p)S@BYq!Xzy9d7*;do7+OQb{mN)UN(!Z?f*Z{qD zWbl7leT1&&uOt#Ep>VmjTs?i>6oNtadu0pMy5M~KjkdvnW{sA{tNLcgk%>rUU z6oD0~o+AkRIS?!6$=!HeVj?Dw?`e1TfpruyCG>gktJMm5nH#M)0w(k~OILG_XU+2u zAfdepv=%y7vbgI1iqt@8W65x9r&2jH{D2Mo+S|wzIlg`LPhv0LFN0slPUUE7at7=Ge}wL>xY&%%fNx99$S z!Wa-DrO@T^KS}yd&(_?(NYN5C1f4Oh>w9mLc@Y%sM_fA|7>%2@}SvZt*8@3RcccYHw%SX1xH?~i)@8; z@AaWVG7=>pl}gkL7_Sdtg|2!FgiU&#cht#uPZ*ZBzmXP_vk!yxCb{;S6i?K@&-k_)_^W<`qjnP$;3w<(^J`|+?{L)GWGs>iIngn#&?^Iv-|v}ojGemZLSfBn z8^Msds3&pQc4%u@IpY5E#qsx<8A<5M2Pa_CubSFYOt?bQecVA8Qxbx}kExas1Wd(qu?m&svJ!($ySd4waZoS-O&Q>(^;l=vG%LtqyvhRwd&}Jt>)V1i5%3XTBRg z+Fq5j_L_CQe`Nr`6)wz}ukAJ4k_-ZM7DxtD6MpS+UA|Ib)vKur76KP+&PC;VR3?w? zj)Dy@{PxBNq9=U} zhYY2e$50y@Ia({EGA*KEFv?&vj^CPy^>pe#?2XU;c>h^a_u_uH)iFojQC#;7I{NfU z86Qob2boZhC_)QKR^+QB5B#ru8@LUB_kJ~Y3tH+eQOPRLpgC9-4SzwVKhp;R!3u%V zyhXR$ulu*Ni=ZP#kAeXt>z`l$g^LW{r5*jL)Tb!T53qh$51>G=f^+C!e4myGBK-p2 z+(_?p7H~eN%AE9WPs^dDqq`Qcnb5~5;=(DxVx3js|ET7fJ;#zh%c6>qBT0X!WdAMY zrCxD=8sYCy3CG^!+~AV)z|C6jffa~vFW6vvC@SPU}RzsE>aAKc6Osu)f^K5`u zS^?IgK9mz#{g+DJ!BLo$*NL84`4R;Jc#;?@u%IiQ)ass0k_e^Dp8i+l^J|m7iUxqt zr|j_pgz&`mW%Y;e-EVj-U$%BT{Q27V_u@@rlXtOJ(;pu59Hoq|>|9ukmPI+U{NOo5Ru^FGUaxjz=-BOWR-o#w`;-7; z8&>3Zc~qVxR09Ov*d_Xf20~Bq-q4>A)X~#yuX{|U<34{btk`htbGo9679lh>3UDk&4OPpUb0 zKO}?zr|rJ8`GIJa2sFrA<;NsmQ-pq}j~$=W5^+e_7YgRz)Na};?OWF*9DdA1?UFDr z@oMZYFd3iY0x;m*d91}^=`Fs&ECRNM2+?o)$g=#9%2$yHcfKlMPlf;Rp_FW)Vl6sF zcP9}i2IPfGOCQMImtD((W2W&;02ZAziw=N!$+BP#D9s&BkT$gNPUbXnC8o|K$*~#l z->2{c@g*yQg~?826M!4{Sd3TnOaKD%Zyx&x#x!cYy?V93t2Dw;#C-Zd=cV7?v)5~f z-X1~7lL+y-XmUs5?Om19lDP9D+Z`8OzSXjcHPADCwW`G*vAn+!G|jjHuaE68i0cxY zbO+EU(^7-xD-4_CpR+Qu!fxhFQAMsv%_ymikXz(LRmIjPEa`r^F9BI0FjJ&E3H{{1 z6~3y9T*h)oLJl_=!S<#+9%e55AAb$LB71Af^`(|;dwu0R6+{I7l?mpwT753)f?(Mb z^K~LAQ}jBdGi#R1X}?{lR4I`BGM4P?#h-c>>p3(vM0$VTi--%H6x-~$b@Uk6oLpS* z__O=#HHBc%_WmxKbsVc!$LPc!I#!8)GDYx04Gcvrp^*A!0NY!fxmFBKVWHL())Dx5 zX0Z1|o+u>eV5(AXf^q%XGV{@z5}WEOoj+_lH(LT4%oJdrP~e6_O!rE8N0wZQ)p@xg zk8*y$>!fO6!7m!fJRX&z(a*s}kn&x(TFuyUXNaq+flyD}NpGRH;H&Ofm`Z4B79XkR zQpP8D*{#5P(V(R|a8@p<9!3;MnX5k&JQ7c}nhZ=(*{z)zvcQ|JDD zAGb#Ikv@pW&&`twrvp;p_bu;@#3PRG2r~QR5V=B}*?<}mf&H3|9l@+;F5CG1>oX=b-8pZaES!n&OdXPl%|oa5sHY#bO%3Owy0!awLW_h zOKRqnMA!DxlgEz7mj@xU@#-8~-%7)R)%bqie0ZKjK}$9J#v;PS`1w|&^|%|IN`#w* zmf9wsM&QZPa*4yAh4PFi)B%1@GdKtmLPC1=vY5l1II|9nLLZfY&0Hn*v?1nuhGlmt zFlL#`?3*|R<#gpTYFSvNA3UebN}!*EanIfDPL!^eb|I`G)JJ!;*H7!d=vhca>r11l zzYTABvbP`!+uFdnk;}u$^k%TH2*HOVxB4n451-SIJ^!Wa_A_nMm z3|_%mbk`f6Z#coX!D>#7L9mbeP0lUI3A0<+&}OS&hpw26y0l^Qnkde%ISsR@kX%kQ zeBsH(s$~VWoU9;MK?F`W~sJ!Y{n zqZUeOix*B3Wte@-s7x!4wNrQbz4mi#dq0DoO$C&H_)eOoK__$}-n#DdB|J{U z*@N;6{|l_G&&MAfeihn<{o?VkrqC4wW&C`|Lz-Yf6ru2194Yrvd2gXk65m_%$4rxo#GPSlj6%LFE`v$di#VdU1BUC$td-ZPN|jA&s~2`+Xxrx)llx2k@>%WU~n2mT5S?j(U6(YN0!b_G}>7F*UxuZwGLkD zLc<90h-0P2Q={thy$H2qBgUJv^zA&sT-z;G4j&|2IyKn;{@#6~XlG4d?LTUl ze|WY``qO)hAd{nL4|lo&i&1El=T*GM ziX3}t?JPCXakyIFuL>;dg*e8vSRm#+Rs>YYuo3wx^5&rm#5++|qlJ{wr21f}%8Z(9 zw+iJ{T`865{MFr+fTLhmWlaN`>Iq(L7ooaRyjR8)PSUbVPy0&(Aze?!G71yVsrR<4 zEv{vFop$tANAqXJ_&zd)0{A#KE_A579Y+<|1|X}Yx^m;!f=CCM91fweK3RuiCp%a0 zUJ@_X8EHI*4G@miYHZ4^T0oQLAgH89@t6}l8d&}LAP9vb%}I;>b+Pdk0hII3gkio~ z{}NZzdR%#LEwSsZ8AbuW1<>xi^_>9=or#;w3q1O+N$GU7ZS9MAWcXx>!chn7POa2H zp=fnnq^2fxBA9Qd4!RKZ+)Ms2V7S%>{yFZ1`0PkO7kGGssj1!{;p;wEmuZ)$lk{gx z`b-oFb>6qK=u~`d--)|MU0zM|9=!MUc4j=dsWLcEJ^IBE zBBc}n@HmChC$7DB{)2N|^%9uX4Zpba54-;SS1h3kk(gg|L7-&1!d!caQ#@gHEL?G*Ec^{>;l=Kt{dq zAFL@oR3?1KvDkIv8Bi2M2D%FlmpzGZQCUOT!#6-c_9jQEuiB;;FFb`N>Ssj!uYx?R z;d8|FrY((sM&Re#ia< zeMh47blE<`D7^hpqH1K`1Ai2w%kJI{4#tbnp2@#et+ym)ygOgvK}n+=(O{8T3cm-Z zLeDy6x;u)==YH1fX{semvfhlXs7vzwP%@F9KIwh2B9B2S+?Y%*7$p2Y7W=Ljq~J$d zO01-T%OD=4iW}3q)oOj{kR2G4#Mkw9{rY&ZW*vlqy-<|Uv2YM!Z2?|BTbIgKBm!q$ zkw~wM$=<`HqdM*RNnLT9@Sq4JWS2sZuwZ)k;PCPe@({gKY8>MKUiZ^80`r@{K5O~$ zuB5k--cjxQVqB-fgV*$wS0%s)qcX@=N7AJ-GzD?mx|suvR?^y(EMi#e`J6akpg^m| zrspz#J3wnaTh0yZ+)W7?xb3~S6cdT(v`4ote%+MMSDxszo+N|}Mn<+p4}x!>1LTGF zaHz$f)=*!nyj_Y(k~WoAV2M(CUWADmC5w-bl^L7vjH{b> z%6g^uA+{7LSbY8TeBAu$p7bLc{7FRYhem|E#Pv_vgLW|W<&`}CVT;^U!q|?tl}=C) zq0qMFrf!AC2Fk_NwFLAJz5w5UW+10%iEH6PCO7LOOcKnBZ;4FIa2Fd5fW z%X*-U7j5o&KXDe#$otUQSn(w0<14Q+i}wEl_AjZ`#~IU*sXZ^dABN;u?^XnKi4{9K zUC_B=HzTH?`|aKsHfvha_1}dQJw=}zGV;)3X_cO1vd#KD?T;Uy+$3fHJX7q^4@C-< zPgx~4AAtL=he|Y{XL|R}s|#<{#X}Nf)Qy85R>VFOMXhXh;Ly5qwD0Bzy3p-sGlgiS zNo50{OPiqDpRO!MHXw>FA4s(9Fdu4wipA;`Y;n{d8E5S(>_33|Y*LwYVm}gTH*H&D z!a92iCKxL$80PWBTJZ^K1wX*Z1;pz3CVZDxxL4;iS&ANwdVpgT$1L)I=g}!qm;P3Y z`^;=<4D;tJ-ohgJ=mAq(XbiO?o{w3;XtlfZ(Rorarsx4IG>?KrHQ3T-RBl8A9QAf@ zpq#sPPrpLj1Q(D}th>FUExvlSPybDc4fC{A0P;2|>XBD&{9Iv=x6F#>MOtN9h&mmu zpzB7#HHXZUo>}(Wd~HK&=V5<7+Z*fm>^(_RYKVF)MztPoOiHbX&5+Jiq(Ws;)TE78 zC}FvPj#@M#PKRZ@q-TqWEH!X@J_ai~DYnD6c9e}MYhJ#QAUfq2{0r}9Fa))nZvBha z2t`}xcrbcja1^=N^-mNR0zRve&VwE(Ir`c5PKt7z23d>XJARSr1Cbe0)_4m0J*yDu zDRkC`*X1ErSc=HHonvmV^?2n$)12C#=M$@kAo3okU~1~a4i?QI$qmD>F7V^DH`hdH zA@TEXM0eCDuG{!N#w`5s+#Wz%9KL`<8vW{R6qP~+>~L<=CYo`L7>OEe9=@RSX`%R; zxeZ3|q0x!MQq&i*ec2ocU=^ap0c>O|+6U2w0%w?8yLVl`az~|5xOT;Z-wZ&IMGW11 z3JnM_g37qdkA*1jUT)x?cNyW~&^rhly?aBx`SbuFEWfd@VGF$oGz)Sk(A_&+aBh>w zczw{)J<3v_@4{q5`!{6fE=6vR2%iVvB8jzJ6{d@T9HaRM6 z1^pj*AVoI@4$1uYcwUl$w(xGdumume-{giY6=>}~~p{hP3*V{(KFRHRU*n2u;rc_}H z(g%Vv9o4tUNPZ+Zui6H9AT*t3@(Wm<`kjLg*|C?5Gc^d^I!f3+qqaLO6B4T(h}iSG zMMXM-r`I-6z|-LxD%wZVG*ejls~? zmqpk~9o`iEqjF!UnX=#?S?H!!VB;AYz~EH~IkhAUbF)1E-7 zj6cM;EKLE}mfPi@m^|5(fD}-RT5;`Zk4;xBY^rxz2b(U@6U(#U3hSm49-LViFS@HiZFI16WHv@uKQ{ zHw@&=x^LV8AGh%I~narY;N&XMK;2W z7fJ*g-;im&w!LL|pNF zqpzAu*OMFwrVRqbatz18m`{K47+(rKq7DotP;I^+cs{_z?=YXUA>ypS9t8n@t*uJw ztfh*r3wIALonyYM4m@|>(!pB|=k8gOW0NBh2K4GtCPNx$zBw?^1kk?IIh0?(TNff584swpxxSrmIE**3hiyqWh@RXd2Pxu+?FrzG#q$alZ$3tR;a*zmBDAE93PI>bQ@^ny6^CJp$s>76oC-_HTpF7)28}w@E4?- zjh~T6j5oadN(noL!+h1K>z>|olC!xj$N%~+)y3vE__gE~iLzP+ThsquP zOeFCFp8^y~XHN2GL|b)B=2ah;c8YFONvJFJTp9KY4@?!phCiFp4r7&Zy#s4ixQp&l zZ?sIABQ-Qm7}3v^OMG2B^PfT@?T`$Y5ElOMsm(Tb>=?QMokZ%}zYk}HjW_wsqL5!o zZb93}MY5&5SPc=uk$W1lo@^&D)QUwX!`=1Yn+(Q9TB z?43TenM@8Ut~Rp!DvS4iN$B(D_e4qcqKE^uiSV#zHpOf@XG*ZUv6g*U1p17?7?U3& zN)v=gND=Sxy6asF*%0x}aQjQQ1y0qExF$HF8~$vBY?|?*>7(|*$VVNQOk`wA+!f0a zRNaHbbO(m=_yH*RKhl+hx<>u|YRmL5eGF3>|4N)$@rA;9nJ6rz{P&W@?AHh6(4oGy zHa#TVAtEt8Y4CP;cxrqt=l2J${J4T?^gO)rJGsS}f1}nL7!Y40u<%a$G`#9%u+)Da zuAs^wFRY+8qc@P>z-T-Jljx&UmNZb{wfMrH36-`KuCzq47@Hk*XqZ)R93PM%jhn0m z3FxHf=r&MY5?W2HG?C^%@-wfKfvkm|pF{YCkkfqx2uDLUi2;j|hW}@dkhDvn{DYyC z+V(K}4MlD&5Tu^XV|%stu$TKW!($%wlfT^NNLM)$SRQ(l_THfy_dW#OWBq=eAQv zc`l3Xnp}CcKTOiU*O(O0eak%0eOdHw0E3oTZGXZ|gK9)=%=nFYS@PPw;R+7x4v{sG z3ic_}``CxJIcF2sUv4BwT_pm) zxBB6Caye6@vGeI$xC6#KCa)?o?r=l+tfJ=UiQ>>R7CcbXJ};_e)9o6R-rs5mo9^w@ zWM`Z>D_WSp#L2ck`g1&T8&ul9>SzmgO9Rh@vg;`sHr6=E>Z`uPU{u7C)L;%*!kiri zBo_5tUA0qK+O9NFzta~fIJbi^m!6}`#;6t5+2pl9y#H*v-Ar<(T!+V=rtpzEiZJkm zM>!j7G2Icr{j3+E%j7jK5e3f@y`^HJ2TjE9SyZ=v{O||KLtOVbE|loF^BVjF%6E>V za1(`^8F$0lGt~+-LV#pU49W@#Y}Mv7eT{V8ush|0u4}CNhU6|!+IbwHe8ktFzeO=A zY>M4sK+>aaDvWFLBKMtHq0Jz5&tKDw_)U*D_GEf0=d39I5ksOZ#)u+TN|Aq4tC3=C z>cQb&C}Q@g1W0ilJA4~OELY|5qWs7G8!x7RR&KgFO?F5T zW)Iuhp%9!RcQ~|&>SReY=CtG0!H}7OB|)05htx28n)OCvkhim}xp;v;YPPY&+R&1geroCY@iDL*-i=u=zPU?QoP5g4%y4)o0q-R7pj-r4S_ zTmw{jUMV`U^%n1gZaLrVR_Gjq-sz`v8?}ZENDo_wY9-Uz_-}*_%~viqCLicl>D$%x z!rgnMtQD|5ip`GG$7@_-aBIn)0Ed;eC4t5A5RsQS4oYed6kURiqT`3qa7N?hV9&dg zjf0)fGYMYw=OjHq5GP&BC9>Mguv4m&2+~%&en~8ePlr2UAgpBA=B7~-{We9=6N-^8 zR<=$nV}QfZU7R)zC#}gDhsv~Z)yy3dDY3u6(x)JW=lz(fx3bg1U{XHI5v&$V&BO1( zqMfVbnvsBbu}*B~k_(*;9=Cn?KGD>JC&bR#S1r%{#eCcxEq(K4n=i=1fTQbu!>YN;zAX51>VBaSo*4VL<%-jzcUrEXsq+1jKafKJOWMjxXer`!PZC z5fC2eu}z5Lp=3EeU(Ox88;j3{kxGNfdndH+)oyGX=(U&&xMI@BnZDsN`GoFTQX`F@ zRJ$z0&`n)Xm%K4Z>DJ&l6ky#l0EYJDKZ~ty7^8vqpepe{h!q7Xe;#=-1^gV5H@KW5~fI zI!L06WQ?H&>@#2%F_gPBJ2;Cp&5Sc6R;%=RfE90+ss;7fhys&+z=UyvQQ3SA|K0rzU=v@;&-{5VEB3LS(~G3YR{~xO!;%QJCW2D;1LaiPqzroj1?U^nFD#PO+RYMnPMAbHF1DM?eav9 zIgX2?2FY+2WN~H2&0%?BzFJ*=3SQ{vy8*wy=^!-$nm>TFc>}VPxudmM4b}4b&iv+7 z)vq#hbSg87YBP#B2&>Q8iu!(k(!X(O&zF$TI+#NLB_WF&TK3X-XJ5!+e)Z;9C}DpD zJar6S&wSjU6ta!RQ%J{qGs}^Z=z&9i!;i#P3&nHf5}Z_Lf=Nv7dn21N=XQrc*JIn8 zalIF?E!3dyyqKP#kQ^XwlUtg>U9weZeLKYT155wctBb{2=T+PiPpb?rDqF1A;^EUl zl#UB03Rv8H!pQKL4gG3x#zJ-@-XjvPKW#Ms98>Lzxtv#ot55=z88U9>r-w^XK8iZUfuC4y*gtdO#sw)8L*EW8@gk69t{ zIzXr`H(y`RNNJIMUu_R*cCd-HCh0Z(HJ!m}7C5b;U00m6@#dD4G3{M}=VFaSalE^5 zv}f35>_IR5(a73&#T=u=VsN4)1Qu7nKa}7u^O*Wa+zp!SO@Dhrx~21%H||*Moa1|v zzPjg!<0?)97Vye>@k71)?rV?y&p<_VT+aOj<{GncQsyRgUC<8WE#zcbSxp&g1f3ub zdGJ0KOkGj1@pr=VM~zMyM4gEYL8j@z32dJ(%_ZLm)bA{6yrz_)PnXP|SK~k0D$cj= zMw4}5uD!4gVm50`WT4!@?FdwlWMl|b}q-|7$F+|9qY0CnL7NqcBVvRC8OXs zp{V+FnH0s9E!pDCxF6&c`iI|sHZdLN*I2|0?&LhNylIZe*1a$2yD7m$!H*2MVy5oX^fMJsxj;J~@y+1Y&v$LJ zW4t{|2~q_YlFz|nvlTk@iXx|eqSx2Ir)x|)F(c{64hprYQ|BO&>z$!{Fl!{gUZspm zlG1Z}v|7Djk%eE~%xfU3M$kF`;%YQLOLecB4B`F?!z>^q`45 zinKD@o#*6dE6rRn0-o14LF~FgN327IRXU+q2~sur^XiZ`-}732pCumvslD%VL-WB1 zMdkOvRBP__fUL7nuJJgrBB`vi9M1!eB8gT>y_uu#YT1~gdg-jDv;w@n55078#fZR!fK^Tzia#kqd|hn1#_H9I^Z(?YUSf zub9?)6k3dciuUIP^&bE!>#8}(GS3*uoiSRZ+)k}H$A;J@mmQgeDWdZO;`kE zgo2wdPxOyqX@So-ksH_hn2Y%(PEGe~ga+UhWI)X` zrAFO9TUe?}gV03^`JuUqa^(Df-$$ifu2I9SZ@rz3gKO#X81~N-fuKratt_CC_~QKs zALD02QNu|WC}i?JSd3(5pEJg3U9F!K9cWVkjJ~Nzdp(RE>H-7SMF$#rj+nY`Qf zK&i)Fv7&40bxSm{YA&D;Y?-J*h~rO0U9d=#_YS!w*GTvtE00m^sbtN5c`G{eboxv7 zrxKRCX1j+n+`0G#vW9Z3oG&cAzruVsMui}&0qC-nYrqedHq{N?jytiCKYt7@ zfq)y89%_MxFG%|TzSu~P+1OhBt{5H5O_3eD4OiENiU%3MwvhS{5@E6(!ce{+l#GWB ztKJ@YGk+Un=$BC!%0V9AR`jl-ZJ zdsA_xIWT3u#by5%^Z;4+m3sU8#W_x*gzk#?ePQ!znN*E`{(hRO+)tE7@uLeejwDsdMn-A=vtw zeK&1GfP2qBMgrFpd)uLIhW3XQU=LRAcR6_otK?#L7I0ih)bXFtAS=ye&D9ca7i6Jq z`OQf&vN$!^ey|$)5_6YsF)nc@^0pw&E9CFfuZQF&QLoSs|HA@gK>L>(nA9YC1N(;; z_%G;>uK~RPx8kuz6C%gv(QReoF_ua9VSeVflEd>+`V_Y``StNzG3`DrdM}b|(<6TU zQ%f97TmD0TNDDTZ3F`r3>bS2YvLR`xQn04XTpHCe_j>(~AGN;`Q@9C8v}-G{ry*RA z;a5ntVct|_t(qqhRjEt#UBmKK$S5JBgWtA>2CkZP6spWXqjDKBQ(BT{lvwC`?La&5b*OF) zrG?tJ+L2ZlXm;WH>|n8`wyNHX;IxF+*d3QF8`K-jV@NAOnz}y$Q5*GCv<1 zqEfEb-W98NQ0TmOe^kNYtk6iAaKq#2#cS9XO8KT+#PF$WYd-Uq**hg6ym@EdchU$5 z(y6{6QCcG`_pIpMXOD6$+p>AN;0}ZaKXy&yy4GqAfNFivfa7nuDRqGEJ?m8lBv@9S z1)PL)y-Pgykzg5=`Y{u;`d&r8M^(!MycOJ98{ujKNfc|8q(2E#aI=>!Gl(5iIqSrQ z-=@jjBVQtc<3IXQ+T#2r*VG)(L~fZ?z8*)D^gAzg4dROZTqV883Ax`kv`z+&Ba$5k zHy^~;dKo91n*47T^|miuUa03@8Ue@ia?i^%RD04}kwvZd&0_FmtKWHJ3~j zFGTIJhX^wgJwlZBXv~O3-lFR!e}UQompE_gkrZ7z6!AmvOfL^`-mvgV^j_%YuwcV5 zWYT62BZWwtS8Yt)n$r#*@l~rzU*OzV{(Tow82=}K_Hw(KfQ+`JNN}d${nrX4Yjct~ z#4#!}x|#0$MH|RBdbu$$>xry3H7?gA;SN32OfCb88|hf7wsQ_{V=kA4>Z!z}p#mTm zIm9d%uC7vp-F3kjGu>XBco*HPkSK(kRw&otjJJxM6n0?~MP^MPmf_K^(1#MS{=>|Rqb1~G6bWt-dz4U5tegV#yt7V zk4=EdkH%0L9yK6;QG#RWY?i(fv!m+uc4Z+VODA;J_h@JD`|41TtUmklGpPJ!CkVuCgk?;)nW{< zjl&>nC^KoOnX}Z(=fgt0Qm8Dfl5y&Ibv_avQu+%U% zCuaFc13QE0XXOQ6?=}T*KATyNrwEi7Hv3Sw%XvtD4O{8+2a}{2ZG51Nejd}2>G)Lx z)efm~vFO?MjCoRwL^E!UOrqmU`@;eI9zq~ym%)AXW;EMLUa!d!gOEVTX&G65CosccNGl8KDl)2=0Eq4#8QQ3dhCv19{feKb8)#bXm!!cKG*u0u9xpx zqIQ~bk#^aNbOOfs4AN<1Y~zoU6)N|wDr}!?e_VrG%{tI2b9qRNuW%Z+tMUaZ^;8rQ zuZclO!p`sBWVeNKElynq89^WxvJmH(?9Z%IFPI}#n{pa}Nu<<}jXo=wkOgO|)2lYZ zyBhFUFw*yhL5056>7_R20t$j#qP0n>cN{|eIPDpat<(;vN9LOB5n(3LRz%mhSfq(k zE>v^Rfr=l=0`Hzwwmr|bI`1DtuJ*W!2*)$)ktNF=t&%P)>VPtTpv#5N5ay}16<++O zE_W=-u({h~p++JyRp!(5SdssBTiI#$Zh z`Ac@XSns17w&g2O5v4M<(`t(-JKB3*$o0tI-;F3r#IuwbcpCg-NL+hA!#8RLJNIbG zv2bGN(`VTtBvg>1>KCs~hkJVzrG7ADCWqEkliW?Mzb#SXz8eEFwn>*Lc2V2F(;#gj zEc*GHL!V3avQu}n2M6QYLS+TXgjDx?@IaZBO=Sx1O7idk$j3JaJrGiIfq(L0`x($M zG&=~qmEKENnJ+hJq)f)1V4QxDMkRia_&KNUo9R&Ed;WOCWi?0i7^tz}V)y}DMpO+X<76?OmR_GVO9e+Pg>Q~9vVC{OFO)7u+kTrD6^vp*W;Y#F z@YyWz^nD2@pMi#pQCS*iprVU7c~2UWP){WC z>^gZ6&L8(DpCp}4DBxx_Y|i0y1yTPKQcm2l!yi>!gpZAGgi`5AfiP+lky2!~;WHC-&*nGxEbh~fT;#wUG&oo{}%uRQbI_gT#A zRes9#f}pE6(cAZ+!9>cRf@ln6Nw?+&^~}!}Rb~gG*;yInOlo$@ z&uNj;&epr*HAb&CoME=Kwih0kl(<_+@S&0SxwbLx{08<9Li}gF2spaOThhHvKD=lH zMP^#!^%7Mx55F|4M2Qvif@x#}N!)CTDa`8;BQtyu+Bnl==`rd)N+lcU($6uD3uLJ; zN~hk@5K_*pu|S!B0F&~{QzmQ9V)_z$e)+|tU#egG0&7nw8Y?f&8HTJ_6(;UaF7ntv z^At<<1th^Pa%#*F?IK5I^i#Omv@T&M}|) zYxC9B?#rKU5bFSLsp~w^yY$H=>u1{@3#;rnt-t=wR(*A8v!3SyDn+#l{Q4VKx}tu&eRJ$rQ7;FLl`B5$PQNdGytf*-d+zeo!ngPTTXLnBs?L2~x1`MX z?W}dos2y&M9yna!&%81RQLhuR3q2tV10M7@HVAn60k5(vKFm9P#`AgAH~##-fBc^z z+xo?SoRtd>vhvRWPJ(@$Q~hiv`@ak47d40DT~7cDSw>d{Sr(=ip@-$Fp;u~7{|~Bp zBzWQH!q3cq4lG#$-tyDGTa>`48$6 zcsMz{Jv{%uoxsABstarnxc1isU8*_i{@nf2s|{kHwL_DDeT0?Yfux^B<$D%g)}hY zZs?uZ3R|HCU27TowpnY+i96R96P`iAnxhz$Cj@B(nxF;l<`b^qN#zwaf>BKhVCWA}BT1C2iY!MvBA6zW%(axDFe|X-pUH3b)%j z-GU3_f#%0MnrrRADAlgR+`PhpA`l0x!egfFJWul-^&2K3h-SKQYFG8J7 zxgHH-T)nVYjo&+A;gkCf~Q;zLsnZU5%@ z-2L1uB8GRUmu$7C#ka~_>?g$T4(1=I&ZIpJRfOQ{F3a!>B2>Jsu7djjaR9n z5-vtBeW>+Q*}Ur%lgUW(UlW6S=R@QR=9`WJ>>Dvq{4j2R%Jc7_#R=7`U8|jaQ^r2F$k_Bc2<{lX^GC-%-3p z8%z)wTKNTKE6{Up4zYrn!qf9r?8Yu~OT$gsl0lSylD2|)OO=T1d$6n!X@$&r=EX_w z`4gJSn=fkSFTMz9hkO?#769RW!AT7XJZ(-gpzGX>)_Y1aOt9HthG?N$JIWD!^zb{^ zYnnFm6-8VT+z}}fvm)P*(QLyNO&*Szp20o6=UFGxZg>=HVI0kPM$4b#M=?HPf55!I zAvo;!*+}7yn|+(us@^NK^0xi9cwFb;;-qcY$4`~2wIa3h2g!6G>6XvZca49lzg35Y z@o?`wp+vS3l)4f5FJQebk1uBjecz5=J2+&n*{%)X(t|th`C>Mny4l%n$8PJdyaF9o zx9B#{ZKvW=_&gf|3HmN?tRjDClQSzF`CC7&9D(4J>Ve$_Piuf zB34c>bY1_(yMQDkYF1PxBcUf~-APN?|vfG6~ap*+pG6sz|R-Ed=}P^Evbj=^2T!7`pEN03f_G9 zENh)Vj8U{a&S=BFDQS~+h`L!co?k)3!xbzP*h#>(#8dg8hfCNgRCrieH0^#GQQDMn zk?>T_cnxO_cFmv@yHoU9CC`I*B=PNC#+)Ww7SpCaxR^iP!uoc zvkb5gPvp4DY{!eMNIaC#^9}N;zwkzzZ0w;OH!5tB59GOF(Bd5IiA#11Obz!%+6BMQ zN;8g_yVX(@%n>Xm|5d(8UNgs8K0l%+BI&Eo*QZ}UL`L6rP3BVdQf+1apca*K)-+)D z>WR=t?qRFPRRvjAp#{xO;Z`43EqJWU-O8oQ#mtYo3zlB)Iqm&e>Ry^6WhOP@Fn>1m zD)Kt&>S@CC4i{t6<<81vevUKWyI9iKYr#t&QM;?e#BuAEX-%e3ZS+A7UtF)=y zyfMM<@;>c@!8?PXDKpQZ>2AZ94NY(xbYo~cR!8V}!YaxkDnYzsJU%>ps-&p7sGz8W zD6qn_2vzFp=Rd1sXHxak98xQVrl|77*qpTI-hX{mlD3(O(L>TRqA)0r8JW&gW4zB! z=1CUYDG!?{zBN)>jAM{u&`?ud+cEiKN_dK6Qfq3YcINr|r<_rh(Pc3#>I-T|(PU9^ zj~e$S_pPI|&dw(G3G4iky$8c4vL=<0?i)5s-AB{&Q(G;&N(W;bgIm^XokvCc-ajq2 zSoSBjjv`DtJLWg$>-JrDVaGJPzP$%Od*;)z8c_q#UZXuj`;4}L)`>=go`hkB{sY|^ zv-UwQmKf^OxtlgM|drT9dSvh zoq8rqE~7R}oyILi{iIQG+n-;aE^sp4Wekfak5lTv?wE-8{E8U`;ej)>@#V2DE}^eu z4>Ic~T5>(GnD1BGS2|O|)Wj|bD-h9YKrkG?IZNj?96zbBMbZ8A7$LpFqR+8$+dcg8 z>-0R&{1R2)^trTBwj7>8fpXq*l`axkIK(W-)aJWK231l$L#+VI0}BmKApyRlPqKP( z971AaZo3s7HkzJRu@&C0mlOE3YRrtwHXUTerKV%@(R}yyZu7s+xQ)N(z01BUp{kbX zVU#?~+fng$)n$-O!}l?PHlp*ESFn-%p6X!Ip1F8xTheDUer^#Pmp#foA)F2>8-I9v8hxMRV~?h*Kin5R6|A^wCw~&4a?5g@j9Xo3lj?m ze{B8uA%g45`9aG1HeXt;ri;NC)u-C5;VhR<>ZSC!dZUu(+cc+|M^lqDl?}(BQ^`%K z&cJp22Y4hSbk2k8HS0X<6BKde<9H#|wzm5(M4ei9zC|$)zI&ZhqX47Iq1Lnic;wc! zlTJK9Y{0^!E^eDXFukuQ5R3}P5z;!hYalW`53U8m}Q4k7@+kTy?^JZUs zd}=edx$Nq&Rd>R1S=H;hJk2o44li+U*!Q0c{61sjJ2SX7?6Ys&*z#7PG9V;wSHCyIp4KscrrYD5e> z3=iOjyYiRaQB|CFDW2IL)ODTSN5|efnCJL9G-EWq-pXhD8+$|GT(B9$rh(rF=hUS{ zpsGsuJ8b9F2ZJkfay%Ce;eiRNU{{wNQcPy^a6j(!WJ~t?0d4p ziF9?u@vENzeF`|)zYWT^1hu+@oc33m_&>qcCtTSgv5sz}uz0I=s-aze)%F~qnWy%+ zp{zs3wSNXCMGIu_%|tN<78kG0o+z2Cse#yleJl_<3ONV^*h2xn;wTh<@5`gGg3zwd zqk=#oRv`4>_oxHESDzT*d-cq(U$nSj5GHU$1bp2xQUAId+ba|8uYJ%Na1A7-C8MMS z{A!svn3>r+TG~0uOpJhm6WI2OPaQ!ZQl_gfijpQH0%(84O8be^6E#&~6FVDDV^h25 zW}I#|_E+tIMBRjeT^lneV_G+xm$r_=ZesM;cL)RfSEs@BwAZ&dS&Pv>QF}-$W9MK- z%g@Qh$we=Ila`iN)WOtTSW{O1_v65k7`>&Flf5t)?CR>u>B_@t=U@STAS5IN=Hdo( zb8`T9a5%c#IvKlh*g7)&YUHnWWX&8+9IWh}tn6%Qui7ui{N5dfd2bZqr+4%6XQORxv{Nc<#!4Pk2f!9en$Z z=2vFTx}yv0P0u6m(^Svi+7{34bkFV6w;8TSPCaAG6P`tmJ>oM@*;l{Cdat6Rph^OI zfkF#H_X81tQ2#Fu{1X*uJCu>i^LQy|;Gh1OOESTvGBla^eV8kO@}R>jlC(x6+($V9 zdHac5q8s5~Q3e0(36>}5nlcIf#IC4s-0u`^^fyIC1*p7`dssRut z%ckEs)Z6cx?4?16C;&AniP&F)PJhuX6tsZ`P++{v?8Vj9>pOtQF|UYav+?}uD%yrj z*ElA4u9K3k_A9;J52INW(M<4NQrzpQkfh;7{e*c%&nV7xLDvJp+?QeaJt?12MpXX$ zWHeI$F_RZpliWB)jr2r*@1xB9ZxS>sjEb>Gnw?PLq%7h#dI0}2<|a5+Zo1$g+_T~ z9H#f4@eTJ`|Kpgf7C=p}K+a0U2Zf2#~?i_KnY&v|eUi_p7vMy{g4f zWr3~84PX-@fd$zz&Pa*6)eJk$Ns~gJ1Y$$-Bk;_&&zZJH{8`dxcFxj`jjr_pZC~n| zy9AI!KWvDEL$ulai&)}5;;}QidQ22R5RBJ?(($k4i8gaN@SJQ9?TSu}t8j$ze@}eo zrvEwFui37^2e=s=0=6O5zrdL5Ttt~|2}CSz-SI;#QeTA0AcZ@q;i^R8o{96{F0g=; z(MlC6+*pwLITa_2?Go;6^wB{h6Mw-x-~Ov7_D6gTAS$X79jz>6-P0GogHLt0Vc$ovt4ArL`AnUiX3 z%;ZIvYofjodH#=iX$7Mi;zLf*E0*bhieZ89@lD9~Q1+w<{KO?`pryqazyWY*jRwre zm0bWV!?kgtUEV+u^|BIkycFQjAY#hkZXZ7vU#z6Q>Om;Lj%!W?gn<9-L98Lt>AUCG zr2UbG@fMgkeYyam&UiZTxwsKPmg}JhR)5RAFu*`v|bqPtoeF^?nvc|Q$3L=K?%{B`QzbYlDqEL z?Z#%|AKw0DXRPJq#RXPEx?9LH>bkIm99g8q5KU+H19&U=*P0L524d7t_*bMp?tgl1 z7p`ps`mIwCK17{Xu7Q#oo*i9%9*RJiDKOR?LutL3i1TtSTu`Do-(^vAyuyMY5F~4f zwf%Tdu)xDAYMz#skw?lZRY6X?TlhtK?vC%<_sq;;0$un3Mf<(eB9Xqflq(@uZ+>7y zbA}C3M+v!EC{FHmc1eYoYF7s`2#G-BXp6*ETRC!n`UT9Mc4ESy5PlP)uXd9s=G>P#n_{iks5D1{xfVZLg&lE}} z@{nsgNmX4jzalWi#9WH0$^{*0TY(x1c5Md%dk76R<#3hkWU5jvD}X!gjBj@VRhJ-t z+&pq`iy&PEp-&{z%aj=1r?A4XPv`==Cfc_fAW^UC%d-G#(4WKn*Fh33!1*LU;zc$B zc=jzfzZQQ42`GV;6!?+*WD^32DFyAfB`j20uSKkn#+yUR$g&`(Thu2vhjuySy8Yx+J4BO1zbcT{z2sdeeTbfJ}dR9hj^!8QfG4KshqxPmzLsch7AO{&gdBE z)Y#7j6+7vzHkX;LjdE<&f*lRqHu;oQU?or<#>dVm(2|G;uU*&@LrU5@b^E?@=)G^n zXe+ooG4te&5jDJWds=Nz{1_S+7q=3oK$9nWk1}u&vF(`x)JYxQfo047E|s*Z^MOho?YFeqPqLDu$0ertCrw4EdLd1A1Ux|%lK#Qjk-Nl zm$?-h--o4aI%jKzkKapLn|JTBN52U$f|jVCpAN2c#CL4&eJPeglM}U``FI!CbLp-E z@}zF}CN7oOc!|&Smsbx|w6Wxonl%sWYlK;SF7y^}dW>m}6f*Vie-NO zyb#_!-7dtp7QalDS5M9z*pWg8gp|C@5FMrav%iwkbVhVuRu^f5ceAHkdeOxF39lS0 z0d-ZpkthfAvq>9cE55ABOnsxow!tZI@0?j@{<$;g71`VtQ}8 z+3@{OrjHZprT5cr#)OeIkBk;*DJf#AHOhU6@5Kc;jbNoujK(xZ z*K}XL>02WZbzIRM=BBv60^v`0Fc?fenWx4TL)GNzruCg-wjCK4ht!XBh!~u8TpuXKp~(R_Pi~j zDznzMHgb2ddmXF5jr({jgeE~Ce!OZ@THz8Y6I4J1X7P1&W82BxvCom$KC9cie~R?l zGas+=ZOBvQJ_w-founnihIHnu@t1a7Z3h!(;y!Fm_`kvs&zK2AGwxFp4@f>^Hda9d*-d-D7LPCNp{&0x~ z(XdO)JGBbv=7-6^1h%Jft=6!JlzW~>n>mqsRmpN}-Uf@`eU6xXBI_`A6p%M>4-oc- zxQVy!WyMFCoMZiCc@@(78J{1BpOpRndt4JJ`Gp$pQDLJZBOkfD{t}n@rkeYn8k@uL zQy*u|zG7E9atV_gIv<|IPT%43E8OX{ue~j{OgY{To>sFO{>*-5f6Eo~e4o_5eLeQg z_^MZ7@<=vF<$VK>oi^SHm1*u?&-wL|LGOdLBsq9RT^~HPL!TaJ+8f5)m3o(8;sLlp zwR^i>_jA0&gzNUZ*UoFmUEzNCQy%iKbKEkqvW#A4g*bNgZ>l*}Gt_^u>Flnc#uII1 zJI$5%-f!!!al#ffl;G-TyboB3&H!r{3!*fHPP$S$>x zIh5ddd~!raoT^s+QTyKbcl5G{OjQJM`uZNVrH55THLkk$TZ^YqJsLO3pvH=YTAL0c zXR05IM1{y5pL3_-Bat8*_Z-lO1O$zN&=(4>4FNBh#-OAA!%s{4)j`3=6;|d9K4P~N zW)GeuT)FXv^q>Ejexx8yx7;$xu0IU6iJbv^2CR4?!!qRO>v#3yR^{P^!!$n(J*3`6#~y>ZZjg!xUmYG6t)wWZNq1+hGw!3JaZ~2 zjlSp&^3Ll6G`{C@If<<~Vd(5h-^VmPm4R1Km>q$X@pcNNuSB_C%aZ}W~Pa(A_P1fo}& z!t2RzbFxICbGVrQW8Q(`#Y2B z4m$nOcr!X=@%bzz7ea28zC4}$Wc)om<2I*7{ZyF=ra!{WKh#N4!GL_rxrfnf%4H$) zJ6g`@GrCby!A;)9$VbJ_dWojG@0_f>?+}4=%Skyj-FJfTp>xHyGJ645Qp@Y@7nyMrt|pR-J&HY|1&Lnyj&X8R{cq0L*UO^5anSj% zWD<~0!upjrvb1SDmpU8^RK$WfevQZiJY-=ogP2&Fa5*f$Kc3~oS+M)wnKYA95VKc- z{F%i#V$Mlpb6i+uH^BLDzHMkf-P6nL5E@gz<-Y$&Cv^f~h@m5Hw}o^p-rdTD+U*tj zLskaak0+Fx_DkPlU69`xy_C%AlliQ=*h~6^f^{oP1asd&o8w>`>JC)Iclo)o@uA>s zA(QrJ&RM>fw+xJSL&aVu8Wmzg$Y-qV8a7LCHtLJ+g*ZC_UfYoSrl^-t)=zbJTUxKF zZ*=%%s>zAQzVP$6wqHKL>Ak1AUCsktQHgzX9?;Jbz@dl}q4ffS0ND%EfpIE)iH{p8 zL+9Ue@7tJk!>yZP2_2O@3K0+IXq_o`ELr(?)wi8BVJQvXnE3DLaufm)W&zDs@d%QR zt9k1vK6P1>^%i!?6*aW8ap>iSbr=#=4t6lCZu?#+Eqf}3hp8cn@FB^FJWTr2EwGqp z>jmQM^|Ee}ftydzbvo~_8EB0blT=hFszPG?n}+^QI6f!xx5tLe3n_~#COr>cU!N=_ z!QGk|`S=*<44+>Mhu9BzJtV~l14zHbgpsNxO7eZst&$u*s9v^gJ>C9L=1qzB$ugGy ztw%h9$uxNC!{h#^K{$Hj&^VO2&cIr?h@HWp8K0bRyDw5rqt(vV76wN65PD@YVW)?y z_7{E#61->qHYpTX5E`{K>8e*xWVCAbJ(H)YB@9WK)$q5 zy!ko}`d8+yte5(BmnUgnE@2*(=tq{sneFSvh7fPuw@j9d+^uM_r4jjfzgFv^`-jh- zlf@$r)?tu=F^hC7J0e~n?z=I!j_gTGm_K-u?H}Kl>{?}&Q)Vr61|h+#Hh-;zjC%ms zLD~H$%P#nkK*67Sk_KN+Z5OBVr){c))&pRO1NIQY@F!%mfVW*uS(x~Fqj@BZ#urg8 zLBpY^evrbag)D}Ai7C`KpldJ1bMmvtg0KTCGaO%bI)~mp4_ETZe0=P%G0tQJQ#!$j z)2-Sk(a=LaQ+h`d*gdl>29vh8Ekb=N+Y z72soNq1ke@*TxJYQ7+9QRjz zDrNjviW0TFwj)tX;HT1yob?I(r6IR#Et_F(Z-pz|%j<=6MX?~%&ALDww{rU9!#UT) zez$;jWQROGk6>%!6i=iQlKgl+d}s}RdUi;t3?zD}@gd4&s8Cv)$x54GPWwd7+Lf3n zWit51xFMA$H8os1pyQ1^V6RUgJM$IG;l1adrdsEWN(u}V-tY;4GN>iX`9k%kmtS*r~sfV|%m zIdsDQTmLutC4%CRl2yw2!X_$LkDiY+^3R2{3U!aUvDvv!KTIA> z(q4X#gG1`(tb zbl$l0`Q>Dy`~#t3Kw^Kyccg*$SBy*W?g4sal*l{BKCqsugcSa@z>R3c{GQ9ZgAEyP z8KF~F>816$rxT6uhh@h5P3M z^3PICc9i57Xh{_&UM@er=DHF7Qooy>J;|vS`D790Wk`BgCa!8@g2mQ6{DZg zD=#nakw?wPE)PZ_p`^GtZoRy{ig+7SJ&v--mlUsa7w0PTxudLR5+-Q`oLosG7D9G$R9qryq)~J*c8z#(R^FSgl$2Md5*2g;+$MmMg;)CkK<$RfY$X zW`8|kAm|OdSj{Z%lqEkPcrV_tnW>Rw-yV37TocnHOuzUXrOH*MRtPUv;GTa4lvR7_*#kZyC3tI&E$~BcTSLLdV*bGozX+ z3%W_lI_q{n|FtC%yezvjHp<##P8AEHo|#Gc&uY?7Y4k+Ifmq2vQnlTyd(am`+>PBk)C6MPBsA%Y1vhdG zCK58dU1sNJw&8p%ULgtgFkck1>S7T*C_3NNb|Qeok8=Kusd3*84FIeTZ+~-4Vtku6Z@VKV!ve zurf9Xb5-8=UL+xZJ`zMr-3_qaKrf%u##B1wEsKb_I+{(T(nwMR4vaJJMz>y~oReF- z3!DD?n7sP~s_&uTqfOn2%OsP;(J=dG=UTjVbdnTnZRb!S;&HO? zso|zG4K@>N7QW0qXMWF|SF`czZ5H#jzsl#vy|#=PE2y|+J%+wvEfiin=Q(e|FL+vI z%O()MY#~N5?dfw8_T8gX9*_Js*M3n7w50H0eef-n$omJLj;XEbLjfK1czW)=InmFO zseY~-v8I<4n^2hdpWk=m9i+l~gJ9ZZsO|w6`0UfmyL=uS51b1jrR{_4V-$v5Vf*QH z1M4~a?aD~uHl{I0iB%z8(F~AA7fUqfH^_B$2;}{*fHJgdok7&gEP_(=pKFf4N~n^z z=mB9+k>=leS{}T+)UhB!F<3s-=X3tvr1LFYIL#i2Y#iog*LF9`3)xdfbHbsoZ!+s_ z*QnHMk8FA%!ngeCV)Q*4&baG^%_F8G*bLm6iZVn-V7>PV@#fYem(z2ArOGoGSC$31 z)4>b5rLp!b^t}w1&`*h#wuKvj7SHrFzHvK0S zXcyGy3;1kb)0b3}T1+&LhV{C${Z;`NtxPstd@vGpx}|kWeX>8;Q`4B)5iuoG2?RNMflY;6Y9vu*}rEZ=SWL^uMmrk*>h|L&$swz4?+BH+k`vkeG z9)_Xvwoi=X*|S1?+e_w^y0VusO3d2Lu&%+q?*_(WgZhVEt`kZxfgW+P$ zx;h13?k6vI6yRQrVL+ek>IFl``R#0lRHvBn`-u);vQRhr9D9^!oX39czf*26>NTZT z#i{1AfZOSQ%be(m1YQ_>S-Mii^Ontd=DGEzC@L%d^TNYH_jDDDkBFwSVJhC3%2@87Ike;84 zo{(6~f5x%ZxL4WuNtba!$bYVM&>9jT2;gz7o3qC4oZ(g31M+Ug?=}uh&^^V4I3VID zt1!rW+i=o-VCA-kvG1xXD9q!RPbq*6Nr4{+7nZ{439*Il(bas1?YRvTf>|7>U0#JC z+UC&P#)1P^9_ngxfKQjBX*5Q3i$6*$!Y_J6hQhJ=>3hX?&=1!7r1H8YY>DCI<*f|HNa6K)HicJXu<7dqvFD{|zJaq9dwqkV-qssTGdew2aAxrqrd z@EC^#jM+~9?R4)s{SbMGn+F=eVg$Rux+yozNz8LI{*!NueDu5gbYCec-q`|{0#`q- zP|dqkKx&m5o~t3v=J8R9ST6NhsV&uyL!z-4`o3Zu=BS3WuYib@|EOSMulIv3p9RHu zQp-Iuk!{g85+U{CI!FiWZ7&Ki8XS7-E*7``9&U1d??@IuI6RmQnz`may#0bimPxY=cp*{YGo|cX+yxx-VIR2 z@YGz4cmmWl)ObbIpz)74fv_n7rPC3(I|F(7nk-+U_2TTjijB?rk^3E-$f3PbIcmoCo(refgUn{Dtj5!EFdn#RG*n@v`G%q$db zH|B79P28wk_7W$0O$5a^2(AgV0oss!ISy2Zcg5CdQPBTU$NKA_l^!+A4LR;R3o`I( z!7V)1N_xU)zNSqg+$Wl`BNH+pgG-($_tpv1M@rasoOe;jQs^?6UJQtY1V+PkSn26d znY1(GQq}iIjfyce2`F@M zYlOc&a3dcINtW30F4QW2*CTv!cvmp=#@^YW=9@63byCj0u~47DAg7I?d~jqcmw#cY zt+UPP9kJp1kmwaA%(~AW&dH(<=xKVnp$j`X56trixQ`GAP1$n$F(YVW$@t=#A+;`_ zWAdT;yx}%(ZR{WC_`BnQAkimH_+E6Zd74%YeLPpy_VlK<&ArPg8IzkyJxWpbg24ZI zRK~(HmCnXnm(B5?)&bexn&o~4j@r0~F-;rqU)obmJP6-%v|M;t9QZbHjJru%*!)0P zAV`L_YGc}f>VS13Bw$01J27-wX-D$zYpi>ZL+6t^CZi?Z8GCg;#rIFm~GqBse8WdHwI);kh6F7^ScaPa(L$Umgpx{$= zt)y0F^g;$vQW8Pgym(!zQE4?s_iD2%g4w^}ej9S21vF!thVdyq`h< zR0+#yZj1-HQnyKadQHS2E4vRU*9(_ulbYc#1mpv76k7ACJnBi*u!bs$mqRG5|@ z59cZ`WV}fkDI?to#AQ}50}li~HW;7duG@_>&K`@fWHgk*1CN7_+eRodW-ri=XiB{= zjdu`>DOHLfl>g$3PdbTTD?Ptw?2ZP51rinXsBrxhnKY96sY6eII2Zl5J0;6!-<&Vv0<2mkBL zbIhAd9Mbo!loDJJ_Ff+=8STMW(_s&6TyIGNueuz{)rONY){gkU6yCjFlE}W`-^8u$ zu)P|ct!&Fo#+&QvHFfhI!LZyXPE--l-!yBAd7m22tEyf#`~IjA%L|p^3S&qI(_?A6 zHW~Hi z8T@xCI%GKt4GE%QK{t{xmN2GCtw1{@{=4+$uWw2Npp>kLWE&^}+hbxYL5~G-kCuZ{$sz`Jz#r z>o+v%YRrw|j1}Gp7B3++^o0K?6ChN{Mht-Q4k%6`G69H1km~JlDdcPVQ53v4zZAF4 z*b-nAs;c5`GZBC{W+J)9+gw`Bx}%;gH~vs6pds2ijh6$N zmx)$8Y{nw(MB|>1mDC@fqDw@*M2bO9Q#S)L^M`2j4F0zi8v7P<}0)$VFeY2 z3yhhaLb8=JfF8gUJPdWHlUjn7Y}7W~@}eJ=NVnVZqIe>Zd;8kJN=l>>{DyW8M5V%g z02HB+D+b>``}r^F4&?_%BuDXTc>%Ca5o#4L0R12x@Q;qa0|mPwZ;ICR-xGUtNW+w& z7}?WgpYVwgDDA)eR^}bpg$)_=5!@j7r$=k$KvhMaJZ6Ov{AVo(VYM=%0+~&2AgfFn zf8ME82|y33C9r_aZ=9vHa7~D}tuO##+=_Mq zvezJDxVl4#{r`tK1CrOo_SyOv&AJIF`1XQ;1o;uQ(F52ltjf)Yv|eK%DSib4fSn3P zxYqf1ui1?w$a@3zk1>98#(IJUNseBEqOOV6>C6(}LAip%&GCs~FJS@{67OP)m`i&m z>6&wq#D!zs-Zx1llS1 z7v6Ys95XxwERKWq&s7+pdH<5=|K*^S^$H34U|hrr=a=X{# z_R=rw9(68)X=zzyLLss!{zJL(31@cCZbw077*SDI^E2e#cYj9LY6P<^ z8~>>x{@)0``A*m{T0`%39J6wq-#(?E&z0a@RUAV1?eEZ4;t=v25yu|2hW+ zKqk}HKlK0NC2(iqlX-z>`)g?B^ft{v|5h`;J9Q{1>(md&zNv0jXqI4{Xt8(|6AN;y zJ{+|f<~a*sYsEGIn@Byp923%j~&9)#HgJRmZ@{R;07sZ;d2iY%rc07mQZ z>y4{O=?e4NPCEo(g-O^nEnz8Fs(vUha;tenJ*tCrlH`tb6`d2+681j23+>^i`lwY17(NL77cXdc2!X8704vimud<4S+-mFUU&5q0Y(-}a@BpUwO@xLF3buHe ztrMRvfxpP1-@0vYwQ7}+8mI6kKR+#d2rcAnJd?KOilGK|uPTs|AZW=bMZSrgX0=w+ z@x8Y(Q8m?rtC`a>t|-63XWup+Q0y@X@1aq{Q77^(-vl5?IC9UU0SS;`7SbV_0rD-X zNa43yeMSxZ0K>{q;sT!%z#RxC(I@$^AlQQM1)2UH0tkzd78RK2+Q6ItnoGbzNZiOe z1Um9CR=Rc)3dmlm!1(+e`)wZ#Vt8u{jBeaN@{y@h_OwiHx!-eBKYRpXwe3SAUuyx{@(!Zy{AM$FhsCRHx;j(gms}Ch zqXK0;Kn$^TD+K}p0AL93Pb#1*+}clVfw1T;T^FCol>o@#HtX(xQin45Bi>-;ggSyy z|CUS1d4?-tk9zP;u2g{k4)K4J$_W6LG{8py4*)@5T=8F0^h!W#b1(qyNB&Rk2iStY zE-Ofc1qtJNEr|K&jEP_C{hdJl+ny$Y4_^7I0?yp`mh=@EJ&Qibr5gKcEn!yyC{q0c zspxB26^gp@L3pp;VFIJka&xF4f|gJQjg&bO!0+4B#$#WFT&U{&z`Ec~w;wZd3lMe$ z%GXsCl)v9gD|Mw@E|^3=vqvc?BEtTgwV}E(XB{Hz4Fz34Dv3%PBftk%fxut7?+80+5+m|Dc_f z@!btD8_uT#S!OTE@<|l4L=yxmMee?LwIIzN@{M{l zK+}%)qp<86I=v15Fn~W)qCp06%QBfr{rKbxb?iRD&*pGA=5%S`y7)88!D*%m3)0uQ z5h!tI&id4KroWC!<@iKnUp9q5frW_)0M33*7oYWv2~@z-I_?DLuf2>;GwsjM7|5p( z-}_+e4KZQUDolV>a|!}zHs0B8fqNX?C!u<~0XXklTGX7&V|;wi02tht=Rq$fvL7+9 z#Yp%nPPEAYc6Q!4A$z8s1mdd(**JOoI^CW5^OyR^`yze}OTy@yg99tbFkFCIbr>g?(kLS;V*kL;L&ThwX^Vs-HJg%3#g4%Yp@8jziCxLR|D?kOu ziA3UZ(yf~>s@*4igwAKYVMa3}HC6R0!MxZ>k+KeM`X=|n+pHPgS^b6gqVHvSDx`v2 z;^K^lLm#eBRV=sgHLBNH1exT1w!i8#Rp{P=tDQ1}n3oEORH|ETqo=MSSsyu-npyJ4 ze6PAX;ImlzFvb81{%m-7x}EO4VO`?9I3)dp>S>6(?I@RFh*J6fUN#ZadzrnZw`EVh zqceEfTwZu4H@r;ph#oF7QyCfVH}^inrV@03euq^@6zUdlV9jOgd{`Om6+il5=WWNp z=D5@5vsfcplv981;^$m3Ka{0JJas>JhUQqf5;6Fu&?Y?i=%N2$>Wo%!MGN{1#*>}p z8DFg|o}SFxaCm!RF4=dDdsaQ$5kS)F3Yadt+D05LzLsnV_3$N$9w9uP6ny+Mp`O(! zT}JrkT)6JVvH9JGMwC5}2S|iq#sxrLTaQmx@7ss8|G=1ADNb|xx|)w!C-}txSXX#5 z#3aXk6>#1nKq+$=2IR`jdMHy&;#^k4c|{Vf#Rur zk^+~bne~bT>hcb(uh?CC^e__N`xql_ZlLpEecMKrqTx7rT);-TfbrABzk8JC=O{wa{Sw*(USf`>9y<-aiKOR$zw)@zwv{7RuS^5f*S z0M({rEUf+rp<)$FGhCead^rt33!00ROZB>Jt^`r1TUeEgk`6X~Wt`s6#xIGyg5Kk= zxz{}=_`q1VLGSgZ{@`)1u*>OTagj-iaBR{R*&el_U3Z9>^ZN}kg;{xt6hMSA4(VN=ZmEia-!$k{^+OE47q(6FEFTS6?o)?&U zITKN+4^#ltMLSAF%Tejl_|`_1_{Peeut_=>dz%>Y)pHI~1p3#&4cQsKr`?zaLd+le zi?vdO#m~4%qDqg-n?!$O`agdVhP9maXX>qYp1|*op6iGjDtHmMr2v6Qz#b`(a5`Q3 z_G<|)DGDrP0sjJI9Jc0w?8E*i4bv-EXSOX0{2j-0r|YF1fJu#O`)J^K_+Crg=_jk$ z1N%&6;NJsj%4&H}!^uuF$wh30i_SK9fb}D0n6e+jtPM>F|H@VLs=9gi=&v1Zq0jUf zT9kY*vYwgiTwZF0&1}6pHN2yUd7Kv$|Ix>p)xe-8L5uJEulx}$j4nnG3!?J4CC zw}NT+a>n2{0F=4`wCLR&qWo+Os2>!~TPSc)ogC&hL{S0uk(Yk#h+-}LMD46}l=HBR z(IHff(wTxwGEWMr$@LSN@z7bJsO6J@499%h%f(>aVq#lo-&7ysNW^FNcD(V=t|h`# zfs~86rz+qNz+W3+5i>6jM{~_KB9bb0!@-is0Y6z}N)Mso&Byy*$nA>oY`NF0ng$*7 zZE4A}^`gMPS=<=B+7_H!#aE0ykh?F7kBtx0Rz=suE=qW*4<_iuL3C>%rQiY85c96WasG4IqmOltC}(m79gX2SP+H zhy)Fpr~yBp4cn1MlD8XH9?}RZ>WnTto+4;@{;k=wK<@=B-&N*!T^WYbJIHGIG6IVD zd>TSyxps#CgR}Otm0Er|X0j|H5Hg!FmLG`%87^v3>s{U1jP1$AiKj)%Bvyw!=ibMY z$)n$l<7~I42VGCZ$Y)dpVBiwz&@Cfyoz~cg`FSuL2)exp=ziCDA|&gG;a}C$zV}LC zBHeREf0#v!WrafTdAyHrm$?bV8nkcSQ>Ik?5`jNAykMNmm-a3mq<#y$n{r0+1w%hh ze@?hi-8(ESNNgtc1%K?CI@zH)YpxEE@u1n0)QdGzUSj-5tWMa&As$)GP??N}A zM>L1PyK>Pwx!m+#kqLe}eZXF!60uHb>y!UO)mw%|y?#-{79ye`AYIauqtdN(BP}^9 z-5mo1$PopkySsCwdj{!729O@4W9XWppC9M^pZ9v7k9>d&xaZz`?X}lhx6qy!1C-?u zI6lI_ebUE>Kyj$OCBZ)q{fI%4mE^7q5*w*6;D0CU4cuF!ZBL8!*~=UM;mS%PWONV@ zE)D>a9tieU0K5(9%JZcHsz6u+CC8*F6`=o|eAiz<(1{PMILF*W_emAO%jVQTJ2%hE znN@!VuZpM--;5vUr=C>z1G_}YeOZG}I%Syx%#Nx% ztg+j%hn{!3l`*JZXs@jLa_jw_2md0sMv?$8HmK|y8vqdgV{qztzg_RtG$mdfuu6~~ z=QMKzBx9H8@6kC%D9)C$1CJ;GK;mv{+V*S=Pj^+v$h^gt?Jt}p;^F)4NnbcjfW!Dg z3IZBjBQ2*(Oz1yH_0N!_5j2OlXf{Ad?u8K{7+h_;LTWpS{IesvQQ~4=d|o?=k+zD|`4o*F zzu(kVXI6mKaj*=qH~!PJdGGE676UTo3gqg$-*LS7q&X=QrKz&c@dQEpIIV*-zrQPE zXWA=JdUn{bI^X8d3ScP=I2e*0)_lGUnh`X6Np`zl^uIAL`rPPpJa&Cm1D*FqjXVUP z8{&Ls|JV6R@r%ORU><0@h*_etl8Fpyzlj52xrhV=3$Cn|+ZXN#u;kVOKO=8z;!`c< z(bNB%1puFJO-(4%mj>I<%iWmzFnGXEhXfC5mz71%*Xi9hWyv)lb@-JpLX@xuBbe%i zn{&=||5JT8CV!T*gGBJC0en*Yay?T!etpNDloQE9F4WvFIV|8cJmYQ?Y@^#^5UmD3 zTa-7bgYfz`q76ArR_scg-Eo?7l}MFz>ED@$ttnq{Vvf&Lyx8@snEA{6?x*{9Q~v2j%~g`HSxpxV)wu7O zpd9Fpy-YMD)@0qt(T9|EwqcF;@e0{8;^5d*LnT$^Txu*TUtso2_T}n6wd=ZuKD_Yk za%gf$b-eKkvMprXJ=yhk@zesQUwW!@hEy&BM5YhUFJma`FZ4kx5?e>Wys+7=dVF;a z-dy^;uEhjL;mmmEt+TW36D>4f4VyB>bl&W?R*I}DDr%e!UT-hIHnN{X#5eymYlBZ? zURzJUvB7j3mUvD4W^mKO&~b<#6laXTh_x&$8*F`QH-a#IG9q66YF?x9&DAw#p=3{4 z(4oMT?OJ1bdgTw5n13j?>0+-hwkAS)-C?RSb{+kxd&806VbyEfT*BzXu@TrXJqh7= z|LSB!((RY9Y?0iz$4yg*oZ#2$hK(^uB(hi~1V0X?_;qMuFPl1Zqhdy&Tt8OMW7le$ zCvK4Lk3GY0`_+qGL?eBovD00hk1k1G?_(k6iF;>MwHYHIVg0qap_+&j+lfErHCH{E zMN-9~#Uiu8XzbbKb5|M;V;4O1$G@@|X*;b>c<@{=cqWm{V0`W=^1sP6D*TZd?xO8k zjrH+sa3-_S!d-Z`-lFB9!8S`i*Q__2@bF}cKD}>ViS2=SKV&^ad3FW$Z4V>4?jx-~ zQrtIrD#CBGU(0HGr73>1U%BmqCyB59*Rtq6vj!fR2pg4vlmAq1ebK`^%O0nRf&|$UNrB*5X%yO5eW_JepVZm;-kWxOYYv z1FD#=4)O>8QLMyw?-DL@F3Xl)rKu2tM~*ktMxIjII7-dW)a8?3oN5q0^NZKu2nGZG zXC+1aYhre#S8{Gi<8d_&VnKk2K$Th`ulDj#{gF|@qiNewpFNGUsz2nCO+zcfu0=YY zBcUa&m%rW7_X)8((lZW*O>sP{yIr_#E0Dk1%r!;$`CV9bSt z9|KD9IpxX;8+LP7-<7ccm{|B)0m?M-`C=yVn}VRIk93W;rMi4SFGwOLZKgBd&G50>4*d#UofQt$%WvMgxV27MYk8MvBJ9Uc)*Sipz(dJt zuYI$7u3DypS~(^2KNEziAl2%k?ROZxppEAEcm(Mwf`z|YeWp_zVs2e@&4Q>r#eoE^ z_yL~$&n-z!UlhYVl1E{Lu|MyD`mfvYA$qm;FoEPFJO7jhH_9QSRrL?gtA0f3)xp+v#m7V47eq^Av-CVK4k}iel})H*ChRb5#B{JTPU+{u zgYfyTvdmZ?N!iu#3!-@o{MZgEu_GG1&4K5eg;BKyFeB2aeVt-y`I(^yYdlZ)vdInj z1O;_Ar7(P&2x)qsIJ)Msn`^oD6Y=wuAelOJdZnBE9{lbFd~;`!Ow%N9y;!v9qWtKB zwN>3R!>a05`VKy_^u8wO>CD8BZ}J-KC6_7f5b{6%? zTcbCy3U=@8P=AYsX{Fkr^}NVMOFbiFU*c)1xM4hq?)V?Z_+ph{|G_iUg*=m5Tk8kl z$?Z;sSm&=HImfvIvHX@IMujG+{Hf;%418u` zji}>(49yk6_j~`5C4fMqj2LbfF8{~M!l%#Bf{hh^Ei&Ijzl#&odT~0sib{n2&vmCb zCVBZAwwaU&E?C2$(3}gypd-#d#mfU|ncD%BB1jltmN zvqJe-`M=eI{Wj|#mj9__Q*kot@zC-Fe*B4B(CP8zERzaJHqzKD-ST67#nx9BpH)<* ze}Rjqcg5=?`G_pc+F(BA-3r;<={kPfs86$X{x{+}4jT}-0A@NP)7oT*bS6df_EVm@ zMY`W|%4IComBdA>u+mYDg29y>;gX}U^E00G&CSV$_p4(jKJmK(e@q6u)mx5)F;0n=^1*=|MZ-+898GI)yZ&K!4iP18bSz z*qsJi*YG*@s{Hj--N}IHPVmqP3TPc>b?~_42?0{(8y;h;I}6Iw`GCHfZ=;Wmb}~_! z_t!9HSLRP*rr!)4Uvp2*1k$CKL5tBlaG^a&uMu1X%)y|Ok&7TVx5pFTUF1L^H~5kS z|M4)-Qayl`(gp+VYRn68QSDM{*O+i;Z``!2m8gI5i$h|NgXcPVlY`RwNff^U{M{4Xs77OiHvu?1DNKE7qLj+ViD*Os zeO4;ncre!v4vzcec;8?pv_wBtY!?z)T}aLFB?Qn?68%`(`*%TQ3M{Ccd%pH}e1BG_ zVm;6qhrG-&X>sFTBTr{MG~aCTM7>TuL#FJzGX-%N>^wDQX09B7m|V7-{d0DydCFk& zlTvbAslj)*b~4WL)%2)~M1t4He(pffQ@UeKCZcOcp+L~IkH+b5=_4p>4_{-mMEH6lodLnCyB57k zl_JW^XR;yBs7>Sd`5`E=nNE|BGH~7HR{(|g~K z(Z9}L+XMuez09LI@MEE?$^+5+*J?m6iIE{u>NQEIn7lIkR?+EHO*~>k1;?ROSW4gP z`Ut9b>lC5U{)fK3tM+s!i@yuh?A^=aRg|7bf(`CLu*lAQfM5v0@ExeIcGlP?=fz1b zSRYgRKjlCT!Ktnb4jEZ}LypBxAic2NdY%DAz#$YQaudM1_5LbVLPmVgR^^JdMILs< zn-JC$NwSdJLV)UGO!hfX<6BhT7lUlYG1B$V!Fo-US4t08QxOIJSkAR8E!JYyQ+nYX z1=ScLLYFtFu3?E(KT+?vZm#oQ`G;x!4|Eu`to8d&%TQxiI;X7};^TEqYBwI#!Umfu zE2~~EC`LkrSkhXBVIk6g@Vae8I-|r1%an1nOS}J1N&J;~J7{!7to21X_S_cStCFnc zn(5nJg$vl{NOQp44zT=S8!cUE$)hmMYwrt>@upMS80|x3&8CFQl$wbbd89=_Z{iy1 zjQjh*uGEUAA7t=XZDn#){{e|A%G-+R=E+fMDSx)H8;Q8i(I8TmcLF&x30%`)A?WlrX=_ zg2_}r8TP9Oi!u@Wp8oeDU3z!V0TEb*S!(vXs|d`0VbagB6&QLl^mOO5kKei6}EHX3NFi~Uf zk$VjV3siNDze}~{J9m}O9$}5u1V{XE*gpSp`oZ{!^7E#!NsE*@ok+C|FnrQI3!aQ7+n%jXlH$)z&d4CrqI4}Y{b-IPL)hfUhn7v-Co~|ChjBCB=TSVB@TNRVhz{l;+Y>0NSEqQ z&ZD;ekQmaulwb5HXmWy)u8^58>zR#A=Rl;_1yLo5nF>s1L~Jh3n{Rmk`o)f_RO~Zd zl=)tBK_cUU0AEbs#|bPIBPvy9#X!4oZ;V4bKMJh-#4nlWX5mIV{W99gebVF9E5^IV zf!4cr#}wW)c^10lx2N|0EJQB&-Sm2_X-~1KqPZFge1)QQsuZb(S+H!3jEM>mj7KPa zii$sqyRr=dPDa5s986I}(0Uo4s4diypDHjaKH~l8UFbszm{j-{JQ)!^DBcQK@is5> z3N!MWVF)!##b7H};mZmyN)n=_*%=!i;{d4T=QQC`?jkSE;loMz!ibfpn33Ye@b4vpE~tk$wG^#Vf!Y zy%7y>#4>>gDxhWQGlgE&Br&<{05K`cf5zz%eXvbheV{6sGNJH4)>?om3Q$u*vV}4w zr;ynJ9{X2w;oI(pCRvk~Ro8>jOjq9a#PB@LUNTdx@D)wR7)I}s;Y%PX88R%^s0YNg zdL~)#gC&Npg;AOsgBMQ(RV8NHKq2?bVOGc=-JbuSHJsSn6|jagn?$9V5yiId9>CoI zRe`DM|0dJ*2aC$(iIw#<+%~kSigJvw0}=BqcRN9MHXyH~3RV5-oR4GG_w+gzMqfKC zCY36bmOj2?TsQ7WI|LZ7KTIq~26d(C0p72;GGcxDSah&gvP%q_-YzGCq?8Um=!ZXD zs?noX(i;N!osAEEvP^yy-iz#x$mTsW&g&)I*11gS#uSV6u{~b2rG&Y2t+;nEZQEaT zka9xT*^aEoT}MV$yK((oP!37}E=az}9+Z-Les9cQ&CTl9YeWTz4mAhrMB0`m-h{^r-J{ zMSvz=WK0;^>0kyru)x{O-|79fKmsrrJ^Cm^c5>|?c~RA=JY?? z(-Q#Bt2d{6+xlNdeUfXjrry2E(DY%Misotj9AlOC$v1TYg4)G)fDJm+DiEa4_0nDW zsXHp&uqI2-8?cz?J5l=E`&gm{N`O2t9O;B>AmQS%OvUSv*9g`0?fMtCF%E$$wWiw} zS&KJ61g%RR@rVRwUOj;A%?!zNO#d>0t60DGdO7qN_JDCV$w~7~;QpIZ$YL1}30$Vz z4J}1@m{Vzd?6*qemn^2i{X#kQkfy=~1n#M5!ZI2NZu1XV1SQ3plt7eq62^dxh-F`) zzrq6tJIQ5fg^e)4y(oeZkLl(;13TS>v_ksi?-oqLhG_~+Kb9s~V4#}{YZ<7iw8?6JH(2_yfI=R(?o1b1x#yqk0GA1(bC60kQ^W4^tUzDFH zkL%PBQoY2qz$6h06m@n4?NvID{e%H&h^P1gys(QIdg4kFBe1t2e%r8_^ zM|SX%HKU7pyx1!}Hu04~pc!k%Z?iOnPh#Kw!t{dDefPq{4HkpSo^`~OJG-!l%5_INwKU7T3VI$ReJ{aGbcp*p{ME>GAbizU#MjL+t^{K z%LA{Q9^MRonNkbaxX2*yW9`QR>A3>qFo^B0EQvLs&$&18`!sn2*lak$lO_1(7;Uxre#srOO`VjTeU|SB>g(o- z;+7OCkd!3L5j4ZzKDj`H93??={?-_bide!?gXdHaxO*;?!?mOLzgD*pW?4`Jy%`3b zJkl$P4$g>d1h}H(H!=F3=UD-3bbqnd#!M-!Zu@P;%{tgnH6G7eu~*q+G!Uk=ePTY3 ztNSLewOmVL^by!Xs3#IT!^ps+N<^q?X{2L93Iv2OlIR103VK;TotNHiWha{G;tERR zZlRL$QY&aQ7thFs+;rL&7S7OJ`1GeCa|8Bsgoe@JpKc6CKNTZZGSpHzR|fYy|XG2l0306I|ge2=5kvQf%1#k0Fm0 zZI)*p&-z(3HZtTm_GH3p_ype2I|s)CThTlvT>@H__!he7;jD0oEk?nD7=F**_PZ&6 zM@>I`y;8oW0OS*F{Vx^NpP&=O*P2wj{J!KXy>I9Cr!JDga3nx7oX1l#g*8kC_(U5*WRF8njRPlB=!^l(`=6#6;ikX(3f_ z3;@nn(M8Nev;ynriafR|34@c}j zpT?Z6`++@E0%6>jIO@sB{Hs%wI-uu0O zM*khDT{~(P5B4H38D?OUF>zdoFqni(1HRQr_-WIjcib3YpfeM#rjLm@7TdW9(M3Hu z5-d@+^zOwf>9@O_((vlKn|DxmZ2X8}xoRTao#XY)0-{%(ZEoTeE#ghR+u4*i8be|g z6x5UZ0yedLKWVs&GN?X=XdZ8)qa8~VmDetbiQ}TORYq6j*yj0WQ>+j2Sm*RcReeQQ+gCKBNdSx(yHRXwZOZO5e$7Kw)O(W82m|2aee-umzAT3VrgkLd$lAnZvX5Hmq{a=ZBuC4nOWZLj=OIRL*^ zibtTQXz1QEM|Fck8yAs*Y7%jsD zb@%)9B0TxE^tVgVpB~}#%nMgo5@Q_%oFfwFjW*Dzx7ixgC6PW~#P$6zItWW_s9|i~ zKMG-hA-h?(EGx9-liHB0SUA&0@S|zYk8aOR*y1C1>6b44QCLWqaq++15|bz^h6cgj zV_8&ggMz**sMWa<|GO?{+u47W0?0m{rCvNf<#jYL0i@k`3+bXZ7txj$X_=n8jeho_ zMkWc@OQ7cl64)2vlp>ul2`ZdTMQbR#Qb>1p)d>Y4Z+^+beSL2KPZZGg)S$V-d0@$- zI34ffmA>)K%|ab-+=a5OspY0@ATXgfROOrWaYj~sB9INJ!e^uqw7F<2(oE~%6m)}s zP>?P)}?G9Blur;4SPotuFQWg&PQHGFJBv^f7~VPq;hz!pPBWl z6D0N{L#wk)cQZ!^#&9N8(bXDmHI5I$PT02Vsj~)3I(oqlY?Bn<#mAl>A&YQCd>;&`?W-K)X3o7)B4C=ed6ez3TOIcs!=rNzZyU(@<@ z=q%^-8rX+?{~g}zNObP)TbnQu{@ zbQKUz<8~0JGc>ukpWDz1S+&Y>HCw~($AHf=p6zu+e74f{j@hKyB$GGsyjApF{0?N# zSft+RQ~V$Q8Vsm!wnJu1AiytZ@bWFuSf0Z|9_Ie{52TTvOGIj&bM=Z?mqTcQcqGo@ zpNX4R%x0~A^@JbbxbUS5XwND6j=ldMw49E3>j$@tj+D}hQO~V8j-|={Hl}#Cf%uAe zf2g?LVf|TI*l)9e{Z6#izacYfgzwJM8KjGfcT7RRt%#IImMl)U0odusxT4xI43j8N zgvixyx#1|jann?`6ALi#*wTaRSI_MeU4=^m%Ru#J?OR26BFJlkKqtGMa&a|#1MKvD zS2`_5^4sg%fRq`~Kj8pzGZz_A|Ctv98s4=5p)5dXY69s%@wR!Q{xElZN9zg*63yXr z9>P}Sk746Fi6N>lw7!(BA;a@cOiL@PPQLEHR;_H-o^%I#O3o<#JnZ9bih@*EyWe10 z8q60Qa0q+}7`2lnUFZzVuBW75ZZ*C4ADd#SXxen1%CZZm>L)$@dWo|hI4S1fF;oC7 z{TLahgD>Is)TBSZ?Er{bzTP4KpUZ!^j6whjjxq)M2=H}CL0-a!EzYT}Z(PkXf)ugd zK7ZfZbad)9&voVfQrE4>qb||TUL7l(Emb6eYtvjf%wKLvvp)hNk4Re>X>;EXGo>r; zw+QUL{5?=MEAi9;0PU(RKQ$BZ@CtgxgyyNee=aOXY9S&r_D60(@YWby{A6#NAtLD{ zJMUH+W;R6CgpQV~6~M z9gV^2e;Z$AYUuBrDNXjl6q}NJLTl?yjDMA#D(@|Vh&gM}S^e@Jy9%np&%=%7Kt?k2 z@{t-{3#}If={=tw);xDaYD1pI{0@WF`Kpjxo{L&-@p(tzgicAzSkx_M(rjgQN)wi{ zsx7kBt{1!wgxQL?Yvkn*Kiw?|C^s-5VEME=?4K3FL47`zdpwoy&V3S--2W*yu#<&2 zSSF7nOfov7urjoWwHCMZBd$B6`8QF_;IdWwB!6OJwmAr4ri-J3u8jK~Isk_BfgY^< zrR244%j>h5eQU~s+U&3KlY!vcLV9+NErL$-Mi3e&TYTAtY;)frP6tR9RFjOA;l-OsXcpeiYy)(a)mfD~gV1N&(i;qf>-SdCD(?ge``B*h(Wy-;LbL~*C z_$Z<2)6NXX2pD*0f_0{4e{-0S&#w^ z50znrFi_$F5ku?ugbNk+Fk^>#T8HfGpoK15ZCWUpdTW}iko7ZJC0uxowYjIqANR6lv>+ zzXM-y)YJPQ1-h7dxc_1Z_+h0MxCyqI^W#mcI>E7&S05r3S9urq%Mz1LViF)0N=m_d zpW@XhsacOi=iZ|b-=wiKgY@u())nIAml-}k{z(K^^q(KdAE_Vt-v*m`dlG*t8dKPX zF*ZghEHKr+)~~c>wUB5})Tej>*ca}+fB&EPNACeZNDujWip{%U>?CN-cf8IU@FEwM zBcxO*Pw{f77?ksU_%!ENhj_MiUW-jvsWq)nGajpy2m97j^8DFh{~O z1|TyR;l|plvR~ZszhW#GWAewdAg&7@F+we_xTHMVy_#fdKw6BR(1)o=diLwnE1^{& zyV)EEVQV{?9lkC8b|4m*w%a6H-34+M02#u+c^dZyC+k|hN1IJN+k=2~H5j1-(DVOE z+WjpS;Y#11m_PZt(w>QAvc{}yHVzeZ{WN|*e+@G2;>~*^>YuaGa)a-z%rq=hGI@IS z;qVhNyh-BlQS02S2*|v0gLubdioqf(h)EVSw_1E!>@FgwifXnUWiMwr-82j)+M| zN^IQZw85%T|Cqp@6nt9l;nX`o_}2Y$WazUNWe0aYi2uGbQ_AWC(J1^dn&UU*3B`!( zw4ix(=!zTx*v4esHz`KM);_rhWQ=xI)bd;ot#;+i2rlH+4Q#6;vSG-mN>g9ek~a*W z-0v`!zybt`fIO~0+g<>E7aq!-4vjngkT-HOA1Rrw)W+kY8CEPy~n&UNck)kmHpcG+AG z9_!8`+>TDjCa)FZJv)QW&yeV$ZKrM2vCLO4;|onskf05mAQ8FTYlRccbY z+MTP9Eb%zzl0b~LQqOzFB8JN63+c;OqrmmrFV#I?jUx82o)dLybRAr{Zy)||7lw1Y z3(F(9Kw$s*L6PUfp2}xQP`nk?H$A-sb|&Lt z?ejMW(vMykNSy@d|9N0%VlRDSMHXfRB;o-HyChz|-(ZO3Z|*L!04B*is&i7nc5mZ3 zAgLX#tZ4nK|1r;7w%k+s`Fa#s`tqQTmwNXQS_dUM6YPk->?ia-8w?kl7puVAbeyKi z^K|fWZ%Y3f%fz}ClTE`Of@izqs`HQ^;^$9HM}noe#C|%PDxhDbv}BL)YL7^=W@|b6 zIE@uVF}ebrDDj(!C>NFKju8^Q@uMTo|#1|H#yfqqClys=SJEM;F08?VZq< zXKo=w)riv%tfD|tI}5!gX^D|6ZT)pQzt#DxNOFg2Cf1CmOF@FK`bj;kNme;o4wJd= zw%+hiNuV{O7l7%e8pty8uEU5`!HxbjuWdgkp5<=WW1fsIn@UpHNuk5P!f#KMyNCE! z@@f?L761?3mw!df#})8JU@iP<=ic*ney5|1CC~qUlLbXoi^oZ)TSp5Z1(JHh*C2XE z*46@;Ry?Tv7j4Tw?9c`;9Vt{{Lc4c!%hZ~4d_+Y!H54$ z>!3hSnf=3BO`abNpPbj)jH;MMt8B^6y@2+Kv0E5=rFMW$(KEqR8FU6Wy5)N6+<^Cn z&VClz#+Erg&2=lE(ma}zJ4_oO4DTAC9wg46K}G`d_K;TpTsJzEoHG;aER%5E?)( z@3mW-cKhJKGjPj{JKUc6wa?0<5?e!+8oqstb0=)FUQg?CX^eC10X{46@B2Iy5hRWM zS8T9u+r(av*_B4u@9hT)K`qP7%kqqw^Qd>Y6C{Sj9s!3OkCkq0I@~$curl*z={E}cdCqvxrI1=c| z?hk*mG^W>#$TbA0X~Tz%P=KEg1ca^BujXD3;z z{HEK$=DEI#-&fnV)oNrPrg#T%(Gy2Ri|eH*jfaeINSnEtI*M z=u+eZ$!^`N9(j3VU8pazce~4!lM*1N9JbAfe6TO(u*qc!MB`Q6q_oX7o?7@pYFXRz zEVS14t3SO&s{yti;2QucC40`%w+n4%SI#S7ySw@swXk7JYkVEUJ>p+7Y99&>2sD=Ht( zUwAB7cz6g*&odjwxL)-gCkw6*sd%6+7aDat94Vs8?QzXc4v*$WyJPo6QFp+ORn-SH z&ABNO!;g03=4W^1T1>CSI1GL_j=8=F0I@IMm$jo!*m4en>C*|lLm#Dd?kUjjEZ|~5 zhDtc9Z);3OY^)x{&D{mdtEcBM^4k^<&GOm3&2(jh(k=ySucmui<4U@+e7*-4aW=%t z_^>hSdv0Q#wL5Z1BDr|G7sx@H4%Y(t(p#!qBgPb3AfA=F@yVxaq?1DSpHPXVCCA^Y zT8Xg4KUGk+^H)2s3ZCo?RZNRxXTyczi%*!`XN08c7M%MK!p`hoK6&-&XEk0T7aK<1Ga zLrV479?WbS+zkh_5r=iShH}^*{331BJ0^|Q{sIzS?yj{RlkwnJ>7t*x`5PI>KSjDf z)fZdxYfng$x7_6C9jGjr6F9d9P_Fxt`5^UQF7@p zTo(B3zjJpqOfMZgdVq~NftwYk67Ga2^HXL~{04-uF|V1p4Vn`~j_F^N7TwwJqJ1EC z=djj9w_~=4k__QCWD}i&xt?-<0aeUKPAF4ZB5mU&wKgLouIT1Zg zv$cId>iNnUFZNBvLVe`c)3ArrOZdR22J4+sRZif42Mjyjk~S0y|6%01bZHj2?I!Ht zyme6al&VclfKSVKC?oarPMjJ z&Xngd*>{N7cd@IdP1v07s22`r>J9j1UXz*+VWXD!<^C#LMBKc6@w%l#{ zkf%Iv?twL@x-SXV$KmbiPP99mPZsoRVGoE6&i)bQ@~CP4&1Z_ceR?^Z#;r`2f6=i` zK|Xx)DXihG6#P%-0rm}@VLC+s?0qw6<#NkqrbUNZu>a-B=I?+M_!+Bs8{*e0re1g` zrEIyh!nt^$XuMA-f4RfOtI$bp(<_qkx^bmuPK<#-cqxu)(F(MTR`Vo;c>0+I&1t4Vq;j6z&yPXw=!-syN2!X2!9L0ueSG2wT)f2BbNa?SCs~v&|F@MBRT-30{*}{VkO!>^H2nuI7ljBdgk=mPf0)o0#;a;p-FHM8DF2nOz)@ z#@&g6r}eqhBK&=>(lfpD-`Ls`?Pl|o^$b4Wkc|Z`jG@*}JvZMAhlw_+FV|7i{<;G- zwk%TC1SyFP>?m`x`DLdKT$|TlC(KbA*>Vkpw@7Wdgy(%H~ljf ze93=O1_%{zPIag2gi0>Y90*JSg&NV;vD8qsi|-*_+P#p%!m%$5Ti;gGB~ti1FpFSv zhIC`kjd*Q6c*kguxCX4%Hea^sxf$F&)mbDppAj3MO8Jzr< z(R~ttX7Z~bbPNCqmq7R961S@7tSU^=CGpz*D_nVrXOC$h_l18gDwpnv9P9W`itCRk zK5k2tID$61mUgp<8&})t7+5*M#*WN4@b~k%z{1>FY@-p>5pqtEa;8NOACCDx3IgRy znMC+(?wijwpBYdNS+C9{X|`{j_}+#0SGphb)CpslJKecNJAQO4JRty|LkA6(;p6?C zV#j3Im{vt;)pIvO4o{9&41I&1VIQOHao*T(!@tU9Rn>_1-PZKGtQo*lK_f2IZJ%q0 zN8}@ZAJy74bH%te{G5C^-oRBe;+0Arvu2q0_Eq7>=Rv&AB1glR&_?h;WFZhF5VZG=+MkeLrMB~ zl-QZqL7?B^4OSBy6NbV;n^&tt8S{84#hPU=6_tcbGt$wR-Q1?_4BID)2iSq)u?YQ` zg5gI{1;#NBS3o(oNwl(o5dNDqrR3+FKk>cqtZ6n4{-hmzdzZCpRNDx#ET1}uAKj7 zY^gBhiCt9IZfHthUUQ$$1OIU){yYx&t)cgMzovJK&~Q z_tcG-32P1ybQv+Gk)x;P14c;k{x{$+Fy+H*(|ecZVBJL?)l?G!y$CPF(V^vSK@&2XYCHfZc#_j5^)2!=0Ohu}K4a#SA#G!3l^95x);DNlLF@ zS&^ddfe+3u`u~3=f^26$A3}WeyY3q;_KjnNHP9x2LwY2O^Z7oU>Hg<-LE-4(;QOf=F0?kTFc_**V8c2AlD0sFc7y z_cwdz|KLgWz7IY5OQ_cY*blaIAKXMu9jI=vo>?q=YL7jzGZJx0lJqNd-5!{Y4t_O4 z18*5^H+l#@5!~yUuPJQPGdkwfFBhT3o>|HhHC4F|WYNvjj={scQjnFRg^?`-yW~v5 zJq@oPG*GpJcy%kDRH4K?dPvw_g>Y^!MSYi~=psX+%PPLn!X`YBB%kNLSg}#)%U8sf zb^Q=7hsS_(N+A17dMQ3TG#`TqX^k7m=iU;Q{X98;{Z!l?t2qe?_(!eTfZuTUOiAsi zj1}4nD?eRLTEDx0LXP;k&Y>k||0Xk&Q>(EsEf4al`#74ZmJp#wc8!!) z&#|htIiB6Ci(IWN_fH#O8nH1wrV$8}qtJ{_`1}34{hSl6VdpAUsQp6KoK_HME-|Bs z@<^oeE?9YrHvcu|N#ayLJzjs z{h#)5$Y+}lD_85EqD_rM7eXL-V(V>jkNu?*y z&$sxS__yFew20Zu@N{7ycJI$=_fklQV!_~P$`-gZuWG__T^R9lRt!HnIp-pQUaDHoM}d0q2>xW>gu zva)Fcx2HZ=gnW$6@cbVB^xKb_YO#eec~`vhzHowbXO3TKkzz|V3KflbPez`6TIXis z%GE3BYh_<3)fif(VHRIud-7AoxLFNW^4+*G%yVzPaP+di0~iP5^PodPk!~UxR^s)B zdbr1V^N2K$VUq4~uh;Kn)^>oh+Dgy6f3iL|9XH%gDh-1LStKcbWDNK*Sox37&mWu# zfepp{5sQMuX1nvVkdhrgV^lq%jsTzEx%W<=^Q<$|Vc64!7@e8-`VORHje_oxo4n~; znr$HAo;Y^R-#(XR@xjFvVVexAA|Gz1-vYLe!P(0(^>LpyE?jL3qj$&D#~!@iQ!8^= zLjSjpydEtB;*56^H+UwSg~Qq=*A}IV528L16si6pRd6xEI52-PSE)>=eOz*~k*^yv z)WubB^^x+w*#=wi1=gmAyPL;vJxp^_St%V#HlCs-XZ>4QiE)_3Rp_co=*Xap@|q%j z1a(>!k)FXZGmBUDXX1#b|0436X^V*iP5;HnqH1bbwNZChh#w!K$Ien-hjv$U`h-Se zu2%KqreH|$u7{a0aClWV+H>7(}qKI%dieMUba~Wwf8yAv&JWT znV6+2cFf%Lgi6MAzCky&KC$LYPH}UTno29>i;D4|J%(a8 zR5~q3Wy?(Pz6tp@Em@v%+hgpZ&Sb-QzGYkDIsy1~UE+c3(7caD)*kHyK?m&?*vI?oPaQ(#xd!~=d!-u40WQ~IZ>AY-LO%}BS z(B&}cxj&ceqS193^=|}M9FJKRL?>+b}zzGzLd<}p69#@RsjV?TzJ=o zbyLOYY9f`lqFi^b&(>=qjK}irTqI&*D!Kic*pJWAHnlpjteT{up5ppe-+v~kQ>ah$ zCrmVZie3g~Qmbp1Y=5|>DWr)z4`*j~H$_WiB3+t0vdTC`2o~!mfY4wh8K`%b6%~qGcFtg#YO5 z7R$|wg|$!lEA+lXx4CS5Mc>B+os7MM-gWkW&~ZgXy>G7LUpSnW%`_L<{P7xk>=i+y zTZ;9~5Nu0az$fpxD~<#IJSE#k4tWg4X~`i#yZYhZUkl)?U& zeSdWtb=i=B+m@(fy@I2Cr)&GNJw{TUu0EVycb4@80@O?k-#lFIUFB+y4Aq{8Q2iZQ z?{a!^$dAWW)qSR{!M|;M2ai+&sJG#92@DUMU5VvWaWnkn?1dn}TG+uQ>%cDZ0FeOn zoJ)FP2cP4ISYoUU6+5AG&v*Yu=L)A?qvI&taJVyjcAGXL^CI@T!7ISUyszIue!As6 zmEq2iv%~X*=b@2#BX1!TonhsJF5y>0^yHBT&P=(C>^=oS?3Qct+6H||!?Kzaw4cc< zAF0`D2hyf@p7h2>?L=gV%j-GJy{}NvRkP06?wNH5N|9q^Y}}v3ra1K4SQb$tGPrjlU({=(+F?A zhO3L=DQ0qP%CZosCa=`0WzgJ5|2soweeuD8;=l)e!lD+wJ^AsdjmiFPvJeYBqr>CQ zhvfP@#+oHd7?z?m2JSKxu``9Y7BU5>=2_a0<~BbNM&;|V^e(ke@J`ls3_aDZrD3f=;4TTdWg0)&yk=oOx^*oP#7fl_xr|(eQ zB7~6-Nf5$^b!bQsQ7zWn>8mq@FI6d8i!e761cMG$H z3vmh5yL|;%_LlEHmAnmUe*OV*4BAIQ9A`~w%PJTiTJ`O@SdEt)!EEI#O-~ktG+W+s zi%}e!XwDJGi+XI|7@hLxn|21uAZJTH)p%@JE|4)?)Crh8LEi$HO;HLJU4@H+i6AJT ztH>J_k`@(b4Ixi%<_8OyL;~geK9T4*=53Zr!ae zoPtMi)1+Gh`*psebpKNg#FnvOs4VsjHRK&=k}p43NSdlZw})Ka;1wMJ=>e6De`GSkXNRB1W>v!bu^&_Iw zvA&E+H|eZFsmerl`3%@p&aeJh#)6XW=0B+QO6Gp+Lj5b_kszP4F_6LNFBy|V{e-!( z2>s-dK*$dl`=kJRsx^I_*;}|cIqm-vQ)e>I&*PRj`0XIY73k)$WH-+|UHMUv*ljf* z1)#tBbBQvz+zvU8uL&mP`pr}7zlmgkEC^<7>gJ{eNDzL^u>JkVd^(geGasv@dP#^H zQb$PMTY%(OIYS)Uv4I3xQ-wC|?MU3}yIkyc#0zUFcnX^KHtn&#Y*a482Vt>Ny5au? z0f4s}Sjmbzx`98JAo!x4aDn>ahZrgo6hSI{5Hj(y&wJotR-&pZBCS9*@(JCV14@&b>MQ<}oobaJ**pPyk@NK?Yx_ zdLe;s$Au@N&{m+nd=G$!9#arKGC~2j{)_WpXXf6Um@T=c1FZ3IircSprIhM^c%pyK zp~^^I{r>%A16k+v$X>92e4w0sa6Gt);E-AlcLZ5qq3@aFLm8*rRI!pQH(zyp-s zX{`H)e<35%;YP9gTP_wB;61|n%A(+|=}q+sl?n^Z*)CiK6KXn4ite4Qsx;gE!F%QM zuIN0z zRH+Q+0+4Sx^x4u4G5|q~e;_DiRSpS42y`Z+{`Zss$VB{sOjlfB2X-+!byi?#_m9qT zLL|hoCQDgT{5{RV^>)sPHK(5MfNzsam7IGvx3%;4;q&ijt8n<2h6U$F3^?6_gky(q z^w{X)%|_M*K%U&%Ni`&}ZgK4M6M!b+6(Nq5z<+g0?$+-v566`yUXY`R|b+?;bq}`*Rv{?upeoMG8E22mscfL%oOj93KW? z@Y0um4hA46#iAtNf-&ClrK}-0JxwV8l4; zf38{P^J31S$gK!%B=H;o-pVTdaluOmJ@b9O^erwNbv?2>f1T<-c!7j2s@;hJmR7Gh zoFJxfP5O%HFf%FMh6hTpTxa&RehbCQMq2rb_K+{K^>s$KTIa_Z>P{WeFWZHN3sG&- zt9R0uJMRT?i67wop^(_(pbIqjRdrVq638SdA@J_IOWot2sMDx$%mo5H{}o@_yf1Rf z#RmnW==ifwfF0B_d|mTEfiy+*1N!074U8%Db6CQ29tdC17{I?1|G5_a+hjrB9VmmbB{nZRgD++;dF7%Iywge{$4Ak_VG^efJo*e-FH{Hxh*R zvw3`vfEnbGXe(^o6HkYc|C0ZIj&}$M8NeC+KEJR}mG*C#{(-r{T&fA*J>})^-|Nhy zfIYrvRdr3je5TDi)6>PA!mGr#0(~CwN5fq{Lh+=4L8-rfMIOOCaek?~{`cp$xBNZ6{PmTU3u7P7ZL5EO z?k@$p^91;cDBa))x>&@P)Ef!9=&=|FL$rUKntveHKM?hgbom=z|MLeRr>aE2&0N+& zX3YznZFFu?u}rE`=aoizheaP6VSxjmzQn)o?^0)!bEMZ`qHl4}`pRjj7=2?}2w_1= z(_eN`j4tOXEId3UCU)KM{In+???#y*P9mW{Uh;IgP$ZsRlq67^a`^Gn@6T67J@1zA zuset{tS3CIww0vS(4-e3bi(K=uNm9D-*ZW4+%P@Lc%*{<56b+5ckhMj1hL$F)ssM` z^(g*=rg}@Z(aE%e=H$;P|MjsDS${8*6E7E@p!i5n?~IaR{){KHM4tZ8hn-1b4EB~t z2q!jWNiu-hi>`+;F%LI>qIM=RD5D>4G(El+b-MZ~jsY3Ix+oH){(o%h7i~Zc*O8J3 z{BvwMJ?w>2MEuR?j0=v7&|#rxH17aTeuDl8kWTLyV$_g8FEvMzAk+!SD>$7=>+c?P zKA?u6JQl{s`j6!M4{82@%?bdWTuefLyZQfGX!K{|!?9IpI}c!K7*RPS0)tT1UpI38 zk?}ApASF%9k2Su~v2iP51>=5^`n~Sf&41=wa0L4^@GYBah*jK-8L5Bb9 z-ucA=u#*0(^S@YDl2H9IC1Q+)dPpjb9MCDyuVN>|W8(cPe*oO~U7bQzVyPE^mbCbz z7J5N&Piea2JOwbS&;NQ}finBETmGd(e0zRXhTHE%MJgP%1ek&SafSI;I$Y`> zHJeVpKTHpmqmTv;1Cv}oGMMp8nd1@*HDrz5M)^TR=%XiS#34*DD`pQn6o9K!NyG`jF}Wb~^o|?{&Z*I!{#ou)yCR>y$-@ zYYke?k99kwxLcw?2E*TyGcpEOK5#=Q;0JS^@WmDt0+Leg43Kh~7=D08`@f9J7LK(h9#Q|EnS`*q5zzrxkdqZ$!u+YT+p4IS!E8h;!{yN-f;rHXx zQ3j`=u0Y8q34H+l7pthkk(cMt(>W3>$fU}9?AdEu>ZzD|IT~Mwd!(x`@hh`U!xOe z1?FewT^C%+_!dwUVFGpqs1kQdLNyW!%CM>z*8|KdJITT?2R5({T>~VS~mba|>4-D|E1+9T$qb(LRGvYU7^!CALr@@~3JP~7c^cgw_?GOZO_y%{ z_MiYw2TA-UV(pwhzQNr7eg@P0a<|zQeJ<}S7hsB(f->!}T%g&n@n+yx|EDrD=14AV zO*)}aH(H6+x}C$uT{Yc16Ai zNMlyx7rxMWuTygK;b{LHi;e`U*gI4Q|D? zwxqhS&(CrZ<%>%jR<*hZ`)_NXy@xUV?3`}yxGNAfhq8K44JNj{Ry{|8yuw7Nk3i3tH2Azc`|uk_6bVYjnYD39R!Xl|Ok575B#N{Gop{T9+OvOh zRuiu+B(P0bOxcEphpHFywoZDTzDec9a`K$_z00t$n}0cd+p4^}YQlTAZ*eL)5TDqm zcL#lPnRvJCxkpZO3v=ibo~85r!YXFy*D_J0qAGe_WhyT7N)Z_ktmV`4*tNn)nMB-u zQFk7+^)#T4YmooTIQ=KA+yYpWbjO!O|2thMthd5I z8hs|(cqCcPpXK!z8rK`h4X^jQjBGKJF9O+IE^UgNgT6JAI!6tvBe;p@(JHhtkUGgt z^CRX&r*iD`qz&_ z!+@kFdieM9S^d&_(vA!{jT`~B%BZ@+ik~5at+by15B&px6SOE4D=Pl%L$hK7Y!{mm z?XRT*hMT4`*UZ2ay=a)cYZF_Pt_G@ClW69~^*4;1l%R6+SVTKEUIh{Y2;g_<>Ac7B}~4vvaF{_{G2BN)m60jH^al2C)yR7I;u==mkO{PR~t;X%v2yF9`KqRh*VA^ zoNmFFuq@xA?%EOch)@!-Iisl&t%7hpvd$#gSo9%}%-Ru(uE&b(ScR--;}O7_l)|5WXNi})AMl3b0g?OlXYIX_)vC^^ zhwQjHG$Ov4Q#aQ*^39C2N*STQPj{rFYG7nf>xjSL9Bx2Q0Q`7wNXN|aKQ#$?RlpmB zUNDRd_NZ*7K$NI`MdR4J4KbCyTWnX&)rdjdx1#8CE zs>JZvHJ#66M=sRE4Ncd7ONA9ObTmZGS|bp$$-UsjfczHm<3jh8zs!6-kIlb=gAY1V zGtB|S6YK&oj3y&Bg}J_wxrU6n zk(9#U+{GRm)^AG8)Qf6fyGA-9L%@$vI+M6cOsM|h0r<;n<9A2w*9BPb{Ipn|Nosf_ z_Zb#>uiB25eO)XzT?P;<0oItyd$WoTcoe&JwZ=%0T>@GKn{&9<2BD;~B%ibGjIX3n zjJXGMfXAbPf5Wgpn&yg850l5ipj`@rgZ_{ax{nWnD-tfV&~$(x=~E7Kul3#FUwt`^ zvbV6wa+07)xj=i!X0Fd!iLa}=bdDbbseYnfZkP7+{Bs^FNE<&Am4z(ZwVQeJ!=LE< zj=Z_r5p1C!rB0xZbbFg%1hy^X;81O z_4VZ2NXv!C)u^B}pt}v;Lhj4c>#a3u&&b7w5}^4Z$wzg2eppkF#9k86d5u)t<4Aho z_y@hq_O_Z<<=2bc^xXC_9IiKK*d)xNqQV@zc(>cP9CT1>)tWfv)lcX{wH$?UD2HdS zBdO~gp`>&D)850fboqqRp+9Z7I;c;?Y8>cjEXi7B5wyj__Ex*Jv&|Yiv z`h3gTHTen`tG49?kN)U}kh52rt*KDYvrAV`$On_l#7gAmtWdENT)tBMB)C&laV2p3 zdzQ~tM5yQuZ@t}A_*il}cK*^`-^g?p0iE^;#OwT^bZdRw;10O2LCL|FqLI~)YXsV! zPJ3{8w0`=$6+c8VOUYrwvY(cAb08&-^_Y9ZgAn%-(P90;gw;mTDZ?G!#mTPLMp=9Y z==UF-hJp%$8zIsE#p0GL+cV1gv>87x$C=BdyCr!!hX4UZ;;5tR-SE3t zhIEyrEK2=vgTIw_s!oD6yQmnTPOAA6T%#sU*}VCD2TmfMJ$IIs5#6YH>F zK4uL2KtT1jZ={=ujMHgRT&X^c%w5Q#nMvT}#gV()12Ojv*8VeA6>BHo#B~_6f$M`1 z&Z8a0BTx$qpU&uU%rMx)p2#^>Rx7YX>zr4kE*yvv}LgMbBbDPxh&35HLNWz(FPRIv{ zSIz&#c1-heH4?Dfoq*%-By(L!d{{%Hxm_nvsdkTO98nrM63C!j=#3v3a?eRy=w&p& z3hclMVH-D1q04!-aCvWJGPGz9QmFXm8oG)5V;g#Xc4nueSJ zjYTCbEOENYn;sOom&kIPXt$j6TPuBD?D$ZC!#RD8zjVKY#$qJodC8r+Zp(<>xL4ES zo{0Ut*wOTIJev)vXWTq-`50#Ay4x3yVEoaMgrf9E%Tw48ObTF9Ri7O;oUDu27=YQO|GL_{%JS8oUW&N0FNJ-5J}plI z>C1~8a$Gy;u-aOszvfWdyhK_ zETXKG+96AG`~@1q;!p|llZ}35#c8mqQQ)T1^{3sAqaY~Qclg1xl~QNe6z{uZv!rpd zhbBu+j`97AV)M7>!P>n-yR1CqHxm*R0Dd&DEsWbS+ zxP>M9@>aL}o%E-*#J+p90`DItl&N)@)9L=AT_rn_lwR$+-_=nJrNT5LNmGeb& zvFXkE@;DW6vlWwJQ1eB;Be#Svj@Vf|lqY4hP6`uxh2ZUSfoCRVAUs4(HkD#6<(-4Z zgbAp(YYtRJ-12DYe71R(mtO$oAT68*uQm8vzZa!YJO#&4ktWWAO7aJ1FAgS_Anhc) zUq_x4X!_KsBvDRo6Jb0oH!kX0*w*mueB92~SM`@+BGy;u_$$k1n#H$~)_N+r7tdkmjv0-E#WrsK8IDhV5DV{L?3R^xNSort^s@`Pv*0V*{0sVoi z`FW}A3(?m!TSM+Fs!~BU&-iWDc$N)hapoWgY6FVMaV1gI)@bjnJNe-B$M>6shOzhC9|(>6s6CpcCewkA&Q|cl_ar(F3wid} zH^##SL)u|lq3xh&Dj!hX%%tV}r)__2F+5HXa&ojhX*LuYue~IP^-M6izXAW!WscNK@{*?(?zY7GKB1*7*bM>cMnXsah%Bz)28O6Nb zWW3BOzR^VnEoBy7w7CEo6p^FT&CZ)qxCFaJhMVZAXO2u_^6ed&%DG9EZ^mJmzjZpZ zOmi^lds}!bZ&}^lG7vBIgS%3UIH%9A)|ra>Gx3(bzC0n@mX0)Wx8F**<9q(>jqz<_ z`D8t-YkQNddtXa$j2h7KJ!{r^qAY-@R~IF?_l)~*FU8{-k`IF5800M{mb(0|B-@90 zINgO^N#YPBG*|NIi}OpqR9@%AOF=GZLxo=PW8C)4j^J--;2md&#U}2J)g-?=?vISo zpFRt^m#357RI69AN#TL9sy-xP-8w4`hQXANUIlJHyXz$CtD3(?J$ZVb{JfXi&FE?r zUygd6?#gC1P#L9Dhc1=Jy+Vq0^jI0=^X&#fbDZT!0c^qR)fy&7Gg{4yV4!5{t-}k{ zCp!q~SG~0-I7xnn63@W`R)@x(g_-;2a=)M8zw5&5Ts{rNnXEw0iuVY6X^Y zd6r7zvaUB?3#hg}Zr6b`QjTUrj2Km< z08>YVki5bK$SeY<)!PN5G@IRqpD$_I%ktRc<6V-EvxZt+AUv;OyB()^B3pG<=R42fLTvcp zuOWin`>R`tNE%*E$UJoC=?U9|aC?wbb6&brc?eNTcLfm>4X4LQRq5swZI&SAf|*Ce zh%8$Ym;4BeU!hR9+I~u=_rzXiWk*4_%89V&9MuXmY0FL3sHF3=v%8%{K?_Gl##JcSAHlL$_HnKRn@nt%g3)v}rc}-=6pTf@; zDtL43sH}TA*9lQnHayP7`YOzP#N7r-`#AhZ7`p9XKjP)%#UpCR+mJZ8Ty~X$n@^fP z$2YPzGKvBgr>u}^m%?x&{gr@}f=uwU*$Tc!*yfh|c^s~91jrn=z}0uX5r(DmJYLX6 zY;xB-7=KNe(cVDf`Z&z#I@VjBf%lLRZndWL%vi7g`hwYYAinPEhgyEut)pqYk9uS= zY)YfBxM4UUWwr<6kuVUSAd;|eLgtG+&VcqoZ~Q=`kRPYM^EQeioX5t>U|JB4bLi@^ z3ibpNG!>SKK;H8Dv)>f5MVm3^9p~lp;bN0d1_9fA%Sx21dGq(3k3qBDj`KsZ`nUyo z^}5*SneK6`^j7GW;xP#KqgP}=CRXQ=#9ChdH@@cHrF9!-RM2#q@2&14S53;qL8$n! zoitE@HZ^+2qKmtEbgt@;#n@YCFMZ&|q)a14%Hh^&B3qzRu(xn)i`&nth6r%Orj55* zO}q7u2>TDdF_3(5LpW_V4l;GI&_v*aHhYd>hm2lH#3nYIgKl}#!h@u_=0;wIFF_8P zr80_;8jKVJMbbNGLRv(NtXh&S9imKu>w|V?jX6zNqH0Eox^F-3#kaA{j0|3Ioy~>< z>wsJL=FiTg%nsx4XeB)@yV1k}(5cG8-g@gDT}~-~GV6NV;!Plb2?H`vgMM662?>R` zQQ)&ccev3nJu|*V^z5;kVImYf~*;DEuRFq*5h)5c4=4bsGJuuSl2MX z9-D`*P6xE;q^?wgUu5Po#_%2H6;w9|q)}xfzWOXU%V$ofC30eY!3D6ToRaUWBPvGG zMEdMZz4BL<>BS_V1&l9lqqtcI2LE7+)P`b`HWYnIc%VJaTDx4K7-2p}juMejCGz{s zWUDI^H>ihr*W)6yoV$-Gm}@pklxs7R?m$kgCFEQFE-t4+`1WMjT=l%(<1V5V#g!Pk zE8mjPi?!@0E?=m!&}@Z;&7wUstsZpm)$yKP(G{Vzrd$1ZomIHdaiE+fuQ+35xVE=N zA3q*%WY5Pg@sXUytZ0O4#m0L}MKd%*hLK^&u)KQRifz7E(IDmdlwEGL-nTAapbkfn z(r$Mu@Fg9U?Z=cmBVzbXG0J3#Gks1IST_*E+brch$D8Op_lYOZN24rnAI}C00pP{E zz*sdhmARPEm72+P6~&RCD=@6Hc|1_ph!2WY`0(_+qTNg4(>O7$o0A=^7vFKwsNU^7 z*7O1gK}ddN18H1l)f!`*%*mq%LzX4c@31Bs8^&eTd4WrDgaL@C(Qp)jeYba<6H)}E2x1bT(_#`gjR%8GO#0x=KTZPzm(s;iA6^?x|VBtNh|1X7PA^}=Qz$@vk6 zNEME}*-@b@b#%Ad1NNxwZSHF<75C{x9>@74e8;KSa90$d1$S+Rgt!GtZVoVM$e7SW6QGb<+_lrRmG+KQfI2TJfQD{ z#YyzHQ_W%O%ji?8#*~EXqPSFh-F8avrLid=mKVKcZ{^!9yvq}ZHdUPY_U#I4Mk*E_ zyp%Qmz*s>Kq<{`%v$CyiQ1R5~lX5&PoVckrcrc09WZGnAf$_2C0TF4v3K zLVj2-jK-;jPh2%)Ua;<&zCdmM-4H>(`C&ZtmpRJTPF(^O27WeK(~Gb7C%SJ@)TN64 z>}#G>f{mVf+~x`{``Y`$%CHxA2HBQ#by}^DEQK7CUj!l9QRbyB9nqISX?!02+u$@r zP6KW_w_J5!v01$K(7eb`N>`Zjx6^lJt(Nc=mWy?I{_v4fXHt`Nh)q0qILqbSi-*tC zS4o|Lbn9E-u8XV>b0Q9?Kv@3W{^e^_kj?Bx;aJ$P1hK^Kbks8+J1-&ORxbf_{xf!f zBiHuMt`a%$lXA9myTvh9par8sZtG(Jug5y#h>TOLbHUqB(E>aOX~@4*44+v7Yn#j( z7jeV+MC`fAbBT&*afg=)@11&_6lra7T3^FwB_@Jz(E)Rx$R~y~SwiBbZpU|X?ZSLc z1Gq108PGUr5#J2j6ZsO zrOvWGf~#{n3U`roFm2k(-2*ZE^rr)Gxp6(XL8LsrlPDADI`DzAMK?WC<&}CLmhn@T zWd`r;*M%N9+iY-?t>JRTa&eCa_C@trXgQbMfDh+d`Rjd^grJc8h-OjxGi;Wp> zR%oV?1XM}{&Qj_IT@Sz!_7i^HYJ=qa79-{l-v>LX&-FLJwN22xr@XHpC0MX|t9I$H zz#f3y^Dhb1TgeXPkQhRYF(BP3$}5kZ-|pBM^?^E*E}AxXauz#EzP}tba4b^HEsl8ibm($;YSY7q zGmWS!pq(2y=|~*GOse60H_N%osY2O#4w!h);VKbf0ZACT__fd^?$d@?6~~cnHT)kv zO_3f32D#R!)vmqi0|z&F0|_rGUkx?yo3Xgd#5p5QU@4LKMk5V3Z-kzZtd(JkG>zwT za~ieEG%s7_STF)=$MdCKNQ3k9#BYM)@fjHybK|#INT+Bb;Z*Cit+`_6s^{mz?k#Ea zlT*WX2C}_;B0ZJKvFuR39PSPxBuXQ* zQc70nLOe4Z!V?Z?d3QZ@58rTrc`ecwzJK; zuTSlQ`)&S=PRaY4-j8qNkE`le8I-R$+HIk%$<4Q*b=ADa+ukE$Z4}j`xPx;$zw~AL z>N_M;oXKAjh}bGKy>*s9cgx6_VPNQ4+}r1;I^v@d`5AVRo+H@aPV0#S-q8&7^rE1A zQo9Ib^xxW6u!u&$ym)|pY&hGKiNzQpr#$s|?#jAhW?@j!$G1)YGp*BM>9e1J0DOiw zVV@`8J6ipj`|V=>Ia52_h=xxl|Cdc?#JjjufyuBo6bPrI0-BZac+3!VnHH!{$XKiE zTNk)hZLSNMPs(GTIC?bSeqMahqEF>15Aq27_J(Fhy->SY{>(*53N)-?T)f{wS6F-9 zb`h2_Yo_;#dd{VpE1XK1MX4jgoaQfE=3zl+a%L}g!v$HNc(VYPW93g~FMEUW{U>tcCu(56gx*jg(TE;-V3;mo2HN^DI+ zPyJ%8os^X$*F(S5N?v)^aX_`rJ%IjYotBA$E| z<~!vW6yzBYT*N4WOQq10Ik;Yv2niY>W|xD_9vK3zg_^mkWXj289<7F?iGqmWd8D3* zEWA8*Rk3_Guds~MEVE7}^?Jw%=XsZYTPZ>ClYfO^{iC&kEUj3O7~F%CDVR%F`A%-k zk}j)L2MCoh%F|m!(oqb5grx4TkFHsP)Q5J5>0$?AOcO3<1I!Afo0p~>;*n%2sS*vx zKn<^kunL_}ETto*g!6>=owr`SW3E&1h2x^|$-6N?#LF9se*9_P%=7|i)>4}1UrC+b zb=y5vK8mwv8cSJ0j7muuCdiRF`|`&Furj4+@=~$o)g!y*g>_^ISq=KI zx-i$7S*yt)Jk4kgEt+e%2w4Q*A*0_oV?~E?_-71=lf;oA&Bl@;A#_%+h1SO6b1M+q zRI22&dL{E0Ys51T3zP0U7ORkIRRA?>3!Tmn4IjI;UH?67ol=Ao-!Q8LOP~m6o4d zFDu_lXE}GpJwk~~aL*8wHzZ?MFD5q??jxZQ&pZB|-)D4a z1qvk5}2%n6u)U_*6IR{pjewZ`i1N-`)b|O;mXQzP*Od2U+Yh}l%M8kmdy@x_&yz=3 z>f3Xsn=&yVZSDBLIGL(mt$A18{CC#Rz9hf{!caSzw-&{|wu#q(S*%h%ymvRpG!Uc0&ui=#F)|Ws%1nieH zNo#RZ^V&sQZIYl)c;JMU35)qePFG!cKQ0nzl+|iQ@g*gx-{O;DJfn1)d7t_y|Jm2B($SWwQl9M{ZRS^zqwm!XW2ZAQD zv9wd^W-_`nQ`O_ z3gq{jiCLV@rzKW+AILQttpqBcnJbJVo<&T0qH0%sje(^Dtvk#KnO{>EG3<(gK4lK2 z#L>#1XFHi*#`zN=_b?Wsij-6V)u9y#l8-`lTBPL6sgIK+p$nxX<*&(+!NXW|~N6g%#W#WN{d3>f$`(Z4ibSSW zEwp!UK&dZ%%HCEXaoC*uJMloF5g|?;jlRu1XO;WcS3la?l~55i>gXr`mG}GS4|yHr z^Ui6~uceI(*eCyrZm^~@5rl-r3gJ^ryxeH<`D!r&KjxnN{XScRuX>FKJSXL#X1Qh+4CtXfMoVK0#o@dl5gaeM{Ch6=UCwizp7c6Oy^o&KN8 zCg|Y(Ep~#%!jU}Uy|#`jFM$M$pEN**j)(Po8=1mL&8;4u-8#NQzI<;H>-P=^wI~pm z(ATqa2K}NDj}#NtJDMBt^Wf^Hp;zfmKjq2K?EpPAStx+k^LaVcr2Zs1L0<<59=xC6 z(U;ZBh}6mo)(TZm$}s&K!~7qr1Nkwc4P&VYjuRH3chCiL-n0vCFAzo$X-uZ^I#+K_ zZ6J6FBO5GKFo&i>e?60_dAlaSP(XTX>%X>#LA7$MxLVsBAwy9Kdx)$acqa)>u0)kA1R06l^{z%U} z7$T-c1rY_~=8#5E3h__VS2}vm;Nh|-u&90F*O!coFu z%Vf-iNP6a~R-uBHM0JKzJxc?Vx}GmusR zs+4em!G7m_MuuFLzm*r}NY7&-X6_>w@sUvB6Q?V;KnFT#v_r!mX{f%;?a~3t@Z3K{ zRI@u$jLa6-qIRB`N#s)UXS*96?;pHXx{4wII<+mwQjL=C&z9o5nty*Al{LbhFf?tR zkBR%W3+O%RQEQeHVJP8-d?)k4;fMd;Eh7+(-Pm_EMuRNu&wn&proBwqRZcFl(49P5 zym}PhvqZjE!47hE7;vlID)GMN&8DLfbPh^Yt#4+AZ%+nbTlh|Vl!F2SEV0J;+5aiP z`dL?K5o=1+Iu0$9(x@N{_b2V&&L!?%Ri5n^J}ZCB2GAfjqn6ES>oR%m*YZ~JAtqgk zR(SDQ_`K38I}V+%m6xjcu&#ZLX#7hKahFTn1o&w+rTZ4X#X=PAo$a?*lx;sTcE~ke z?{2_@rtKHMQY)vG_Q1zf6?&-RF&h|n?vdN^u=d2{MKJLBYvI6m$M^r zIN}q%RepskV*Pzcg6n)wS%iS@nX|Q|Kq;ne0}0SSfZV+^sd9^gY(>$arMq15J5eK_ zx45%_uWEVUZ2Ael3c0LD6MenVDPMfjd;oqp73?jbOP4jY4{;i$P|ZLtPPZa^Xq0Il zM0BnJX^a}puL1>KE!gVsM$7eBx48PgJxBRhIN@>keJvJTh;zvOh7Jko?yjF9`UnY0 z66wB-elG)P*)~qs=gT(^c_i;O8*BT>ToJ23_MzUj9Y-kt=re^h?Yhdbr@G_MSoD}l zo<1d!mydVb=NrHFU3Ok>K3HlToSGQqU6wk?yji$v-bz2;Ts*kCso9@9GyKFrh}~oG zu%4H5{!vK85SOEuo80fudulMT@#5NW4;Y6A#O`cF9TENl?8@XTsFnGCy!Ti6?J-XC z_VtPPhS%+tYN%#YcanhORqJ{pC8SL*gv`i88UvSZl~fCIjIGP>)sXgEX3hDToNdFx zQWCBA_9|9^xn?p)GJ9H|HI5=+4B`7A!^=nf1=W1m`wJL0P@%KliQu%C?%|s5`8xtDu z4g-h>vzDLH)2jY5YM~)z$##BL$grnk7IA$N6tKZtZU*ft5g|;l?zuPC;fwGQveTQm4A${w!}7I@db_lGLtr-el`G$|KJC}!B|(ZX7Z?!}{oU#t z9VbSYT1hJE)vvJ}szXL@JnBV7-^FwJp4gz~Ny2|*ky+dv=3YAtQWf0x`rrHtZ}afB z3-%EQ8D0LE_4*x7zROk{qGY`jdX($lcG<#y=N)i;a0xTprkD4%IC6_kzveJssk5tKD%l!Oj9gZWDC+ZNkx<0L9oxyA zxoRbE!IM9}E2!kO%HRJg9qGFJ)9MOPg?W=?*5kx!pXo7}{+?fAX=@s@XaoF(JJjgdehOY!#l!#BSzv_OYayyV%u6#WCEFxcbU&J=X z>ooJcfhCTy(q}pT?MI7lxfEVse_|F*IUg_6a39r7#EZiB-VtiQ=g;AMJ+r4?QCsj1 zRi}*~kIGxpI<&J6lnjp((ke=ppG>wry>6{myKL9Ka&$=byejZnnIeerqalCFPWw}} z0#{U3Vc&Jmi!W!f_P7&oI?boj>ZEL?t*_3W5zoHD;<&_pk9~RPd@~@+`kCsIPg;N4 z*_pst)g2O%@2&q;zJ3?Rv&;!-(k1v;h5e#(eX*fY^n7Ir@lqKizpb;E(n zfMFWf{1P?x)e7X~kV0c<3O^+VN2H4C&b3|F`RW+w&e&R}hU(qS{aU0&cQT||!X}RY;%J;l0O}P!c zd6%UOwW@3ZHg=z<=VSZqfuAa!t7ITIQynspD;0bE2i``L%`*zpyg93{(Lg$-+1pGv zrr(2pbLhz(JTxibIH9#1$>KT}JNhi3csnT({?&14`%`87Ug=VpO23g$4H_uSHDjsd zI~Up)3#zGdn3!}5uWJdfmfGW2GDKi4ZIgLbn*fxytt?LkPt4&-_eW1chP|(X4w7Eb zILgXt#fj>E;`$Pnczu}`Kgl%;Iar#!d504~tC6$Fx9HX1OCvHUWXAAqx=`YV{Y*BJ zf6E-i-~RuR_7-4OZCn4aAjk$pN33tL>s}`qaON^EB@G!aN7(?S6@CedjU=&Ltm_pTqwlYWvi;3Ngxg z$~9y7A}xh?La)9Asvn{}1SLoAy>@*`}Csd-~1UppFPrYx8EZAU)aqspj z%DJx`=bDEGzP;KTKU*fow^o;D`G(nB)0nV|Q$^(i+sg!B+n7N#Fq6&Ui7?N8es*QD z;Jq6#ot2A$2H3D14IMn##g+a)xso4RDT9AG9t6j?Hb_MGkeI4zsu0G|m}#d%NsHx0 zPqYNpS;>R8ryyUO8HPmh;h{FF#vM)4$tz~U$Rj9KwI^&J{;^@j85#Y?2Whtzxxe(N zO8)E!`J)lhMPUA+?} z<^Je}a~fvqIT9%9hd}_LBR>&`<>jD14BXP_I*yq|AXwD7th7U0T-{sWh6WBj-y`}; z3^!@`x)SEa4)MP%*k4)^LXO^A-JR#3i+j$JC9ytRl;)ylt&&00fDS$$LH$-RAkP1F zRHJYmsZwaTGKl-*?Dmgm$fd_m%ov(U`WvDR2bd}h&BnSC*quVK4t%DHn?vRKfgG=G zyfNo!;Ed2h^P&r*#i}Iwv6boMe zX5g(J_lwtT5FI6>^^J-T^!7cRW7(1pXQiPuldRtm2%Uz^ZI4ENQH=Cs${o+ViRC*D zz5%iELSeY0SR;fmTpoTL+N5aqbiT2CBgHL6B>pn_F(q_Qa_za3y!lP!yw=r8$_Pk1 zhf}XS=b2|wnz~xpG6;{mPiH@^oK#c}I6`%skl8ius*2_TYj>Mz+%nuUmVSNs^$qwb8_yRmyC-|T&A zQX@jsQpuGqk*C$x={(mqzP|_}Ro;uA+$tZBf7nbS;J(J*_72ZTgB?8Eo5_A5D%9qk z#`r9b{`Mt;=wc=T$F?&(-qCq2*=&xHGLiVrjFr!Hon!;y%#^h6NS$p8v!1wJ!Tn=* z$N?5y)mS@hRW1sXX6cH`H?DS)7)7(yNR&-&4~xn6e0z5$Yckts;9h08JOMSz&!4UW zw209rFZ*gAR`mhb9(IV%C;Bj=*oMoKZaDH%nOHxZ{bo6qE761L*;|FC6Z=SJd))a#(7-2Goa)1!c%Bnn3Nve>$g?HB4PgthW~~nA zjieawQEYK~uk&sK$O2??lZ1qhQQ1ia$J)KK$iPC`)Hc zlNPDv4jV}m>=0y$cZfPx(q-iyCx=@R>${q~XQbyo!wfI)vz$$4U+#G9q=88$D+ePS z5TMg=kk!IaG%3+^qLd!|YckUV}Y-ATB!*n>?lnMWf;{S%y$hgjlJYBl{kC=_%xs037s?4Ld;P|HLi=8ssiA6p66;5)*+Re^K-F*twa!M2 zwc=~OH=60X6PogLQ?Qjc9Vde}+uM^JZ@lSq3{Eu%8f&$r$jAjgnl7HDWR|(~dJV03 z%=}imLEXydFOs-40xD<@4Hr=Ca4zf(fHX2->BpB+ppgP4x#fjvx6Q{7?Pt3zKlYac zJuqB*iv{N*3C<0=AyFS$!KsG16;g|<1DRV-c2N1F6T~;u3ujU45APKzFD#Cb)k8Uj z??eC^LCAm|y_L3-R1T+nflf@Pud1Uw2y6COu zHX=mTe5WM{UjMD_H8S7_J<+juCMu^kzf>s0*@sN;QQOIp@pd=fA9t~B-v$xZB-%B0 zJ8YaqUh`x<^Xv0sQ6tYkT|I0Mo8iDq{^VSqp?m#~Sa)5NoZtMNKJ0U?EOY8(jotc} znt7~pWI{Q-Ifj#9H594ZS{l@HiVN)gSNcc}isAm2@h8aaVQEiany5?j{lwLB%f1xG zQ}O*QiNMQFtj?Bxh`Fac@=k}^WRa_ND+ee2WnPuz)02Z`EclnZ6fC&w-jc;oCRl#Q z>*LMtaaOG!(9GIHjFV3R7w9roc#TL)=Q^Tu(?3SP(jXw%$EgJ7y zt>w=v*7Ld-*MTS9I*>MKF95WsjJAb44z+%FKQ9lxJw9KnPIhM$3WlBR%(#>*kW@fP zD#3XQyt)d#*`|z0qPd(PutM49XjbF(YrG7^h!qdy8g;+6D1cR>8V$1dp@-+>ZRd1G z!X|8ASH-8(at0K%4hyP1tbi7i+UoWIkqJCxZg~P%SNGCvc?Rk9<*vVFrsS4MPZ{97 zBIzC?LbIqkB73#T;ayUuuU7A_J?HFe$c`KNUmieG~nAl(-{_h%acEd{|{^mZ~-<{02kL9HbjazUKRodftxx$5NsAw`976&Mc6)6 zhlS5~vMK4G1=Rk=PK&EaSO&amibSw9sx`th-0$mGo7F_n!0xudvT zZjf+q!?$@N2>a$#XK@fcHyO1y zf25Hc#98g)^330S*c9rxv7Qy3E|M4_Jd&M>^&y6I@wjz)({;=2E0$hSjyCsa^DHyB z_%EXoYtN#RY-)zfRi<>D(7=^Nryy$;yrlc%xIzxX3E!#EX5GRWBPG$_i#=Dg26ZJ9u{Wf& z?EU`UW`?Mp$B7}n8!*MHEtmk!dcbApkR?&V=L^=Bq`Lx<^YDWz+naazBpdpjH5SXa zD#?z*Y_CcWh6vjGxarp}n~L#yZW4J>7gkPZ>K-~@ciY;dgJ&%^aEl_Jyu>?y_;qmd06ZeObXr zXN7#J>5tn}MjQ(2cxR8hE_xO4zK-_koW04a_6gB=p01?JpD|c; z?B`Q;Pszp0#XFw!h1%0s!qukB${QR!862ixkc&{lAPpRUyFaB@8MKnj`ywIiy-rMvVG_?IDs>z2aQl)X~Dq{7C`W~uYr+TEkz@rNR8Dzh$` z{9A!bByNyq(47W~>@k9mRk$7h8>#uB4JAnEV#0?`3cizJMoGNJlp%m*mve^VaN6#N zJECA(dk-6K#td<2|HGESgN`!Zq7~}H%5>($L1(nUdcpf*k0?#~=~Ff91y*{~V!Iwp z)xCHp3&uipbTEf57JN^Wh!ZnpNuMD+RS5MyBu!|H{4E-fuzzc3M7J1{e(hk6U*U%i z6+#yo#NKjo?Y+FsRK(45$$NP9&#irqgMtNrj$;`ih5GfK#A|eLu>{7O#9||U1%ogB z*Qux zF#fXgA4SoFfIidPJ?7-W##bnu->N&_%J%H+7gGbQqYRn46>-~?ks5HUXMczU2?j=o z0y&)qr~@*=bOB+QD&##~VI6eg5KhT-_O73So*wvo;8V)WE-yJKi(MKEC1)Q<00@L!GeDz zTwmZ>yc3t=N%n#JF=jips{UKWtANvguN7HCprfq0s;RyV?GkaqFF$TyOc8 zr#*-Lv2{faavvLS#NNJttElOvN&Cm(_9{@v6T@3LD^z=B7n1!`t8L^T@$ogb?M!e? z5c1rRSws4l2vXQpMz%zwQGv+|ob9#-y47u(viOnt%qjZCo@{fXFzFDDRI`?l#9Gup zGnU`)h@s{ZLC%GYq5ngt=b=IAY|$EGq+A^0u17%!FFaF=l?H@yPTCb8Ft3n~Zc2Y( zUWxu`UjOxH=IJDw6LB#JfBmaY=q+?`ZuLa@UOd*T zWSGvLVyu(L{EwFX<&$WV_k(g$-DCLc4qyqy^H;}&mok3}#8c4eZW(uU-FP6K7(w%R z{^+^dUcCPyxc! z5Zp=lwovlMh`n_I5Jima5_c;PRE)LXYML3RPTjP(#^ydC)$wh$qez- z`!Nmz9K)$UHe5af{y92K-yMN8j<@4d|3oprQ%_X)PrB*0d!Ul|%h*x(fynb;bZoH7 zG!h9(NbH$c7%y}dm@NR=D!*+wUVLRf>ybE3f^wc(sP4&PgLlmoLO|%?jazBbF~3^_ z|LJ-Ld}gJ!`5#)@Pb!okrkw}DTaNW220Dy<06hgy0Z`OXAH{bs0cI=mu-L$~OSOsH zDQS0q@~pdgx!bY8IwJpnT1S2WhkR@OWgc-mlo&3;RjLpNh23?@1gOCN&i~VkC9u%I z;QaB0A9C*SKlof!(9b#1h4Ix! zAU11XEg&8<1DD%$VKP5wga5-Hv+*BN^1ph07O()Lia)9PH0=lYGSqM6up0X+l7@Nc}(2-*0?_q<|O><)A3bRV+qB4p={AW36!! z`3NY|W{B7)73w3OlseTVgwd6m&VLIZQT2s{Tl(FzK=yvFhK-XZ(2}(X0QZboprWsmjj;tJUHTzM*n{zvo*W)Wi@6 ze*fc{C#Zk?xl-ut-2WdLS$}=S0Pq5yqiZ~lGpytu4;Y30;Eq z?ePY0Q$Qb^gp~R=%E6BO!MJG1&|;~t;=1M zmj7z{vS?6P&)#Lt9o!3Wa^0xzh3!jW(ffacxmj3=nb_?THOEm}v2b*;MhB_D_y^iIWQdt?~BHB8wQ(}0EPIdqwYxdv| zT>d+N{Lijk>u6A7c#PP~iajEijU;7yP0Wk*@h9KAoi^4*c|$74XNdJx>B;**4(81A1dSmf)5lpxsxJaVev7vtf zF#CwdpG*1y*qR9XN4UW+tL1M8SSQGj2$H;oGiV>g10sP0jVL??Qj)E|IEO5y^K6~s zl)9~POym?j#9r=YP9zcp(rsy~Q#O`=zTcO~pQYs+LkzzXN%{kR|7WB3JCErSLW4kI zk2v(u!Qfm5wiL~>A+8&4?&k52bn;mX1uDy1ja70KU(Eckcor)^I2pV(i4i61z{CaE z=Hvf{ZAy$$|6H&e?LQZ+_c*4`*`(`P@skg{^6+!Z{QG?=S_f;rG*kD;;~qP$@o2)w za@sl3w9h^q&Lh>n7@@@8^9D4XIM9E?LUe*8SlA%4^20DUJ%F(}wPE2wD$XGpaU9yJ z5A~f&QuA9ls+|M1L>YUy@1~4pvcsE_H57f$#pgPJ7ETN=M|ogw)%!9?&TqNU!}C%$Ini@Nerl^ z?F(riCoBpQmUm!<43WeD`#Y(8Dr{Ts1-W5uWw$#_rGi)cJVDP?*96JLm1V ze5Fo$pZ6qcOqhW_73uN!G+3sxepK6y+j$sWu$^1ppv3xKlLkGKe->VVqkVj!39qoR zB7M1~$(*%IC4CzmoFS9M@c)NOejkD^mY))BtA8bUy-JD{4GpYGM(h)Av9R^(%5kG% z+nV$QiThXFXvNgvTzlE8X3f|>!j^GPH(?_-mJys`A@E~KfCyUR3sh& zENk3eP>d|#e1RA)1(GZKjX!v-mZ9@po{B#U8=&-&U?CFi+4a&$y8C<_fN zn#hqD&8n3Q51w%g6M^i@#u&(&DhASH)y7xKqe_VV zCtc}3V2qx4AOHqYiULca0f>7G^T0OSr6TKN`Y~362(2xUvA!8Jb-F3nIhd)m{UQP| zn3APV^CR5+tf?6DFT19RhP251xkn`vWhpKuoGGU}u{YjVxmV@hAz%s!D!ekd|0gPI!I8mv@C& zUBpDju4g7CY~RZ#f?|^9vF#WWuPS6W2U6KXmKL*(`cn_ovk_UE6HcgfV05R|s3COh zrx763;+v5nZPtn>Cyq@++M|o@S$(7N%c&5LK8<)5Z3(RWFvUCn4aDmT{ki6>Jzo#H z>T8b2zs3eTOq{i^vJY(@qW=`}93j&sB`lNg9&b1wuEZ8%q?Q6E;hei+#slnEYG07G zvpWd69wHW|bP@OT)L4F4pHrkjWVYUQ0^?e5Y+A^@x^upvwNS#C;9#> z9MY6a8-;}p9#km=@3;+RJY5MT0^~cQ&U&od52n>6j$)ix2rAKvuJ>3z74=$~2ZARH z)iIMDEZLdkSQ85g+@>AZqmJ}1D%jK!MtUbd-XIVpVlThUff)hMMEv3i{i8O^nFO|Y zUtG!~%*OeL2m1&Xz}zxq7Wfx8CSdd%&~zwF`_$s8ZPIQJDn)NQ{9w*jEaejErv)pf zJfEoGh^S;AdTv`t#S}SCcOk3uYW{3_;QWnef%uPEyxRWczhw%0ga9KySd)L68w3ON zK%0y2>}Rj~J!DAwvNLz?R`A_asaO=wQ8#W+TTz~A2wbw|8s=rC#xqtG%+x4FY)VU4 zYZT<1*O-{t42Q~#kWT}$&t~#6efvc|cwO8#g51d?x{Mx1Nr;X2X+i&5v*bZXZ_)V6 z-Eb*2P4>Vb@u$xU7=NLFrVZsLtTKoA{mx^;L?3OQ7B(bXuu+h9J_IIVde6ca-sv*js51W^_8S zN|Pfj+gq_}XZl5RlDNZQgsyp#Ny%nI`a(-$9=(~kz`hwmB8K%Dj>9x8MYk#S(bl@q zb)wdeO1BfMLlxtkd~QMcx<4H_jmp@NrrJ5=kP}fP{uux4-x-Jg0jYG70Juboc9NlX zpK<{Ti+Tr0m0QNY7~6O-Q9=s}v=I;nb?)~>Ry4LtMGur7Q&__!n-g|gK~?JSOb|3! zkGq`l;;2x4GueZz*{{H=>#dq5|v|Q0lP1 z40zN5_EIo@L+RA%vv`hi2x1l(6^E?d#w$Ya8Ca+8{9nmOW<3E#^ zB)bx(d}S$1^#FlY{S6n2&t)avC7-$!E50_j(RjTLUzqXj4gSQ*ROW?!LiB^uY-{iD zl)0~u0j>5p&yW^QUp4ndUTmmkd_#oL-S$retMkrpp!E5C0JRvZE`HGa+wbr#a+_Kx zZAmwEn-h0%&GUMAKC{W-7Z@SPD@bfDCUv3{@uBFO9-URdLCXD(6yPbzb$@QvV7L9( zG`$}~@B;;JJ3*ST&<;AM9owK$cTnD^!ID%`ef#yBMHC~CL%8v`;U;6%AFPL=wYq*3vF4pt|2n`M83)4 zJOa_f@OBTuAaS*W{$rj4Dv+C)2fw4B`d{I=B_+|qcDNB=jt@*fVDP~9_! z)2J+O9(fEF8Gz+3;WJON=2`K5*#1&t{9PD~f(!(3LRspmw((BiJ;IasX5A~br70Eo zl2B)&@yWbrF!|2q3j$^8K`u@ACkvvV;jVSBWKDD48+Gy%(C_q8%rU#QSqkw@QoTfo zmAr1jMrMc=wMCc)kMo^C3YgGV$kVR`Z^%(rat(d8L$rMbXurF@CQz`J`_xtOF!X|t z8jC;T748=JyKe!J)Cl*Mexw^XK=rQmNB_UX&aW2yKkLu6P$iqShP2iRArm35W-abR zJgm5$|7AxZ4FN*<@FXN2p$vxMD{bJXZxj@_^st7%k|I>dEV}-o{Z^{ZkBk?@HGe$_ zVvqXfr*&j0tNq;W8$lA0^?WF|@hBp~ernREfEwLZS-8-CY9Z9=$awM9ebDbc!Z(ux z`{LOOZc%xK(9O2-xb;tYv|pUmx<9$RbAmh4N(pUfDmy9B$xuP76Bl5H|BdaTX+yRN zaN0FgBa1|ah(1_a*135vzyjisat4{m*W#QMdFl}(A3gr$5kO@_rU_K-Vsq$!&jgd` zsQ}div;{9~2G^5m+%tkzBDz|_yGq1?q7p}jw750^e*_eXZ`#xnL09cZ&!x}`U0Q+^ z98~HL?xHfTFayUUPtH1qO-ST~Y#|T=% z8_Q$5xwi|W+v5JBSdQxQ@Uoll#9y%Ce@9AI(oOgf9G9X_A3@5M&5;%oH1OHb^w`l) zgB6yb#b1b6LI~eMyioE8r2nBsbIqW#$Y7?vh0>8L4@O@7jx3f?@5HPby3}Vc3dmSL*s-);2*6sn)+ikwlL% zu#q*_Wn|evsN?9SzJD1wX-AgC%|@cjyypnrlhgVf8)<|a0NNG^{OTz)8En+M`nN1S zIUS(3Ap4em9M2eU_U!)tb9v}Euk3%CWqLyl3$rxxDEMyv` zR!Y{CBoN{`Wb~frfQ*A)j=wAac87}g1>I3*mMhKMhbNg|UXs<`Z46U93V~A~O#Jr; zzgkQl%G@we2vALt0Q7@x>PAj32wyts-y0;^lP)G_r*ih~PCry(?8w#jmHU)^_$@q0 z7S>$kTZU`(k;o&XXeOojq)ryD z`3;3@|Dvz++(m)yVVYosvKFjk6JU-iKA;IA(gujIgd_qKDjkTQ&ypv=TM=MmI;e!e zt3t&Aaj1@Caeu3#!+kuHM6%pmk|_+YELpr89?89ri(JaNNf8_D504aj7^!s{)g>)h zglkHhv`=F*?8&rt_KEPWk<@jnFR3|l;y9uLc9YM3e?@{kPn%y%<23GtoyU||Euy98vrG|5}X)wpM>Wk<%NHO5~pzwia#Iw zDvAOrkZzI}k)K}%rQ3Y$lYiKPIT9#ENVg>%1Z7?!sa0P$Sml=*;S|; zgZyD3s1+ZavnS2kdO-{w?r(c0hGweCwAv-e1@eR%VT9x;IcMbC3?$I+6owC1dAoL0 zc?VW5oh_#)uYny5CV445kQ3+jjmp*ANgH~afFt2 zUo&5jlKijwivJPgE#rX0+&+Avq{moHe5%2IPMs&wa;PStIPXT+6!q#$&-2S4;=J*W zQW90sj?Y6affx}K6#3A2F_M@VUV{Q{W*y`{PcFpnMcEtDOW)eJ6W;xX{zTq78ObU3 zbbqG`H(5!2T1xd_^*T^R65lsCA#;I5{`BA#32EE$Tpp*)Gn>Wz0u-o4F(tZCB-3N4$0o?Tb_U9fCVy2SAgMtv!%B_TWnWS%#cFQk`42eTSmtxIVnm?dQfJc zLAdQ&O$lp4bUW*-2rX)bwvab#m)^0U{|mAHOJif#GMf5cg=(W%XX27OOB0=-1Em9n z1J%4(legdHtyBN-N?dJLLjhP|pbZNI?`>GXckC@9lTi2ANKZUD0a{M&(|6-Sc){)6 z>w{F?yEB&?L)@{x?rR&d5>6m9VqgOcgorIk1s#mLX&A(cU??fU^P>4kE$Gzt(Aw~O zXDX5-1>ev3Cg2t^ZSkjhgjM?BiTy`hajgSqdT}kMKbwKFdwUwA8uzbu{c^*B=LALf zti>7Sm*v~lgtoY}WDv%`w8hQ-k6ZwF&NSMR7zLAU?HM8|B}eG{0ZT)Jj$Xk$fDwGM z;ZnTT<;fF(c)s$Z)QM+xYJ6$?rn$eEy%PQyOJVrSY3?TkzL$=5FrE($HO>ObiCE{m zL{TBlMjPZ&wPu9kU-<*+J*cfI0pxGoJ^sU~5~1^m4?|^IedVUQks{=%sko`QvtL7g{*#J(GDGtU zI=t%O?cklYbK#&b#G;Y8=!gAS`v}S^A6m;rtS`#&KuI76dQprOT!XpAuh5#ku1lx@oR+4 zzi&B2ONN0V;;U=8y&*=_xCir)dCy|BYAOfuc^@OK+v1##K&1)kcj-_6^NMnMyYAuH z`EQk>I zvmvy8zr%&@Y_fD^AlyM`S5Uy@9)C9o2vm89S9~e;&?SD6L#^~n7!fNyI`*nYV#H}3 z?O3Xy^MkFx;`JcWzH>%7yx@jjU7@zb=;Ga#ZLbLQw)^R9WPbs_f2A=;NifjQ6GDPE zN2OqISA4u#&(AO&d>nk-j|Z+N@UJg#C0t@UAb*y#@j(9g(eV&&&ChrRdx%=@+jJmS zf)OYQZD&L~>B4QzSl1QMpnS{z62VuK;u6!Z-Ei;Xy^4u2{=TJn7LDk-T~E~razVD+ z7;dA7Zqr2-cE}hkVJskHH5H=NJZ#$05l1nIwWwNd7k+6>RIn4BA?+nw0C=XJcPDZ- zGEbFRYPAVvBzqhz$LrTPg;-n;Vc=3mWy(jM9oW#yN06)KsS7fzE$dH|EP*HL&m(hO zZOO=;S9|H#(%wYJF+0PzXX@m~VBpN1Dvx}wA~Y^btoshDy$35{JUFq-{p|o7Q<^9x zQOJ<8>?tys>J#~1v4+vbCk62V8l*Qg!uh{sa+ zKNT3%CH7sV@t7Cbd@t_T+wO=h`(kUZkBtUa{xEHMBA~qs{e*p)gb&Hh!M-HBXmg#f zEx?vvy1=Kf64Rd&+OZkv$DhKU10$c+GaoEm0mq3KfqYZ90)W!m$WYH*g!{Anc(>R2 zdvM5=bxX?P#XL~{?Smy^02gf<05QkOVE-0c@+!Y|!m&F9@-rr+8yWjsSPAlRG!kWz zJziLWZ&X4c!A6yoqqmf->OG#&1LgRB&d`>{MWaCkz67=1WX@D4e@Ao6ct-5*(0+vQ zZfppp=g{=l7v-%F&N3#;UAW=Zn@f>%iIHNSh`1$msfaezw#N;g@1x2FI9QYt`x70P z?gVKx&O@<=3MLMn#&p(_O5xtZJ@$=A0!_{*nT|`9PRL_k=`!yPa|CgoYxy|D11~}J z+)S@#yR-)-U-~(BL4Ki5S_v4$%tFxr>yn7x1~lJHOcCl&<5qDtJp~mt%#NQmDtTPLm5JKe;*-U5QaDihEDy z8~af-J1VxiTKCi$OGEfjcDlu(d&TxTTLu%ZGiH(97Gn=j1Q@A`s9b+Y z>mtf>vpMyh+e!#0z=7Jc=clc>XQMH_V*_xY;NEgA)Yvz1Jv zFLL=2!e}|HNAK)X(6}{;)7=^d1>%iuYU%6O*PpM3KVv?tUHcf4Z8sx-q#L2|^?4rz z$^??xt@#SS_WB;0yKO_x^(hQk=sgUXlv5R3G(@bL?{p)e_V9Bxxk$?SF~P&$vTV~% zv5vgN6jB2JiZ9nlaMaFJRV_VUu>_F8rebuuzPj4R=iT@idc>xW!}K-|H+P9#@9|=o zic$`52`jANv*xJblbu;F`_$ev;hDAu&f>1o{Beg^^o8$>?dcw2$|9fFSqc(jU(~xZ zS1=`XR=c$Er_6^|pl%t0iqC>X+4=a!wyl`LUhHd0L6>HUk5wa(`u*ZR8%7GH_3+MMp{wV@}5=73@y_*(45(d*t2f#PE3@$G!QgQ66o| z^OLD|p4aNW&ff0K`e!yCo4F@W&FG2M9(*zS&Ep78T+PJrHBy8z7{WcycS=F?Zlb{} zBt@(4<+`KD>xbG|px3$5Vj z#_P!(&F*&!hpQB$?WP{Xc0F-}9Xr{IX8Vb~v_GdOP@}riwk}y1+n72~qw(q>Q@sch zFan$V-s>7#R_o%STWLGB*zXrt88DCSY~AF?uT15A;S%M0eXfKaME5?Wmw(pXU=4{{ zb#rx&ofl=DRI4TO(g*2UakO;`#eZ8vBof`9cG(j|qN(2ey>STcZL0E?P&s)%zZ?th zl;2)2BsUVoDO|kkO$)7!8y138+p*OoQ+A^%y z^czv-R<{wR@-xn;nOfkqS7^L=&aPXlsZ=b}9+sjW^ZuRBW=Oy`#%=tS#{Q^5%g5t$ zJg29fRE;mjBSdXq#B@T@2cme)IlggYV+EQ$INe*2elaEa9Y|J8x`sC6O#GG=kW74 zXM2+772T=0V?A#J<9gfqY-@W~62YdyWW0IlSfq*7(t6fzU4GV`Z|1yP*_Et>^@mmx zg%^1o>?ca$6P&tLK8i*42fF7+!;#C#)6P0%)vLi5MK!B(rpSEjaZRx;7rShUQjk3J0eS+?k0z{ML` zrx@~WpU^54RgZw6rqEOKYL~!0Y+$ph^uzPwvlZ<-0YNZQmTiAPN2GW%ArF$L^sSFi zb(Av(lYtbCT(`ca!<`R@)%HenUO3mRB-Hh~iXQe#i#f&Ob*FNeEfndO%7hn`QwDcVd z-|e2{r8HQ!PU`GWQ27?TR8krUi4U<@MkIniJh57P7h)s0+Ei|3pZm%3LqB?Tt+Ui@ z{dtyTz?Ywr+%~}rz-cw%q>oi2C`C;U4wk!FTZ0=c7qLnD`Rtfz)=N=Z@&Jsd7|$}g zTvSK-q=ifG&YQT~ck1T}%SEpoOly!QwVP@#RlO-&pU8l z*SSR|!#{d%dNkPcBcSMIqe3%Gu_2crYY$K?!}}=Nu+=2@auV$)WC9NL%pdt|OM5@8 z^b+Zb1B-7chTr~q8p5^01G?OsJRDl%a1D5WP9qdD!JxW&4ho8lamT*M!4tf8De^Dz z=ck1{#mxsG(b8!eyq@+i092D`>JRxww}$bvjLWaXjfQ^@@}0jn0`?lx{R1Kd?qc)x z{RR>+c5&UrL84ATRB#j!>T9xqc>a*`FPEbq8V?pqsJl@bXpw!ZA72W?dZPp1t@*Q7 zJ-&iKLIV&79Zkt^7fdeV{-m84Zq&CKL;@|kFP+>}`+N?bhQ5n~KLo6`uLG|tX4Unt zuMXdA%r+F-zpBm`KAk2g^*qL9n6t)V6NGYVk3M|zs?-X5U#&aF{C#%b_wU*b2^FPo zJ1pnOQ&+LU`z&*AdK49p#zxA$*3!5S&ezJGk#q9TxTIh$>82coliDMClYi8jzmONps`csslHQ0cyOlX^xt#81{R@ zMY_j@wFBG62Q>G{gr4j>hl~nn-wL~L)ShoD-#PiPrx|;i0J}JDtjeBoqI#ddO7}^# zR+&Y_tM0-F1pa$ z1gIvYf(O#h>1(~`s$y3TCf`8=B8O0Y*HKs0ecj6wl@A9aSx;nTps_4HfV@A-7;FFEngWleI8t?Ppf&_#|nc%e8Y?r)t-dDA)Bw zLlKaHaqmu?6qh;d$#Y6Y2I{z=~ z4j%|W+GnL&mh2=GF6+@N5iL*R!jxli&m}yzaNWDE7sKd|jTcd@#VCjq7)M7Ys;qB!uS3*_KcV7R z+x1!m;^6T%rIE@Qf*j64{^)JEcw*Xv48p(})Cc=&m&Xn_h#U|-}zM`KjFo<82BuQJJrM`#m zuXVaBO?Py^R~HV6)|K;`VZ4BLWqV)F0-KPnUgC4$<5CDcs&QIzL)XT*w>=D>0_ig_ z5IKMsDoagd<->_KYkAbXs`a)1#Vdq|XIqqkWNRjPGAr6%SKy3bf3szH?3arXO*pB8qVlL2 z$Ta#!sWn(7`NDzyd`yonoVVm15o}~YXJ=GOonr}F`tqwqsn_Yfm$=SnAFS<$tixl+ z4Yw@XAT`cw+#C}2wHt0P83m`6+s*1_Qo?2h`8^ImI@Mg$F$opH!N$7fIhMQDuO}%4 zon+2C^0F)6^iBuTmq~{Jr$LZ_?TY5(wS*o-TTE3(gmnjC;C3l-R=6b7yCzps=tJDL z*Fn^_DRG$ar}@?c(?YmExJ}qWmBpPaJ@L|O{-%T2NXfkwZDALEkWbmnRXC7ZAe z3vQ*D$7yFo^hBq49-CJq&+K0Lc%W&nn(X^;TxDReoUrfZw~5l{yXOlFyloU2G&MW1 zb>a=jV>U#F^(^OyYZ?pVUTQf;@cRwEY1@aUH$JF}GdE~NPUbKDNzm?TF8iJGY&Tel zZL|pyuT?v=P0OSSC}-aN%$dWmsJ_nQbJgiIFVTYz&VCyIl2xyLMCdz;PT8vb=`sz2 z$4&(%%NUc{0mfYiHm2aM(yR>w2QZ&<3lIqJiKXqJ$VrzpA+%Nm=tqU9rGhnQ1UaD9Y`|EBLjp#Cf zaCvv?0sV+}sFt8Str7r&Gq_TzEU%S|YHX$|+uMN5DwE}oXipt*CkpkfP16wSVO279($N|S=P&V6o2!&OCmpOp5HQ%F za|SjC)FJXTSX(;wXK}01kXx|#9gA8|r)+2I2amjGTzRvshTY(f#GD_$UN)Q^n{`8|Os8J72KOB~pNYy*fB)Pbz&AxsSKoKu(|`$L zbY6MKOAM#pY=NT>lCZrYwKlkblM<%Pgf@PI^>vD2ELex{2JOE#$15UNX}`=royJ#r zePiL#bM3Q9e@+$^Vy0+`k3XE{SUJ2i?gTyHQo0cIXXVCzu&w0H?&Fvf1= zG*!({@Os^a;qSfFIVb4 z5TmHlB%Jhw_EVMqE;|ohNX&So&nCR^Cf$d>=v?>-*}rltU9IvcC7d1V#w5L&Z*En~ zRih)i-iiL2Uc}hoSqZ8W18e!8C?ysRg&@+q;NBR0tp$UbV6I&|HJ%#?a#E)2+<&V% z;A2P%&B`mj*XnEY5BK7JOq8hv-}O-Wq5{s^O}yr&lhAkCsUTW8U!#i7)8MSgtnCcI zxQF=aoS2K9d44eS#`-SFePU*(VBOiA98JaR#8C7nh3lkqJ0oP>Ei2VjYiU~JO08|I z8Z4ZLYmp8IWn5IZsU47j#k0HIDomZahbB1Q{Gt9k! zl_HxMgHCxyPgad4^sQ(-{!e$Em%k6X&(NkhZO!8I&*1x7C0CLPyVYXpM&#mMsLbWk z^P`-4XDcPXTsTPX7<&0p#3TC!UFQ%%n=+uWKQREcsLzcHCH zT=6|6_o#we%7V#^LoS<5TEr<4USPO$eN^2frbT(QQcZlAh6jxRqT@y3w$)Uz z|H(n4l5#3A)lE9Q!+c~Zee&tS*e1}vaaKPmR29cE`0jgmti$%n<+XR$ zT)nXq_rzzB)~?kWKiF=v5Sa9ccf^+*d{d>`x>P>$m-e_{wQc9UXT7gaQpvlhFQQ{8 zX7^T@SJK=UYq!+=9tPQ00y>RWihL-#UX|y1KYek&GdZ9qce=Ua>nPoPx%`Z&%-gk( zFrA}sMRfod1l<@&lFs03o{earIf;sKU*!7`CoL+{W7IM#wH<}Rn>8~Q%DZbi48O|9 zU3*99cYK<^9Mf|+fYm0Eemi`zK6FjuH?;QeUi$auzlQ`nJ68e1@IMqHfL;Gd41yfo zk4^zf@pZdI2u?>HFa}+7bN6Y~y}BZ5ZUy>A%D5-_#OhX8H>t~N8@XxF*DR<<|4jq(VIZhIG#Q4)Xoam|EBqdkfCicND9O5!0hlmF|>%N%4( zml)@SkPOkBOlfr6KWv$H3B?yLfHD%-jXmxt1&FjvcB&vR0RQW3pKncxSp+q3nCf-6 z%vfdr=F6kii!L=msgUf6=H?~8Aep7qqCHLe#hU?JlgzW7-o45HyrqoINppL)V`@b7 zu9Sim*>Ovyo?3w)PSQn3+~bTxIxMhd%`T~R-cSE~W6@P3c#eTQ2SE;U9576k{dv_wLXt}>hJgikA zOohrm{`MViw;Nx5Eab7DhJB{7e7HSsZsxb}e4iP_p;PSC>|c1Vy5gF}qto^bH^Tym zz|6uDDhwOb`0;bMnw()}3nS#pa5?IC-3U zS5eL(CTvIG2yGn5TdO-)nXwU`D#i@aShRbU8t=(Q^XY4w}C1?(IHS zO>yn6*+<=*k<5n)z^94q##!gXQpsxaUF1dTl-G_i-Vj%u_)SgoyDcg2L73HQ&)iLw zMG=9~qQ7s7@^AWfWVq*DS>;W3HOmHO$59UJP1|`xCeNCZpVLD3;^h0xkENk7D7h+h zIa^+c3T*6qu*Oz*r9|Do%#y_9uIkp7!JqAnPPNG(#zi5ZJjt)kJ$^B;@wdVX9pV(@ zJ^5bO=iT#n^c7n-=a6{s(^qB0?t41AO0tb*dp}(_CRI}hBM7Klz*US&zXlmozink* z(#N$Ga5jG>XE!EnZ(&t;q3OXa>xEGMo-0A5vh3ulRn~ug<h0s7LTJ;C~dDHao8Ny)4{aK736Q_gk}T z#wh9GsJn5XKcVW=`qp9vsfwU7&-D64aQcVd%NWLD zZu=PEW&252V6)No6Pv0hH56Ow>gBbl^l790wJv9CdEicth<>|FiuM5_Zj)8hfYx((0WIF zvwo67NZZ}z3RQhj%ljqi!&xDZ`T>cTL9jg8=plC47XehbK_YKDE!Vh=0PS?*_cMbR zwK|oiOJ(ML9ffhK5-53qjDzY0xbIHQ)-U(3%egLKTt;yidW_$`Fqe@78Aw?qO*nO- zldOkP*pdrI=YC*E@X!;va?INu<4R z8;%Vf+YKdJg=Vh9@R9j>jX!?z@BQtm$b+Ejz`-v_Rb1`mnAW1U|S`%x7fnd1cD97FRoe8CH3C!Ua5YRdOyGj7o+m8VS zo8im6@=ja(L&2XU=kizduke3)KmMD?;y-4P|L2Cke9IUbKrnZ?E!UO+LHPYek)u2? zZKsIq`&oNUS*1@8{grLveo{Km8cy6__9&%3i_5QWy|9iObwK3$Uh>v;(~iv)Tl{t0 zREyh=@0UmGcc)6^vtf2KceKG6opz;J_Baq&+sPL`NB@9=N$epJ7qqWThuqho+;S6C zM>X7(9gq4xu!Y8@oUto~_4VQ$_s3yA67LlCJyYGe*Oynwfh7|k$=b7s_H*qbd8xB9L)TcVWM_Ka_y2G;9ph!C1gT2 z`W-M63S!CzhCWsD+@>%?-PqWQJ%cMV1KOE!FZcQTC-8pfHIpffaj!=lyjCL`^=W5` zk==QeuNP>iN+w72J_h6bQTa}#8O@3@rC}mbKU1s0QN{Pw9^rVX%&9h^&C*?*|qF!2| zuLc?|(Af1rfu@I_=4up{z1G~XPe*az*$0XP92XOS9ut6#E=Z);tov2hNI131nEM+N z>36umJ7%0*lEr;mdzLi`HTbKVVY329T`iR}cIap0GI{P^Kx~UkyD^pTFo7j}cYNXp zPTPCsHjN*p?}kHre33DlfoE=z?>Jm-GM712zYOL9Vo>Ynl=Muc@kUuvMvu@I@ub)e z-aqs>n$8@}i5;LR5G3oDsc)0L?!5X?+5D*hSOd@8MGo4wvh2Iae(lYj>aXd5oGEBN z+by+}Dfk#uh5^G(A6LGnRWq#q5Q*(@JIATv7V<+Ng2Zmt|0jUcyk^Qhr{AA1u)p%5$t6?NskdOb83Kj8sPjs`GsJeAn9%6FK_y|+Ny%w4i_P#%q9}8OE9~;rm3>aw0{JjH6NG$c#{jQ;p%_b^#up_;kzIu zIY-R4Y46&+UlpM+02do>Ig}H)nRN~&(9e)~9%W<#emlPVk$3b=ybtP)K-^5>Z4n)X7W59%7{ms_np0obz5F zCzM9n7MykYLSTf`G~Y9{#iD%!;sx_ka*Ir zO~ZDo_*F?sW~0jnZ17Z75SMc+%lB)h$`EDUD1>)6*HShKp_Dr{S^g=KUK2m zxpC>DdVJ*EIrT8nY8e3QeL-i6Snfc1fInz!8a+dF12{Sc{fETEqV0UPtBTo@;owg9 z@u2#a*slL^?alpH92&K;T2&k!0UwS)US7SO;LCT{ zCmTA^JfZGXTHL-49Z>JK=}E>|@7t3h^BB|J(lSngEr4U9Ac0m0`0m3)>FKk`7A3#3 zIH$4(@Aa~3eiK7`S*BBwx;vfOdhiU-KA%+a(0LMM?}5AzQ{d9$26HqRt$*zbDsXqq zkO(lTN4rY!ZZd9hqolt(tMndz+!We&IaQLWKK@K@eM9e2@ZYW|_|Z#Dc%*sXsum^zjI={M!S5EYNd5@jSz~J?ws`Y& z)M}UfgGW3flv{0E-2r1|sHr@kK9RJ)!u;7Zy@2E#Q|E_d;(R8`>{B;HuZ3U5Qo> z4?IVg(~bXhzpRegCG>C)+7F;7lEdl$mMl{!*7cnuE+CX4Tt-|G%CQtvWx^h9+HezOu3gi`{719pHplZe z5CUh=S`V2gizEBE(oYgoNfom`QY}@@+vlL3^(5=Ak}&Z8B=wZR-W0dJPoXU-mtlN) zKvjG>ib`odIu)|4fTa92!ZyoqJ*Eu5BYzyQ+B1$g?hAjX#NyZq4&jO~G#zc-Kk@bM zOJhev&6G}sD|q|?^q#qf1N74SXB2Nw`iU66t*wh{0PMR5bww6`iggLfEhweIP@Mnt%TU&^_H^)i=f-#UHr^wrl5Z8 zu@?rlJ>tlq;jRoid~FZ`;<9aBAKe~rR4lHL_;GW6MH5zU>)uKzn&@{Ydhg}9cYNK} zQIH=3Pu{%}uKE-Y!xO9XHW6fb70CD10qGrL&~m-`_+;G4%>+X11_%t()*v`Tq-v20 zewaV%S4jKivOc`#8v`5hbx5en_l!~lt(xr7X!CHq%g}q2`9@>{y|4UR9_U##Qo05e z^)=x1w0vLx$dDJ1VaXVeQv@K&SHh|nBeJ|QH{!%Yu*3LRsyrSht`vvw9rsW; zS#q$>q2Fk%m3PXAl^NCmRg>+6#tEBG^tpTAabr`m6SI&FRLe#5c$y0TL>TP2T= z@U|Y0@=$dyxiO4<-YoopEQfCk3-Oqs5ccsmZb7le)pq_UeT`A8#}-aHXTYaFTc01# z+OQ12_8)+HOAE(Rp^U7@pIq`vAcU#Ca-{#bDD8K>pdP-w*_)JoT#%obeB%3z(N!hQ zN!O%v|E(Qez#yvwQ3w}E;p6}(gd5ZyFKN=Yo#!);7p2JjZH!Gh^FpGcTUEe~{o)m7 zAB~&e-SOLNHQaqRn{CYyALT+a6EAuB4w16#yUk2w=qnLDxaiExg><+|e3gMGn@yE! zcC5zN&XEsSs67Z0OMQ4I-~lx|Si8FJa?T~dIW*}}C49u~@*JKPXoR*p>!N+}gc_dG zCVBLz8b_}9>@z0Q9i9-UW@1l{)LJ(@G&(fxB~iO&6`|(u42n=+q@UKWlDynka$mM# zA=Sd}9w}np30GVpa?b(-e45U6LilmPdO}LX6bF8oNJ%{+jcoD!-qBAi7JceqLdrtR zE7r(YK$n36bP@+P-83QAcQtb^LkF)b&1!FVr0?$?&qOd@hs5h z?usDq#d-5pe0!+!qQT}LquHenl&r8yqWl@qA%u}ZpgKra@U0YLXmXbZYE}vG5>j_a z9K{i#)rd9z5ZXZrlv^Rl64p~v2o8Zs-<#2_?HDe zm)w&;Z7W|YF)i3aGRuM1Hj#Y=dTL4ge3<|=Pn@fi?eC*JMatann>j%aP6+Z!Rc4%X ztDzi;Inw6fbGRXE_K0sURoHQ1^bA}Nm3a7xH1m5g8;6O&eE9ktftmUEx=eI*U>ecc zoK@4=c;w&D#MS3Zbxdcn$aThb_VoX+_8O`6XhUHH^esbuxndh!^rw?dnN!Je-dE8+ zN@``1SP=6KdIN(W+xNF}jF=D_^a3`2j_#7Pv`A|H@Gg%hMjNL zlVIzR2gnPO9Ap;;l93vVZAdOFop5BcSyVgq@zY+b)~w0W;6^)`ruw_pSlLop`=p zgloE5vj`*Ph+lv$m=M46w2zuB1cMs-nk``KJ3*dWt$VU+mc;<;yQ|Vn`x=dr&KuiG z7P{~G=F?@DR@x51PsY9po$_gsW8%rjiB7!Nw&&AaXJ&$G8->DTqy0&=i!{%%b!1X7 zC{M8o-e+D|7aK2# zL#6c#9TrT?dN6NIZ-9gM@{c@!a6I=l8x^Clt(~whtOLv_zcZFz5me-Co)<|Ra8Pp# z;vW&M^>yaZf&s`9R2=Zxi-cKsAjlYu!n5_0S1a*djb!z~+c)vb&GxT*CpA$B5(ag2 zr+ibxpBDd%e*2D`fA_1?-SIm-`O{QRMwugSQm#i1>H=1#}3)8yQqwmu5{OK#1a z9kVfCPL&kAi(IF#E_=Vln&w^`v>H^#66tF+=!0)PfL;u=ZB~Ws@sbL7KV(qda>lp zoI8H6qokJXDApZDKgZgSR{a=0^a0o8-bNyzN0D9kNfIpsdw5WeWhY zhMezyr;f=KOBcDc`+i%L?LSpY;n?!w+-TZdkjSyZkUKJ^|l^>;*KD;!`U$aa#W=G1)$$EeL{Mid&2-$MS z*aa5sU1@(%*bs%ZI((72;N2DUOFExPsYg8gpIfE>)nHtvMK>qH$aY;Z+t2Qi8F#<%&#@>8y(Ns<= z-6G%APfu@5e)jm~q2+12e6MQJJ|U?Pj~DT*&*7t0yJaM>!+82JW}< zf|i3?ST?Q|0te&ymXB;I#_kkcu*p+puGpqlW`oQX&LvQFG@^89O<{yHl{y^7XuViw z`@1s^m4hlYl4;~>Mo24he;#=1tnckU!Js0|Jq-!=j-J88$yC7gMkClwNOud6$6ejC zQ;H;nKVm@)BMul2q%lUw$gqj5N&DCM>Eph7)*f61|oA2c?)f8Ix@P-2Rw zHSt1J9Bt*}Jx_6^e%|g64@pfa@cCmFfSI!K^aJ5TIJLhb{ONMV`@T3NWh^n6cz3A~ zRC-*|xNp9lAZJp#`+)`nb=jGJZoY0)RIgd1+86siSu*N;R5_%(=O@6|Q_tz`{&O*eDKodRmvl>|i4m z1CzBnHmu^VoBGRVoHogO@LLE=Lp!_wd{Vd-8o{S3&>8nVuHczAm=n_&(0!O}IS=Z* zC1_pwLA+##eL(k4kl^Q6UTg8?@l!{&dLXZ}IBGG8yqKxd&r(AGV-Dtf>l)Adiu2$_{ zG#AHx4@8ig_^{7kg!=~|5A%BR2e^r{=%cx8(+vmLgf&O0LFXozj5Ro1_N+(>3``(8 zyhQOwLG6b8&Bx-98~@*#yT_NE55afzUuMADOSE*qhBMAPkM?WVLXDvH4Ljp&M2dR(#X`i-qOV|;gFBt#Qievq@%~j#iWG@_GeLt@DtY?)J$h4y_VeCli)&c0d z=TGjFm?eHVka68lKN~<%dHkscMzaZL5(SS@vFXN%&vVIE`Th;;#KLMT6zzkMgbu-X zQ%kzuq<`=o&kP(!o0vcD%g#l6&4tlrzt|*qp5>)~qL<}Q17-x4k__grk?#xEZ^tae zM(n1FOVCD_JXhq({O^u^=Z2nY#c<4#mfHW$qzG&>Z1e#8+;i(!bg*ixfj{<9dJ>kG zMFwp`l7EIWgWXZ5&m)Y%CyBn%mv5Ne(~)l`$g-TZf8gEYo>Lw22vKHpF-kxp@Q(+Y zq6)5$P|9O8;w2mjgJS_s0b}ACOu%>I*3@-z$qEI$b@=lb2lSSNCC?9nFQjhe(`DGC zZ*%h2dv2*wTAiD4y1Ou1s_CeM)46x=)8uVZm>VO#D!AiwB?xtnD6GWbQr za;-J)ZuS*dZ;G;fgru&>^Fz3dy*~KOFke$#%I)y<8D-H!r=z8XKi*{4jQ9!n|Ec!G z+_88d7FT=OdIaY*fbDYP#zyaQ6y&4!9&{m;G(FYA1mfFOqvBiak=Pb6DlyKa#Iu{R zU;V6WPNg;l@nkBZC6!ih`tI&`p0S$*+#bZSbYZOxWGUlQY}nsDm8Hb671lMu3gx=} zZ1-Lf6-+QR5Fxv_A{VkgMI_ePrbu%F)0`dsYN=tn8~#!egnXMJS))D|A= z^VSB`@!qgL+}KSEOHH={=_ga#GJBFue-VkctBHjY?&5bH!jc~q;_&&oLw@)oLL2i* zt_G4+FYv;tJ789>eQpmH8xr@&EzmEWCefTte=V**+f!#HIn zx;KQV*57K(+&j0PT@|i+Wat>m58wC%2)oJ1_c<%_Qt}qm&Oki$Rn<>2UL!OJp47H} z4U|Ev|6bt7MADH{%db-Cn^I)svW|d+FQ# zkzRe2aq=%3XfwXEq1CU<%#DKK2 z6y3PZ~Cgxr3FSl`mT^4;I?8Q|*pquL!w=@N0x9pA}G28mgyb3)M3Qr{WRkE%W4 z#xw@#DHg;pCi(5AG(G15#`6#-7Z(*-*jxW2*&_utNG9LT@{5xx{p0(YFXsxkic&@C z+R<^X{$m}I9*+`m)}O_Kib43SLbVGL6*b?7r^yE=`Ky%g(8gm5FJdT-8u8KQlI!V4amU>V6ssyR71T{ixY3Y_L_f zpS{<1?%v6+`lF4CZ2u?;Yp|bEqs}p;1ygx3NO%NKsY=HNp6d*42#4EMOefCx3<#uNs zsac=Xe%lVC&`CXsb63r!hi5f7uINb=?@Hbzb^6C&#tN5)UIc?D*kR8>)P(GFy?)ML zS2|yxSJt6zRA-49Vjb^-)Jf-FFKN4m_FXFPdzi1T4AuniOrI9rqOZzG0{IhtVRD|-pcx=7L~Jv>f7A<#Luwi%^-ll{w*`` zu_3-H!#Z>1I~OV8o{$UG?Zc>eX8sz}bX(5zXC%o!=dn>!H13a3tX~}O-G7NqScTs9 zIp}4#N$_y`H`pFmkGZLx^QLfRPh&#TC2SlR#L*_+mNms|DcRgQl>>j8Xr6>(LeMF6_-Fi9 zA3fq(Q;?C;`V3~tHTJD2@p0dFIwq$*u6X_;ar88WCo8V{d}d*=bxL|Qnr-M!X@C@e z&4m4YAKW>d(gPb1ZgAdbW>s2fv^l~90v9cNU~FLfP#0RTsE#`Ib}prJ%u(e17Bi?v zEj3%GB7pPkKHlW%79?p@b(#85;;<9!Ki*D!i2+o&q4a$M&@trrtuKV8{@YOCH-_EO z|Jz3NH{x*R0!@T9^(WkGIiSNZ?>(T;KT7FrZ(}GqLJtM8geEh-M3j{Y#9%?3N9*9a z26F=>9R>~MFzTT-6OU)^bOj5JjzpXtS6n(Qoj@nc>$WGLc3S&UO#O*B!)~Idh8}_) zrXPuZjSac}9bAv;I*U!Wl?$eOmW|wCvm&0 z{a{b5B2y7Aad>1xGbVaEykrJz_tz~rk0O&+80tQ`W&zX)3UxzamO5*7iT$=( zf?T28AdCPt4aHK2W?K^taN4>E@UYcEAuYAaJf7` zUn^pBC0Q&khH`UW1$V%8m(>jZqSe6buYUlXpeG7*kNf<$dZ9-ufI1Psjp5^hi<#?kVIGo$%z7HL@l?kUwb@<7LS>BtFE==eAeCNBD z+MV}2H^V(|kIrFj6A-^w5tWr!=-MJzUh%F>s`rhhoy{3!_WA1hNsM>1&fvGVu;N!J zIf<~@iq^Q060!HmKBaQ5q{7Em5utqT^=d?4wVlwu_55YvTo-eD8GD6~wIOefQBT9b z49&%z!U(vwRA`nK-jysnD&lj&Bu@X-{3E+K+JW}oF^J{wImfK+VT)aH8_p-%^-GZx z@MMY|jev<6+6)tXc3jR-enQCgWAsBf_;5b`AKpg0Oa+k>jF&W)n{8t)PoH1N!}hx{ zsp9*Q`*H6%9WR-@q1xdS1dcgkF~XQinKZj0vjmm&%DED%DRqqnD~a|1q?7JQ)J3CY%^g8NgmkqUxI=BkCAQj*qQ4 znTgv2veR~a3{=F4A3|U5A!ql{iY8Sh=$=!~_p%2QbzCg_Zhby7U0$YGC+b+;s};x^ zrSc20Z{8Y$Ncfydgy>CbiUhSiS{)h@d3Y!Ob0)b)vF5%c0X(ulr@BEL1noE*KaP+V zF5L5T=b_TcR=o;}(sEh3eJdWcRogeQ7jT8xQY#uspRY3OUXuo#{p*W;tnCs8XZu20 z{a?E4oQWrR%&3pO#4D+X1pIAKa&i7Dhv^B@?_zcdVkJ5%pSOuJIPb4Kv>eTLQk89p zQ)w++EFi&*y#<%TjEgjiXe$<^J<^_`P`HEIfU3 zN9Jq4io#6VE5|@mCV;7*ke0*vWueOVTv^QOt1SuY7Y$1uINCWs&rSTV9Sktsxr(8W zshOetj{ad^=5z+x&~;B069rm5smlk|sYpQwiBYQbau*=R8VQ?+b#ZqW0!+Jy`^&#= zKCENOs#h+r+Cw+KKM(N@jjLc}gxZS%TiscH*^1+la+d#%QvbM4RoUn`XK&s_%uifzx}nTxa;Oto_u3~B&Nx2l@aUJ+iI?Nr;6Yk`vQx1 zEVF*cy@JB;ULDpYUfq|RJm+?ABp_6|WWXx&U39L<7sJqi01PIk%_Zjbwz2m9hApmC z$a!|sfjVlpEIaH8ac z3VbZX=fi+lIceJY{v5>Kg6McMwXy*eW~sk$Gqo8)v^(J2vy=Dr+`VZH4+mkS4xh+nl;zi!N^PmDQiJ-8yYzjkb1r&0yyt+hW9&p}S#5*FgzLtTYi}4M zo-=xBR~pGC`%7JvvfY0w`KFsxsCJk*7E`)x8D*k&=kl;QDlz5jeeXJK8sD#VUY`%0ojdd}ZF8Zb~88skpL_ zLO(kDi6e$~F>rm@j9h=W_xFcK5D?xtvx_xmHw7qM^C58dmKg-!&E4cXD_@l|%OuU9 z-!^{olY7)q&8vKWRd4&f*w3);t&+G)L$YhD#B~T4U-a!3QOCr2KjQ*yu&oR;ko$PQ z_L5O6z`niR7oUuzv8yE{?MU0p4}hK`BdT)c3x5R0Bl^L6{?_%W)K zxGm|ryMxB?(C;4eDx(K|Gh2i1EVmbi@tlfG-EHA)!e*aGeO{))lxaauhz8m4}KsnwA+6 zD_DDra+)jCrJzLK?A_X4y<-7Oy~G{>qkkmn0IkAGW~^rOLwWC6i|RBd;!5~w*OSeo zNqPLQLtt)VF4o93N}p}&5VvsdtkAy6&B$Y%4JlinogWXy@C7BV_q9wXw>NviF1gZc z=Onw?Mi0Biqqa0FYK$?CyxR_^|8Le83;raI3s$fS-RRVLk*yUZt)F0ILq}3Nf41~0Z zyKQ-?NxP32UC01_H;L{yq82wPuZ6qwe5z5pf=^4FSim5VDc9QpN91WW6{glJsN}AT zJ1p&t(S9hTJqQ#AG$TIg6J%OHzdZklEjrZw`LQZCVo!I@JbRv_lBRQmHe4&7(ep^LmmUZ}|CJw zk5H!PH^JIe6UW&PP)*{`!n>##iNwCfR?DFU_{#kSJ_k2k*GM<*h-8+G35tGAcI`NT z;iJhD8Oh+>S@VrC()P{AFNEM3Qp=sMwwh^2DYkU$>2h=@%F4Tu3()gvBP?gnN>VrE zGMG4YB$f?_s3XsoaW9#4f`GOgL^rG%{H!nB*kf+ImVE=$Wi(a zdLI_5MAUJT5k?J^)_PO4;2NxFSZlkoA$EsYTGh%}m(vj5IBny55p71m0k#9w^ z-*efb_ybu&eeiX44gSa>8`)wti~MP#6xCL6z*Rzwi@r$halXsNQ$h9WDzy@ui41=4 z2b4bIs5@8m1z7nC8=bf#2;}|^IF%99Q8^fikxaNQS4A4%4wZWDoaV@y&t5`y z0J1$DJl3pS7LMD>`pskBY(~pJ;Lh3Xk1rAkSbfO-+KUaDS^pcHX?ImF;6P4{wPQV~ z+qkl9AWnEED?E1x=neR-2Q!iwbB>+Hvc@}sujXjm#fO1w#Ba}wirG7mz9BK#9b^~x zU!?uyXt{I8(^og)XDHsPiQ1$lv ze-;=D79Xqo6{+}vJN?bkr9<5I**A8sDFoW2w9HLXolNDzXr(3H6ldwP85m6eCeS;Q zK9Ms1rZ@`z0|6VuWDJ zHZ`e=zbvEnQOsEJ?Yvy>QzVt9l6eXGsI%?^ZJRi%R&!D_eZsSArjGU`p|Z-)Cja>| z>BRUHQvAmH3rIGHSJ&l%VI13B{BrTB*#pY67V&0{OTR{0JTE;lh(7`;XulRO&8ZX0 zJLPw?^R}1bRQly!&CsVw&UW8MZP_r3fHQOjf<79 zZMp^i2*3wL<;yo!KLZQ+QoQ0;Q?k*h3qv5j8dA+b%PmOPR$AP-w6~;#?O^RR1QW-P z)n$~}ph5^cW$sWeW+-hzK0DNyE8k1LukeKi78uzsNOv<9>4Wpw+Ez34PSzzra-hiX z_SUUj7jnQgt1maQb}nK<+EQE8@aANx+UM}R`OB;Q`|I#o@cF>;iq~4F;yds|;=aSr z&&M??x<6etto^=o37OcOR>=D)U(MHB%Nv)kOc6fBOdcJ!c6Z~6*fUx+KpflMBSM&c zwf3HGt?UZz0_Y3n1dMCndpWhT=$W4(rtjTg!)ZJQgD1D12g2N++ak`Ub%~_UcU3x!P!~GZel6T6 zgpVS;FM^`bG`K2;MnrOa=z+^%06MpM>R_O06R7swRy1)EY zDT`;&o?a?QFB8~A9JGv+wN#Tlvr2bYdU|&xNPqHc0U_j1(F7*W0Xh5V*^edC7`pOe z2;fJbG9N@`AWG0>5cMH`<1^nl8mgXdf}DVYfC338JK(EC&cEe^wvGQ!Es|gPO9uR> z{sLQy7GD5B-wiHPbvnEy#ou|+$Tsy*luat%-{>=sxgOU?)yi+*>QKQQaIc!HT_ytW ziG;~Kg)-ApCLPk19i>nL6j4ffcnvP`r+7(IVDMBK+=G|v7o_TM>d~0A>h_|V=yCGC zgw=?s`e$B})ciPcdGYN=EO-A_<6^pplQuoY3%Sg4xNZZLLrznmQ}`Pz|H_aIM0aZ_ zee556ZN-_Bc%M>9`gKYaJhxs7lL?;*6M}L%3pV=ks)m*l=QjX?cC%=pz2=B=wFisn z#9grh`gzrFUa6qA3k}BHokus=Fx19OK1bt z;Kz4N1~g_1-!4_QW^8m!qQ>iw6m}0Bjf;F_R9v>D%xRtK zV11m>QL#%?nXAD{d^0ksN1tn^fdMY$4NHYOdRIeq>d?&GhBprL4{k>IWLqe!-!~fa zOfmI%1+7xSmH2(s56rz*hi%kg;P7U4T729#u95N1;6=ejiGuvtc}Rq~0N#msN0!oN zy!6F5zGpu@<6Z>=%CMZu?AfT$)-%RXFv+JkT?9L~$N<8Pl0I!q{Obju2JBQLA0`!k zM(ulYL2>>?C%@XNlw!WFkG_}wTJqnz+e+A=#88mqau;8Nfb5p9QH1u?=PX7h| zxgdLrY1Pv8Z2Ih#3_-CB5MGz=1M>4j-GIXe#OjAS>K-kfndy8WD(!eYk-=On7xRKg zq1%|sbDvte{#E++>1gLnRZHb6Fuu(X?qAcFc)!%g4NoPQRGZ0{Q>wNBbxg4O`t8iu z9K>PkmITkv@foSfPnkqtk!$cy@>xoq0zrKb;)B$Z)Z!gk<^RDZ;Pbc3` zZ61=Yl5X~8{Y338Uen?#8Nim+5r{6q&2!Zo8XPS9T*^1nb{>|04Y%)aj9y8gZnm1P zs`;RxqUTpZ!;j#pNUuL!8kVdOwv8;g^fG8Jir1!5s~_T3Ih_4~BQ5-dhKa5!mcFV1 z)X}ig(a;uPpM!is<0LhJW5J1!)RP@KCd6DNBgQcv$C(X3EuAn~G}L02Ez!ydutYr| zuVwBcs8=2|IGI!aL;A5M$y)*fBmTVHAcMwBfc^tO8E>Rk*uQjPfim6yd$&0EC2?MC zNX>(D@1N^*Er8gY#GHGAib=Czz~>7T9t0eE(VDoj8`RxfzcZ6}vtdV!l!R-urS5sL z1nkh>b4&`)!)C#dt?U4$g})f5Ixv&>SVaBOsJ-lm;gDm+FDr%bkw@G40dkXV1$OU~ zSD)Tjw*Eqp5@~xE*-Yo>H|ACbG%G zmvgK-S5X;#%)H|A3V@Jy$a-L_@mVw@{0a*r>*-n3>=G=EUt z=h*$_Wwy1TY4BK{d?5hlF7f3Z*b9$H;Btyrwx#Aw0afClcF`Xx<84~8>3f}L6IqIw zigja?b-adSd&Q7W^TDmZ2EY}}W|Mw9$beukxd;n%9+jd~uH>(WyxEh^2JCi5a2j$U zvTMUnHmuk#RtP=`lI?6;EyecG^bqn71FYxD357J*2=Bj$@Y?q3hiw0w%Bo8f5xLvG z1z;v&0DliRF%p{*^pqz%Z1J@j%8Tlin_7}Q!5g8OJs5uC)~Msjp+pu$PjF(0E86kS z?|a+1&5wsc%+E|(=f&>v(zCO zlwCZi!~f1XLDW}b($GNNzXmNPs)n;N7_w@9iKgz;l86RM3I|GqTy78bZ~N8fzfK^)nT{uRM#!j}rZ^@$z$WGi*M*rl zJQ2^ZA`*|nqZC%xR;!_2kjiPg**(=<548^{&ZoH>Pvg;q1GJdUuC(vhqm<|L>6FZw zeLxn6pFvyHeHY{L`WFVXXSkAVJF~Fhkp5faQZZf?CN_DaB%k>HoT{hisenhPe=qI{ zbBy;!vAUQ{#g6G{<nL?Ji!^F|O8Io0)B36Z-M8UmvzoOE-Rc+MUd* zskxr$CSR{yWJJoq%<{ASV%}Fh_~x8IK~;_Hw@xYy=*;srPq7%jH9iF29tSAGo1g4;@{S)~~Fa;lEbCSh#q z%nFCc0u=_|DJxrcMQL?tpId%gEL=-mwX&SU==JU6S37ChE%nvn;cC8YL)Tgrjuf7a z75Q`^J5FFxZCBZ>dt>lc0`^&W?q}evQpEIEMT7mZ+yv0yFIt-dB;hW&QA82%2jUB< zumOil9LaQTtgu_+Q7Cu0-)$@|kSnWHEtA{V+@Gys0Es!p5r8KLY__dvNmbwDKK}Np zDc6y>kXIk;0SLPum$uapaf-atreTHSCG{AMlQc-7H=l=G~ZE{|K$usMB zsKgTOAZ(aO6Jt0Yilh8UwlA7fQ9?848=5Rf{I5hTai70G_uz-E$mG{5--Cyv)CyHd zWf+f|wTL$=J3C7J+f>|H60i4bg$P$YKOb811(I9ns^cv>G|qdAndSqcQppHbHr~oe z-38qv!lzR#2YL!A_D*q9K2F+Pe?$le6XN@XZvIAa$5kHj`(6~y7v|l?A8D%}qRm!w z(#E@@+pQ-Knoo-CtBFjN>l)m-X3cdMv=8w0PZK6}(J`SgmG_3gBq25+TF?l~&*lqkyZOejnW-%93 zjBY&LPE>2X@IW+TjYD?RRyvhjhc=ubULun>r_fKSjfh|0Ma-XDz17$59u(1KBB|lrjq4_eTkb zJ;RMzwx6(autyyX6BE`N~zjlp4UucQ4w%YXKbDp@C4`bSdxuoYa_n2SFfo|Hd*eLk^t|u;oafB%pSkb(%;$4o z_jO;t?fd;+cmD6576zqVuC-&N#n206HX&Fis$foAkPJ8ZwCMBw{q$S|PW8j52RZ$3 z277K&A%KE6U0zt{EnldZoBZp(jdN?j=BRYa2{{U9{yN{@vh;x>`V{Ts=*T_$7Q)*z53xLVuWY4NaeIB2 zxprBjDO~`m9B%p(+;ktrI?1&p5SFGE&Dw*asHe9yTE)o>iE*rD&(9a)Hv8P`&>3x* z2QKK9la|}NRTr&uA(gniceOC+P=RwFtE|#*$4uDLX|{?r-g~op$=b5p|EGeN-O-W5+-7PVCVMpMB^u`(X+4jJNoxiQYjb@_MN* zoG4E@O}6|z5qf!M}CxPj00nFVnfAjcbA41}Z##!#Sm& zia)}{N?>mr}b6$@BPW5OZ1 za*$(A?l0(Pef0B02Y0k#hl3YKnm?zs9IG)oT9M{9d;_W{Rn}kb;l}g)Do~53`>=V7 z#yZYS1wJ}?2I^#PBQ6T3Epl_g4yCydJF6Pib0zK$wbm_H8sZ5SC1rxtx#<|mXe)}_ zL;6o^rVVa_Jk3{>`O(VB0r`XIUCG`jBS;^s;h`E>Jp?0iDZc%2XlFna&6Akjpwiyi zZQ*E;TRZi>^Rx5t-megs{DPsC_Yq7X>TKn47wR0(jW*}Th2gqOFU^(@M3uG+XzlP~ ziOm^%hjPp6*k=mxdCqLuaM=yu*mIS2Mi}t*bj8Qt&*n8NW_L>r?t`Iq=@|pNNv!$4p`#F z$KRvpqKlU$hdds9lRn*7tC^L#U%Oo8E#}L99kn4y4MDXi^U}y3Pi36>$ZDM z>Bs_6OQ4=-r6yQH^nzfPLez0Xw_JI17jMbIJW7g|z+D@0DAS)E*XmuyR&U#P9!wBL zQ3oc5*&NTc^V_8TtI>=uv*jzNHo0yrc z=AIpXvDn$(^+|q!mneU{3BTZmiCi5RWxNG;BuvSF2K6{*!cNXeA)?)TZf z@Lckqmf?SobDej<7w8y@!HYJpKupqNT@X7n-!(0EtHf&83YErh;5YJBI05ifM*FDu z9zyV_PEj_RE)UB}rf?OC#@-}V8bU-{(4dUKNV2P|!gSj&29JW9%*6YHB{B{iSW|+n z4&h)Yl%TWnjiPtG>NtH;cXaC;1I=T8ZvEyF5KA86XB={Ew=!yn+WCE?4bDiW4=}zA zuBqeZ$Jd{lE}5w7ji~e-x8_1{TQnHV@ZLaQ{;pz=;d^(`$6hb&_kB*SA-4I<$xo-> zLXEfuq!R{slfT#xTlfGxcj?gzW@N63*dwUnb2`8rE6C3X*~0cRv%TiN1%V}Ip|g>; zkhv9reL@9t;w3#f00Z0{vOZv>R*ebVHqO_1a|21wa}e~#Tl%m6QXX*s4m?**tME#` z;xRXB7D9T9{2Bmc(a~+Z$oz%WNaPiL@G)7#0pmSw2sxsY7HfT{O5J_g$Fuh1NE%zwro{PWUF0CA4#Db#_@oPR6^UCH@Lbn+Bi=?VtL)~^Ukvr z>Hr(mkLZ>7sN4>~LmjwcR)Ad&x3JmmNgh*$fs=13*tBH?Or|~ZyWZ@prV!2K$MJcvJ`Rw};bVV1; zNk)3Q>)h}Z)9ZXZ`nH$)D%r580cIpo>#mF$<5i{u9(G!KwldU#noni}4JHcLbXUzw zbE!qrLPzC>=m|Y5-%;QCW^1h8$yw!`IGDWe6KCUB*NTG9DQ(!1(J^RJN;2*rEFTcC zxvlk7wFpx%!IIoKctd9;!N7`KotdYh2W=GJlN_-++AR@Ak9F9;tXhJUuYbJ&lf#2S z)jBomGRZRH!Z}4kHA8Qau6_q)WNo>Te(C!cu(q6E&d7pC++RTD%%yK92|Z_!u4O4- z{LWpS8`@f+AR%_qR?`Cdd?y&*>QE>6(azWMWi;UyV_a-C_`7oP(bk5Q5F9HNzJ57q zt@?TN{Us<4L>*yOPYdnxBSGkE7Va4)#m?DBy&LY%I=q%z-{HGs(n^0rD5kMp?2alX z)nu$81msr5jQwF~|2cl-&$5Bov68?m^~2ngs^PaXKp$`7JKT>Sfhi;lkD?Wew7p-q z2jFQX@d<*zp2?xDlpd_b5*@$st!cN;c_O_AdOy8Pjuja{>vp(DYM6Y3? z_?*WBf0}<%ij!2l<8D%s_O#qU)OArk)?RG6 zyVzVqgNm^O;(2nL((AzaeIy+J`{cA-}mQ?ffU%!yYcdH=Qp}{5B=~CwkUt z4rY9}*^uYC3K`M%+%aDbf$0_%x}&Vbl@ANd-17|+!<-)qj45yLySUKX!bm=UdQ9nl zLc4}Lc#oXtTV_Bn&6Cu}1qHd(R-#qhb?s%{#0y#*m}U%yol7L2WT_Uc8xNkJB0y2W zb<9lu&&`{2*`JeNnIqd>*@K<_=!(@ZI<@z@&^qsWT~GCmXp%rG&C@eNlj0xf{0!JR zqWfjBs^HT$JA2Mw7yV~K#P#n|`h4K^MfAKUqlYE6a=IE$Nriw|pc$I4-yfDXw90ks z1%nuva2aJzLvp?1dC!w!1qr;D8FaZtFt7bXh-rt$=RTdMWD2YXpO2^R``jqel7Ql5 z!j)g+77EIt!B^a}b>;f*W~*A{MW4_tLKCG;Q^UiiXdMafzFj?@`#IS1kEEwerYn`b z|I3N#eMy6J5$3;6t^kSJr z$x^5z@88L2|MlBc41#!wQnoS-sFh?ple`bo_cQ~#4jNk_z7)4Ne*x+xpV-pK8Cl36<-%fveu2&*e#Nn8(<)&p%71sn3ziMrYK@FPBuy< zBSuaPwXK;%&p?gpIp+uqf>1C!$Q4yxncWY6b#E<`mjmU5_uyvT2w;)^3ufKb{mAtF zlfO4~G0ctUf)-0rZ-wTiwR!;wiY;WDnEHD%&B(F(98H#0vKY-m+R?Hf5H~wZ;D4wO{B@9ZhWO6{8HzDB(ASZ2eWBW3jJSYmyy^h-AJjR*G@w4C)S&|Nmwl0&PXmA#{5A7I(e&W9=6YWG zA)zu_PD1KF{hc1PV_XYy(F!^8Uff`bkrbj`jy2LcaWX92|AHACey2?-#W_NYc=i0&H?8Vf^@GtmyR$=FDm;xaPjI@-8-%`! z2t=6Z7zB3biLH<~F4XKE!o{8-gS=4YX+ER*SNUQq@JcH8;jvHBsseomn0A_mG*yyR z0D#8188)?yu}F7RD}`{_&9cLSHI2DE3vAi3uJ<~0nGcm#v-BjK!)zI`90HZQCj^e8rd>q~$IC^hms@3uwkSmOeWW&!Hog0m-}ow8JXVBYeL z1)vA?=M=dpaw83}O}oUnG6)w^T0$;4+7U3`zXs`?;{;iv%B6x|c17 z%DwtaZ+@+u;QeoQIearVuBb92uXmxQT=!jbo7V)QiqZ$$KZ?Z%ck1%I-kP`j0!+75 z+s^a7zLv5LGrVa>mP^FLnSQ{nJK-=Q=k2{{zq(-Bthq literal 0 HcmV?d00001 diff --git a/noir/noir-repo/docs/static/img/tooling/profiler/brillig-trace-opt-32.png b/noir/noir-repo/docs/static/img/tooling/profiler/brillig-trace-opt-32.png new file mode 100644 index 0000000000000000000000000000000000000000..cab5f3b12f26b285fab0ef31034f2a91065c3bcc GIT binary patch literal 114318 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%0AKGcl$PT#g2(wX_+cF=d`4^#ZBt?bA06neb&Tl zAkse&vN1U(3;CCxz&WPbmgnW44=6@HmwqqkDyGw{t<>-R;^`a=k|0TnNulC%Fbm~| zkY8O!=)D|~+*l|^d{*xOrNRT}rL9KOYvpYd%Ioc6IgBoUBLoP{I>l~>KEacB?%#vboZVo^jQVbX{%86jTSY+d@g=pb|hWH*=z;Z29!)G)26N zMdbYWvtqY{$CHPsYb$9bfV{bQm9I#I$X7&#K1`gDgVYPb+?ddPbpZ_J7FWF!%WHYD z-$mPi{7nc{9vuLASL3EHM{B>9omd3-zXsU}6!-@bo)(n^7)r2Q{oNazh{0;M z`7AImSc;1`%*g-Y`;JL8>ATgfOBNH9$|qU*2&tya?8T--GghG%#^!LxqG|E_GnPzmvLfARxuj_ z+Bd1uGz_{2#DFjbJV;H}`%xuN86TuxpK(LfT8EajG0(mJ!vbJd(WuL=r~FiYmG|Wi zRI_|K6E$9mYKq3?8jlsm1=?V9qwCw|`?$!_xD(bECLZ3{$Et7kqirBpG!K%*oas8P z3YK@<@d3;$?z^90_x@K7ZzmR}V zJrqX}r*ZEK8120?x2Yq2@VPw}-^$?SS5gGudOuf+vv0VwnnF#8n~(RdZ)1eGn3*e) zCl)X|b%R+Fbq$|-8nyZwH{i0v*=tYKHl7(l$PCbp+9rB@xPpi74T1pBu?D=vD}{eT z*d9p~T4MGn>PRC5mpsOF<^`$M=RekRao#pcf{`88zo~E?hVtJe*%3|;4cA2)MI2n1 zLFI(KpZyJNyhlX3K~ zF7+BaT$3mjq|%t0LNs%pWR{%91yahXS+9Dre4rJ;M2(grT<}q5)q8tjxI`!aaBnu&<@xwR-!9g3o`)^x6=@}!5yy}b z8jQ+cEf^DVYLVYxtE#j$S*J`%@!Q((ON6$an4ZXpspe#>*AY77+uhk9I=UgH@VCy| z?wJLUpMXrYN)kUCO`@sP95@vlMqDJi zd+6Jq%_-rc(z53CdRFRuxy$-@DJ?c)bdq{bEI4yS=0F{=a*X#j)tKi7mXxBT{fKy~ zInd@CCAR;eEfQyasBACY>TJyXgi#_4;)De*15z8aWv5M;@4`3wQzo-tm0-9>+88UWx6OqKaE3$1dwdC3uNi+vi%Jgkw#-L7}K!LSPD0VP8TL`4|PfC7CRIu!BXQ z_9OyIKjF>ys+(@EP5O<_y`f%h)*Hi?;z=WA1~foKhgB;YyZv2}^)89tYaNfHTv}d3 zxSx+Q^pDLxzHr_M&s(C3#)j_z!ph0Okm1=Kia_iU2JUQ9YkEMyO*&BLS(LZ=i_-Hz}VeJ(nfBt49Sdp=)0Xp2(iYrNIx`7CY7x4A=bx$5%LL7(+ z&COtri=kA5m{Bxb)~tfG3Y=o3;qHW|QcqIBM2{qCr9w!xQz07FN7rM4%&N~Z5T_l5 zqqTPs6<>U~N&esfhLeRRpJUQAduRz6aZK;KQuYObk$rfVe@P&W0W6 z%dCff2U@;HVG((3t`eu0p2L0m z6gienBItB_f5Q`UR+%M9^?Pac`ty~v``RVF8bqSv@V3~Vm>GOhSwf(=cj?ErhHqhj zdaa!esphmnZxSzZPi8ae0lZ7S#n5}rxr{b&*dq4VHYr>rPM=rtY9|R~!JVc;7V&XK zg?19!SY<{zuzyEqzY+_Nd|aqoY=Tm(HgQQm5QA!@uoT9d0ePZ2m~NJ5?B;1;lfJEc zRd+Jcfm=iX*aw?G-#k9OJ<)tnv)26y)K2#oqJ+z-_O)n>uhvu1bwx;p5rF+jvRDh2 ztY`i%`WUUO88D=7dsw*QxN`We%Q)Ix_OeCQv%Ne#))!Ye+j20QWH@;8NzZ5E_m8v5Mu$efj_y65a~9vC z2CCwwIj8_+O3;J;a(gwJdba$qJE=CKy%tm}%{BnG3o8yesmP-3Xy(WsZHKk~xw`Y+ zGxnxbc{m?V`weeTo}}K$>w*iEy2W7KWR4Eg;4xMRe*ViMjUr8+f>)Nma~@)~$KM|z zn~#x?&F#nkEWckyqC=TWLapGom-}K9pT<62XE!ObhTsg(6!AbZ9o*J3+^)8@*=D=W z414j2QMjsH>fW?qUG$#aU*W?`l`hbC%DGC@1xCtn2G)JE-bIJteP!hJ)hRqxVE*6% zr$c=gG@{9C`T=VaWCxm8<4(R_|)9+0+!vo{>whgt> zuAnkz6}8iBetE@4PZXvJw1WijuuP-N49;5#Q zfDX#AzfZ@Iw)!oaVr!I+1F>{vW+%WPg@$w!B>M2*Q>Fn6f)(MO>hGH0mpYuCu;yx^ zc^Xbi-Cz2&nedqc1KNUeWgE7L_P#;GpJE%6A71c9ckP3@`nt8fhjZxU$!}(KXzn}b z>JdjT0l`M`R`BH)n~XvQh1F0e4DhX&KU$smdC=ZZ)cZR_6A8v|TYj+3jC(`xYlX)l zdrTIoZ_J-0xw6#gVFRaB-jk7N-2mbL_}xo(j|Gh9vw&#PfouzAreL}rhoq`$VdGpCG zs(OF=tW{8^KX66Xm-EDNdi^3~tFFey?-1B&YQWp36G=KY{+APXTknAIj z!7oj)r+S~FAT2VDALk{!{z|BAsfF;i%94=LG{i5jHiB%LshFY6e;%q5({I)?cPC$2Ry?)FBe?QXU)? z>BT)bG1j#@MaXHBAWazb1}chm)ZiMh&9FkbEq$_NV2}P2X?n1x+KAJ z@ne+_`80BTF3#&g>pOkerAvnaPA~NZ;}*$Vvo%k)V<2UoSaw5tKI#f}*TGoE1qZbU zk`0j)5u{a0zH^+d3M}?poQTPJ=^yZchaLNw#+3{&M6<%3Qm+GvoqkN$Rm0{KOae$V(jAu?R~Uu3n0w5tH4mfg?CgE}`BvFs1>uxYE zF$4)uM1|ZTM!SR*M5FFnSvVS2^m{Z?O=k#;%_44omS+SE?sDIlAS?hZ!>KR?14Qk+ zJ;ANvh_VQ}$seox*a|X|7*oAjohNNXi82Ukma}OUf#ZG?qK!_KG>+$#uZtkf-M#rr zHMZSRXE**m_XNKScca``Amz0*7w82@Vq)W@S!lc@78wFR6RrCBE-xwxmxsG1$$d_vt-tA=@%qdq2 zn!*|TYoW|a+EP?*np7oc7ZByNwr*DXS7I3hJZO2*)P0h5VCY|I_VVhBw_(w$^O`8> zlS=uzsG?f1~6)Jt_`fjc# zV@DRNW<8Bu>x)rN z6fah6>+X!!Gx3PqM(@m#=owP2wQa#x00sCt^h{2``e()HSa4#4&?J0{$AZFIZnABj zI)Xqs+iy#>$=dzg!A7$|bM5(4Dwc3L!2Wi++@BomF9IUD{oJp0eONq|G5O7ZtwXml zk{*OzZ>|fUxXJjkq`v>W8qH|5?x5%{x7<+m&TytoN4aj(qZp(L6R@)c@ivMh>bd`i z*8&#;1gSStO=y2QagY@MjAXmcQp;3(0<_p?urS}bPTdonwv7bwFX%f;z&AnQpkNx0 z4ln?XF^tOc0+RBqQ$3^oA_H5iBvG4XKM&LZOi7fu_iDGhp8JL_h4ajr(^I{Bp(z)( zXYDan40%GO&M+9Y+HP{W2;bYu$lk|hN69O=9-^s=s}&Bp5IJsAx_oI#w^x{AvRt0>P!_zJ?pz zq{g6%?lkHR9uG5lkoS+ybAl&G>0C&2il0EF&f%@<&m6zoLcOJqX$C@u}!!l?lZR3TNbb2emz+1SCvcB#jn!pJ$43Ep4*N z;547oIaMQMX=GzCbA~ij0C8^Soa;+!fXWruh8<6GBW|21OvDAh*guG@Bauq8HLKfh zG?_AVlN(cYwv>d%8OvDNw&&Z*mK6rvf6^_;rfO%UhXKDL4)H7sywonzwsfgwYWnF~ zLIfFeV_*{w25a$YD}}(y-F?`<=!!C~7yuM*OD8}QjvKhW9Y#OLyDbEk#Y14YqpZ-m zPsiUS$Yn|83e#W58?l0-G=VlPP=*c}=`n5FwCT=q4(d z75{a$`1vzX)QA9!0WhmZrF}r4AQ4NEGS}W{VyPg0~<^w1mHZ5_-aS$k8ax zXDlu%&)Py$v-_Qk=-O!ufqEITv;YLr-?y6t$~Y@d7)CGbZG22U(7RZSUvGZ;raRN2 zq#9-+?&wQiIb@2d=cXpVRWQXfEMU$;Tp3a_5rm7q=zHy@-6Wd0K zsU#(=y}3E(Z=Dt=eCBK)z5ms3ldTgMDcmQ~ybS&dcTUgb8XBQNzHNT`YA?joZUP&6 z{8P~wo}qs;lh$hQ<1@E!ku|z>d;LMjT5Ja^1@L?fxO-Zvc(Sw)2imv`)@9x`V~U2W znb5DJoGRaTa=Sn3m!{an9TeKD*S_f^6eHmmOW;Q&<@0J>?}wUAMiZAiDL}E2T)dt0 zOO+CNiRhwXekhWUucPrGN&CMFh7A4vvGVHAcZJLnMBqxQ?jijdjn3pzN3?lgWo_Pn z&B&Q#;kq3KiXYg3jH}zW;>ZE5=f2@IW~Mcx_o?$HTJbC0BDN~=7eHbC(v(I82JWPT zl~rK$){Y|S-as}VG4*u@dD>TE!*9ISUlO$Z+j};s#m`^-XG|M_1UN|*bO{Wo$gAju zrea!7N;e|WBpwF`DLr9yjazE(&+z1E0^oV~q!Dlk-+AUGXKOq8AN?GVS8|ziFo_5x z`!0gT2{HoQaKP*jn24~`CuGye)6nKbt4xkX2$FvC5WeTNx*=uv?`utQ=^fZnQ+ZF9 z{%(g|oWxFXwxh0cC33wgZw1T2p>(YVM~M*WH~l@*v%c7Q&Er5PzzzJR356h25!{QO z#3b+%leB-lb|{_O79COwm?e1PN!?V(KbIB)E)2squQ(S09G6e1kzXO@-A8}9L5wd$ zm=J3~K3z~t#siMq8)l1-R0;57>Edhb`w??e-A66_l>!me#udP$e3HzM-%^8BUgBMr zHG&7e_1U9nohxs>ZB&!si&ovfB`8w-UabXq(loB&yy9IkS3(Xn0*T>(Z0ESmb?wp+v0xc4W<9e@6{lEY_ ztFDgl{Y3kN{J5O@y8Jnv$_20 z>qYPT-q(4rJBNdA!P)7 z>HSqraN&=ayRY3~#1XHM>I^h)p?vd%M_-filC%{nfDa655==7~()U&B* z^{b?EOm(X>PREKmzZY$1oUQoK%dd+<;8PQ5g5}M`fPyAP55IEi*a(dn)yr?EzID4Z zaTYf>JPGTxj+AJb07XZC&~w?Xe6=j{e~p565j1a7VA9Lvk$ zBNK#D8=wt(#{I#?6!5Lr+qNNLQ zxw#9y@R5{YOYS3Q(O|t&TpN;Y=W+Y1TxgqDzophnaawYnt>>1Eicaxsq<{PNeww=B z1P7vUbzC2H`BuP{qIZ}{H9x<_V{3BncoVmafqP~+p9nVv09n!lvKraVvM$?Y;rj2$;TMjjyHSgX# za=J_r?k0eHQf;RV>)t1}8!9IbC_4wv zR~}Br1IzS?e36SSdlMR>*^{W$O){EzVdu+m>Gs!XMupKk!p?ho6lXh%AA@~m1ygO+p$J2(2TLD&yIoc+f`c4Y>1JC-_j|G)Yu&IleqAi81?hB9w8N9 z4#e@%Gk>h3+IDXj>b!6N$qgb@P=E(p5zhnlZFnh0aVzQWqFUW&FF?_sjdPs9x0J^w z?Zzc5u(#DIk&P(wM$DQf6#crcx5o$ji0@Ouf{iF7T^;Wvfeh+$5>^e*6y6n`m+y9_ zbz#-`BVGCN3ER~o?`pjh!*x0ni{Y;HiE~pz=1q(}K9$J{0*Lakc!(mC3(d7`xCpSr zn*B?jy*|vjzB?~G`QCYD!7DWP&YRi!L&)nA3{O@H2F2((P6y2&x!t7%Q$={J+`1bJ zpW>JNoFf|89e(QvsDO4U(!7`6AK=JP_?kZx(NN~zMi9bb0F-YgX^@9smZhb=7@oN+^(*8}Y2jH?rC&=hV!V));AZZglcx6}LlkCyXt2KZa3e{F_y7GYZNK zii#=59q=iTXcoiPyp|s-QDJJ2dZ)e~Fm7K5{yFxF#(e zE(K*CLOVNN#h8PTsWrxksMhI6yx(n|I_7&@(dYwu zK30wR=ZNND@h^V7<12o{Ol?bJK0WVd{kP-Qjqk@pd#5pv11njTKa`d)v`N2`i*nhn z5F9GnH3@061fra?KYx6nIP#Pb3@QR;31-rM!I)7YwTrJKncg<@jxj$&f zgCxjb9k^cI#uNlbiqsM~_*k5idf<0mxX5Z@vPPzKLC;beoeA>VP*0h2rlp@oy{Ox;oL=u}D0QUUT%Gv64{4{-;cH?ly;fgQV zmMbAt5W^(QFCaT>2ag@}UV%aa#r$o;nA7uca)#gmv@Xd<<*WFZX8U4ZzfH7FyAIJg z^gfd79xO86^j7sC+^jxa6iI-5EwBeCxk2bceuuGDVg_BYjPDS|#ql9=sE{(mrbjr| zFe<8%hIcSJk1Nj4Ul)rIGYnDp@CAqO;;$WpPd<95fnro%*PeV0sE|sNd$Og*V2h}8 zPh27%GH&mf;`;GjQQPwlcfXNV%vZ5V#O<|7JJXFCT9%NbDZT{qaCF+ahbbi3zt@)~ ze{CI@HCSJOc=tp+Ggf|vTT3>4uUcC66?Ub&2#b>{UUkmouM%w^VT)Apb?hw~*ZP6= z^RYtM)u?{{mjrbzc<)i&MPx$T5A&j+sl(~BRUrhaQSTZ75+}+G>A$6VFD5d) zwR@LaTQ#o!t+6Rm%OiW#<9+S&!w=lb@`*{UrZMInY${X9 zVSv|oe>9t6Z&P-WrifazS5caL^;6CZ?l|vSd##GUtN?UsgPa?vzaTKexdBnRB_F(5 z^hL%P4gUy?4LTIWLa+*Y-u7~A&P-3B4oU69Iga@-U@j@Wk2&WXRf;Y$bO_oUDdxE; zR}Jj0oQ^tWRb*+`7NV|aoL=Y0xS?K=(ZgDCn8mAwO5>FV1ISFPv9%b#qQMVtf|y@(1J+4tE1iWzxc9WxKz~4UHRU zB3_dI8yGI7{Q!`U;{kk>!{44CP2kwV*^knSsKQu?TrhHgfqq&BZS>x8-}(cz{pi_O zfyW5THAFXoZi9W%)3VurASi3^TJI@^sFtTi;k~Q-)ao95U=e~KGIrKY| z?B%b`0r5|ifMOcj3_u#g?39LX5?2~Kd1cMO{Y(IuqL`ZhTDVh1+gC2SKvm)b!WPeA zuQusasn=utNn7vtnH{xotx{5@5X8w!8&u&$G}Fyt)N}_xgCp3fy}Lc&J8KM|Baevl zfs%xb=CN4d9Pl*+b#GdSwg~z8QOQY6TkYx=zZ2`rX~v5aVx2ww%L@h@?WWktV%_Rp z`amau3z)Ygbn0NK8UXVfff`1SkrBQ7@SK}{Q{>^lgs`<91Ns}F3%0L!r=Z1fGQSICjve|qZ=SB#+78?@lX(uT`H-M3G<754k6PJZ2Oa+ zs)~bmS14Xrt}z3(&s~u_?TzF?aJcV=2`(Ht8n&n{+kMbhFI3Haj+k@@C`c{l>>^6t z1?^X-`6qp`LCqYaWgU6S?M{N`g3e`IqfxQU?>5yqaQ}H+1KJ2AKWY8L8t!k}KL@Hb zmX@)G&z`v3bh9GUGN=!S%9yv)(y5AAh?;R^@Zm~SFsbumCEFay6Aii-5ZxS`N$Q^u zaOg5h?zC8DmEHgR;%REwyPL$pjsbPvbSVv9VW7m~V<}vAsCRy^<>QISoW`Q1 zE-ERxP_Jj}_GBS|4(nJ~)R~j{13<1Hdz$~Vo^+Ev3+NtRwbQ`vpHhjhVW?!}@$G%8 zAzqXKGf(Xe=1v!n5jy(FW0j%}rB4bOX&SP*9Wnn{EY9`>G*$t!GDTg9UIGF^n)tn4 zldSotD*4$u?>9q(JZHRtTcx@0(kK{UbEWi@{-?i&Z?g&VRi2}L|Gv8YOa0$f4d_rn z5$$08q|XUhhK0j`>WcrZAoZ?}lF8(%lhuRv@fJten|A!$Kk2*GD!|(4R1dh^5g18g zkbC9>t6R|xCmwKRF#>Qg8g4#;>b|{6C4xI&a1)^B)z%@ zUM7x3gNO+SglzUb=g3D8Hpo1B^0iO=d5$`ecad`6@NIi?3#`@+T*`jNH$cY?IHN|GWvJ@_%lD`G?AlIG|2sn_ejgBJ!}KkOI{J^II=>{-dZopvHB8mXEb7 zDuCB36)v{{_~1Vfvp&XZ~sXEQu|?9 zojm=&^$vh@w1gOO9%-}A!IKM#Lv_ZnfSGynmnxD>-d znz9$M;eg8@sts%z`2rsrM@-OhE^Sa?QsvpT|M#u__d6NPhZN>68}Q2t;{aH$_pewM z;5h`~VNX5=Ah%>dzsG;yv@M1xD(nWVUlRpf#}Bw{`8kpb4;36Qw)^S{9{wCU`S&k@ zLW!rvimCs(Z~kvdjpW6{Rk2LL{Xef3a9`uegyh$@e`ah@BNeC^n7wRUJ}hsY!JNXZ zjAKnl+8>kiE=Wq&9Q%8$#wRxXU+)#eu!HcjU%xnZTR8AtbpJy$G_%oo-kiifeB^q* zXgj~*|Hu1W$VXzsmlYXaARVB~OST>hdKB}|mkcok&aT+LW}_6^Q&Cor>#xp}sQ6TH za?gA2oaqeNp-1N-x%qyC{GDRQPklEt#|Y^;if&fcCMbdmwOOmtO>>)2{QS&T}H z`T#L_Z{F%?ZNPHuh?Iw%T;yF_dO(DE>HVu?%M@yfp@pc?>3DPn$p7z)t^u1waEDj3 z(5-2HTRio)XCY?Tf>UTF619R$83)!W3YI}F!rrAttuSRO%leAiFO>sl$NhhteFapM z>(;O$Ei!|Es7R-zh;)~rAR$UKln6-o&@Ck;(hVXgF(BP7-7Pib&-21J2 z?sxC`{&Uuz#bV&CXYc*&e)lfXiu>o4nym^PqI08HwzBc-8>M0xHQQ`9T6$kD8r<%B zhD8QlW$ee)a}(A(cws@YU_>O)ZVBKEl@K27_1pjc@zbM|1VNJ8p8IJglHx#&s72}h zUSYHTC(I#bp1eQORUTaRK!#bWfFSmgPz5X@Nnb431K%z#j_bc}$PD0SD@5p@=>8q) zzyI7O!8!yG*GC2o5>f-kp0qIBR*(ZuHWwy?$~hWw{ewc$l25J=2535!h7!~-I;x( zj1Ieo0~vM#7Pi)^_s>)O%&|1!arq11sp~(GLbFO8_f18z@nGheeN29Hbdr9PyF(^3 zU|?a=f)2~TU1u|U#Gn0ddKwOrX5zv6c=CW-g#yn8qK=Vd1Lz<(n$^sneADrNPRS-X zrLJ#1=gdPGhy1u!l@|2jnZZ#)ca?=1Z9*Mmv?GA`9-(-&mkL0`bF$24_DK@QYyW)M zf1%wJ!>=PByajkAl@&>df|C$@rU%&b2^C>IP)?cfPd=ed_T@%~aZ&h9lErunQ5amh zTphuW^6=|3-<#0;rLq904Dim`dVB`%wBY`WM|l1tkBCVFi|;e~@_(<-s^H~wLeIH) z)a+{IW=k(QULCLsg#a{TH1DrRz8ASI_iOuXjRF_clkdC!A9jaf6c{+iG4cO{ct|*T z?t(*w33dI-Bk{nTg2hUXDruok25&|2m4LW-4tsAq02pMw`He+NbeN%Sv>wsJBxDP46NB@&-|C_`9d61Iw5w338#;m;ve_$mrc{mcWZH@hUOK%K3 z{_iaDnAVR2ihM50?v8x^ruD&p*bo0F(z_mFHR8cm#KQ1k-S^JgJ2c_BG-1jH^sa_G-*&u@uz6&QZi$#o?(K<- zAcI;fE?E5Z!bG@Xg6~1RsvH-2lN;jD=3_ z_ksj=UFZU!s5OVatN)FKh$=S|*4K7vGjCSO4&|CxJmvvFF|}P_4S&a`c}Dm|6)gYt}GDOcrMEEH$?a;1*si%{mX{-L|{BN8&!dL z%viwW&m!IaIR$;SsUENH#Y)hmhA=S`*56Kg6#Ul(q2U!R z+ynXi(j5bi*c|DVb5x?rw(pPUTuUd(NVe6yFe2vG=zE}=p9$zci#X=AZq1lipO`tE?q1klweS>tQ!ZE|cKfT6Pei*vUoQ^r%O64UP8d)roNy@t?A z{9(!VNkFW)QBTaX!}Y13rYH$@>|aro|1glbu?w3E%IarjG4Z4JU-7s#A7IBmhvSe; zellfOYAZEJT9~*bwTdtCF+FqmGRdczc>8uMk2qjUs|0rb4JnAec^%R6%Ne>G(tMM; zo^g@;-4CD!U4{7w&S%@dur?7TpGZ#0|88gbdE676(UHE{#7VA6yh_ba+?MY8UO^8Z zE}*WRtT5JFJaX8Y~~I1ZzLY{5gV^RZj1WaX}a2z z{PuQFsb}}?wM=?BC7njkw?7OD`b-0GEroC(#C(5C`ol?)IFLr0UQKuHj)c9IUfVp6 zZc%8_LS_Hhm2u&{3b;VgNe5L`a#%3Un$VT>=?~D_Wwx`bktPzo3KFG(P+F5{ryzacLb3~j-{Gj_`l zesA8&DAIk=FxZ(q=&k#?fw*>2humK1*~EM6nSM?g`NX}e3-w-gU~lZrTDV^dj*1Fw zSQhD`%GLYA6ZFgpR|a97UoScIo91Ul9LmouR@v0GT4lUF7UtI0Z-5lW15N)1=P*W_ z;lFta=Yx2TAO?lr^v{QQ0bt)HgXjK%>7_{FX`Kea7QN z=xxe-C`Olo7*nNy=j8oH9pM`VLSNeb)(i>mb<6&Bu9L)ai}59gI)7AG1dLME2<>Bd z5ctC~&@4F0OK75Ss$|$>PL7Za95X>ICY}woM|D2cjeiag>OtZkuT&GfUFSwMG|nmq zePrrmsamiiX4mL5b<<7l{AAkOkIC@1(XgFgTCGbvrZ6zvOtnvb)+<{rKlxtwuM1qj ziB-q`4sT)K|0JwoZS_k8Y0(Plj*j+c3ZeQG0Yjafhc2N2-(GXWSgzl{;wtgQOjSyF zq)^XcflI|h?dp<(k0tsQZe6dph&5;WnG&PGHBKaF~L55>9k|) zm&7K1YeIqs@Mh`6M&3*K>hKEjUl*K`5c*&0$y)<(`-D%Wn`O7BKXJ(Bu*>h{ZcU_+ zpeT|vG+`Qa|`axwrSb2pP2F z`Xb$5$L2MH61YmDGZhPuSK(f4J_a36=dv zfaz~+d!c8v9LTU;ZET2v)OlPqz?a6q_%eEs=L`-S^{ftdB8T46Cu3-OYm>DumH2xi9ks*TOF2Z{a-N zmlP_uQ8lYTkG#)PdN21mE`RYmaT&h^nRd><#SNmr956Lu_J`1dY z0L^c>Jb(-K8X(`h-^5Du|8l9PZefXZc-VO#Q4`6x-dj=-%y^byt-}FY?FvXRvQm@3 zxV7xceymkl461T=p5r<1rODs>Aw^Rhx$SUq*W>_kF{3$|A!1u-#{6fDF-7sa6V?C( zF$W1zcB#Nfs`>va}xwbp=etD%>XQ%?f-lIj?`$OSb1n2Ut3z;X> zk+x?aB@h|9B6>dH$e%0QD%;VNe};xGUhKykEtaWu3-^TW->W%gh=;+dD4hx*?**z^wF9tlZw8OOwX`5zBxb77vB@aA?QHzT9El z0!7Tmsjxr!dnwU1Hgp^w2-!^J8Hk z2zq?3+f4e0V2Ga&%XOCT&36dsZ&KVQF@DJSfPu!JjohuH5;>B)NLHa?tUIP@n&@r9%<$wa=~IqkGJEob3JI$kG>&w#{2+3Sm+bCZ)9s7=InRN0Mp^6eUe z9<;bUKgsWc0Uq)$taA8gFM$6Zr{(ZQmSSl! zayKbBPK<_CK`Y_3d=g=Ul^mxlJ<-_7xlEmwf%4Sq`G~({li-B#RF6-HP^uwLCnTfP zi{;79^jONag5Le0crR`$%7dtW3*W_y(IcuR$noKMn{HMC1oy|3f(-_5C#oPo)QsH; zqv+K=Ql(Cb0{xMse{>GE<*@q2z0~%cYdv85c^_^2lS?c6<`zGUuTPcwc3#3Fk59aUD$T zXQZjX*=dV=ex$e6g3Ce!v+Ka|1AQuVvY_~x<7LfJW8Q>;cPRsY`nyT`iOez*n~2&~ z+Xc!LN?V_3%8E*icq@PZs-xnc*<2oBl4WoCc2uFF{+(3z51EnQLDE*YG-GuS;PoXS z+HSuG<{%4@1)ljops=ge!4B&od!g?m`le?pDgqW37W9n@39twJCGvy)UFNgmb~k?5~$VH*~l3T?IkQwon%_cN-IDFLe>`u zI91=9d|H$UG!}7z-$fXsMn=_W1Sq#pxfYJXk4GZ#MQGPct@ZUq8ol(*@ko-X7LFs_ zfwjz~E?vxsYzXmVQi_AJ%o8yryZGHt;d}4wEVa|sua}C2diy&!7)-F zj5@=p4F;5Z^l7H#Q?PIzrIWKt)#JqARl0aOnRl4&Rv7i}cW0ou_h1r%3<^CJoT0)C$9rBErHiZOKq9+l=7 z0CD>ry#Er+ztG*0X%3(u^7Y6G(5>RoQWkFk-t)YH2xfvq+ikmpJtb+qA=M@WpkGi7 zG6OAD{YnM6wTWvj-U(C%OSI&~dD7omy%9(PmK1Cu8<-$8*+$5*oPOWSu?{$jJm2N5 zIJ%5uZVmT*i#~QDc|%*DL;hy^GOdbAMEr+LlSbz1kE$#xd+!$TM;9W4e18*<#@!F8 zRb0PGVf4XyKs2wpZK~Eyer)OnL)TO@SSH4Sj?zd4es73S+NdBk&9A8Bn4|v4T0wAO znm=9_D^mU<-m?o2dkGOFvUUL=1VD-1F|D`aZ2x-sc$-jK3X*9Y5pGczWNWL{b6 zMO4Ws9BN+kV%ltiAiei-ydz-b7Vg%nOP>%&g6FJYhnMh1cRD#m0s78-AHftvb z_E%keJ+GYgx-w9MMR5(Kmn8!`r%0q(TdlE8t0K*W=rNB$k=S>~$+;o_Op={6jDL~c zJVuYEt1a1eYhibv`%h)3J{DkNF;t1qajVaDBd6xCF@qF+QGTfFVHyd7G{0c>9sDTv z*cSl!f)$81)3>XIkhw%|T_z;Iu(~(YE1LUt#VjE^ZLuA76I`yBkc+lb(&Nw!M~k%# zlcQ`WivMF|)YV*{9T)f-Np5CHG(+(tIPr$=q(CMALRfw3R9mD`r|TAlGmrSd+)>MK zOl?~%0X>(&*QkQf@e>vMq+}Sh>%W5>f+ot!@CMQiF}fnxb^UrKG_=*Url?!Q1wD*B zR(afzs3`rLr@cqbzX2S7i24^)k2h~I>|Fm1{1_xXegRzLh6K$J^~xtPRS;xf5EJ_L zM2q^z3vnZ?RGL(pmR?7w3nQdhi<-Ils0{#82x$vLF~Hr$QSdPQJ;d>OSIEOkT3=t* zo7n{T46-$3V;$`*&hu?xqP>s5t1U`)HcG+=3S-8N-VM!vsomU z7OgaqudVFlHDND*F0W?gITz<(5g?WOk=%S58MRLH^_r|X4>x3;H@?VJNB;516Gq0( znacHGz%D}=%tL&%@-EM-T&{1;yf^Q-J(#y;pjk;K34ZS|_3)EX!?LZ89~O_=S8_+A zowgr~7?rkbBhAwipKxM#m=a0n&4gH4R90lfQ)%>Qy7B*!($l5dRVV6=1$qJe0zfpF zOPJIA4j)M}y)wO~G#_P)-h}=p@vNNfF#jRbw=z8lQf6d2z$I7HjuL1@ zaE5#zvn0lT<=1&?jwcmJ_(qnsR6Wp0q@(xF$D>)(j=TI9N%+mO3Nb2yWXe##V;igT z(q;7G-3KJwTNxhXltvjy>^i?yBY756B*-WQ zwQE`>i1)i)c4UHCcaMwEw`}z4+B_ePH1^4!keFbZ$Ox-!FNYJA#K)8<@0{Y-uv(74 zd%;I@jCV9d{A??fqU7Qw2o1K&GDwIv>jc4kG>dh;8%d(+=ofDs(9sM;A(Qc`?Y-;( zz}A$7W^4rjz);WZFRuq6Q;(*FoMawx4hH}q778c%T^HEN1)iJ?A1&UjZ{-e_|2Lll z&2v43J{8$Qz`{Z7i6XHNvPyO3&eKSFvLVlFIf2zp!7WLjFDb&l>mS{ke|po}I+2h4 zzKqb%9Io^B#NuMAh4?HgZ0dZ($6H4sqG!ciK2|gn3>4TjY%7GRH`~qfDJ2>$2mh%f z3N>fb0$6Wa-s#kU<6O_{Z!5ipV56>)1-t*W=uAQlh{#yWw)o?SQkvrA4Ui@tod(UP zVZy6~SWW)j4E$HC(62!X$V4|trJ4L8VJAt=cunn>z~_^Eu!>El;cMDp(BVJGzEoD6&X$8WXT|y#6EqTy?GnVtdk(P?u^bB(;R_C) zirBLsa~paW2-%kh2Qv`+s6hhOGU2P(hI6~C;&{r1eu1lt*`Mz*&l-w{H8s`@Hb(H0 zf@vC=D(8{sh0(W`xF-|Ho#{R^Y||brz6R$`P#Z`6xru-0mx+JJE%J{Ro?G;_c8XK( z0|IgnpRl+Nv6H=E9>lz(vc5#p2&-+lPg3XcMT{Q zo)Bn}tnQw~iin-I7Pns(<@54LhzQA3Yfn6IQYBD_%@?uhIn!%|)X{1My$C??e})SQ zf&7cbH+lZdercYkr^SMCpFJ_Q9Y$3Dfg_{k@lX9Z7d$9rAzKH_6>?ec;dttR|GGMLJksn`_Dqo9?6n`so?}^p==gq1`Xi(NI8hv{RV1`=`ovW31$f-8GV(8ZsKef|4{nUMiWW!^ z!L~J)6uP`>kjdWDOy#zsp`)EwcaqbXn(2$LFY&^fx89OClMg8m`#gP5?jbdcGyLu5 z48BM3qloH8BZIqJo#Tr(D%5x2-n--i&*AwV25oE`UQZNMBMoK z3j*dU@DAatq$|_}E3DU{<>tfLb=p-nIpRu!s6JAMavztcznp-5>{{2WuD;}o=2u1T z={38xH8;}mup}zb!btGvA9Y(Ia8o{Bq&mC=LbMS~&&eji5n#)f zFLa^|f%;q*^v7uwGKl`0&T5fw2FIsm``|75g-;%ZROzC-(?kz)2Kz39+) zQ}&7&%P7jDK~eP?GPYN$2N*Z%adDQO1)nQLw~!&JL8IYpqXX4N-=S(XM3YG;ahb)a z##ddXO=cf2A$#w-83}5KLfxvCPlCv6u__G7f+LI8Hv4mPsH>{-g-Yi-x67a^<3&@a z+Kmj8NFlwpp=h6hp9Xy%?2o&Co`0}S&_&rc`>MPMz)kDFjz>nCHR zx{h;FexMlCId0wUy0E+@I$_TGn8#&9gv48IUX90%>%+ZBHSQ+83)lQfu8?{cg_{1o zQL}38hWl9D@68WkJ+lmc4RY5qG!Jb2#h%3jh+h?jiXod{EVU>G>H$sT--K_GZ)@x5 zTrYJ5+tP8jkwq^%?Q5-j>^-JG^p6OD-z+*7Lq|?_Y&lMdH;F*@bsMizuP(e-XGn`d zmYNTKw?cuR|Fp4x+1dLpEmWhw73W-d?6h5+HKi=Jv8-cq<*94>$++|s_C$92k0iV= zKx^ndMDj?kVlsNS;n)CmVTU=7Xss$SmmE2s!oWCFsx~kk{y1&5v10p|=hXGHqm*vb z{L?smHnUGq2fce|CpYx%FEqZLY>&@=dgy4L-=N9)?8>dkb8Eu-?f5sJL)^U>dFr*a z^b@DvUVB`YcoL{r0z_x9FLA%X4cU^S)EOD|lhE%#>%A40z^pcxAeLzRXV1&CsiXnBU8#=;+Q*M9)p=@VpGs|7b*_Rh7FFL|b!iH>%{Dy5N zj~4?cgHLE6kH07-S%{y)6?xqbh|0`|38|=;KB%h{Wm+!=$#=K!cYi$#*vG5Kglab# z#fp}6*}5NF*@WHckYh%Ax33{mg~I#fX(ISCypAK1#5}U(n0!>kjY67$PQG64-YqkILsNE4tV|jjlFu_Km<>GdDt3&cq4G%^jZ zWBSY`p7wH1YAMKznRz!Zo!sMbjN8^Y@56f3CN}~Fc8@QD9E=PdTnrT+|d&;;Z zb}kW7&RhIRM_U44WfDh%IKpvSt0A>^u;t8KH}q^`yH=%Ek=lVZRd?`-K!BL z5tNgUR(q&omSG^`^q-YG?8}$y7Q)|N35}<^&fmi|VII|lV^@W>#kU6C?3&;85kJ6z z%qxQSh#s6KiMD$`Oifx@pRrwd+)3l3^l`+C!DFTC*;*r7XQP`->(ST)C-5EJiI1cd zLD&%OE&qz3W*VPDjs4hw;gZE(Hk}eI7@nTX%AMBsrYi`{6s*ylg8h*jInt(w4nWER zNcn>Cxx&nRkq%;D=p>v7FQiSH)N?F$L+@yum(Xda6|OC3bx;_1vM=@fz1RMiOOm7ul?fNd`rGA|8RBdYNB_3 z9`Whd5{>m6NeECHeecxYQXqoNQe(m!|wu<_(g#Wzb`r3JRtD#e~Y{O6_N^`!rAMeLcd&$s@#w{(ShTy`drmb7N{5f zbUS!Z9O0TuDl#ZKI_I=^86(V{Bv`7c>~wEhzPy?J`zCd-)%16udK4Fp1ry_UIFzhj zh=1LHBpyBQu|p&qC=};?u}|UHb}Qm_!R>LDKnQ@bn-<~7bFkY+ zoaVf`l-9;bZaYC2-xrHf4>=;-V4-P6dgQtE?yDtcCL1kgn*c1>&kC5pYH#>Y&TQ(R z3m&$7YBcmj`o9Up75G+ zA+aIOo3n;&n&R4lmZpqEqryiovF!;*gHvU2=P3G4cW@0&s3(j5g1&jt_DbuRcZ>AC0G^D>paO7{JM` zLA2$qI#R88{S!qrPlBmjN$v&M_nmu?eU%u0+ufmISX?!7ve&w%OLo|?98shpqspXzfUT=Nb4I(*V7M)`HpyzreBnI7>4l_d-r$ue>g_5!#w*& zHOj5rn#51$YIx`B(YuJXaZS99M1eV{Qc)UfjEavJHEYR^uZZI3P(2^PMhpgfCA`RJeUtdLOn7$b1KL|fS0=7A0c8jb?V#P8)Qy2(KU_fjx|f(g84{` zP>I)<1v-fEZd^0)J}Wxgf;IIQRY90-&cHe2Do8`zth>@hQY$>R^TFPWHoI}09s$Q5 zv-a{S%rD;E4TL!TU`fJ*F)3a?3c&TE%X(q+DEI+A7yAu@CM%dug&(45y>9P3WjRu$ zr-0-TjuZ6^Jn?9i*pK-|EiN{s6dX~*J0?ENE*%PiF3PwUBdd>AVowrXcFyw=!T={? zUfvh*ejP-}irFlnUi-isIT?n0=eXYi@-TXi7^E>KjtA#(aq3pS#MYQF<=|TLIxk0h z`yn^-)ceFvLRzW>4bX~dY(OA3#P%{{wDleMU7;~E#y<%Tuw8l~Ko&mYV+J0vSIgbxPjnJbJPgK9A zw!7J&D2jswu|S8oirSbcvVJquM$FDY9j?OD5l%xZ@gULG8@;1RQfsZw6?5z5n>)Z5 z2DDThU^y`sZA7_cemJu<>}zM13%?UP_$f4de?=6%o^NK-Xoa&b1s-_ zR;#v+Yx8v`Myu0MdUFG|)^iCx%^2M=PTEBh0UM>%a0&=C*K?p&M{l2-A9L;;VU;o7 z2N+zI&p%0q{Gc2wZt-quG`d+Ad?ola|EbxYo%dAL>SG2IbcsiP=YPwNM)P6qW5M8# zj}HS8ZqQ-`ps5{%>Y8%#fZ-S|>>}xFS3aB}ID~NvdB_@!_diuL^e!yD4fq<9?@wn(oAG9~VXi0QMr+;e>eQ*EL zIR;v&OWXQr3uil41;T?!b=1kv=%a2Ig)+g2xtX|Rmk}AR{pCP^%Y(QBRnvHv52P-1 z#Ip)bm$k1T?Vjo-hEgPLdhqYgBJPI*2hk=CLK~kC);_7TDG&`M-Tt=662Q-rp$EC9 zmtVXr9kE0VrpGOwUhCJTUy56J@`k8&rKN7D29ZwvZRdv!bE`!Q>e@@d;P1JSZCo2A zQ61v}*wY@JHfGB}!`baAi`}6ajeNULLObOq4a;vt?xO1U*7Cb>oriF3Wll`&iT}C}RFs>3OL_+QfLZcge}+BZkX?A5`N32pT}roh#kvb7fnJ>Q@5cLi)%(Dx--sy4mMjU9`4Q%~i* z6EGtSg0zgRdrfUUi+%g;#ShD%9WhkjvRs57)1ou3hh(Z%NXXiNe)fJzI0ZMo;~}^~ zx%mqjH{^+y7i@blqiL-C-MB9HOV?1{$=JgO-!!c6f}ss0ZV>z}=$IOk74d28GT;*K z`P3~>IV$|5W~;PdV_b8A!q%X{*=Ku7pwH)O>*jqDEa*yCw+`;nsXq=)w{wXvw4zQ; zE}`W};4Ls!tKUs$;-TJ9Wo4S#fi3cKmIy8w(1Vvu^8J>?YfxZtzRDt~$lh%%RL|zGC&0T+TTgn=5 zfJ`_jOQz{S`Tp=0^NZvxlPVi)D%omjSyT(E{|$kON+L2qoI>tNv2jOOSCn7WZIzc4 zfe$h=8*UAoXY4Fa)0A_^GGp?SL$l?8yL{gq=T`a3(2QEPU*5J%6R<56T1#x>D}Ph* z4sl=9L@8dQSg&C4$A0R)6LeNY%|TCDe=m0v3*CK*4?*VTgFMUVYwCh!_5^xqH;57v z)MsXg*b5<;s9A&h7b+ymd_OBZpj9dqgQCfNGGiPJ$?4t%dq=H7y{psE7o4tx9}olj zj3*m3AA+!g@&r==m#<(?CYbnW^{n{?mx$s_H8>*FIsg-3qWjKDdj z32@v|+hq+;O!||#Wp)OId*4?TXztxXq4(HLuPYr={y4@3c}oR?%{n4mm)ow5!LB|c zUa&TnI(w{16uK^VOoj``N5>OPgLTt9Y|;WR@M!AQ+RyKXIv61t*%Vm3C5FkY<#~=H#9>`A&x^9`Vj;HxA_MpzS0UY-}gC!RM0T{S06|<*?NT zxEAg;dMJJi>czO&aJ#v+z2{a8=~O9S;%@R}1yQx7msx?jfveWb1baWkh9qtIAmKb~ zi@_r6(Km3*Usi?KTo{J@0Nxc?JFoJR!@QWMMuBYZ2fW)mrPe4seMOCJ5^?rbzpn9% zV#3MAhiWl=_BvPFJK;H&olyqr#RT=RG{C`Td*bAF>I=-r@H%RZIxDqXd_0JhISapO z<=L9p;2}X}-$LAU3EZPvv~i&&ptA=(Aug^P#j~~r6Nn2QWV+o$H&wq=nb%+!uuqOO zG8$uL{j46EALxMDh&enoDvFVr+8pzAkg?FJI}*@^k4RSS!Z z8kaWDNJX>tnhXz|KJf*B8 zsl1;}KkVo>U)yexz__(>lOEqQ=4mSh3TX7U3YfreT2r?P>x-K%l^sW4@U1vok*kD( zdMveaV0b=}BD;)drplJ;<>vPNWv%>B9!1oq=jo#E+Ng@_jx{-b3}6Z?UM_67xyrI| zHyoDF-bFSEvP3>C?@-iMeRHrFjJf^8b1Ag)Ah$Hz6{04x;4LE6s3<8Jt9je%*|$&7 zH09SK?52Qx(>R}sb&eALR_|$JNl$<1FkjMi@mROfkslRV8gMrH`I!f~S`eWz-qu7> zTnaGTJO8Ljx}$RLruI9{uOnv%3{h)8K@%!1{z1R=M$A%ny?nk;-$Gu-E~C->l?f2r zet2?&nh(iJk+C^$`F22f3v1u0UV3vZiY%<7>X|c<-y{&g9R^2~n4Jz|11Sr8x*F%z zc1VeHWuH6JL7E$1FRcpZlY#-EmXFf*Y}x91&sqQ=sZ9FUT(0A98NI+sS!BrX8NF)y zRz6kBdSuB2DXl1wiHwiGnVZ@M5aUR8Ke9Or^QXIy zM{JdX^aPW0*A;$u+iR#i50Hf!Ss{S%5IN$uHD9f^=RF8w#B$qz3F*)W6C8`8 zS-SaJ`x0wypw1uI5U&hY6AGey{h>B{n#a9GlT~y&<;EQ>+3c*WXm6o(DjJt5KAlKJDU=I6 zW6R#|*XzZ`m@4KG&9dULo^^aq?X)$aMK7+u14<}CoX{A2Vgf-D4dd9-9xMb?%iOw0 z#FM}W^f`G#&Ry+KTi~#&-Yzm=jB3F%aS~N({i=@MjF^TKa6&GEl?FT-ILgJg3e^vB zIhmg@7u^B|a%$$e?kzsml8jF6SiwYLT%@}=f}*+$?0%AbB7&Pzjq{H*qq1a z4iu|G)XedyA@B{Hjds%BZkgP0Q*N07`&6j&nLdi7jnqHN&;g4(A$ZcUpAV7zD>68V zm2d?FZC2NOPARU6IUvAnX!xp*saN=;fV@7EAP^62(R%l{C3nwRF1P~~k8iDS=TJQjRWAH~4~j!1KV zhvT+>5q6w5c^QZUxvZdtrngw$h2YIgFy@Wmazk0$9(>a~d!yEhrVE^{?GU~DxIK)T zK3>nMvS=7cTNt;FqE_j~o9L|&7iVlP zqrfIhqycBh==T!FEASD?(Wm&y8Ydd49ru zV34*I;U?>h+t8NSAs~pxzF->G(;q90*;|ob`D}xnEpQ$;VdF14+!j@*a0~khZUHW9=bSpNQCxZ!> zf~*QJK3Y4q3U>MCBlem!<>GhFlswh?l?Vsh`pUbM(!8F*oi}pFWe+i#v&fcP*b;IC z^*ZJ2+4O9)=82Dt$~~Jfs@__vdV0PK%l)gYDmI)tPVp`*?+{7WnsqKW2%h=mLj5ri z>6FTtJ*TPyEr;=M5-aa#Z(N4a#xEN*DB7u=81bVst@6o}Sm6H+2X-K9=()I)?zf=c`}E`q?36FGE=@6?V} zf;s=KQ1~AudWKDmU^3|9wAJXBxmRR3_Mbc|`R$EWNXQ!Q0c{e5oc#iDL{nrBlW#MH zS&luXpa&V64SK7jPttt*KvLQ%H!qFrL^~9a2@<3vWEPEb9bEQ51IDFk<~)5JAzWoM zhdW$wQ8;M79HiQU_+TE42m?IcxY-H@>G0uQ=Lu+j3$a^SO;zQj#C}sXSDTMFH%o)+z%aYz|T%54NhEw zv#i%}AX#sP97^ikagZ&JwiIsMHK=ceX*T7}y?YF-$ks9+^J{j4B#;K=gL{tTS@io> zQx*aK!wJYnDIZ_6p@{o)4N8dl$vemCF{KzevUNa=hclE$QdDq_cz?Uth}KO5EFF(Q4bR zyNo5M*K;#gOd)+z8%_aIwCk@fIM#k>P-+~9uUkkvc<-RF9Mt$eO8hEJUC9rB0?2b_r9>O9JFaPGCQb9MtEv$RY|fd`dEvR01Qx-T zOY{-t!^KO;__wxT*T)oL*9cc_UMyBzAlqhiycdH&Tfx|R<%j`{Bn_Fq#}4Vc=CpTr zVYX7u&J6dx57qFv``*3RyGHF%A4~C#*Bq%Pkgfu3^~ML*(eUi=#vSNGdeH7(!=y5# z%r=kil`~J+vV)pqtkE_618?fRiGsqDfL3Hc3V6z8Sl-g;NY46(_X3ob28knS4eQQm@t4{OPCtyd$ z#4vIZRU64q{3!yqBbU!Vu`(@0DUS+Onh(R$T_57F_asO_yhZIkyL;$V+ocn3`gC}v4uv#2ej)?C>W!nQ-m%%7J+nS5bSWw3!@qTfUT~EC;Ou{a&$iuC;CV)>)Em#e zW!D?e8MD_A<+hclReD1SecZ5ut3l9jIRvLJvtM>EqO_?J9NkGxB+7bD=Z$@0mQj(B zg$#JuNku2GMuGB}tyVj*?Adi79)5*Pcik9KK|G#dm{{FqM6?trrzJKkZflE|>fgmn zuPrR;4Xt^sl>BLVEIMkBXsY#Cs72FeY5~4wa#erHqJ3ZZg`mVQ@V~=bKX}vv0LdRq z8UJYhGUQFltHPbp)(J_m9nXl;v%HZlHxKjMvo6L|f(O2d9NG>jK=Wp)`BvT-yg@hb7yL*w@))^WVchY~ z`SapArI7K*8rWM)G1}lThr+H&zi6(`~1k@MiM_wvWz~$dViRKNCY}I z?Eq+l^4x&z0Tzb)b}*NiJ5dZeYG&1Y`GoC@gLC>jmkbNGdHBcN!qg1MOTWfh$8$q; zCIf{4OjVrk*^@#bQ`#qb+I(v*cmuUUyA+jP}G2r zKjaeFN5&;k_Api$6MoY1OYl;j2P8>XJ-j|1)ZF$UE9HnO?EKj2Y;eWs<^>7Tsv(C~Ol;%a6b}nB zjBQh@l$T*gCd1zKL{e>88tm_&5AQ9y4XA;*=pPdt__>!qjnMd6DIfLr%lXby1JjRA z!uoBji}Ni3XA)d=@MFibgV$`k{!6dbXU_yIkKF}jb3(|AYiq-bD31h+y`5Y%-x6DS`d8hPw_;6iRi^oH)GV$p zIVAxSao^B)Yo0WyOXBh`cUXBL>>Dg z>JJ7bGT1<$cPt+WmPFL7Z90mgS6&nDVh?a9?(FO>9(;RE`E>^9&o2t$I$dgEH@?WU zPG*|SVu%ucW&PNSH&E9LEzVFHBLbzK)_`3q;dp9jL5munxKjC3j`wnvP5OtDdgK?3 zI}W%>Zb@z*;pE@h@HrdK3OqFYV^KMlZ?0Fkw@r?Sjqu5hYWG!FNQ<)(75!#})6yzR<_R-E8LT z)no`;Wug_k1BIqZph7VkKw#CrOka$QnAo`NM}+;YK^Cm-%iGUS*j)*08%~!jS32pU zdc5CX+};*CGA8>57PP~H@t8afsp_qI)wdF8D&?0zMVg^Mm$;18aCiSg>-CTF$vAi- zxB%YEEDCDaf9i3j$0sz#C4v{nZa}+Ga6hDKG&^ez{QYS2=wsNPKMoW`XB+^2m`J;{ zIbCMrt0KPt^vKuqX>E6*?nzaWkYlpDpMy0x(xE*>YDvu5-(5^(Ez!h8F0uRvQ2O!c z%r6YX6}k+BAw%)kZ)`tTSfOL<@;~in>g(4oYoriQlg27~gTmiI>e$q*iXVlnoww*+ z9Zdqlyuwnm&T8%sw|6Eq#Qo*Z*Tkzi{X9kW{f z6n&@detpME94!AJ+e1+9?#{(}XoHMH$=?)Wc4-+YLk#V=_ z%07)$x5_4thIE!(C&B%r_Nj>&l>AuQrp=K;R`2uzXP<521G)Xq*r0_2tD+9JVLrW2 zp?)A9H_?4kf1GWzm9HgwXHUPE-qlX;-g0D=8Q>nE8OZ3$%}V;c0{RanhTq*C}n&!42s^xv8k;E81(1V^Dd3zqQ(MJz^Es2yjlRdj)V zPN+VSy-Gql5LNqrl`{X^uih(ZuAlYwgno*nltA{vk7XZJhBPbE^DSPgjxg?^k7!nf zF4yPK7BvRYPy?}SAI-aCJ@p*I$;sELZf%NQLMg~kv}|=Qy@1(HW??sSe*Am^vX6X; zAs!BC&qeR~K*Yosr-jQw6Qm914=>Fs$TZ$C!Z_``0keZax^$+3#kNnfhSK;H8R`%< zFFyJ--Pzn^kEvF8oj0`Ke@d)d zd#F;=B&wIOn4QBACHiVN2x7ZiyY+k!l}a4Fa&d$1evT#f|KsbcquTnmZcBk;1%ekR zNOAWf!L?W^P%M;EihF_J#jQA$wiJir?hv53lM1fE9fCW&)8Bpfjd$PszHh8Q2pMoj zPWC=~uRZ5nb4kHYcQ2Ep1a~RJ@98|IWS1XmDVS7|o*?VAC1EKXdM0W&WW+B)Sqs+NnivXGiN1{7c6H^eQ)>#bMYByAs*urVnH4b0rn>HY2YHYX`|OhoD>2ZK|~ zlA6v6sSi3hi=|BPJa*nK&gqp%r&13Ut{vle(^qHs?;j2Z)4{nUYEKaeFYIQG-u`P!K>_C@MS?@cm$%@`g&X{m9&{WuSU@5MIf zb>%(+0*U#^Z`U`7`j|)t>!D6uT{!?g!VaW=aUJvi=>cTQ|LVkScrA4n%dw-ce0ry~i+eaX8(qQqdVw7*V$~ZZ=d8J-Q3dR+4yM$eWXz z7G%0}2hf%ZF>olpK_#f7&g*!e@ik@sw6o20UCF6EX(`1j$*8CDuZ;~!_N9lD*zG`g(=A}J!>ulT4PzHw?@%c58L2r9e>&fA{z?kRw>?CFfK zq)Z1%G8xA7HDE@fi7>0YE_Pbgk$WLmG$Ot06%AM^vQu#iA+Z656CabdP~eX+|5 z$O7<{Z=|+(bf8i|AD3iX)g37)Tt3?HDg~QXdCQf)So4?a4Sx`^F$%dKCN&o!={tY+ zCtb>z?gW|tumbxU;JM5WX$~_o<`Ix~R_L@pQQ)N8cD4IVZNp2;xL30Wt*j3%RXGj( zztb^ftwPp6QwtHwaltbUpqGXId=7ILbgi3MDRT&EW93f41@P(ZX$+nZynzRH7esvD zOU-)%m)d5yV*Q$V{|x+gXWu|;DjI+hExb^-hfR%AK#fGeZL{H$(|P)3;Ld|K>`r6+ zZv4?SirBJqp0v2hM?`#sWv^H=@8ll2$H(nJJtSdZ`=vix?W?)bv$IWtrtk}CmN&`b zMv2cqC(#B{NK(;yOeMSuygt8 z_DUN`HZJlRKvCq0+Dx(yJNTFQw*j5h_>JTIOw{M^aKL&JRu>e31uZ4#`b^=o7$l{1 zaDU#eH*67SC@X5q!2I@b8Eez`$6>SkeZO7#9*z@=PQ)o=TWXH|z+6x~waJ?2%^}tr zl~?XQ>vfGqpNG`Fv-kv=oJKp>B5&`ebSI$)prF|=xI+I$-R;D~lBSH3I~~*I9X!wN zEVs38c{?C?X+JH*OddSRQ7b>w=9hM}SZ+>aDv$k};rWnb(A54$W^E^wO{B9hV^nG_2b zmY)v_HDxzGDaizeQdwW&4J3c7f^+#vGf+~G~qele(M#HAjMont0=!KyMI5ln# zdZBQ=8*xb03n3-@K!T#YX7Kw=_Sqm>g+^MNk^8TlY$`Y5DIURVRY33X-KA7B538c_ z3{das9w>@@Grn(p;bYyyo;I$l-r%U^?fwn-r>=oFi${U?sa3b>qUW=!Z>-sk96l$Y z>;-@pRhL4`r-EG`5iQd)T!C?C#K&!b>ygr04F(%T0eFxcjovrb-@00nI}n%q&nXxy z*T~>KcgL)BZ>J=bLsL32Y1JWM5%*0dj!u5fyp$|-`4+^Ec{n_P*iH zMKzVqG&{_;<Dv>_w~F6Y2%$v6>94tR*aYm> z27hHoN9%@Tamt6hESqWKP{0lIc|0+Ica1{_XVl~nYjD|X*~r)!3+o{7@*)2X5b&G} zVr{CQ8}_L%$spxlUNKdk>V7|YK}~V8xpvhxLofL5D=rffjE~dV{TEI@xU~ez(^#|N z@3zCmh64fVWy(*}&{}9UWS{Ddd~#cSDb?{`u=NMi1O8MJ{vSNDsI?F?vHFY_24B9T ze47G)7di1klqI^?L@7(-$Fj;v+Qo)ziY<*z zeO3x9f9d&eFMu{S9@6Ys$UE)EBzqW)VQ0Q28eSohq<44(i29#q{^EH_=wRAh`!eu} zq?hbx_odLY>sNev`6Y1#j|d*OhfkVrEOd1&_&gznbx@Nl*r=!SB=Xi84Aj+UoPQy< zC;AQm*zIqnO_(OlU_&E6#eX+c+&(AeMH$uGS5fjI@h>rFeNSC6b>_0e>AqKtczx}Z z9g_@VD7Wa7&Y%#0e{yQ0tawvq>{OQj@M$frghw*=*WHcRA>sCu1fn4yVawl&q4l7z zm2#o?{S^deKS7z;deg-z_`izCJdd>m0u64j5dG%~Zh9Xk3w4$~iQwTC?T(?>nf_M^ zFeX2BM4ta`LH@kgZ^yE^S3|xnb``Cr{!OoRG0jok9yf=qs@6ThdLJhM!P}vdZzA?R zH2GYx{c^VkygWbQZ`PMp<)fRx%<)oe0Z(|kK#Ss%Mp&d8`c43h;?OUxydfrdSn+TM zBZy~8BBB+fPkM%*(ONoR^N=@L_GZm7C;RdJ|5p(IZ(p5i%^y+_H#)d}`HgY6(pj6i z_Bk>mQorOW9UMRk@G%aFGHTimdzGQkG0kGDqW$TzjWv$qV3`H(21%CAZ2GaG&+b%2 zY|pEx#|D4ol4Jy8=?av6lxTkqz{R=B?TqK+S_zgZ$yJq>ON1wo}N zJG&z*yW3=Q;uU~IB4VX^Eo$`5Os%*pAt}UmemCs69_GZKxx6vZ>`p!Y#|X#_#IXMo zP%pzD{CYyeZ|bf`1}K&(y#2n<0OrHaNx#{Dbj}8%Lr5sRw8f}&9(tsNkFyRykFLry z0ZD08Nt4J7&u+)iSsaK8A$|S*Q#i10^=r6M=Nwk`6%WZrV-fedMbjU$)5DP76*o;A z-dONAe&o8Vp=NOs?Qdrjm{{nx{mM?^HUaC_n0}OfJDgDGd)Dv=UV(g5_wo?g~*<8&kDJYg0yds8(WteKwg?GU+Ap# zoF|d=1!Kh#fiFGcJxs)rA?P}MBb`+W;H z{gLN3VIkqWQ-=;@%bO1ZA?Nc3Bk#9QV4HA&TmduYD?L&(VlIU9wl(I2q=~-@n2wt~ z|EpJ{oC^_gf~ueS5kj4_r;14E7S>1t4LseCab&d{96>P4!YQJRY?IHBF+4!eCRCXI zi0%Vb!-j+iPc^NmOlE)#RwMClgdOO^>U{7lh zHTF_okDkiauv5Ju%T^7>>CDqFqZ20y&d^tu;_{%RnyLo#9-xB^Mw2^G%05s zJm`aqTVa|^MrSSgdLduQU5hO+%Msk zijk*h>(@Vym##Z5Q|I>gV@}apGI;6Xt{p#L?_@5<(n_4T3CoShVuZOy2qUwx510o3 zu$VBxxla%7i%iVwqi&6ZVg=G;#lHu^esSb3M6M5iS*i=B-Tj^4e)FO*Pu9Oc6Jc$b z%!h?(NCv~~;lb{>7+}@ESWcHoO#hu9#|IF8MEs68Wxv||ED$ZP5})Es1AoX8JIyS# zUR0n@>bZ0#$ot5+#UsX~eLZ4o9YjQxp2bMM3czaxjQ-xVE$m6hDs|>v-j_>qv^Xqq z)g1~zM?2@0`8|gPmONA4<6oq4O2-I)ee-uw?(wnqUjL%$O51g=8B$Qrv|J8xrw^E^ z_meNLvRre!tvFiA2>VJ8P<_5LwKlKsZrqwCA7#`5htrxUpYNKq6-z8SPlV%;r=^za zoHW!T_E|SGPFX;Kjzowatn43*yJn(m?2Ag< z{RnWZ7-424Qn2YuI^T@PeYg$t9z`LwoO@e%3%+ijrR{=`F*l8Jc0)+CF9BT@4KKX1 zJnVPNYNFav*Z7&Yzs_whq7PUPqxHYnjm3phbGc{jUhbULO-d*GM7P%Zj(kN}+9A3k zE45$KKFF7DFDZYf+-hLjzb*hpOJqy7-DOCR#zH$>+-8B2 zurK#_4PnprWHz;^m)M5EEfsb>Q7NtgHF4guUB_mUgn&@(*pg@Q-AqG(aR&GE!_MGx zlgqfd`;gJDr<)ILCrUZa6*<+qTOf*fC|Hmi^YDGS>Cy@N>E-%YtO}jn?-3gS%-4%Y z?#nF4{@e(HNe1)yeP!A9W54L=rK>-Vuf*Gc|pBbEE7r6s!-EPtOwZn`fB<3#~ zcy9jDt5xVm7vUmvQm3@!4?Mt4&bN!i-_UCyIQ5tqBe0vo?0t$^XSn>m`@W>M+~?Cj z6qWYV*!(}P>#=y@@qaX1j1_57?}XkxUvHK9!2rK1_Ir{nkWuz~a2nEa`ee<8*Ix+6 z9fZW*nsd`iGP`Tgx?JdKh=YV}NNu#C!s)N5O_JT7m6^84(lLw>RiIg{`u2Ixx1SkV z;HPd1Aa4omu-Vm??7O=4|G09Zii@sCgHr()SKy8UDIQlxg8trIPiCA*B4l)DtQ^j7 zIRO9`ga?a-I;L}c(=*GAk5V%Z;DSaCjig%-vgAUiy1A2}q9AzcXN(%q127kM+)5mHEfqJM%uDvHd*?FDxm#6o5SV#dI~9 z!meMu_j3-T+Rrez;u3eFca?rVobfS$o#+dFOejdhee(lvjZ(MG{8)4fUe3A(-^Fk-`o$ zZ=3v=->Hj%R~g~uvesicp;DQJUhe_b2K?uTuCP{W;;3w8Sc&VxkL~dmJwuxJKeHL7 zcQ^2{d7*y_0+_PP0}>Iq0kBK)rljY{(>XU&OMF`DW7GtLYrp8i6Ru45sN%+ApK}5u zEV36s=Gv10Fp(@HLMSB%>^&4-nl*$Af4zcB z#!)E8H}#Aeb|L3luT6OI-#H^Q08VUY<_3te97z~vs~`9X|63_C+`9u~1J}9I0ux&C zC>nmN5SVH}+LZLA&|U{0IEUtBw6Y!}$E#KzxR?mYw3t-ANuKNCd@}AQM+a9FXB9#~ z%qAILKz)Ofn5@n3%7CKb9lyz|l_Q12#w!9!l&-vivZQf| zCmEtEO>V`>b`L9DR@y`x7G_NP2&73pS%WG579wt&YvRCr0f?2ZACm7!Kr_{A;x;1g z*!bKjxU<&lcp)`y49l{)tmN|-P>e|V_UsI^tnpRL;a23k3Q! zjb4K=1b`{WOb8tAxvz@=?ylHYkuc!))Mi*$|Ll5JZ)odJ08yN3oUL$4*l8=Z%v-D~ z+TX=`D2yj}&U{v*j9$o#_)i;QKT33RC4`?=5`eCQu{8cl*`k2zIJpk*DW~b`)Tq!b zsxZ;oi(}8zdCAcJlZ{NQtzch+dWqTn`EYA%wUY#nZnjR+`vFfHlnTkr*x5nbNMummNmz*yco9zzhU(#F@;qHTaiRos( zijG9gTzkhH{z=)}ey~hk^Z$HJl>zv_&RX%0+==MPUVG^CR=r zptzwL3+gK%`BxA;I!IvJ{+XYukL!_ScWwBKGw&JB#$z1!#4Unv8GWZK%JFXEUlt#^ z>i{}%ZV0H2*~UULoZa+_iaS%#DqacHB|BM8-(R!2KupQId%<4J1h^AC_2hKvq7~&P zX%JD!Jr#OGPn=0=$lE3*=OG&(Ec9B2(?!dpeA4muY9vR$MVSBV{e5Z8wzU{n zNI^8gFPQMxFBE0~KZYH#>s0}5)pR5_Hn4&@LjXF$wT4`{tHQ1;jC)z2Ohrz5TO;kP zMpCRHL%-CD>`A!uwJGf^DCRf@{}*;B^o?N*CU@Qf|F@_LtHA3ak&O;<9dnx46~0tFab~X>FhIXLkoRpj zlu{~e5wJuxf4sq^<5^?KIlMNjeBC7JTUfSs0W9^fC#3#T`;G5LJRW63z$S)pSmE4< zzOglcC#3e)E{;X#>j02q^^?@b*L;W{h3=1D$y;VXZ!K@mN%6 zftV($jLAkuR8JJ#A_hI9jBWC4cBdx0ZyDAdk6eOpU!{hhEx*NN1=+L2-rt_P4W}5) zUufD_T5b(&YJQ zf3N_`otd01`s1@6N^vz%MWpr6DLOom=`BFIE7R|lE}U!cT}C>xfkRt;y#@xx0#}o1o*Zor=Ym zWFD1st)oEgfY*|)&zb!@hNF+g;a5cqkjczh%`_2%9%ZrVmpk0Bj6yrbEYTt8GF;sQ zIz0mP8@fDja=t-geK4$fq$XA<4?hkr(Krv}$#8Q{eX8rD978}ciJ+Pe?ab@kokQ6;!ES{Kev8& z6YbsJpFVdy2$#VY7*^)o?QFxJj<5)M9fIr(dlS-#N7w410{w~&uP*|1;=qBy;(1uX z@`ClaNHu9gF(lyo5j})9;-G4aSPi|sY!}uWK)8~-47OYFyue!{SH&b6<|VDThxEz*NS^0sM@Dm z9lS8e@F55UNc#cBQ+M5Y&(-~uh1Q8AW%hh;40U&|k;Um)$VzX_%3@c*i?(oaCX}~{ z(x|qU`FwM0+@GI(Y1j>vnfvYS2`1{ilL+C9!^arTI>&`dG0Lmo6@?w{b}Aqx&iqr8 zvRJfS?TycV52s%zVNmm)Ul9t%QP>gIt#`Yf0XO1AgjY3kNmi*E`V1meN;1fa>$v*G~?Ao+O9` ziSdn%hFSaS4k=bJ8QW?3Ups_KDwpP80-%x}<7?r>+s)(X0RQXzvvQ2#sG~vsKlp46 z3d~u249`ILk?J>3m7c%;$REk6z-0aL!-rbNtkKsGgFYxRa3n0+EK6de7kjDX@O+(IN+z=X@y?FolgmLyvU7K)S3P6 zkdWZRnmTDig50!it=DK&R%>9!)OAj`&$Vc6ivAgM%Mm*LmGCgCgVLC_eFW zSm{M3zdZ=}7C?;S_jVFMu`9zCy7?I$4qTSu=i9<+(llm82M%VwhXh`%(N^IDu%+85 z5;!2Dk@hNYEf^~!-wzxyTOs|Rn>0svSqwA@d?CmQ{+LBA&qEfUcgCRkfh8hDJLvw@ zj3gzn-v5?@QTQxey9{eBQ`JQ0$M(QGxvZ-a&73w$8xuTWxrTzOsVzauEO-X2yRl1wcJPJ76Si4PyT)`U@_}u+vZ5h z@}5o~WdLx|#EJHEG;;P%3IO?SQF|4%JQe5vmbY#hy!?JyN<`0tIQP}y<6ob}+meVs zB88v2_!6>i_$guVA|gM1xm9MV|5Pz2FO;)>d#*(;XFzd_d$pEofb(lxP0Ig(H#I2r zJkCwjHLr;5oBIUfrBS6un%~LAnd7txeXoz%;o3VL;_C_7))ehu^$wgmXkyALYKxOt zXFmJWY}OO+5w({TPSNi45W&R;!J%vu4{ck?;iW}Wem1LnV62Fqsfx{B9{1(*FfaHB zNi*LV)moF2F7x0gf%n$8$b$9e;mun zzjVtorf#yAFHbfcM0Qx_B5;f%aY^jS!HN}{18;Jqv7xycITyoV2jXGy7oITB*UN8)^#$nn5Gx2 ziBQzSqHiJRcaD^g2ccuSo1y-7BVlpmmMQhk(H{Nm?4HHVx1PyJ!o%tDU94@nGc&ow zz*Pnks!2R%!pR7hJN2Hd7lY1zLZ>{ssOHm&{EjgEw4FR8=2{WEu4AfC4Ketpox6Ky zfC@S~9$OA~EiUZb=e5n3kXB%lBVT|q^;ZX8Oo(@&6LO864nws0TaZCIN0w7Me~X)H zaz2aepwOoA*%znlslEi*F~X)l=UNWaTmRYR|Kp>e(!~_J$pJnR;c#JX6^i$%3=Z-2 zFMEf`gb2T?-whn*@M@41kywonNCL*Fj`t-LexcRTdcN@VoOoo-yo>fK$r9*8CvaC+ za zUK;w+hR`5X81z_OoqF)#*ryh+FuV1(z9<``rMz=;DE5m|)AqUb#;~RwxE^UpWQ6)vHn= z$LUm~RsDI?dJ^#DK9dQE5DUd9?{Et6@wjjV{1k96Ums(V(RN&)Q9IRccok9`hzIv~ zj}@i%`}8Os+3z!X8Avm(TFP{%KSXmycSp5Z?;0(`{q#HIUg6 z=WpxG46XPn*%ST1tOIdMqE#C2Ibjj(hINLmxpkPEWzkm;@TLc3AA(OVR)$jG;hx(n z22jKDjaQ-qcd%74#Hi%x@U3!)Ae(!-ORvkId*PH35pa#8T;)a5(pTo+qAy@y_WWqL z($z!wdD|`?`?dS;H?&RKtXT3GQCpHj142_%1JHL-A(UuXr)46|8BgG*fY5FK8M%d`3uHNZ6KD60cOFei= zY?$a5SSe!B>t`-{sn5vLuxn(MF02XlzTxJ&*fA@3^h4x1<#jj;)<=hzQgnj@URoca zF6nEB4J0jL@&<4k)NfPkyQSd^66-r)6}I~$@js8vKPHDDZVdeJ`Qyv!Sh~U*kJ=t1nwddboDG>|9F5`6*(wP$XL&f z4J}8eeD!&8puqw=oxEEKs0`R|h=+I`g{l=tP5wIlxY97y88A{zUKC(R6uVOUN9p?| z4Grgw(H!BtbC+||t2?e;#HwrsPQwx+`yFU;(s@5izxNJoH&Yu*Jn^%?aVkUX&^Lib zimYUsJ=@PHpK8#ld5ca@SKThDGQmS5!8M*d48KLc69@8DTSEKI96I3*|Al5MdgtW| z+5O$Qf$-Z(5=N=T)X%I8>?KqR8Ml324|E|#nO2$UmZCzGdoH(~?vJ;M3c5w<=J@UI zYk%Iqtv%m$9&lYwJ&IfU7E$5szpH(v{Y=rTb}5@zA3#y%A}_InC}Od?o#In-oTL4d>oGde zL&!oND;PB$d4Wk{D4y3FVcz29ZGSo*-APc3zIgA69zZFmKbt+4Qo|B%%kvf{UuzuBO7yuJuZ8J(@=kTVK;Jk}(r zvSwhn0GPv&GhU1om8*rkBn$2MnAs&q1m~k?)o2!i-JYiSi>VD$KRXea_dgm4Uo#K% z+fB+tj%RlT3T%{5HUsv8g*W;a7E^@!%^tT^bi8BV_nYnNiD@LN!86KaUEY1bnvGX?8rKy8x^!cQ|c8WR}HoxutyNiC|eU4asog5c;3?AwLQV8$d=dA@&%ek?EiAmBs~n7b!?aX zcJ}4w5nTE9V#WQ60RLcBcC52SO&3KBl?n1gN}+U=)6>p0|6qyTUF5D#pOCdt)`u5Q z@CJC<7*WHiZ%tGb=?5-UC``vBVvs3i$P}LC#4;oqehaXQQ930TBHM}IEx$@wK6r!J zyiGzr)e$@ni6aR(Evb@I@|mdlEhe5}gA2`}`}84>2;S>6Ub=#dSDE;;jc3h$ol|9l z0Z`yl(z?ySs_?PJxi5|{vYx-ZNlGMw>xU|S+0ep!(-&Tuf{vy6c*m-o;VW&wVa$sh zwl8}tr?TYeOFV*0a4usJJyoyE8LJTM6GtRYWnW<16rJQ0v{CW~Me613SGlURX2jkn z@{~UL#gDHi7%NneJ4yapjVo9B*%E_bd_~z5xB!? z4KMXH1jtJ@f^{EWOHMgl|jwx7|(ZMYT(5FZ42YIcp;?!5o&9ddjn%ta9CqDC? z9U~SwPIGQe+ip&?;5XaxyX)y66dtbIP=K)Boa*f7B}5dGpN1a^B0`p=nUY#s;Be5< z3nd@>7ThP>Ox&;dq{a3d4`!9*ZZ9rXw|UJj=b8v3if2Y_#DX@3J$4_kETns8v_6i) zhHmwd&>z$wMEaw{rw4z0l;Fclm;fK#$;Vi4kA-q1q!xe1MTn}2 z^jj$gS(T2*lw+<0Ls_qF=hfY~nSu<)=-l}iB|L5vBY4-R1rfuK+f}rlWl%sbl%0q9 zi)*=#Cts#Ez?!! zBehhs**2#6NZ<9Sgx%VA6nXm4CYEg+w%J^ho6oeG6JdU2ZX8`+h{DAQCZ0@XOe|}S zbir(|sxJ-j5Lo24;u(W)?q{>7eC=I7-5fM#~}crg|-ko{0^GJ0GW}0|}jd zv)IWqKr2>N7@v_eZXk&s(g3+JFQ3EPW!Y#}8{LUfI|_R1g~({vPbAq!r27Q3>E0=KacnT9$tUu0Ry4x1e4rYGWIDe5iCEE7u1?f37!RLm^ z6@FyFlHz@Gk&=NojGr7RG+km4!14v3^0#r-Ve@>;{maOdsSHW4k0AV1AD#46z?=qD z{YL&$Y?m}W_jBJAFJ2v+K>%2Y@hww5H@!jy2hMzi!(*%*10k*(3%@of%0Kd49q>5z zBRh^b3BPHj)d>H%K>~-OygPO5cx3B4B_^BPQn!z76vTpL(^3`!2>fZUAurLVn|}R=TrI> z-Z+2!Yv!MuN8Is~|z@po|OE*&;9tHX>o}0gdh%FK#msDp#TR zsM7mid`Ya$;`k8na?LdNW*cvQc~^E%DUbL= z@KhKVz~H`2#K$&Zs35gnXg?Y>F1BBnKzqX=2i~{Gp}+?%A}>Vjr`WRhjWDoie(JCM zB6l@AOr)Z)8kUOJJfCmKA8z-vAP_j;#1_YP=k`5})2BR9Ri*4Oyq=8ZhTb*Uook%d2rpqI%Jjt+ zgo!ef%U>;R3bPkmS^B zsyz7Egn9B9-^g;RGSBVvT??;20iG&qunvLUfuqN+rHa9uTxUD3g#9up<@1{RiaLlI zu-b@+Xjn4gjC*eJG_O=vZmWksF`v;ux3dc?4^sA%IDEvZ8JDPMo0?H27Q>rM5XnaIi0 zX22gvMQ;S}>Wr4=!)NS#I3Dkf9%IL^or&6zAPQ{g?R949x%tJ2ss4mafR6=jH&3I~ zg~(ii;_Na`{)2ms0ld4P6LwTdYYHE*L1>IJ7!iMm;y-=~m3@#9ad(OET$|qcgp8NJ zt1E=#N@9icp)VAO*i?tRYJoDUv~dt66XmxrVdMwfg74x3QFry1@`O^xs$9_&TExT?*_h(Lr)yFRXGou6$zN_2Sgo?Gk-lY} z+A@%1h(w;IJewP!ZofI=(6JYT%ngxot`8|vsM$U@BST{g1Ba~t2rR%Z3D`8)i`@g} zUZFBJvJZzi;xWVC8>V%Sp#_O%8 zX`lZG<|AbAdA~B_p5oR~xZJ1PmO`CadG|=&fE?dYx4;QBcDxlP@Oy^NpWG&5qK)@= zEkYIFA%NR$9!RCvw(fa8LkJs2M@g;FU7gm8xbY*siHACfZv~;c1q=C0_?ZEBHahX4 zU*C8HB_@V{|HfzD7(ZzCM@?isGx=@eJcksk^!L;U)FHO^6I@rXF)n2G>=xkEAwO@w z88>;)2J!SGxn)=5SqP?N@XM9MzIy?{u33H! ztz)78_sMn%@1R!(CuH61l*?4E4!7?<{lHi2WfV0h^ul)M_Iz>tYAE%dyOhvfq_7qo zLoND}P6?jB<9-SFE5USpq)kS97K3-n1zRE-03Y69X67Y6FQl7{qy+4AoJ_78E-T%9 z!N9#JN+5|co*j40^lM{ZD%h|V)sFKy`NgteIrCeuMdmGrG;cecvGK4Mr{fcM+MjJ8 zHB*soMp{Xd~Gsi&BCq%ATNwVho;!9%PM`zw_s?eX90l_S!Eqquq zR}ZuC)Gs8GyXfBxDEt>s&;Ru(T{MG0L6gBSKcfW5!x-$)Whf;MtKe zsOG(T8)AQyKMQWXMYw5(eZ(sM%T)>pdY|n1;uPB71Am)3{Pd(>{fd5BTr3* z0hS-ZoYxpGjF{XxOG!yi#?{$-z;Ycpb+QzGz`0cVeE}5Y>@zbyR*{+;5K@Rh$S2mfpkwCUXmF|Lb3noRxury8QIUFj>OHWs;1^J^-lDbawc)jKgLIGc|L|YNYbzA`OH>r(a_t$>6eiK; zjC*_`v2Pw2BbBFB-weJN6s}X@+0@1ki=h$cZd{2Ce<^)(*jHd9*6%Ha21EUw4uR6$!#=++!>i&ZqO(kmQKnn+4LphfcfzJ}>{@^i%TZ_5|9QGcfa71FqSl;K z{2*dSC}qR(1+Q9^Jm1VXHk3~7eeXxrj=v@UKfY+w+%%j&5SWYg2>$tbrB{QfodiPy zmmUQFDtruz$rm6Dsn}-p- z4*nI_#Vw%8@4Lk!_#}e@O!_{Ar1LJOQSCW?FywaY z)9cHlb~5k&y?y$Sfs*pCWI_%zT#YBcWdXuxrvY%dr6$L^|9Z~Uq(t1Y)3~CEKtMiXcI%eA~F1YVcSHx z&h)YxPr|En;LV^?)+^T73oDDbZ*P_|CA~G8fDF$`_eJ%}=$8L=9U*W)CVfmbCXZv9 zFNf;9H7H_pm#tH8&_xeM<#+S4xO^$5@TDPz&rNz=!%=U}k2?^q041}up3(X*LT1`y zN&KHr*;NMiQN8|)$W`xky8Sah!j0w663=9TH{Z^NZv2$?P`i%6hWh6;GXG~%rjP{% zKL{dM!DldmH4227b8&|TrS^NM%F!lUY=!WK*w>;ye!>cx48u28MHL&=ur7A2`^Y6$ zn3@iuY6ID+1A#k}a&>TxQbYv>O+tP4h)#AqgNW;>L>&vIA1_hL&O98z!{E7)A)Ll; zUtjA#Cp9~l{8yg-f9+`hPo~%-BZCbk?Em8|S=lY`!V`(UfwuRbt7ul+63K=@^9`NHlY|J2K>agOsWYXrksZlQ1 zAh=O3uVo^G%b)-~ybIs!9|LMeW#x|&hLT$Z*GjepFa(-YgKepWK$v{e z5S_UQ^r!s0AOG&SG7?$z|9!B*>9c}TC8I1D|MSZ}r9=ZY0WHx|nq`o3bp6J!nq4U( zK*kOF+x0|rTnbF6G=2-6N|%+1*CSM6)}@GJ_Gcb9S=>sUT_oS_F`_~P&id;HB1sAb zb`GqAQbVmJ6@3kZlLZVwLiVpX{D(>5GDrb;p03CK1h`NrK|lo7OC^k&TA-9Z5R?<3d^4Bk>f*?>OaqHbZ1~m~dWo|J93o{_gBD2wc`wa4{56+ZjSZ$gIu_5m3@G2^LV+Kr0zNm+L?lHnJu@5cRu8VhWx%Bf9xnTr)@G1)W{_s^p8Q`!|Q zbN@Huho0peP`Zw1-I7lF%b<=z^p=D%^yIwxmQzdd4)Lt;i$&P_E{k4<@)~jslo+WJ z*SzD_!Q?#{O4a=NyZ-kSj-?1!uTq?j-0eyA`!|X&>FPR+9FBDQuxZI-N6uMVSr@DQ zhDwv92)4&9edCpPp9o%`C9J6TjT~3^H6+fg&K^?O`ASqA-c}#V7J~1@Dnh01dpg_8 zj}fEo&PpsSv+Zlf?Pl(G4c1jwfC=_B7UX6r8!f_gT)??Y$udexi4T*P$<@5&`ODV& zfvnFZ+6SHDN0-H?{GL^bOla<(HXbNfM-pye?FFAD;Gtb%0TFI8(XqpNi~di@^I&Y! z{|#3|tb>1Eco^I>U{qYMO}&i@*V1q)lwPw?|AfcIFWHIpiya<8c(`-}T9mz)z5jw_ z-J<_jc==6P?pMrP2E)*Z6^i1)9Gsk|1rN)QH9r;?g4TGzAC|1p<-~JnN!v1fk9v@R z55fz%kdkGXPdqc=;Lt2UFf-dsQ~?qwE5>ziV5jWrw|toWRCfmLe-(D^(QKz_m;@mf zp~brOXIv&iG?iXx$4Eq6ilQ3T%c9yis}z-vHkoD;k*d_VrdnpzphJ%urM2$1iR)-l zMbj23QkQiL!W2nFY})OdIc?9`{ht56|GxKo&-*;@`*pHgz{j96H<`WGe+2kmm}qg7 zhuMSw#h3E+rxhw-T$gpT>dqS#l8U;YUwKQMg=Lg`36S*wo7ke4kbW_|0|9Flned;6 z)Ww#EwU|ga9fw%P9%a=DQ{jA;@$q1j68R7<=&9T&8@#px@W%`itbEvOH}=1qOLgY> zhsI8tWOr-|#4Wkvb4wjNW&mOB!*xT6#W5a8?$LlYStAXV&p7TpYaleboVjKL;48hd zHRu24w}j`KUm12r4J1r8DlXbgdZT7=l)e z$+E5}(Cxevsakz)t3H4c$&Jakqqs79x*6Z7pO}RZjgd#yK!#FJ`cN1K-lrE7`qZIE8{!Tc77`C&n=g9k}6g#)kV6rm=>CGi8Bgsx!*( z@foZC$?-+~v_xnq`F79et#G1+z{(cB&uJLy149(?ripf0$gIo*>*G0(u^b$37dyMl z_oXI(3{DeCn{MvwhmZWUdb@VxfH|?qH1(9Yn7C!4p}9qFYuZS$9%&$DzHZfv80%X& zmp{3K-iJBXM7rvbFO>>Wu~iT0_0h=D%RiX;Sdtim`+o^ogNqzR(S8%SZ!0W|ch$w; z5obCOzoML~pp0>+MX3LCy$$L_8?r>uS20=l71lZJ{1ZuwL`h*ZS(7rWdT0i`$XLf- z6*YsU%+Yqh+|-C=e{aazpppn_mb=~W(KvnUmN}`h+W@ya2^;aY7bz=7%n}F$Ti3fy zBVH#I3c@BHCq6#tNO*i@nWv-#?mmonB|P@6)%+cThXTMBqlmF$Z=?&JWD*(Qm&I+n z3ecr{c#b3kb#nvgpqJ^r-s!1*X@5@`$kfFL09#2ztt&S$Gvj>`bU)kVf;Re@IlO&? z#_5zW*Rsy)exTSaXREg==>8AQcXK7sAq>v(A6z%Du7<64OatBfpylZG?H~2#FXodK z|DY=-{Zeqfn%KV&@*WDqO)MQt7rbg&0Bk0p{mkl`;oxQ(>3z0fXv zEp(|anBW)k_pCo%x3r*5P&=O+y?V?+J>wjCph@ARb1tSuo^$EY7xpTKaiT+!UnJ>g zqfeUyAR}n#Y6*esJk53zSpW5lRgXm`|W+n#}%EFR49G?^QZT%(FV41i;rV3&Kx%HFcj>6yL zx<&vQV;Dd>@#T}3p>)vZclxLyc%SDDIxnA>agpPq@2>OGYQ32|kK=s~vJwb207A ztw1n4EoOCm6Z~txC;81Lp_0-qxZ!TSJ}1f%lvY}S zD4EJj_TQ6aFrQam$gh9hfDjE!RDn@d7(&m~kjICH733A)mqLm~;T4Ca^1UVq6&HmJL^0r( zt+x%4Arb1(dk8!^q$0Q-f6Y)tNM9DX{2ES(Q2D_$nX!2C3-eGXLt5U@(gaq4ptJ;5 zsi4a1``$iVd~ZkywTy{m@W_*&-IUw$pbmfO)pv>!pi1D~1T2uSxYQtRHX$wZ0$EA4 ziYKHW`AD{GQq2NVJ-z$!ly{~~wjruDQ+u*re3G_&G_0fbbrCEUW*Cg{>4wwwRx=G{ z5ES&CLU}}|2ng`!K0x&Kb#wa=85@)HZz{d~h~ga&VuF$k9T;@bcYF4X{0>C=P>tVv z5SJc$?w!@`X!m^zW)6`Guy?VEHy6+-PKX$Ja?6IFhRr~GHs*+#JUzj)H}aq_d+4KJ zIN+f80CFx2ov(~xLpSUv;+G6!#nVYgdRP6S7y0sdp~^ce!C%t4*EdEIId8NNQa?o$ z<+yTcKz!PWkM8+}!(j)i$$*5uuQk-$lZl3bT2P7NILvn)gJ<;p5xN-b1r$5MedD>G zW?-%_uhJ>28ev$B(nVt?;{=Z|6M94ZuHc`?t(V+%T_T$gcIliv&bpS~xSHwYUag|3 z7-&V|0Gf%-5aHs@u3LZG&iC(p-=kI)_zmH~hibGOw){>kA%Q*j`(y}#Y`bsrL74F7 zkLYhYbW~p+e3Wu~Oy*lH+hoAO}8$oxkDt_U88C)q0>GnU{Lo(n-?EVGQ(Fs@QjZNY$<=ep|3f zwehtS5W{CNhktH}o6lPOX*Jp%u6$rRA6;#1&SK9r_w68VntD{aH_p;OV|lC3K(P9# zW`Ex)Wz}@U>tun!;r4py`~rf)0>v=cLTPs*Li@`i!EN%QfMD|`0*^F56#A#=;*vrV z>9VOp4R;Y&p(*;U>hk=6k@`)@3Qz4v@%uv~yowBr>$~PIj&n>tBouyuFQMG}{;C8w zp$y%^p5J*1vBUV{zAF+!#o~Q5B&G+F@hKDR45H_MT@1k%mL|aX&b*CqiF6>S{VhxO zlah#&?6;YZIAJw%2w#Kya{#|SIet|4rRGh}H8Le?hA$V8$xZ%+WyjiSt}5mtee_4zhpA8P&UP#Yb{vJ>-+V!0IfK&^rxUj(cJs;y(K~`iW6R|oEylK z@};6WAOmeMTfhhs?Jh$--8e0~V<`I^J7)%E21>>%dpY}R^HMW)GhFkuBZXtkVLby~ zAzJ*vs2;V!smYq5_g7=aNCrtp%ZsWdJ(ZBQd8~@PB9bYJx#c_;fwOpS2_9J<4WA(I zwihp`mCkXzCEb!P;Q)pYT0Ks|9-lEj(crfG+}Q;G$jQ))-1yW}9!wR?CEPFEB`lxk zBwQ5P9GTMZ-LKYP8Wn@-l1eM-DcMV0Dixjf)HP*fr^;4FKWC=gP@H2HTHNgzVODxz z!eCMBTFYO{WqdnUylZ*ocs08_w!4Z>jBY?>tUjaqWGq%~s9~&L@src!er)BRu z3b@D}6*qqL%T;Eaq`x0W8=n`M7KVw+WN6mAroiyPh#e75Uat5wUtIyOm8R9v+|)9( zV!q10O0}Y}I^VLce^iyXAhEE=1&jNF>%f`H$?e|kw(E9!`!q7rMX_vAG=D`mXCP=$ zALVvzwL5mZwz+!Rb1C*`@p$^w;&9}){Mu{VO9B<4wV0adR1_z|)Gs(_ zTFB3g#)_k(AcyM61+i)lgY~}q0jh`=EE_;&sEu?sNj{+>Nvq5!QO%SEZtLwvwJmBQ zOv14EuW@2Sa6`-S9{n)UIt=aK`j`qxcXpwV;HHT+L8i2HCYzID*J4j%F!FH4VZ|H@ z9p~Tf6Q42}wU-|1tlz0rDWCK2lW0;Mqq;>Xhi4Xe6je(4WNu_sljku^v(z%yN{pf< zN9Y&@8CngyXW^u@eQRMMp)--8W@BNxtrFCTqhjM)biJ$_vXb{Oi>>nt-%DUpXg1QT zIk6Yy=3Dz-1m$zBfyx}7g^GCPbxCo_BPj)P*G-*c9I8t?aGu7H@li%nJRkYw8LTUO zB{`jPWz3!4m-5qynV!SS`3n1r4Sv^g^mC>LQw%$e$695ObPxsn`nXK`dSj<)*UI}C zpN}uag`#<)%8nK}^W9Pr=R%u8NA}e;gJk+9alv z>!}?y>h0L3Ezm5MN4iJ8 z&Z(7Yw4X}28Q({Jd=2M1`aDmg{puZH+cO*Gjb*`tX+^$xZC2}gbwbspaY$ZHF42I} zuxsPhOr^Knq_fbWXv4CgUGsc|Fcf`L0xFrFJ)NE9`0PSm%4dPfl+hyZthI;j2+(SI_p74zX93j#npv~huO&>KMF&r&p#eH2u2*I_s zzBcD{?7bL_CY~+~&ubP*#>vCgur0fF?YhVWOaZh=7^JzaGiN++XBrjeWkj;9xd6_) zPm9^LMdNnkI6P-w?FasQK|L6Y7)LZsS|t{CR&3X$N0lM!tLjJ8EjreXn|1}&R+7ur zrR^#*+VG9eH|%E*1z5-|OV<7CtzHy6%8ynXCwngTr>zfEuMHC}du!iTDB3ICI@1Yfo#2K{H*lIKcb1EkjiKKU||iAn@cydeXu(CGDkY02U}JBD$4pI2O-sQ25f2ZK)85dSU0zW5pXT5@ zE&@|WM_YCp8W$HAY8M7-8+#KPIyN>o8d`c9dU`5w4JrpWYezj-Dr<*te|Pe)egusi z4D8Kp9nEa4@!tB?)3KGGZ>H9W`Z(` z;1o<{fBlTXS5ol#mV(d1Ty*(s4+sce2r)rEMb~%78Sht6l`#4dKNH}uMu~qbcN8sl zEDD5;L-~-6M=I(f9wzS6;27@fDCSZ`;Q5f@;BwYpTg%Y$NNmFy0z4aEX>!;b_c>i@ zAxLBrHRt-$QV{6fqI^78n4Lp(z~yt{__-xc^414Er}Pl@D` zI(Z?W|93}c90a^%Zvfn<|7AM}=rTB7UH!55#dr{q|Eoh9@`GO(D}9(TM1a#duLo=+ zzAevh@6Ko6M?L_rkM9e_vtK*bAex6<(eIbeXTL{*_0Nqwzku01Zx4C@Q-zlM@7nJk zprjz4!Pje;2kXwef3nEsJhZTqkR{Z~a@HmsJr;gRTFC1`M}m0wzd8I! z1tB2gNgb>0O8z^pGNJXgnuR8ToHr}JsDJmGOBhD>aOJ}X37re=ham%$tMfq2>uuU3 zkTCa2Cat)Cljp;WL%w6t#&%EIoaN0xgC_Rt4jTRM1I0!`KoiHFgl~F+ zX_P+!e1jhj2nw=+L;?k#9R)1;LBRTg4_;Ly-%dhG(ycEWLA*%siT8-q5&kFg%q4sm z(zz{`!0D3C_}1`_5W+7^MsN_x4VG6I#jS#99Qq%k_7?}wK0B_*8#&fjKo^BaR>=gB zn1D8{pCfD>I_tNuv+v{o$asD~W)XXv9peSI!5#i6f*bqx85Rk2_sY3IMjIv$vaR^i=aIwj6khxvj z9Q}uTJ9WT7xs&*KPXOfyp4F>~0U7P&18R$Y)#?TJw>wn;?<4Qy_gk%#PbW~|f6wVD z?0K?oGSn^NDR70y*#+hl_%~HSSNepy9iVf97QXd@smAYzG#C^=)*y3d?!k~Kpk|Rq z{|88vZ~PqgJ}frD7aat>druE9Ij+VNk)TL6G+u?{`HTDb6k*^K%$;ux&b#ZZeV$34 zg;kulB-`hWoV%hS9ztYKfM8ns>=&lBEWrMT6JA|^W~87uhy_tIOQZcWlqI-8AL_$U zY#bOJm*HD0Po6LPsGz}k%PU;>)u-acl<7b0^({K@#)N$Q5$|B%t>TVmnF8PbjXa~z z{@Nn{V$W3|=$|)0T6uYgZ|}(CFY`viRd`^iy+D43i2O@c?C~%lZRqE z@(#KV1~8BU%HlY3^2&^M@<1eJ2XgvZF%JRBJ6K7AJ*Pf3Fv$lZ zfp7R>3B$u+q48+LU{&Cpp#OeXzh6Y)vGy;84mKj*7{RB824E11eR2q@dgvdyaV zvPeI)4vUp|wL!n5gwG6Dd1ipaN4X>3jQCx> zwS$Ml!~{HVPlrnhtuuO0=;ta!G9&5u;ONnm9TzQjL@a5ABM1c@)JY)isAh3oeh{tE`&*@Y~aH?^9O$AFy17q4X2H2 zXa0^6z1chv2zQ>iSVhS^Du0iL1Y)h2>W6~a$BoG8b;jlr=PA=HNW!O;`*IHRmn6db z4(<$&uu2rfRRtNZ;c_M)e2IX6;~)F&plzyv;8P+5BJqn#3Kz&VcrdJ=TJ~kj$GJlG zFJWlw1J)Fr(Slx(pT9kbT*vGKCwbI^K6TwfwFA$ek=i?e`r6Gn_|U}DNmNA^E~lh6 z=!xspyRS5_w&@J4xRQ}YL>mFAHwUG0c(inJUG%Vl5Pq-(v?rJEj zjnKEt$M&}fdIQLBW?i~y-q+Co0HtT+HsG7!xI#N~RTVK%poS}yd( z)+6V$gG%L81rNgX1w6(+Cry!LT;T15;LqWNoOu-D>xI_~9iPeIwnxsX+CnJ_w;H2A zqAbLm&yP0hHEt2&~bcQ7J_e!_f-%C-bmeltxNj zPIF;mfTc#0ALgi4qV@YjH)1E!&mY6$9mCgir<79{^KVdKmPlz2R4b{=m)k;|Z5z|R zXcqCa*?)I67aMoFHS+tTYrpZ}^pIG&cOa3xKRlLpExzKKz#w5f+&8aqXxZ1R8L}P? zEP^7DnGCzyJD6*GOu%;{-_a^Gzi=UyE>pE$Z21J_Ed@4n6EbMp0Mo29AuC_;=EhHh^t?6yjnfBCj? zy0+-lKfSaJ`#i0y8Q9FRU{*}M#-p=bbUZpDfGy^8_#Wj5&W6F*t1$c z!T)h9Z`5)6bhYkm5LrBflP!@-K7l-$)OrzEw$vO&=oksQj&&kex^y2?5`MYjSdSpN zZ?>RN$gg5g(r$QY?Oey`zPS#H7`7epkmS+SS&oG`7Y*)-!kJS9>%p7+8vFshNIvQ!#*^^FYqI~{4>n^0>B zYurB)vpL}0nPI3jQ|3#>#%;Iul->gf_PBj`Ufj>|a5i3EFUyy&pL}Tc$F4C+MfyJ} z24fPr?x1BAxkOj)P+KFCP-_#YNe`_%vC$i5tFP>8OMLG%cqLgeW;uVhsi$1&*x5Lv zwz)~@&`Gj=B~YgJ@g4yA4iKa@o;{q*kcnpm4nW^u$`PI3=1BxFFPh^h!SACZ&7-Y$S4PaV#u%&?bkiiEBDG_ZppqiT=`d; z6p^pIx<$hE%Vd+4D$5z4HXzE}ca@W30w|Z8;$JpB#LJAsP*7={Eq_{~g35sOpt-Yk z8IEY|g0$PST9PzN#h3Di{m-Ybt&7^YMJF6?{?QkIFyiYKB0EN|9CR$0haA2?3elOd z6jN5Ly1hszSP&}T{b3>XXs$C~yxY4xS+2`?#IXWHFW>WN_4mjO$hU3MR3grI06OU7 zq0+L9m(7sI!k^&;WdnaN5}B#~b;Mtb7Ak1B>O|u1Gx($@zfnBKmiqUI2TBx52#1=i zcP=_B3`AyCD|Bz>i{Hf@wXV`=xJv$vfArtNp!snmM=pbF%~3f`etK&ZT`3yPLUo44 zM9k_K`tti?HTG(c)H}T~A*92F0+o~(Q=zbGC*U(!A9z7NSJ6dDw@aN7^lq@+zSx7q zeZ}Q+NSfD?O>#VN!&VOrzdDnrc#Ik-Np5;&(wLPmAgw=e`N1|}eabR^X`_2FELZGm z7f1KR{K6euofBE^VEejq_k2q=;6dM?+`43#Zi6duXtg`h8J%Hd*%UvZF27FUS#gWY zom=*VLoz@){;cT^?C?smUJ;CWbr)}P8>Mt=CG2>;JuaYndZfkmnZWpc*8V}Mv}E0; zzqw+qUdru^p^(V)j}p1c%md4T_X`4NS4U3YDcuOJKyb+pSm~-&>C-epnk>6N3<~63z@z_4 z-XdN|slP8W1C3M&+5Vilps;E%s3%lzIr#WJ_N0rj7TS&Cp$UaLH?M>8G{{k_9uY>zrV6*eyLNm2q&3uRR`qkRZ?D$QCEm5l)ARge@|@xvgkTx3i{!So=R$)C43&SWEh#HHaWn#E#q$b5jm8OA@R@gpRHz+IKj6SREOanGl7 z5VK5{lbF7@hB-YWSa1&!p|&!!eZ*a{T@i&P_ZqX@ICp|xvEC%0W-;e%g^oNLO%|_p z&=cJgZ|k7}r2pw`mqd`7zO{bK87ZID+spGgf zDawcZeEZ)23`V{TL>Dzg2ncC&)wdgMy|+H7nTomhnBnRU$_Hf3Q&?I#KN zF#Xm0&cev~q?6nxd)gCLa_+L%DGC9{Dq?KZ$^|!~%>D}cN8*qdt@Rw`ga6gSVkJYN z0$t`lQ!1?7$C(NPN%@gWUYEPQ8?Smply^eWw>+}<$uU3?f>2P0bGbUpOUjd!j=6ey zDxDbBUb$TSdtfx2x>#eo6FgB3(tD80F}o>}6bN7|L;aO&*n%~Sdu9GpCi|>rq=gp$ zJE@cPi5tB?$BCssjLz1`U$c(puBVibj+4kOsH`d zYt{04o3(asMS-cT06R~Q2 zq`P#6ag!Db3;kX$Wewxo#Jn!U!0+ob8qW*fN&G{nr^@9@Q}Wg{Jmu@W@QpMK%e~ZE zKg5DAcMXSM?KIeXZX@d{9QI@hcmxeriKTPJD3teCP5H7a+k&qq!8E!vJauX?ka*tz zlYu6*(U7fSWQEz%X{{4hvOVP<@HvG==Iro-Y$L>b|Dm{GUxYqlVLyUQVSn#47S~N8 zbnzAP8nr*Km6jzm@f0(AB<2=*DWZ?a?GKFKaXRpl+fQJ{OhCfrct)t1+vn+tfe6B$ zh!x{S&rWR}Xfn0m_s)RPATu-S>y*i#qZY5d2-no88RYO$qK~T2j zytTL~(O#3uv8s41Yjw5iPs+_gVyF(60+(`ykO!}Ym7mrPc=*{hOz?vw1fY{V;Uy!N zf{FV-D&}04jp{sHS0W{Y2Cm2%pp#!DXqS6eaUZs;*$rESmk8cA5Ur*e;z2Bx2764X<)II4yt z7{MD`3&(P6R5*jR-6pe#0K9@)t3p<~W978$@b;~vL7mMw6yG%W z$6wA7KmPKM+a)o4vfay}llp)SN%;3(@ld;XATHvoQ4)18< zj81ELs7WAzla5^bOTW)rKx)Tr=Ft~-{Id7N4~?78d4?~Zqc3{2_Tg>tltz^w8t$4?-m@t~0xtZgOI}?n3=mFd{FbirBPQ!Q z1&f9*%rBQ45$|%6(*s(Kh;NPpFBAz?q%)+vy!-UuuX=B5T3lh(T}Clwk#zMsL@cma zap#^&eJwS57J>fsljoJc+31nuHOt;ibX*5ffA&&)KgivJQqBqV^%rKt!@r6PaL`!V)xl7bVTTQHEw|p3f=Q>P`xe~LB9~QL)YNk4jaP^HmKMLV?q89 z)Oe2sqEzq5{CWkQ>1to{eA<$b*tRs7=3TU)5OU#nwD{9v21?uEIWiaCEn6!~$VP1u zD=?II?xHszR;(=W3xW9;c7x#3ModqcL8y(+kCMG<{l+)H@cv-L;BB@Et_h+dVgp?H zpL)JBX%75?_j41ixQ&(BY8q~ybrWl@r{*N%2qWomsPdINBwJJK-L@JJqAWuh=~?r; zIS=e`C8JA|(MupPGD;UUeUZ zV;q;0{oaiS>eeb$>Dhlt>BtMX(<9owT4%gok=jk;%k!!*QzQlNB%sT z>GjclPh4=dLv#Vc`$N++QoP_XB=#bWMDuCOHSP!7t%NbWgM5$PSlh%4^2j&##&wk@Py6OP_7$650{v%K zs*NRm0$yMX!>QBJR~H4yV0KPSvKys%;%heeyE?CN|5ZB)oXt=rsP;Vh4~og$c?h=dhMA0|E#nw{jlofd zjJJ65AeZP9Km7Ict|2HgK#0|0YupMKV}b-ZJ9G?-E}ffHL6|D-)zmM{)o-y2x2)HY z^4Yy#l5mG+ueu~sq$mY?_E{C0JX5BZ`E0SYzjh6pxZ4}pLHdojW9-@{%bYT4ERiWq zeBOtUs~-!X!f0oLN;))4EqDr^%M>tstTbivX zC_TNv{NgucpGhk|n)yNeyf@X2-hZ#@v8{YG6EhOue!F~q*Q+B3ZQ13ALDXXzjMrUz(XRngTC#CO?Bq{4$M#zu zaYFnB3u&RDl52Z#g-x7Ic~XMZDJAoYr6>p{L%;B;{h|Z8OO^^s%_Rud*xGE{MegF( zp62dD)oN#7js~K0TrPL<;nEaC){_8_B}x(x{v^4_$3`C|^QG;9;?LJVky$QB?lyGD zoG&WRIFj<637t$koa7ECZY@6Wa|WsqTKM}Hm6a68GS_UItlc6f0yIEYW6i8)p|Vb~L$j``04R!FA%u^*fnE_eeO^LFLhdu_BeN{%fP257tH0 zkLYJBY6U~Rw=r2>yStbIOOJx+D(HGDr^bH18howtxegd;?SyaCB@&1Q;5p$J`=srH zVC#xuW}uuSuN~aEJ-zg^n5P4?+=?3>U;Ptiu5w7tKvDhETy@5KVcnW^A(hH%yb9Ks zp3&O{#T&&rw-r{WFQr2dLzL%7!vyc?iYr~QCvTJT!(8HDcwTc`of@OyBylATCC@j0 zch&iG?S@4eO%8_~FOFFX`wekut`j}Y{e;Er6(m$lZ3_z>-1b6mAGdR-jNVm`~cn zB@zK-2-x&*5CK-&ud8>@LFMcr&ohPmC)O*XQIy*Ja1?%Fu>C%UXVOu3x7Bu*)J_TO z?e`Y5%H3a(V=O`JN-C=-3h-%m( zoD+j*5uy6;AOWuypTJ(vvhRR@Jcj@Aq2ZyRoD@1-(L6F8T}6!u z^i{h#kmkjxcCAH&BbCEKVWp`+dn%z=UQfyRv`sbkO(@>`?1M!WA(H85S!_LxLso2H z>D_=MML|ClU*~TiKQNppIy|g2D?oL@eStMUupV+Fj7W^4Tu9f#pY%&Yf4cop=+FHa zB?Xup2sNeDGNGAMCppH0j3h`?rkUDYS&$KN^eNnG@+7+;`&SdH3bFKT+3iRQMOs2l%`&+5-;+9Xw;O^HgAM8;zpW z@q(F9J7m-tRrn;6Iq}kP#C#m3)d>g*&(Vb&iM-mO7P>~hB z;RGncmvjoYTBd8a4Ob$uG+XNt(M}H3J|s9ls88yfPk+Z<<>Cw){E*ivxVEIpWfrF- zr#%h!!-NkQ7zo`!{ETlSP9eD`GGIW6&2Q7k-hqpaBvWubKC|JMeNRk4z&b<#>BGOW z-`x7iZ)k_DmW&RbC$jJeAF$UE1+-b&uE+l37q%Q-h}JX^cR`p0N)(AzbUGqK_I1s= zvwC8E9f1DeH z?T<-Z^VzDGX_~x;W6horrH+xK^|+t90i`9eFac4Z395l z#2|rGbDfB4I=4SPvWNA>XM(mPM{3 ztvHsbz9c(UR5&(*9J1b{pvgL3{?@F)d^=mpACQ1r6C|HQ+r2I*=ak2lM5XS-I|Bk< z*WTBkL_0%K3)T7uN-+~FpTABvm8uj&%6L-mur!Psf4aJK9jU~%!8m&AAM((_x4-5t zTBI1$^KNvx-<68HN@P**3ufyUTE!>0 zgo>AOo(+0qRIX=t1M6+wX1_DS!Fj` z(UD0lo3p9ZDeaDLWWMaCb}rY<@q$S-LEv({+a^ zxTKl}k_%kCHROM&fM5Oprh+&1dyrSd9KmQOCJbT&`E{N+$3TA|vRH+S`>O6Gbi7)m z=;O+KmX)o=WX1l9rQ@hV$m8XT6! z4geyme$RqMifi1(ykjW#y|yQX1~Xcw=*`b7&D;^DkCM2Q8i#Dey0PNr?OsaG7x{9& zT(Dt(cD?oU8AfLXcF|W+r6t;H=2qRJaPEaD++ z0Qr(RmGr#>7=^3l*nF=L1qZ5SzW8Ez-JCZ)mgTT;rEsK&K$oY~Nao|$AujR2C~R~ zOZJaxiMUXc-mB;rZd(L{16+^slsf&OrQ47Stzvwij&ATG1}TF_3mM-TJ)Kk)@;!JJ zC--8t)K(&rrmaB zM^>>wSTY~nBk7Ipx^}n`UHvaeKntyaxe3rGH?xxv0T9;z`s}f0cF-kwW<7wujN{rh zkP5K`vlOyL@?|Lm?cH0%h^>$6DrPj}-`U?URp3Cich3T*w}^B!t2z6Kki<`;R&uzJ zw;4}RQJGlhx(n$dbKr08K6nF^(qLs#5~(VcTbQi{pBD=}{t_7%u6UV<-FaqyVHqYL z;D9q;Gp0&Tc_9c_P0+%+_J8KA4w=mpP(GxL&{f=8Uf}=Pd|h$|vhnZm*ug@e*GAlh z7dA*aahJ+a(bCzJD%D4RbkmofKT_8v$WfKHBgLx46rdg3Xo%w>RpU1@TPmV~iyuAy zyg8Vzw!1pheA7P_X&@tWw?Y@DH{NJIAYJlk)Jgcm^#gLOfGgNdwEcU6N6y*1Wi|Qz zPn<8{h_YmAaI39kDS$7&J{mQtDfyvhPK*Ib759k_$m&se2O;MES=^0S9e zLGAt1@xZer%c%|j4A@etVFfg^B<$dG6&~R_{C^cQwbSVl<$(GgoxfXTCq(j}Q z#j!*Qt)xqzGckXy7xk67pnYY#l(YDe%%klW^~{O+^wCyKaRYuYXxs!WJJef4wI>q9 zY%U)GkN_7&bIH!N;C@QA&GxQ3;v7+C+XaBt)EYp=rLMN(sotJPL6j?FVtpbTET^{o zT)AOm3f~huXJQgl>qxI=@N@ke?1@5At8A1j^av@NWA65z~29`?_i2^(OfFYi{MNbw(TM)x){! zrNB2EC=mJy83gT=P7JwGDI6om)Yxuik^6+O*h30W7@V=i z7BFp`U2drnH`=%7!FK2;4VTnuiR1?RvU(vdKQYbZ*52@te9Wu~b}o>rVIqlZ-1L8m zbA$N*h;utq(w)*9lud14xggG#T$nGF&71<8m!dsrGNEp=SowtPck*>eB3-nEF37*% zaEaVSeg|EDF6kt!(ubXg3+(bo2RZ%@Sr1@m@5SoxkydM#lp2*tg(V8?NW7<@jScH^ zS$0vJml#H_WZ3zLo}VBVFI)D7PH7VO`O6RP4%4;B-iWaJ*(YW944QVuQmlcP0{te3 zBPnj4DDZOnCE+(*3R4TVjh5S$ey$MQt}Vrb@4$FQUN_iHqm;K7UZT}Jm9*|t^ihx46p9$7$lPW2W_VFjCE@TBv&ckBZF9~* z$q%LNv)pr|CsTP2CI<%&#M*M8IE$2tOcN(03edSSTzogUDcPjz&{>Wn%-x^Zi5Og3 zx_wP5q*Bjk7LUAoP%4p4MJ3x$Y_wBaf<|9G-usR(1deS@O5th#*fgPyET2rBH}~jr zD7vb_x>Q3UfQR8vPOH{!lc7Sc&G}sf{`r_-g>1bhQJ+KvfJnO&j$V*36xX~qWBEKg zUqS#j6SjgtW2CfAUGcDRZuIz7;ILzk>S%-hsq3E>O1WTsQ7EL!*Q$_k5rcZSf}W49 z3Sm7GmKYs>SnNF>E8%HD4RGP2#JBUIfuNN?ZLs^%s)Ai^H?yVF*)i%K*mlE*vxI;v zeS!p}7v%>o2;dYGzY6BJba!?V&XAEha_K!OKc2`w+v6EwgH9l_kdm+C1ISSc=n%Wp?v}@ za5#!piy_tho?U>V?tTvh)xo9HJAS*2+nJ5Ptw> zgPVr;#WlWA%k3cjh!lcMLV=jVPpLp6g=VKKSe9Ipt1(ExqL@e6UFdot&g8fPlA)Vy zS}%BwO1k1ACkQ_`Ro}+5EOI(KmFwGpNB$;qgR$3F!BO@4ABL_E9})N*Tg-vKzB5Ok5g5EDbd=3;&ToNl+kY@f&z& zH3V)flkS_9k)69fLbXX9-&0G;S)(2KkhNzic;z-QG;nhs(IcaUJ7i^MH89z7d&Qzb z;F8!t1$|+0RWtS*GMmd4+@e&E^OpgCF#-Nm;(y%e%^bk!7bCIxsJ%G8B(BeSW&s}} ztkO8cKAvL@2?LUEHw(5eri*ZKB?)!ZyuWmSBe{0ubZ_g(yk!zFV{(#v)G+ONR`=*qtpHVBZJF1smtv)Y&_NjL~xGote0=s_(g1DxO+%0WbOlik7 zo5GKw(cNJTNRz#Iq>Iv17>=Z@vxK|CX9+tdzlEylUT&XtkO>J5Y%3H!{4vw@cqM`P z+1dN0X7{e?O7X{iNQw7mj^Xll7;ZN#6>Y3!vI=G1yd>g$@3&yrek6wAV9Y^)?YeYY z#Z-B#rRG*xuH{c`>S>tz*Fg5!%A(6do(VVk@{cl>$LyYh)jH?Tyd^8ewM&{gqI&AR z`2MX@Yni{rYTiE?S1`i?mD;Pmp@vJ>KA`r0Gahrc5+BR_I!b^yFWkcq_P&4@Ixy#i zVh~;g6B^G73n3A9WWD^`0P@?ZchFz1I5 z+Aq*Tx|fSpdW^e!Iy1eUKnZjYvQ@A6_F(qe^#A~z zA|5{4wlJmExm?I!-c@#Ra)LzL?I}|iJT~($N%6WNefk*s4fc1cP(hN~)p6EbuwMX< zr5f`cbniTN7A7{?tUeDX6_hb&4id)vh5OZGwL!})+rie9qxtxZPX0ZXVEe_99C(LI z=)^?K_Y}(UPr@;H#(rU*It674Drn2a#!_E(&s~k3C=F84L0<&susd91Kl)5*8vziB zG(;lLk%YmU+9Mq6J)fP>>+)eD_- zut{xk1>F{2FvGnWV3C8t$_< z3SVM08-M-eQbNH6fCPCfqJo3K&PD%e1h8g-Oe4DaS!}c$W%&| z$-NvyZf0s~T1dsMI)UJCD3I`fOZ*iAW%tzTd-ms6Jo%%2MwCIe!_mAPIYQp<*pkg;n^@PlpFp0OvcJ)Z$boZ&0C|={rTGE_$lG>CEunaZ}{sd|625iGrb20 zDel*F^coQox|WNR5hT#cZgTuoGDYPfQ%Ku_5{-^D)#HN!w#8BFi@WNl!v!^XeQKE*&WJqRafwS=#p} z`tGw^V4JRCkM>PuEQJKR3T|HZ)-y_<*{ScD=JF`~CS1NL`~Z}TP5k*LAOHX1>n(ut zTAH?9ToWK@aCdii4{jkq@L<8+-QC^Yf(CbY3GNQT-Tf@~&i-Hf>J(I=^*n3M^mO<1 z++ZQYh)RE)PyJPZ6mU@1) zECV|=;%1Le_%lW2z!Jx|JC;rLc$5hiprg?cvQ9@?9D05EH3TCwW^8sCB*dZD30ZMDa2G}Z)&}KY{g@P;$NFs zev2*jdX0v$Rfg)>cW0C9v^|kkVgJuCI7d;;?$a|67iUW(yPG&3&^QsDFMsIu%YOPr zd@ z%DYr)T41FEeh!q)6)BWhAl@F=n*XHo-H?lRx!$j2{u)f;XnIcQzEm7b7 z_H%ull?;%zV{!Ht5vwULrnJgnns0ZBPP;zX#edt`5NqiRf)SS1?!MQkQw${(R$f$< zN%^(Ae7d-ReA74GLzxxFqj@-;?San)LF@1+@nDbM_RSVf``d~}5dK0rEzmfyUgHa- zIkMVqLYB4Wr6o`2kEc_Wb@+z|Q|+(M;%N8W(h$6WKM4+ly{NxW8mqcfUK~F?@}~Yg zl2q;<4xi0N#()PgOn?*&dxx9^VU*?&$eWW-eTam=J$_Dhynagb6Iwu!TI+>GB#a$i z%6oA0J$8{oXlI?OSex;t+q;!Ys2Lz>yY~M^64kX>^@_}nS!|7*uzmfI!+9X82;3rA<(pR$TfT4H~m7%WzYC$2K89>ddnCC$dn~RrmkW{;*`&&E9W=QG%ap2rcPqA8xpjv;F3qs&d~t zl)f(74 zD|An%;|h~1^Qj6s>1ZqP*&uBds|P#SZ^-#Dd~SwMF~CW2pT&#=_hE;eT`(fOLu65) zHKTRfLorC<3&Gp!b+T6DvSUCSPS=1%?i)#!<4s3Vu))j3guqu!6dL7ICL&z01Px97 z)E2KDLZa{V@q?Awt8Pr{N3oV)(=Si(@%8$xO;^0_gr%bt9cs+q^Gc;#H9Z3yy(NKd z*m+z11p;#jInA}bbG7|Xu^hhC>-zxomxogImyk6F%Q3yMUHTVs;k)dM8iQd?=h5R1 zCz(9W(0Pf_T#+=JB*@Rq^W7Z}ysrBLIDrfUDqkdMoIU`_N#wg7>Y`i;8b{dZ%O+bz zr~N*e=?}n=Gpjh}o{gzE zcMHkm#{>d%J^KtDmrOp@3s-%M3>zKGtEpaVk!G9#0l06##;pe^D8VjiT= z4(o}PR!8><0Sq7zE{@SV$Qe*?mpW*+xw5lvR|R|t{S?1vYEg=OB6?EQX}dcK;ww%| z?LF8QOI~bNfAZZk`6zQR+h%nrL8h#fv2(R`I$84$f7^A2Vxa0mhlqPHDa+&4Qj(`K z2-6?3+5$K{YcQm|q)Z_WL?#JSYw>fDNhMpDc6Kv7lP>6kIKmYb5vpEuLuZd4i51^r z4G8=I+Fut@=2K z_D2i+7g*_Uo?F|MKSG0(gjy_&r?iN!9##SoDW4xtw8*d6E(=whk}v1V72dSZpQ#bi zelv|6(s%m?mkZxq?XR>HDpRW{!qQ(kG|MIHSn|)Gvz0N*nR|4*{bon7#CqlNlqFrN zvDc^HFd@kcO*(G^-j8J5kw%CSP9g7oo>X*~O29Uwt6LRHlEmuLasnKyWwwZf;w-TL zrW7uTs~(im$w9u0gfYfDBgwDfXEcdK*6PRM>t8+*ksvPUm91`ZnTDYy$-F zJ5q)iaP|dNSILgV2q=#x`EVIy$Sxsr*fug*6o~2_$W4GE^sJiD$y8{E2!h_tP;@f+ zvZZYh?enZb^;|>bc71Ib1X*?ad1u3qP{{$DTypqJ4L3)8o~#;!AuguJJ8FaDbc}UY zk!o=WM6yX)DHe;(K0;_$XXM*$JuxxD{mz~Pmm+ZWr~QuA;DC=JB3z-=gA=Q&I7Oa21djeTf^LB6E})i!~OjN+ng)9?4d>2F&yRo`H-R zx(?stX#|5(%;x70V%qFJnI1xMx4z-GpH_kVngpr6+HiL%~Q44!(Ec%*KzoNf_~E5T7>(xK_! zoY7oEs11-VwOX8GLuyX57J<1NK`~gQ4%x;yrRn8N%YaeMYgrlL&bR$RxK!)^mx~s( z0#=8%M@Np)KK;n&6mq3{oCjgjR3PQJ+xLUL0uPLuCVPq{lir&>AvsQ)5{?`TeCscM z{VP%f(f4s~6Dbc{?Wl;>d?*f;`?K`ion%Yu(dH>;t~;yCyZ81k=}ZM0wBuMzN#8$P z!E|2VtoseX!XuI?j*H?TRGb@S9#T<9+n6EDx%xlKFmBkK-NDx4#DJmTHYuSUZH)s$ zNs92x$Au*O*`fI9l3Ue2lDUg9wf0{cH1AlkA;=7gzCiyUEebFKfO`;W(U`_^quL};v z22pp`WC?FeG@v;k_6X{aop_o(FYgxrDqb>N71?VWRtNt? zpbdfl+msOHiN6YI=QyEkf2d4js8pK%hDP6D2>~xBLO7raR+l6bMua3R+zFMjiDr(7 zwA7UJ(R{mJq14{C<(U5Z3#&~7NwWwfk=DVN3cOlumU&W|=;Nj_B9_YPwS`AvDG%($ zk0%lr!A{>6Ra=*ZkR@Gu5z>wO0Y!eeFaPWI#VqksP4Ha$o7nAh?_oqxE});#9jLS5 zfn6DoMB-#$n36Lr>?c@|Yk@L1I1i&w9Y!S7xN&Up_b^?-a_PNyXsUmHlhLlqjR+BO zuI@En?hRXSsc1A*k?CBB&xxLxpIcQYJc50Alt@L2I6jyzcspM*8xDA%eSy9TFT(P6 zTqflm;&fN6T?JNK@;j43X0E?N4v=l^6F**PJm`D8^p-UoVKPs?%cx8n+Q0yc=pw*= zQZBWCd$rPWcz~MdZm)|Fb@pUlcU9z?GtJjeQgRnnOA;#A)H)JZ8kWe3v+ucPg3c!M zNo?j;d9p+jrTB+v5wgC=Wwi&lLgCtcTR8HAl`frg^G_Iy<)Z}`_6s!aXRtuA10J0$ z=tL$_xT*1{8`lY__cPr$T1RNPz}b}#Uk46C<3!hf?cfP$%UCIYqUEaeHQ#ZGYO9Aw zPd1&{lJt!f&I6`YLXoK@Y6?))HKu2I$Xd~;$$ClWdh2MSRtS6BY!uZaamJ{&=wEHs zNA%kSG3N#&>8amZu(*$^r?ffHq75hevc1DI8`)G(2;M2@5u{lq9{F4$ZGwP*L730% zr_GZhw&^X$vn?yLy`Wmo}8ggUTL9Y55x*=$)X-RAatrKKEL)N@}duJ&j)*p>0-DM$zCAldzL_nNh;tzMYx#1U4DUjz^tjk8QRXoYS8;bzm4(npN(~4i zeCv;?6{&32_$VC0&}y303zl2n;L6X`{SBfARWYp`!6pjcL5W;8^Ii^5?gU+A=6~$= z{;`?V)rT>FJI0L&FL}s(?qiI^By~0W&vfy~AtKx)j)|QzaX@g6!}UlWD$P~Odfr65 zN%0d?^ka1ycI0uJ$qBc}m<;-Q2sxJdCaW=gq>1~&*OPAbbLWtiuMGnJv{$uq@=M+m zkHKmZB>gzu?(2z9*g}h4*Y}q^C4{$iP8@2(8wl}%ByFD5jQw&Y?gGyI-*YeMkJloH zuzNb1kUqS>UXKd+42E`orqnKt`CTQydMpBPj@|-qh0*9upj!b&&TnddNL2L4oE#P#r{o$vxx6}9AJMDf+tB<|P&EEbxEL{_DwhEB-+;UPSW8QT8n$L(C^YacuzBH2Vk&#PTAdgeR= zpD{iDw`1fmzU=w z{Gn7{Y|<)cFU$J&JpL{A_%W?E=|pECVoX=Q!5Fspq_R;ROLPj~Q>B&hdF<6?@OB0h zW(v6%);k(|P~1E(@*G+?tktZdd+8N??wycN1QvT%pb;rGhfqJ!tPew$H->fmkovl- zfAsKl(hVJ#xN?@-nlK+vs-+l_+1^prnyU~Yo$NF1a6A;Tw@)~M;4>uPdKdM43!Fjb z&o@UTr8kJ1Z}Z>Y6K+#_bZhY`U6R}ZFHZ~jvV<&o$>8jTm&t8{MKN0 z1&B9uGA$u#zfUjQ8%BQ{j5A64oN1Q#LC_F2xgD~RO2tK0=gGh1wa6N#5*@!2K>wS5gjRdmqkAWxEl*R@HQKWBd(1;R(R1K&HCj+dm*g#Bb$VapiSG{L)_ zpQ*}9MQBgdAl3-H@V>s+-tf}!_efl{|9#~z!YoKKZ|4{EM|El~2{oX3+RG zh|SI2jKUCn>-yp#0PaWb3K1Z&ynXpY76L>TgvyVB0Px_Rl%cAbN8&AZd#@u$RZ1ay zNFLoh)MitaD?-DL;PV4-K#A-uNYH}()^4eF80i9_LPXraz~^%_{j`yrYmIe_;Npq+ zXwyJp{1ChWVWvfmK+R=+Fc9cFgkzBNAcAiGmR#0MHbk{)7zYnP@g|Y=Zy%6cKv6?5@xdB`|L*iFF{4 zx!RdOuW=a1b9uTdE)94ir!4LaDV%<$oyHz1f4^Y>3I<>?NaZ6L09cLWM}a;6W)jha<3F$z~VT-h!`3l_dbxyBq8O*z&KvZZw$QAlm=W3thGMB z6^6ciPBojF_YH=Twv)znLk?br!s!4#2Dt_OUyzx1(IjGrZ}2fQxY=I?COx!wqMmeH zL?2;9Jl5t;q?SofG4+f!Gq%;H=1lbEev^|_O5eifq{C|)88~I5NU@AQs z`KJM6pb!PvoWfa-_=*yM9Vv1>w*LKRWU>JAPmbg|GZoSsq{*``0Su)~u8JCJ3HS8e zJCYUjZ6#QqvSzzy0Uuv((_=#Z_WR5D05FXIEg%5EFu##oBWb^ZD(EoUzh2fGc2NNJ z&7e@5<~daUY?mtu0o29&k0j!sz|`OH8RGyxqjQ{UwE678bL^Wx+|mlZ34jR$V#!lf8zho-3dO1gx7$vSOK9_gP{fq*w-6c(hD36J z&<5;t6$Dsx@*6wq{$O+Y@4pFj@Pobf28%Mr=AY3s+jt8=kgp>p^=$t<^Yj>=^p%9z z^*R7%_P?yx%WDk+iKyRFc>o4rT%o~s_)03b94OqR0kweYL_BhYKkXxQz&vdVdHYic z2)Hu9juZpp83;p+ZOH#)6EYwS^htX|#}3$vT9W~TMfeGrB_ULDIQX?U?8@34bnTz^ zYHtA7gh-|!5^!jnGw*j>mUeBy50@|9_)VsVMu5X zpiT_I?;=I45P*Bj76aCVyl|*bH$Fgf>RMwJ4F5A51aF)?Hz)!ore}Xz>Bn8NqNBm6Xa*~MwHqu^=&`c{%lZTLS<6iIfb}({$yP!+*!kl#V_;Z`cYS-0M=3wJ zKQ2w62nFzX(;v1#Hi87apiy3_4KRtwv6=<$<$77_9sGTc*L`nvPpNSKU$XQyIQviN z0AO}cZIy7Gey4$nug^%AlkPCmPo5EfU6sNc{Y~LGM123!zk}qHwnrMzA0wmyt{{t0 zg!J#l5_^Lu{jvq?WqbiL-}e`5$IJpjw(nnA*q>n9%zgG8{5QG}h_nA%p*l~0fPVk~ zzdg(Si>1d(llQ-q@ZW3WKUa$gB^a=t$=Zfw3N%%rN&3Gu(CR=+{r8{)(|{xiBQpLB zCF+SMDGOBy`Zh0w((4Z`%2#$$M@V#Ny?=mrf4{uI9SIWmR(o9bYxkKN=QJFIP*O+x zx!%N2RK^BnIrpH)Y_K|Po}{3$o{z}=?!0|nWhVwY@BgPi0tA8x^o=goDcVE%W!J4ikk#Yg)52P}tx2XL_@Jeu#PNo!!Au<7fRbcn=l<^t!-mH-&f4=SD{CniC- z|8W!oJ#PfGp7sC^a3Fd4MGUOm?0$68e;-9Z{x_RyH}%2t$Mb$;DB*(xEgfove`ON? zF}MOG0JKk%q@{?1^zC4k82BM5TSV~R2lIm$U^gGuiLyfo#R!7HVsnoUMfyjoUj3kB zk(y;GVE$(W2you+3rF*vGGOTh%Wpp{Ya%QE`#%z(zAefSD+Rj-V!%0y?7A!*ihv32 za0u;vabcKf#vVrkLm_@o^Y1tH_jQY)1GgrUKplJuL@=25Z}$nM5(oQlYYs{4?X|Tk z-W{M}y&>kP-Gk1Z`i^NBJ|L2z|3=DcVC(tPuu>Za`G3_`X#md{Orwgt3s?&h)|;Dr zUqbQk=ej^b2;5JL7UncH3k-x4!^Wh{15)G`XZ;eqw6rUL)dTbSCkp-R498nS5bzcf z)|l4Fz{qC5u^{EaLiPUL8oEf|{ML?vV7^eW7toJ0uKy9JE=h(w0!Pdq$f9ijbFtnq zqHi9^zENHcm^lUj?j-pUyno}5*ZYffr$q(4rWTb44o?_t1HAO|HDLyw49?PfFqG3; z|BfDRe6#-z8$n5LBak%rz7`4?U+lN>eQ$*Gm&^Lcp}M_gWNu%fr-{s9fR}yZCWDub z2%7FG?ETWvZIqFizH$HhBF=BIB=&lasWOlr%0w>rQeBFAw8igy6GmpSoHtagl*eEo zoiK9B^y=}F21TPO_6mpOn=h9V$493+8->h=UTC@S$uW~By9IM{s=3kjQIoP&d%{eu zxaY9q@r`}=pDw}63+atg^NC#MTGfr!ergi$DD!pkoe4W7Z3!^@ZyvWPn{}hAQk3abOBT;+Z4% zfsTci%Tx>)%cSyPUhCnpQR-GocxdCVGuLY*+~ zo}(TBZ~==Q3$Sos4`EoP7sFf%_OWN^l!DSv8?cE@USnIehE0=O-p5PG_nBs7S;Xnn zw{R!oy*?)8T(+N1Ar>}FY^k9;9`?$pzv{4-HMaZ6KD+yf=ny5)IY5o@iDZ%C{l#=@Ep#=` z0u8XkDMqZCiWH$jBtuGSVBg1hBAcCje$)(}nin3pi7kWyL%~3%`41odwiJNKUi3aF zX?fD)mBKE%a1E#~M9yN1U1WMqUphdQYwx%f64F=QPNQ0)(4HHS|-Qi5@Wm-w%Aibnl zzM82Im)tPK1VcHLvQFgv5YKmVCd5Lvg{VxX>wFj{@y8i?%fF@b6yERqdxw7deBd9; z)5;ZFn7x?8oGSj(=2o{vd=LO+jju*JlnjiAN_h=INJP7s&B?f9B}7#z{%{kiCW@sy zK~LlWe;pCf8 zDJcMkXAMBTtML-v%xHE|!To8vB{u!K#b!}RmTkv0SG0qi2#ANg;abk2jyITG0T|{* z!OmGn|L@W4Bu2f4V{8>wwv(lPU7HBS>Ym`etrUcfAa--}M)NxD2 z6V(gFVu)4$Z*AjT2fu+9lyfjedP!m_q}~PYfkT`Sio72i$yj#V75p@zm*P5#e7dAA zjf;(}C3fE?g7CA;r!=#UnG8iDs;qf0J8B+Ii-i&Tq5TSzrGFa@5z92G#`=i^GHKFBw1j1 zl{MIfWXZOjdYC`=H4$I;+eWfYmsljiQf%O_UD#CA*a3O^u?h26>K`UH`^^9 z$Ec>bW1VZ$)`CiIC>_~K2UOGpzhoQKbuEJ^lFOROI%uqQTZg{3IETiNI>oP!rl+3# zY*vFM{JB5R=yGv{*_zD7%DP_P^<${9YqvgKU)yvzn(Y0~Xp|Okl=bzpAOC87Uz;kD zpjf4Wp|{2)&d^)(Ax|>-oN;lO&SvVOn;h0r$$z1zPDj67LER=Lw)BO(LR0ma&~zy7O3=HWf;!QFjTY5JtqC>U zcyiwSXI$+=)Rs(u-;WO0NPQX8sYm-#b^pb74e!}YY$NW*6UMvnN}ptArTi9W3TGb( zuIBzio;{6mOHbT3yW}yeC7BWHO4~vyaRb}ml|18(0Asf|#ey9V(xxi}9D4r)KSdmy zMyVBHDGh3pgANTrkvToZvF7Rt3Y~^rMqa2}#fsBilp^V;yp#QUCc|p;OPjLo6iL^? zLHCZasX~+f;wig_hxfIXtRAN0se#$4ZfUiiI0dcCFWa;&dJEOXu2+k`k0qYbZ1Mr| zzMsi%%a^;wIoTG2OXnzrCrRQ$D++6pfnNUFMM#r>Ee)Uolja48$1M9Jr;6b*C^a6g z>~>~p)mv>b@+C9EuvAJUlJD5Ju1Wd-`H`Hp4tlwTkS77?_F=5%@V9A{^D^a`WcO9P6^$F5PmMu z5e(^XA7;O-as(0t#7rKQN7mx6!2rHkeg`&~Kk?3Sn(gI1YPje6ETwh?-CB#J1|m*Y zI!}S8fB*XC`#iy3$5xm5FGH)s8E2>4U)6q6kSYHD{oFoHW~#?UPhC-f~pEt&DI33U2hZvli1I=T`HWcFE^%LB=9antG0{*2wBOp6m^0E za(8X`k=8vBm(w5(2#|9Die2=FU23inQ0SL&z^}NSf6g8*#Z)vTi zbZTOH30KdvyQ)tyyBM{@@wPL9yHj)8vxlE4EGhFQoOhI3s%aKlL`mamm46+p1MO`; z7HU&PIKI`0oNvX(SsFabrDetL(ih3x;_>HI z;N^F_MTUN%pkGTowQ9%}Vry3o-x13*ump?kmP4FqG%}(@fd?<=Zx?7Oj zOd-KzNLWslYv)~%D%Q`>=90uJV%chxGLA#>#Lab=bC`CMR5&f{O6=>Q5!CC`Eh3Nx2^}jP%>dNWtA?80bGpTG zd$)^IeLx@KThjhH6uI~8MS5Z@OAEQ2xBb&2-sxxeZ}B4d427jNiQ~7oi~r~by^fj^ud+R5X1Pq0IR;~IC*WfzC-6K#<`pOTl$TIag9)w*0&bRS^t zQ=tdH)-{O%{BcZVphXk!=kYya@r~*GBA!t79Ogi4cCpEx)LfMrK>RXd&t9(muuG)E zt0qSJC-pO3K2mRcdBUon;4Q!YVO{NN?pPkyQt7|+nET4BEIDx}GddK!dh3Eqn!C(w zBmGnIW73xiz2sQmmbQ~lst`yqQyI{C()tMFgb|ondSh(vBbho$3 z?Q&D8=QfI+ccOb`syvT|=Q@|WYevfFh_AO^P@0Do2|J5}ym~d0u)}JrxzL#}vKR~0 z(lJSrMIt^-Bs&AOEB8*=Yy5JQz=f7r#_+*G{n@jw!T_qnAM@4zcswp5{!SLo&tvIV z*sk(!)cvW`Kh#;TQ=fKshW8OOEXVp+$(LBo*3Y?b_{P1>7k>inoy!Xb8SJiOw$(&t z1B2~>QW%s%u=n+0>Cb(a$%*&3)e9vFR0fzfDi_DQYc%9Q`xyyOyK4S7$=bH(+cgvX z#Q5M$`nV0}131e-Gl|Z1yh|K_7slfx5T1RgS}a7LGyy2+O~7AoxLx;&3}jOy4$f=? z+@4xG&4@m#)vTUx2AN59ty)J9c%mOYs?1a;i@41UT zCq7tU9nquV5{~~1(_&cJQqiRxKX&Z&#Yi2#(zOpH&ty0(u4kGg^ZiD2kV3#u>cyDn zc2{`kM#m6{V6Lyv7bSGB1LeaEblm3*sVBhA4!>sl`)q z3_~q4JuoO5ZbcA`edg}ybT*pMwwfesIqvNWp@aNInl6`12E41K-d4LIxROWfB9qb7H6VEMnIRxL2SP&D;@XkeI3u7wfPfB(^ zpehIuzob*Md9H&1-NJH_7bGO306=mv1W^l!>99_1I_xz>>-#Kr4dR&L)T#x8yzOTa z?Ap2hzHNv$FxkmZI>jqlod+LH#lrJqEO%}>GD!Tk88tlFy$mZf3nOfey15b^<1tYs zQ%p@UQCiV7YarMKA8&pe{Ar2v8ai_`9`|Wd2GFi3zW{AEs(RG5U7mpMMDcU^0UUAO z@X%OJrLP;mx>_W_4+MyLpHq1z>tr+b2)XV9{l7jJ$`o$6d8(;7dYJq!{Y*=HYFhfV z!8@AT@vUQ~jpcDnI#MaBlEi-#gEV=2Hi79gmHNvAR6PU1s!;BL2Y%t6z_FrTU0MzQ z;}OdYhl(V|>BAKnS;OTfTRf+h$j))BgV%+Xb+SK3SHGBSk<}({QzuwPeFiB9tbXzmz~fw8rET+tg@AtYd$ba}oII)~wd#av;#zX%g@%*P?*|T5 zt)+0ZvGw_KO76eaZ|lxpiZARo7C2QC(m4?oeLli!9)5<;)I5+bJ*B^$uc7R>AT7U` zN9ytOR&CD)#wG7=VoNFZ8*LF*edN0;W~eHbS4RyOOVI1KqxCsH0}N7hrc>HmyXLs` zFEPZi9}ym3zVkoqjJ*R?akdV1q;9?GX9(0Dr%Yc-_( ze35n}e>Tou(O&f4lB-y(`z+()$z{sfLQi`A`dkC_Yo?{wC(T#G>w+%vfS5;+mCMeq zEER*P@R8ru2$Wu>*8dzbD9DFkg-{bp4}?!WFJGxC8e#v$BI+bgOfecA)K?J|HCnz2 z;zMzL8?WQ|itR~qBeU)4vQ62oD>M1cf@|h!TBFU){i$Vse*9LS(yEzSS@X1uFCOpy zlsVxV+Uw1|!>1j?9ML(2T$6)pgSbfY29`UoH4UhIA4crGxdPT}GrWtnVna;rgzm)x zSogGrw(w7Ey1Q}#vOlcdp1Fon2z`ftOY!LPUApF{FKOxD?RF?W8nL66+*&(ADe@JX z0V<}6TGOXb@rS0VV`HM;{yH5!k7It=IyoDVEq9P-z@q{J?MFJb$9^qZwkhy-!M{8| zKln8;M9vH5oOK8enS(%T?_g)!wY9{t=z?+ej$vRs*P&^#EqQ;mGU$MJgh+!2Nh1&f zCy*Cwx9Shw^~C2T>%cL7WNq_)dAS8W)?-h2&{Q>c1ab!0 zq&;()P_+CJ9s(FgBrhBoj>;bg>o17NLwlxviC5gNw>@M_cbBuYLknfii>k>+j~#=M zqFoCX5P zLX*ae4Qia+?ufs#vmhR1zQS(^9Zu8a;xgdtW*{D(-F-v^4zgbg`b;7E>m4)40V)>9 z7hHH~Q~^}$<%X#{8+;DzT}aPUUe6Q9GFp$RduD@g>`{3`$&d)N9G(zg^+Dbx>+-UH z1)jys|C9Lxjt-LA!szd_b#J;cG0&CPK${DEn=*IzHbnbNUDs5bdPiV~)o2ucp)EfoY%ksw7t|Ni3rS^%~RMn>vXNzr+E zZX*24mVBCOV7z=^slcW{rK*5xP@}mVj3=Zz6dy+l#JU4!-(T_cM0U+;n6|l z(C`=^3!iblQYwPDcfAGc+zAE@Y3zY9H+|QrO9)(pg=TAhc#)cm{8qY7_ zX;zpxE><`MRpU3uwpx*VJcA`hxd9Y-$K4toc{)Ch`_3N8t-juHxK>mW&t~e2Zl=DA zEWA@+cWAP16Q3JDf}S~X3ko`HIQdWZ$P}iDI4m#7_P;oUfj9VqgM{v}P!(BThoe!A zhSYlnp9o)**f*@y)ZA92Up=p_m5kQLdPub1L)ts}!6s$#wX8$EyZHL!?up6GA56T` z-7J7onJfC4g!FyVw&#Y71D|yZPM~4(W~;O+o>q3cBcBm;><|l&y9CF$>bx*G*`ASw zgIHsijuci~fZCQp;U;~8BK_%vw%mqk>TFXNI0Iwn2f4+RonMgx;AGgAWd?H@_|ue` z1{?;6YmPx@P|}84x{g1F^;K3Lz;C5TNv?}1n)7=?(Rjp4ZUg)4-!5@kd?sdk^Cp8h ztr^PZAs<(WHD=nL4Hh@z*dA?d7-a%phwm@uz4zHZD>9?`1DsKYZIoeW6lHsSUa zV7FLPkRNL{GgfG|+edN}{<6pt8GdBI4;rg4=UXOwMd^k2dvq*FG@CPN-QRW-BcrfO zn!gi$F3h?4R=&SiYPvv1v-wvuXLV|`3A!-6W+I)^5(wVajyD~%!MkOdfbul~aOZ@C zna-XcFd)JK2#(HVn7U!K!JZ%FZqh6|2q12W~ z1PLDF(J-Vt44q%9k6el)oj;}3@kxBNaws0c}1g=~2qE^n~0ZP#H^z)J&JH ze4WW?Me~aZfJOst-PUy_Wd`Yevc5!TeOhb&Z8*u`3nd)|SAe5rXGH?~v9d~vd`D-) z2B~6it8xXd{0Em^Qp#9L83JZ6n=&mX*Dr4mvN{6^J&EvxG1uGvP|shk#9~yx zK&>Db*|vBJeO-(?2x{?lhK6p;Rb$@$;p(J!XCAZ@m}l0HQFIw4LVa^68y9 z5tzT5GnTRrW{SX<0uMg|)y44nyJYp1mBK`@rmMEW zCH_Lb%yPxjpDC=_DVFVJjuRjmtXv+$pKC23rGI7Gydxq+Lwq-J>c6L7;hMKVv@)taGb$crK`8khh@?6K}gsKdPP;=1L#;29>yLeV~*|< z513*1tfix26fF=#XE13yH8`2vDDe2%j$?F>D3Zo7oTc{MbX;Vl?p=YmWJ0(07;_znE&El7$hH>l;2ztBv?fLvB?PEJmuIq8RgWiksuYqw=g5Auus2@SaeLqt! za?9B+T#G6*2gj|FR=M|K3Bz{dT&5{ZYCD;)G)OXL;ebaSpQID=u!aLr=;g-i#8_V1X#j zzr0{?1P>)Kjf(6zn~vfG6Zg0I`Q<14NUz9t>nkb)p`_E$zMvnFnMl-%u^Db-T8fTAde*s-QsmKCb7 zCvXdrnFW&7e#`;eyRzY#3wE2wPhbE==^(Yj2JP?{;K~*=`d-?Oct>kCTs1eFzSoy~ zsC>1Zdhqdy>}=_bj72ABIAWY5{S#wW&Q>P*?fxaVhDr)ft4y2dpG*Oi8?psbIz_cY zHrC~qD6d6QvnHwJpgRQ3WH!s&4;E5#)taZ>BAwGWq;3Dsa!DaTShng>GJms|a`oqU z6APlmPFYIrbY0uiSd(ffI^AO#wU<;$onu3)3w)CDw&Sapu3v6yWe(4y8&5!_JN$LNj>IEc*IRHT?SWrYHk1cK zCGF)NwY_dE&`>Vbs5FaBmrIsOHNE6Am^ULFi1OmRM?k<@2TPov*|pI40ut--uGWrH zbyjnRuZkI4HHIR2^yP#CfsO&{j_<=iXt8gY8^1HN?087?XM-1Q|U)E3<4vFlSR1(z{k&# zM&vGVEA*Ou<%lks_s6HLMqp%ZtEYyKF3$M7k<|Qsj3|0?YpX-g;-^!)P=Sf~0F~S* z(^>44sG+5$3kkXFzz5)NU8`r)T#bH&h*G;f-4b-*SRpX1es5jo=+lW>8_kb3nv}mB zXT`6X#6tKLYSMn?g7%;2`(t}d!88|(D1hV{rZOs!zB=x@|4p}B4U6u^XTXH!LYir-Z4Cd?g{SXC zB>kFPPH($WVy!b~=E7-?6*>B_WGE^t{%8<2TxtO#OG}MeB)DXFq>IsCEjTXF5Ydn^ zrCO0)jzS(S+g}s2g0*z8BqdQhTW75m#e(;4{Ks&Y{+uJuVE)6JXxf6NZ9)dhxcEul_>hvcO z7llJ&*(h=8nU5B4Yh_Gvb?q52Jx$`KRt RN;C10Dxp;%}fTK>(RA9Z~f}l&jD9&%Gx>#9WR!2-k3@8Xc9_U)v)X+UK=YX z(ix(7(5L?GOC%XHqN)r64j+x>a4#Wm1RkbDEF0UDXvVkr<3kd&$mKGB@nk%eryFyz znn`po=TYC}%gDJ$hPjs`kd?ayfir+Q<#bnNf}(IF`QcfA#)oM|O5||gXL|0a#%R>1nwCP$He9O6%RZdBFhYm$DL@y-IW{CMOD9UdM>2fNzcAWz}@2 z)H+}cd(;_(4jIK|QfX|^`#wnGNk4>BuzPCd3KYK9dC#1mSMRX+1~azwKr`s7uN*L) zw6T5#iTX{WL2;>h4ZG{Gt^e(5mQS-l+BcGIjmp2~ zhq}=(!+yf`Qx*z!O2F4sVs{{8g6d|~Q4EIugi&i@;-J0qIH=*7S0}S6D0ckKS6NNc znG7$7+;qNMtQ&arPm$|5T++xwtw!Vh`WKEk5~-w)!TIc0m?yD1T-*6*fY}`vg(W5Q zkLmo`)p~)gf{+1EWy(%IiVOV3AjyhHpw3a*zBsE7r!jFke1$likd4WUyEBdO($Ox7 zc*%_Vm74BrV~&||)hQLf5$$q~8rl+WU!vFLfVSWXF$co0u#E7X;tbrQ z0hryG_Iu1-RS&l5VneHuYC>O?MHI(=Ad@2J;kahzS6bV~wCMNb73NRpElmMosJ34I zIMhk$YMfe2Vkf!Vb|A1cig}og}+@dP#ufs+@90-#06c z{)iV_7}^%LL|l{-+eQVtn2xe-j>0~#rLO!yPvl8`g0mRsV;Sf?meU;r<#ot8Z|%fS zPQ9ufd8#SiM-H1GpXTh7<%a`~GL?Qim3xlcacZ{#?%lp_H14@EjV{pB#vsP#$g9+^ zc^VgsL9hw2gRQNnmHc*Zb!(kbn!~v#d?%^uyXv^JtQtgrFh`Y7|D*g#&$IsYku>j4 zozFF(%TWw4@_jYbO-0aNn(p5}tpp)!etn2p%rGw~5ps!#J<{8teA!W^s>fTgLzhG% zCoFWafqY5hriEv}=g8BW&`qopJ^lHJP~#h{nR@5Gk@2;gGT-hKMO^nYbbofG5E+?H zwa@9yvfFryZWl55RCrbiJ?BgF@A9yA1hPk4PdWNN>-15xI>O<yn|f2ipxaL*whSV-x(C<@;KK+Hp4zPjo!MHeYH?z{pIUS|AXJ zR-rP6LEQGdl%$c_V@=P@GFz;y`ohUnN+;v^TGLNs&Uv1+rVM2VwnuKv<5Dsq%QmD^ zXZ49)dFMNO9y8{_$g1YSAQ2r6{h*SbI>Ghv={04sodd!PJEZhmcxJ7mE;keKHk>L& zxX|*j&)J_Kr9p%Vdc~FBp)gqQj>tP6uq@hbQ>bY-X^i({bbRYvb!Txp<|5zLEt9Iy z)@$Cm6J+}|UjXW*>Kuh!j^5@6CHkX3OrN`;pf^OFoqO)u`u!@cbSyTTmBk!9h!#j2 zfyWML^EW}~u^QWm<~T~Pe7Gz7k(<=c#@J&;QczSE&u!xeI%9dH{mfbBbmIc2$>*RW z%lAolHtk!t3*nw~Ud%kbP<*>NjXuy4EIN|~%92=kB9j=tL5aw^jpGw;>}|4| zonsP*d@gr$ZQWEGS4@bF4&Yy5f#@O2!`L%T^uRjKG*hMjaH<+a01p)FD$0=?bhH5# zCMLN%(Hq^|9Fyu&dr6*r*1i%+ZuJw0!uA$1JKtB#AIx~&PpMnUt@PirsB+1RY^OMu3XZ>c6!WAUN{7oHmN)%6KL#}|^DwHZiMCps z>U&te^@cr!Vg=a54q;$?i93nx3(n17e(%OW;_P*;0~D4zJMvcretr5Ix+&87_hiMq zX7y znCnxH2|1c?)$b>_3x!}@2(oK^E{D8c}}HEx;ATIHU} z%-yYe5?ACC(iQKHuZHg0litBifVI;N_~aRdgtCyBmzyYr3G|U8i#EnOz6*F@>8*S# zP%nfui#yP(ZjXBhlT+$BcFhiYcfSD1LoDGk+b%ka?%y=;qyu7`Ikv3cfIXdsFc(51 zSEu=Hd0GLbIsw`)6_{j1SH$jdiwHhGd0tadwX!64&Qj=};@!Q>`}OnkPD$WqveK=O zJzbMCQT4+jT@I;uGWIUA5s5xgY*Y`lY=th4_)fK6HXlY)+L&$|guGux87-!2x7)~a zNF#eSSeQvjNrETw0{S#4=OdB3FKYw6cqLbZ{ihE}j zsV^T^wUFg*0sF`)5~zYcngM=|VSs{K)@Uco+|lF$>>FE9_zXW^x#ta-3y$8ws6X9X z#Ffb_NvmWu(-Dy-@mRp)dIMT-k4`0Cx|?MOOtd~LY8rH$xoD94ufOI$vReg83e9m@ z!!&{O8%NoDrB{plD3K>c<^(1RHSy*QNM=%sG3wca=^TIZGDN38J}$Z})X>6n{?5;M zY!i?5Dp$Ct23;36=B4n|%lM3qJ`}*-o@43b^?(J}9wPWju#&6zYsgBmWg)w& zVs0YcBH8NfDhVf1J+DCtlD@zT3gb!dWbV@_me_uw3d?chg0@!&&ujPj& zT$8+m=WfFVPsqJAUTX81N^U3Rc?B7@pe3$`-G|iPQ8ULs{_lRAfaZ*z_=JShxA4S` z;{y_Sy_D2w?s#pSR|vYA-7IOcpUh+CwQg^UT1;)Knv=P4>G4EQPbS8-22*D0nz+zNm#n;w>-cDN0gjP+*2;svCLN{_(am{KZzL z7rZPFbd@u|l)F{!N^P{c4a4>jkz=d5IMySDmop{&&@c@yS8M6n&#l9EuKPRhLvS(O7-RaF#m2cQy#<2z5todOj72*hN_G$dVc_$To z_B-RBu^VRU5x*C{^|zY641MK!gh;Mg`9YL~Cpj{&fVA!vhRMF%yLXN+;6Z=_4u_gZ zm8N<;VyK?r#$zSbaCwhS-gv{?-zc)Q@Ka46o{BH++0>Lp9T6kcpe6SO_r^9u;qx31 zi7P^`#yY1&lJtUHUTGdE0^1{_w#u9s&hsP;kR$snAXnbznaEnI2|{Em!h+s5Uazv@ z-z?#eYXQ-t&t7jAI_~QC@=Lr?M_8I`5FpQ@jiX1ho_v|jJ2DHl zCvq?lU#jt3WMS}Lp?Sss70+@=B^PS5QE7WOLf@lPnQCS!cYQWSpPB(ryFxq|t0a_| zIP**s!FrEeXmE9jgsaDwt8&_)#$W;5 zMYl*Kr_&&ZN3-hgHo-%ga=1vb9bBJ3yg43!J!X{dHw3Q*-_=2KRV}O|@k}^*ezL@?wmJTiaU#G-DnN#yy1(UzjVg=%kyYHr$rFURoma@X z4>|yXjmZ#9Pt=bk?-f;_qW`($S>d7JL5?LRt$re6KB|GoH##{{3k3Qrlw{!m3ZLb0 zwDynkV4T_{PP}QrGke|N9aH<|1GkXI<>gR@a~oS+aEK60I9h6egMUge?$UJL7Bg3& zhvwC1*Td>A^V4l1wKM260tSO12Q(pB`&lH{RamNkDP~0H6r$>V5t{3(cF*cS=bb`M z{k^xft|#$@1RT#^d%lV=>fv(QL12Aw5Q*sV;KwgF%J)GEOTyl;(E+nX^tm{9To@ zI3tP$yZ*{sVq))Z9!K{6HwER}lOxoAn|?IKF?nMcE0X7BGTk`bOV39s^Unw$bt5B+K*=Z&UM{Z}LWXXrDm(7LA#oq( z9Kel(!No-z@(_*OAfgXS6ECTCm*s`{-obGU`EUynuNaPe5%r*r!%o%1no~Xhgw(y; zNev`vuzPm{k$}%^`8>6K3vyxQiOs%6gEeqz=}O0w$oZx_tetN_Jq>p)r0R^#_rm1M zvsJrSG>G3J@K0J(a!sBOcDPQ|hKp+HXOtR`oPD zS)E?~xoe!nqn|>;tq9;e<7C+b(35S|+a%@38FP5~U$~m^dEVa9xJ^iwLpS!x@ z%&wi43}5y{_b|7?N|0XZZc3P271P0jXPM|`{u2eLe+fv~?u1l{EcY)Hg{0RBPx5YR zv=>-bT)3YP>-{7Yla%H`jscpOk3_|Y3DEEUsOEgU&i#3x<|%B9+fAe6tb1pj;ZJsl z__r3wck+0 zVKQNWogboEutb~2B;!dU60KU{P{;$5MTH-lKaG!#npgy+GMXKBKQiW<%hj+U;2^NS z!fM!>Z99tXTE@vbPx3~EgvAs^C<%RdiahOE`7`K6;YH97o&uhYPFq44l3F(HJV#10 zerOWVA0onDmg%h!#1~08kvGXWBI#%+jz#G;wnY^jNcZlL_GJ>s3vxZ=w$0i|M?`2K zF$o?gEbcC!4}nkusRvn)Cr>8jntTdDC^Oi*;KsaU1V~=l)E={IS4ci<$N26E5%=mz z;|;T;Bv|Z$ccN%Ja1R4BBmlKV8aJCtJ$}1v6dgHa`~f!l1+hItBS@nNcE zFkSlNSasfY=y(GVr40EzR1+#_Wjo0>mZRF0U_VvkIB9iNs_XRaQ{hp03XdrGeSkX&XZTqPR%c5 zzZQ6~b_#rc%u8u08}Z3S0wY=W(rFLFsAchx%Vv3MZ1EnX>y=`pDCi$GzuxPC#fTgO zGTt6pa?@3-HRc-1Bh$52O`WQB`?57C|Ob(hhXvx+d{;25`8!_Uwn8|RUWxhWr4ntfzBd9+hOkW1Pp|)faVG#_1P;A z$hz4c$S2P`IooVN48PDbBt!j8|k1+0g@y4)XLS!spvh*Tc}b-NCh- zAM#>3PlK7O8(!~SZSL^ma=`X+3*Yi5Fz4we$&IoJb%akE&6uIXH&btp<4%v+XsexP z1$1k9y=TKGVS!5|((B6lh4(D+#D`s?{Gje&vmr11zPO+K>4)vZRR=Bt%PDczD@>J? zEE32uf!GfDhWxeQ;xa5BrW1COcQ?I~Z0e;5@=(ZKLp%!u`J37??bmVok5cuqSUSUU zL)x|Pzs@?17?5ziPyg{A8R=bMiDhBW#mYpQP;Bxi8mCDeI*anDR;IizvouG82Y!)Jsb zC%U)RgeqW=ehMFKyEKKxu8-%;ek)6^31r_Zg~kt63CFaN2l=#cl5;c*Oso@&#*mD? z4rlNt6D8vF@vL#zXeM!{zSq0CU^F7_Sf$8U_O_@fvTKg3dJ)#drlEmdORx;Iy|x@DFYp<6N~nTlq3|a^-V?H*eq}E++N~g z$x#om=dC#1!JjA$&YR&wTg=j*$AzDmzq2--jsL1-f>!W0vRoQ@5v@9dOw>+h3u7|& zV>j8YPaVonNxZ={#qq5H1;IjiP{S)v1vN3JRT95!FnQxSjLV#XEe=&ze0JxU%Q9^} zOiWDIi@`+%rvT?(J4^YvFevf)`-+i$RpjResA2+E*y}krtQhg&5_2oTq z0=GduVZmlY(ny`Ru;}hSA^|5)^6+m`M^igl6$lu`x72b~q1}d_jWQ$&>1yQqq}3od z!I35ps=kIaW{8-trJKu*|){2LhdxFLawJjvOx)wBpi3 z$uq3P8l^qh?6b9<$YOj|#9?Bd^?Yj0z!PYMQdnnCTib=fVb41+u__SreYdUV@5La> z&`Oe;u@XuZg|UG$wUK{ZR!pgGyce!fX1KSx34>ns>?o=$5I%tq8a^CQOzogot4}CP ziZsi;E8#&YW)7NQ5*(UAMJqJiK@LZTydwm7!KY3I-|A+#N-{E6YZE-wr zgstfM)gpMqA|*oZ_h+q}Z`+`yRaAQ>J$U-Bxh=?>Lwk!VcT$`I6>F|fLfk;t(=4f)}NP|+b|0{>|uLO~Aa6WM00S@uV5+&`qk5>cT_u(=vFV#cIexavb|ed-FXDVHKu z@81`m&bzIH64rtcz{!uv^A^$UA%8{OS9HiKoKa7KqgAJ0q~m(H8`WeJM7Qz10PS68 z__X?3w80fWmlIU9X$$jg$f<6M&m7c{PFvSoyq4-&%_Nk#?X9l}QW|$X8x)JP3!UHZ zUXn^UTAP_%$kT9|A_K$0C#D)ZMcaxIa@EOeEF041JduFWF5};(N1O8EKY!D*R;urHLMNzHRCxw=hf`yw^@S1|mm zKsL|-LAjL)CuD=JR%voeh=L&_z}6L3b~6SDot>*7q~PQ|a*cq8~w4}Z;Dqh%Z93vzep zMKMq5<#$t%f(7w6v15>BhFJP)@4+PBv`)XY*0$eG6^od6Iepo%eN1i#gPSmFTi4>< zjh)H(NhWn1StMS-=~mD|r+zF+Rf>?U1>WdHlQ-W9`SA__?BSV%El3ah)c--Fl#AINhSB9lV&&@XSNA(EWHA z8`2KmsmOkf$s8$AA=q&IE~rCs9~PsM&uK6plHuRs zxSNTv-97jzhJVed^>}T%Av5I%uk(NwNpq&la|&qEHIM6yn>Tll|@{G%(HPkM_;i$Qjkk<;x0v ziHmSN&zqqyHniLQ_viFKc}6;m$a$g_>Re>z+qbM3ir+}gTnY@a!OT7~s&1P*x+2p3 zk=i}j5jst;)+$wtajWe!I*MU3pK5D3ki+MH(Q)K4HU6T`FB4{#7j#(hW-YUuh8hFy z2Lw)h3c(r)CXDY>nuxuI@;Piz-&(ii9{(J3%LkV5LuchBqj27@1)l0lnV4Stetaa{ zw$C(;v>kiJO5PTb859Au>!CpE>~10`qj-QTx0*JEopjQA-Wl{li8YZy*Q$9?%f?EhBO?#b5fu*f;7g`5A*VNQ!KEY ztDCH_rx+CoZ>!AYX;CP16@&8tx504NPh5Mx%^A1A&-d-=Y^E}$xToXF6K$xOeFOE0 zb4+Lg5p|cFAbf!>a)>XUu%_*{RK1ye?CawJ$L+(qm4jk!G<*}p{==ttJHnj~acm2@p0@mn(A@0j3Y}h| zT4>I1h9~6+easPNX?Ni;aJ?n%dx*`GnAyiO!wW<~)e6pNx<8~u7!K~mdA!Of*?DJ} z(ms=g^>avD!WF3i`H$;XRlOx*_eiXL%hq@m1W(n7-sz66ejO8d+dKW$JmGP};HZc9 z(>(JC(T=(E&p$1~()sP5&yrJW%mB#>rt#m$wzSOG8nXen%SzkFjHwpQ+DDBU$M=qh z?~S*CizDdCQre(cjCQK{vE8^^j<8rJ6E}lQN(3yzzCTqD5=iqF~0jA&z0@GRt1WO zu%kuLiWV9X3%W~-eAxNIYt5fF`>O5gmJ2?WhbLGAh#Llkh&vOxv<&392Bl5rD;?a` z9zu0lW}0tiv>IKwcrZwe567EV(YVlw6%mzNyfyn8+t1jN*HPWCTQLj!M{kOZWkemT zQ@QL>mS(gsX4WlvjpSaoor-J;rn?_|F173@+ghEUCi7!{dmAji)g13b-RZ5f>Ue(yq_9xHtA2Y!29RJ(y4pm7->o z^x7wD_4F=DoeqYv8*9|K+T+9a+%uMb^7x?SBsM!#yKht~s6|s3Yy`^Ii9%m(-Z*rM z_y|uD$I!prGT-e9nB@I=jUrSeo=`9RIeYZPxscLNe&cff)_;&s-j{If^2+YO)r_`w zAO73XOx%|bvCpbEY!8cy#OLf@g+=~A@`^||L?-{B)P$Fej#|7yPZ8O9>fX}3>ExRC zcrBTkcun$e&-O1J2nytNK-*NuO2%a$wa}8I%|2dCkMu(hPY=fJEU8`1Jbba(+!|=V zKd5LQmO*N@(4fdfG|`nPh)=&}U+fbiBz%Ipr3M3gd$Opjknj}I5K^V=%{Py<|7jGc zJ23zzW)h)f!M;R;cwaoq$7_TR_IKkjWPR(7QQOYsu8mr#JKXYN0Lq5Fb-ww@nRb8f zRC{>fqSPI&AfR8Z*nmT^Cr|!~1;#1z3uehsT4&YPlG>eHyNY=--_*qHYxcRBmS)%Z(%=>j?g3gmpx!GhWXg#S4xL{unl;w|r-diL0pK%{*?@7$&3 z?u&q}N;HgUXn?HOy`lrm6~E^dcT>hk>FLX9QY#RrKzdum7a++^v<)1kzwdMaS?)>T zfar})&ZNl=+GhI!21FoX0NBika9QHYm4ixcTo;86mmtG6VvjPtcAA`!?NSp? zOQH4BkXzHAUEV@V2&=0(s=o!Cd>9M7VL&gA%jNQ=e26|lFMAW1jKkh~ru7C@Qog85 z=D88%6~@E?a5ElTE9H~;6}X!7`l@F-_+e#-x-S6x?Y<{2{^SE3n|E+RGza8ZqymJY zhO4{)m*`yZ84njx%7=p~b98Z2A({a!_j)>9q4Trg_X5M3^&2;yL&@|I7e zk3|E{c0t47HuxRPi<9qQL9JqDl6&Uk_G)%*mIP3`3gqtj&q zpKU2`67Z~HKoOU*k{iw54AY{(HiISupj$_qgxXqn~n0 z`#_L!4+a?-rIj|3Q6MX~Zu`4XO-Xt;;VKaIg<_sfaR{g~9zA`oEnK`Q=<~qeIl&=$9#@>5pD4kTV#Pu9JD4rI9qYQ=S%P+wE_rCx6#Q88~y*9(x7yu_eFb@#& z{t##O-*XGUkdc)G2T)sgFHIU+dzj!#D1-L71}t#;>wOd}!&p$oRf!B{Py&@dRMNdPfqA z;90Lu4Ueh(klfP$zhwY`AjboUKv0W*D;u~#D*l0PG

v!XQb6HST}A%e`>a+y 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 literal 0 HcmV?d00001 diff --git a/noir/noir-repo/docs/static/img/tooling/profiler/brillig-trace-opt-32.png b/noir/noir-repo/docs/static/img/tooling/profiler/brillig-trace-opt-32.png new file mode 100644 index 0000000000000000000000000000000000000000..cab5f3b12f26b285fab0ef31034f2a91065c3bcc GIT binary patch literal 114318 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%0AKGcl$PT#g2(wX_+cF=d`4^#ZBt?bA06neb&Tl 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

v!XQb6HST}A%e`>a+y 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 literal 0 HcmV?d00001 diff --git a/noir/noir-repo/docs/static/img/tooling/profiler/brillig-trace-opt-32.png b/noir/noir-repo/docs/static/img/tooling/profiler/brillig-trace-opt-32.png new file mode 100644 index 0000000000000000000000000000000000000000..cab5f3b12f26b285fab0ef31034f2a91065c3bcc GIT binary patch literal 114318 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%0AKGcl$PT#g2(wX_+cF=d`4^#ZBt?bA06neb&Tl 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